1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="fo-verify" content="7c1b2d98-52dc-49f3-95fc-792906ed2a9b" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> 8 <meta http-equiv="Pragma" content="no-cache"> 9 <title>CardCompass â Find Your Perfect Credit Card</title> 10 11 <!-- Favicon & PWA --> 12 <link rel="icon" href="images/logo-icon.svg" type="image/png"> 13 <link rel="icon" href="favicon.svg" type="image/svg+xml"> 14 <link rel="manifest" href="site.webmanifest"> 15 <meta name="theme-color" content="#071E4A"> 16 17 <!-- SEO --> 18 <meta name="description" content="CardCompass finds your best credit card in 60 seconds. Compare cash back, travel rewards, and hotel points cards â personalized to your spending habits."> 19 <link rel="canonical" href="https://cardcompass.app/"> 20 21 <!-- Open Graph --> 22 <meta property="og:type" content="website"> 23 <meta property="og:url" content="https://cardcompass.app/"> 24 <meta property="og:title" content="CardCompass â Find Your Perfect Credit Card"> 25 <meta property="og:description" content="Stop leaving money on the table. CardCompass matches you with the best credit card for your spending in under 60 seconds."> 26 <meta property="og:image" content="https://cardcompass.app/og-image.png"> 27 <meta property="og:site_name" content="CardCompass"> 28 29 <!-- Twitter Card --> 30 <meta name="twitter:card" content="summary_large_image"> 31 <meta name="twitter:title" content="CardCompass â Find Your Perfect Credit Card"> 32 <meta name="twitter:description" content="Find the best credit card for your spending in 60 seconds."> 33 <meta name="twitter:image" content="https://cardcompass.app/og-image.png"> 34 35 <!-- Structured Data --> 36
36<script type="application/ld+json"> 37 { 38 "@context": "https://schema.org", 39 "@type": "Organization", 40 "@id": "https://cardcompass.app/#organization", 41 "name": "CardCompass", 42 "legalName": "CardCompass LLC", 43 "alternateName": ["Card Compass", "cardcompass.app"], 44 "url": "https://cardcompass.app", 45 "logo": "https://cardcompass.app/images/logo-icon.svg", 46 "description": "CardCompass (cardcompass.app) is a free credit card comparison website run by CardCompass LLC in the United States. It is a website, not a mobile app or browser extension.", 47 "contactPoint": { "@type": "ContactPoint", "email": "[email protected]", "contactType": "customer service" }, 48 "sameAs": ["https://www.instagram.com/cardcompassapp/", "https://www.facebook.com/cardcompassapp", "https://www.youtube.com/channel/UC-Th-AsoiJTPhfZ3lj3-9fg"] 49 } 50 </script>
50 51 52
52<script type="application/ld+json"> 53 { 54 "@context": "https://schema.org", 55 "@type": "WebSite", 56 "name": "CardCompass", 57 "alternateName": ["Card Compass", "cardcompass.app"], 58 "url": "https://cardcompass.app", 59 "publisher": { "@id": "https://cardcompass.app/#organization" }, 60 "description": "Credit card comparison and recommendation tool that helps consumers find the best credit card for their spending habits." 61 } 62 </script>
62 63 64 <!-- Google Analytics 4 (consent-gated) -->
vendor: 66 bytes, lines 64-65
64 65 <script async src="https://www.googletagmanager.com/gtag/js?id=
65G-CYNSKD3EBQ
vendor: 14 bytes, lines 65-66
65"></script> 66
66<script> 67 window.dataLayer = window.dataLayer || []; 68 function gtag(){dataLayer.push(arguments);} 69 gtag('js', new Date()); 70 // EU/UK visitors (Europe/* timezone): opt-in (GDPR). Everyone else: on by default with opt-out (US state law model). 71 gtag('consent', 'default', (function () { var v; try { v = localStorage.getItem('cc_consent') || (/^Europe\//.test(Intl.DateTimeFormat().resolvedOptions().timeZone) ? 'denied' : 'granted'); } catch (e) { v = 'denied'; } return { analytics_storage: v, ad_storage: v, ad_user_data: v, ad_personalization: v }; })()); 72 gtag('config', 'G-CYNSKD3EBQ'); 73 </script>
73 74
74<script src="/meta-pixel.js?v=1" defer></script>
74 75 76 <link rel="preconnect" href="https://images.unsplash.com"> 77 <link rel="preconnect" href="https://fonts.googleapis.com"> 78 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 79 <!-- Inter and Font Awesome load without blocking first paint (media="print" until loaded). Text paints in "Inter Fallback" (Arial resized to Inter's metrics, below) so the swap doesn't shift layout; no Font Awesome icon is essential above the fold. --> 80 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" media="print" onload="this.media='all'"> 81 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" media="print" onload="this.media='all'"> 82 <noscript> 83 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> 84 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer"> 85 </noscript> 86 <style> 87 @font-face { font-family: 'Inter Fallback'; src: local('Arial'), local('ArialMT'), local('Liberation Sans'), local('Helvetica'); font-weight: 100 599; size-adjust: 107.12%; ascent-override: 90.44%; descent-override: 22.52%; line-gap-override: 0%; } 88 @font-face { font-family: 'Inter Fallback'; src: local('Arial Bold'), local('Arial-BoldMT'), local('Liberation Sans Bold'), local('Helvetica Bold'); font-weight: 600 900; size-adjust: 107.12%; ascent-override: 90.44%; descent-override: 22.52%; line-gap-override: 0%; } 89 </style> 90 <!-- cards-data.js and recommend-engine.js load at the end of <body>, just above the script that reads them, so they don't block first paint --> 91
91<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
91 92 <link rel="stylesheet" href="/styles.css?v=2"> 93 <style> 94 body { font-family: 'Inter', 'Inter Fallback', -apple-system, BlinkMacSystemFont, sans-serif; } 95/* âââ NAVBAR âââ */ 96 .navbar { position: sticky; top: 0; z-index: 1000; background: var(--primary-dark); box-shadow: 0 2px 12px rgba(0,0,0,0.35); } 97 .nav-inner { max-width: 1280px; margin: 0 auto; padding: 0 24px; display: flex; align-items: center; height: 64px; gap: 28px; } 98 .logo { display: flex; align-items: center; gap: 10px; text-decoration: none; flex-shrink: 0; } 99 .logo-icon { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } 100 .logo-icon img { width: 100%; height: 100%; object-fit: cover; display: block; } 101 .logo-text { font-size: 20px; font-weight: 800; color: #fff; letter-spacing: -0.5px; } 102 .logo-text span { color: var(--accent-light); } 103 .nav-links { display: flex; align-items: center; gap: 2px; flex: 1; } 104 .nav-item { position: relative; } 105 .nav-link { display: flex; align-items: center; gap: 5px; padding: 8px 14px; color: rgba(255,255,255,.85); font-size: 14px; font-weight: 500; text-decoration: none; border-radius: 6px; transition: all .2s; white-space: nowrap; cursor: pointer; background: none; border: none; font-family: inherit; } 106 .nav-link i { font-size: 10px; opacity: .7; } 107 .nav-link:hover { color: #fff; background: rgba(255,255,255,.1); } 108 .dropdown { position: absolute; top: calc(100% + 8px); left: 0; background: #fff; border-radius: var(--r-md); box-shadow: var(--shadow-lg); padding: 12px; min-width: 230px; opacity: 0; visibility: hidden; transform: translateY(-8px); transition: all .2s; border: 1px solid var(--border); max-height: calc(100vh - 80px); overflow-y: auto; } 109 .nav-item:hover .dropdown { opacity: 1; visibility: visible; transform: translateY(0); } 110 .dropdown a { display: flex; align-items: center; gap: 10px; padding: 9px 12px; color: var(--text-dark); text-decoration: none; font-size: 14px; border-radius: var(--r-sm); transition: background .15s; } 111 .dropdown a:hover { background: var(--bg-light); color: var(--primary); } 112 .dropdown a i { width: 18px; color: var(--accent); font-size: 13px; } 113 .nav-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; } 114 .btn-outline { padding: 8px 18px; border: 1.5px solid rgba(255,255,255,.4); color: #fff; background: transparent; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all .2s; text-decoration: none; font-family: inherit; } 115 .btn-outline:hover { border-color: #fff; background: rgba(255,255,255,.1); } 116 .btn-primary { padding: 8px 18px; background: var(--accent); color: #fff; border: none; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all .2s; text-decoration: none; font-family: inherit; } 117 .btn-primary:hover { background: var(--accent-light); transform: translateY(-1px); } 118 119 /* âââ SHARED âââ */ 120 .section { padding: 76px 24px; } 121 .section-alt { background: var(--bg-light); } 122 .section-inner { max-width: 1280px; margin: 0 auto; } 123 .section-label { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 1.5px; color: var(--accent); margin-bottom: 10px; } 124 .section-title { font-size: 36px; font-weight: 800; color: var(--text-dark); letter-spacing: -.5px; line-height: 1.2; } 125 .section-sub { font-size: 16px; color: var(--text-gray); margin-top: 10px; } 126 .section-hd { margin-bottom: 44px; } 127 128 /* âââ COMPARISON TABLE âââ */ 129 .comp-section { background: var(--primary-dark); padding: 76px 24px; color: #fff; } 130 .comp-section .section-title { color: #fff; } 131 .comp-section .section-sub { color: rgba(255,255,255,.6); } 132 .comp-section .section-label { color: var(--accent-light); } 133 134 /* âââ CATEGORY TABS (Best Cards section) âââ */ 135 .comp-tabs-wrap { overflow-x: auto; scrollbar-width: none; margin-bottom: 28px; padding-bottom: 4px; } 136 .comp-tabs-wrap::-webkit-scrollbar { display: none; } 137 .comp-tabs { display: flex; gap: 8px; white-space: nowrap; } 138 .comp-tab { padding: 9px 18px; border: 1.5px solid rgba(255,255,255,.2); background: rgba(255,255,255,.06); color: rgba(255,255,255,.7); border-radius: 100px; font-size: 13px; font-weight: 600; cursor: pointer; transition: all .2s; font-family: inherit; white-space: nowrap; } 139 .comp-tab:hover { background: rgba(255,255,255,.14); color: #fff; border-color: rgba(255,255,255,.4); } 140 .comp-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); } 141 .comp-table-loading { text-align: center; padding: 40px; color: rgba(255,255,255,.45); font-size: 15px; } 142 .comp-table { background: rgba(255,255,255,.05); border-radius: var(--r-lg); overflow: hidden; border: 1px solid rgba(255,255,255,.1); } 143 .comp-header, .comp-row { display: grid; grid-template-columns: 2.2fr 1.1fr 1fr .9fr auto; gap: 16px; padding: 18px 28px; align-items: center; } 144 .comp-header { background: rgba(255,255,255,.08); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: rgba(255,255,255,.45); } 145 .comp-row { border-top: 1px solid rgba(255,255,255,.07); transition: background .2s; cursor: pointer; } 146 .comp-row:hover { background: rgba(255,255,255,.06); } 147 .comp-info { display: flex; align-items: center; gap: 14px; } 148 .comp-card-img { width: 90px; height: 57px; border-radius: 8px; overflow: hidden; flex-shrink: 0; position: relative; box-shadow: 0 3px 10px rgba(0,0,0,.4); } 149 .comp-card-img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: 8px; display: block; } 150 /* Brand-accurate card badges â Chase Sapphire (navy), Amex Gold (gold), Cap One Venture X (dark), Discover (orange), Citi (blue) */ 151 .ci-chase { background: linear-gradient(135deg,#0A2342 0%,#0F3D70 100%); border:1px solid rgba(255,255,255,.18); } 152 .ci-amex { background: linear-gradient(135deg,#C9951A 0%,#9A7215 55%,#C9951A 100%); } 153 .ci-citi { background: linear-gradient(135deg,#003B70 0%,#0057A8 100%); } 154 .ci-cap { background: linear-gradient(135deg,#1A1A2E 0%,#2D1B4E 100%); border:1px solid rgba(209,48,39,.5); } 155 .ci-disc { background: linear-gradient(135deg,#F76F20 0%,#E85D11 100%); } 156 .comp-name { font-size: 15px; font-weight: 600; } 157 .comp-sub { font-size: 12px; color: rgba(255,255,255,.45); margin-top: 2px; } 158 .comp-cell { font-size: 14px; color: rgba(255,255,255,.82); font-weight: 500; } 159 .comp-cell .hi { color: var(--accent-gold); font-weight: 700; } 160 .comp-cell .tiny { font-size: 11px; color: rgba(255,255,255,.4); } 161 .comp-cell .badge { background: rgba(30,127,212,.25); color: var(--accent-light); padding: 3px 9px; border-radius: 5px; font-size: 12px; font-weight: 600; } 162 .comp-btn { background: var(--accent); color: #fff; border: none; border-radius: 6px; padding: 9px 18px; font-size: 13px; font-weight: 600; cursor: pointer; transition: all .2s; font-family: inherit; white-space: nowrap; } 163 .comp-btn:hover { background: var(--accent-light); transform: translateY(-1px); } 164 /* âââ COMP PAGINATION & FOOTER âââ */ 165 .comp-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 24px; gap: 16px; flex-wrap: wrap; } 166 .comp-pagination { display: flex; align-items: center; gap: 6px; } 167 .comp-pager-btn { width: 36px; height: 36px; border-radius: 8px; border: 1.5px solid rgba(255,255,255,.2); background: rgba(255,255,255,.06); color: rgba(255,255,255,.75); font-size: 13px; cursor: pointer; transition: all .2s; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; } 168 .comp-pager-btn:hover:not([disabled]) { background: rgba(255,255,255,.15); color: #fff; border-color: rgba(255,255,255,.4); } 169 .comp-pager-btn[disabled] { opacity: .28;
169 cursor: not-allowed; } 170 .comp-pager-num { width: 36px; height: 36px; border-radius: 8px; border: 1.5px solid transparent; background: transparent; color: rgba(255,255,255,.6); font-size: 14px; font-weight: 600; cursor: pointer; transition: all .2s; display: inline-flex; align-items: center; justify-content: center; font-family: inherit; } 171 .comp-pager-num:hover { background: rgba(255,255,255,.08); color: #fff; } 172 .comp-pager-num.active { background: var(--accent); color: #fff; border-color: var(--accent); } 173 .comp-see-all { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,.1); border: 1.5px solid rgba(255,255,255,.25); color: #fff; padding: 9px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; text-decoration: none; transition: all .2s; white-space: nowrap; } 174 .comp-see-all:hover { background: rgba(255,255,255,.18); border-color: rgba(255,255,255,.45); transform: translateY(-1px); } 175 176 /* âââ FOOTER âââ */ 177 footer { background: var(--primary-dark); color: rgba(255,255,255,.65); padding: 60px 24px 28px; } 178 .footer-inner { max-width: 1280px; margin: 0 auto; } 179 .footer-top { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 48px; padding-bottom: 48px; border-bottom: 1px solid rgba(255,255,255,.1); } 180 .footer-brand p { font-size: 14px; line-height: 1.75; margin-top: 14px; color: rgba(255,255,255,.5); } 181 .footer-col h4 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: rgba(255,255,255,.9); margin-bottom: 18px; } 182 .footer-col a { display: block; font-size: 14px; color: rgba(255,255,255,.5); text-decoration: none; margin-bottom: 11px; transition: color .2s; } 183 .footer-col a:hover { color: rgba(255,255,255,.9); } 184 .footer-bottom { padding-top: 26px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: rgba(255,255,255,.3); flex-wrap: wrap; gap: 12px; } 185 .footer-bottom a { color: rgba(255,255,255,.3); text-decoration: none; } 186 .footer-bottom a:hover { color: rgba(255,255,255,.6); } 187 .foot-links { display: flex; gap: 20px; } 188 189 /* âââ SCROLL TOP âââ */ 190 .scroll-top { position: fixed; bottom: 24px; right: 24px; width: 46px; height: 46px; background: var(--primary); color: #fff; border: none; border-radius: 50%; font-size: 17px; cursor: pointer; box-shadow: var(--shadow-md); display: flex; align-items: center; justify-content: center; transition: all .2s; z-index: 999; } 191 .scroll-top:hover { background: var(--primary-light); transform: translateY(-2px); } 192 193 /* ââââââââââââââââââââââââââââââââââââââââ 194 NEW CSS ADDITIONS 195 ââââââââââââââââââââââââââââââââââââââââ */ 196 197 /* âââ REVEAL ANIMATIONS âââ */ 198 .reveal { opacity: 0; transform: translateY(28px); transition: opacity .65s ease, transform .65s ease; } 199 .reveal.visible { opacity: 1; transform: translateY(0); } 200 .reveal-d1 { transition-delay: .08s; } 201 .reveal-d2 { transition-delay: .16s; } 202 .reveal-d3 { transition-delay: .24s; } 203 .reveal-d4 { transition-delay: .32s; } 204 .reveal-d5 { transition-delay: .40s; } 205 .reveal-d6 { transition-delay: .48s; } 206 .reveal-d7 { transition-delay: .56s; } 207 208 /* âââ HAMBURGER âââ */ 209 .hamburger { display: none; flex-direction: column; justify-content: center; align-items: center; width: 40px; height: 40px; background: none; border: none; cursor: pointer; gap: 5px; padding: 6px; border-radius: 6px; transition: background .2s; margin-left: auto; flex-shrink: 0; } 210 .hamburger:hover { background: rgba(255,255,255,.1); } 211 .hamburger span { display: block; width: 22px; height: 2px; background: rgba(255,255,255,.85); border-radius: 2px; transition: all .3s ease; } 212 .hamburger.open span:nth-child(1) { transform: rotate(45deg) translate(5px, 5px); } 213 .hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); } 214 .hamburger.open span:nth-child(3) { transform: rotate(-45deg) translate(5px, -5px); } 215 216 /* âââ MOBILE NAV âââ */ 217 .nav-links.mobile-open { display: flex !important; flex-direction: column; align-items: flex-start; position: absolute; top: 64px; left: 0; right: 0; background: var(--primary-dark); padding: 12px 16px 20px; border-top: 1px solid rgba(255,255,255,.1); box-shadow: 0 8px 32px rgba(0,0,0,.3); z-index: 999; max-height: calc(100vh - 64px); overflow-y: auto; } 218 .nav-links.mobile-open .nav-link { width: 100%; padding: 12px 16px; } 219 .nav-links.mobile-open .nav-item { width: 100%; } 220 .nav-links.mobile-open .dropdown { position: static; opacity: 1; visibility: visible; transform: none; box-shadow: none; background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.1); margin-top: 4px; } 221 .nav-links.mobile-open .dropdown a { color: rgba(255,255,255,.75); } 222 .nav-links.mobile-open .dropdown a:hover { background: rgba(255,255,255,.08); color: #fff; } 223 .nav-links.mobile-open .dropdown a i { color: var(--accent-light); } 224 @media (max-width: 640px) { .logo-text { display: none; } .nav-inner { gap: 8px; padding: 0 16px; } } 225 226 /* âââ NEWSLETTER âââ */ 227 .newsletter-section { background: var(--bg-light); padding: 64px 24px; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); } 228 .newsletter-inner { max-width: 640px; margin: 0 auto; text-align: center; } 229 .newsletter-icon { font-size: 42px; margin-bottom: 16px; display: block; } 230 .newsletter-inner h2 { font-size: 28px; font-weight: 800; letter-spacing: -.5px; margin-bottom: 10px; } 231 .newsletter-inner > p { font-size: 16px; color: var(--text-gray); margin-bottom: 28px; line-height: 1.65; } 232 .newsletter-form { display: flex; gap: 10px; margin-bottom: 14px; } 233 .newsletter-input { flex: 1; padding: 13px 16px; border: 1.5px solid var(--border); border-radius: var(--r-sm); font-size: 15px; outline: none; transition: border-color .2s; font-family: inherit; background: #fff; } 234 .newsletter-input:focus { border-color: var(--accent); } 235 .newsletter-submit { padding: 13px 22px; background: var(--primary); color: #fff; border: none; border-radius: var(--r-sm); font-size: 15px; font-weight: 700; cursor: pointer; transition: all .2s; font-family: inherit; white-space: nowrap; } 236 .newsletter-submit:hover { background: var(--primary-light); transform: translateY(-1px); } 237 .newsletter-error { color: #DC2626; font-size: 13px; margin-bottom: 10px; display: none; } 238 .newsletter-meta { font-size: 13px; color: var(--text-light); display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; } 239 .newsletter-meta span { display: flex; align-items: center; gap: 5px; } 240 .newsletter-success { display: none; background: #ECFDF5; border-radius: var(--r-md); border: 1px solid #6EE7B7; padding: 24px; } 241 .newsletter-success h3 { font-size: 18px; font-weight: 700; color: #059669; margin-bottom: 6px; } 242 .newsletter-success p { font-size: 14px; color: #065F46; } 243 .newsletter-already { display: none; background: #EBF4FF; border-radius: var(--r-md); border: 1px solid var(--accent-light); padding: 16px 20px; font-size: 14px; color: var(--primary); font-weight: 500; } 244 245 /* âââ CARD DRAWER âââ */ 246 .drawer-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.52); z-index: 1100; opacity: 0; visibility: hidden; transition: all .3s; } 247 .drawer-overlay.open { opacity: 1; visibility: visible; } 248 .drawer { position: fixed; top: 0; right: 0; width: 440px; height: 100vh; background: #fff; z-index: 1101; overflow-y: auto; transform: translateX(100%); transition: transform .35s cubic-bezier(.4,0,.2,1); box-shadow: -8px 0 40px rgba(0,0,0,.2); } 249 .drawer.open { transform: translateX(0); } 250 .drawer-header { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: #fff; z-index: 2; } 251 .drawer-header span { font-size: 13px; color: var(--text-gray); } 252 .drawer-close { width: 32px; height: 32px; background: var(--bg-light); border: none; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 14px; color: var(--text-gray); transition: all .2s; }
253 .drawer-close:hover { background: var(--border); color: var(--text-dark); } 254 .drawer-body { padding: 24px; } 255 .drawer-card-hd { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; } 256 .drawer-card-img { width: 72px; height: 46px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; flex-shrink: 0; } 257 .drawer-card-name { font-size: 18px; font-weight: 800; line-height: 1.3; } 258 .drawer-card-sub { font-size: 13px; color: var(--text-gray); margin-top: 3px; } 259 .drawer-score-bar { display: flex; align-items: center; gap: 10px; background: var(--bg-light); border-radius: var(--r-sm); padding: 10px 14px; margin-bottom: 20px; } 260 .drawer-stars { color: var(--accent-gold); font-size: 15px; letter-spacing: 1px; } 261 .drawer-score-val { font-size: 15px; font-weight: 700; } 262 .drawer-score-lbl { font-size: 13px; color: var(--text-gray); } 263 .drawer-divider { height: 1px; background: var(--border); margin: 18px 0; } 264 .drawer-details { display: flex; flex-direction: column; gap: 11px; } 265 .drawer-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; } 266 .drawer-lbl { font-size: 13px; color: var(--text-gray); font-weight: 500; } 267 .drawer-val { font-size: 14px; font-weight: 600; color: var(--text-dark); text-align: right; } 268 .drawer-val.gold { color: #D97706; } 269 .drawer-proscons { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; } 270 .drawerpc h5 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .8px; margin-bottom: 10px; } 271 .pros-title { color: #059669; } 272 .cons-title { color: #DC2626; } 273 .drawerpc ul { list-style: none; display: flex; flex-direction: column; gap: 8px; } 274 .drawerpc ul li { font-size: 13px; color: var(--text-gray); display: flex; gap: 7px; align-items: flex-start; line-height: 1.4; } 275 .drawerpc ul li span { flex-shrink: 0; font-size: 12px;
275 margin-top: 1px; } 276 .drawer-actions { display: flex; gap: 12px; position: sticky; bottom: 0; background: #fff; padding: 16px 24px; border-top: 1px solid var(--border); margin: 0 -24px -24px; } 277 .drawer-apply { flex: 1; padding: 13px; background: var(--primary); color: #fff; border: none; border-radius: var(--r-sm); font-size: 15px; font-weight: 700; cursor: pointer; transition: all .2s; font-family: inherit; } 278 .drawer-apply:hover { background: var(--primary-light); } 279 .drawer-learn { padding: 13px 18px; background: var(--bg-light); color: var(--text-dark); border: 1.5px solid var(--border); border-radius: var(--r-sm); font-size: 14px; font-weight: 600; cursor: pointer; transition: all .2s; font-family: inherit; white-space: nowrap; } 280 .drawer-learn:hover { border-color: var(--accent); color: var(--accent); } 281 282 /* ââââââââââââââââââââââââââââââââââââââââ 283 RESPONSIVE 284 ââââââââââââââââââââââââââââââââââââââââ */ 285 @media (max-width: 1024px) { 286 .nav-links { display: none; } 287 .hamburger { display: flex; } 288 .nav-actions { gap: 6px; } 289 .nav-actions .btn-primary, .nav-actions .btn-outline { padding: 7px 10px; font-size: 12px; white-space: nowrap; } 290 .navbar { position: relative; } 291 .comp-header { display: none; } 292 .comp-row { grid-template-columns: 1fr 1fr; gap: 10px; padding: 16px 20px; } 293 .comp-info { grid-column: 1 / -1; } 294 .footer-top { grid-template-columns: 1fr 1fr; gap: 36px; } 295 .drawer { width: 100%; max-width: 440px; } 296 } 297 @media (max-width: 768px) { 298 .section { padding: 52px 20px; } 299 .section-title { font-size: 26px; } 300 .comp-row { grid-template-columns: 1fr; } 301 .footer-top { grid-template-columns: 1fr; gap: 28px; } 302 .footer-bottom { flex-direction: column; align-items: flex-start; gap: 10px; } 303 .newsletter-form { flex-direction: column; } 304 .newsletter-submit { width: 100%; } 305 .drawer { width: 100%; } 306 } 307 308 /* âââ SKIP LINK âââ */ 309 .skip-link { position: absolute; top: -100%; left: 16px; background: var(--accent); color: #fff; padding: 10px 18px; border-radius: 0 0 var(--r-sm) var(--r-sm); font-size: 14px; font-weight: 700; text-decoration: none; z-index: 9999; transition: top .2s; } 310 .skip-link:focus { top: 0; } 311 312 /* âââ ADVERTISER DISCLOSURE BAR âââ */ 313 .adv-bar { background: #FFFBEB; border-bottom: 1px solid #FDE68A; padding: 10px 24px; } 314 .adv-bar-inner { max-width: 1280px; margin: 0 auto; font-size: 13px; color: #78350F; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } 315 .adv-bar-inner i { color: #D97706; flex-shrink: 0; } 316 .adv-bar-inner a { color: #92400E; font-weight: 700; text-decoration: underline; } 317 .adv-bar-inner a:hover { color: #78350F; } 318 319 /* âââ COOKIE BANNER âââ */ 320 .cookie-banner { position: fixed; bottom: 0; left: 0; right: 0; background: var(--primary-dark); border-top: 1px solid rgba(255,255,255,.15); padding: 16px 24px; z-index: 2000; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; box-shadow: 0 -4px 24px rgba(0,0,0,.3); animation: slideUp .35s ease; } 321 .cookie-banner.hidden { display: none; } 322 @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } } 323 .cookie-banner p { font-size: 14px; color: rgba(255,255,255,.8); flex: 1; min-width: 200px; line-height: 1.5; margin: 0; } 324 .cookie-banner p a { color: var(--accent-light); text-decoration: underline; } 325 .cookie-actions { display: flex; gap: 10px; flex-shrink: 0; } 326 .cookie-accept { padding: 9px 20px; background: var(--accent); color: #fff; border: none; border-radius: 6px; font-size: 14px; font-weight: 700; cursor: pointer; transition: background .2s; font-family: inherit; } 327 .cookie-accept:hover { background: var(--accent-light); } 328 .cookie-decline { padding: 9px 16px; background: transparent; color: rgba(255,255,255,.65); border: 1px solid rgba(255,255,255,.25); border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all .2s; font-family: inherit; } 329 .cookie-decline:hover { border-color: rgba(255,255,255,.6); color: #fff; } 330 331 /* âââ USER NAV (logged-in state) âââ */ 332 .user-nav { position: relative; } 333 .user-btn { display: flex; align-items: center; gap: 9px; padding: 5px 12px 5px 6px; background: rgba(255,255,255,.1); border: 1.5px solid rgba(255,255,255,.22); border-radius: 100px; cursor: pointer; transition: all .2s; color: #fff; font-family: inherit; font-size: 14px; font-weight: 600; } 334 .user-btn:hover { background: rgba(255,255,255,.18); border-color: rgba(255,255,255,.45); } 335 .user-avatar-sm { width: 30px; height: 30px; background: var(--accent); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 800; color: #fff; flex-shrink: 0; letter-spacing: -.5px; } 336 .user-name-sm { max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 337 .user-btn .fa-chevron-down { font-size: 10px; opacity: .65; } 338 .user-dropdown { position: absolute; top: calc(100% + 10px); right: 0; background: #fff; border-radius: var(--r-md); box-shadow: var(--shadow-lg); border: 1px solid var(--border); min-width: 226px; padding: 10px; opacity: 0; visibility: hidden; transform: translateY(-8px); transition: all .2s; z-index: 2000; } 339 .user-nav.open .user-dropdown { opacity: 1; visibility: visible; transform: translateY(0); } 340 .user-dropdown-header { padding: 10px 12px 14px; border-bottom: 1px solid var(--border); margin-bottom: 8px; } 341 .user-dropdown-name { font-size: 15px; font-weight: 700; color: var(--text-dark); } 342 .user-dropdown-email { font-size: 12px; color: var(--text-gray); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 190px; } 343 .user-dropdown a { display: flex; align-items: center; gap: 10px; padding: 9px 12px; color: var(--text-dark); text-decoration: none; font-size: 14px; border-radius: var(--r-sm); transition: background .15s; } 344 .user-dropdown a:hover { background: var(--bg-light); color: var(--primary); } 345 .user-dropdown a i { width: 16px; color: var(--accent); font-size: 13px; text-align: center; } 346 .user-dropdown-divider { height: 1px; background: var(--border); margin: 8px 0; } 347 .user-signout { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 12px; color: #DC2626; background: none; border: none; font-size: 14px; cursor: pointer; border-radius: var(--r-sm); transition: background .15s; font-family: inherit; text-align: left; } 348 .user-signout:hover { background: #FEF2F2; } 349 .user-signout i { width: 16px; font-size: 13px; text-align: center; } 350 351 /* âââ PERSONAL MATCH BANNER (logged-in) âââ */ 352 .personal-banner { background: linear-gradient(135deg, #071E4A 0%, #0C2660 100%); border-bottom: 2px solid rgba(30,127,212,.6); padding: 13px 24px; } 353 .personal-banner.hidden { display: none; } 354 .personal-banner-inner { max-width: 1280px; margin: 0 auto; display: flex; align-items: center; gap: 16px; flex-wrap: wrap; } 355 .pb-greeting { font-size: 14px; color: rgba(255,255,255,.65); white-space: nowrap; flex-shrink: 0; } 356 .pb-greeting b { color: #fff; font-weight: 700; } 357 .pb-sep { width: 1px;
357 height: 26px; background: rgba(255,255,255,.14); flex-shrink: 0; } 358 .pb-label { font-size: 11px; color: rgba(255,255,255,.45); white-space: nowrap; flex-shrink: 0; font-weight: 600; text-transform: uppercase; letter-spacing: .8px; } 359 .pb-match { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; } 360 .pb-badge { width: 78px; height: 49px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 8px; font-weight: 700; flex-shrink: 0; overflow: hidden; } 361 .pb-card-name { font-size: 14px; font-weight: 700; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } 362 .pb-card-rate { font-size: 12px; color: rgba(255,255,255,.45); margin-top: 1px; white-space: nowrap; } 363 .pb-earn { font-size: 20px; font-weight: 800; color: var(--accent-gold); white-space: nowrap; flex-shrink: 0; line-height: 1; } 364 .pb-earn small { font-size: 10px; color: rgba(255,255,255,.4); font-weight: 400; display: block; margin-top: 2px; } 365 .pb-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; } 366 .pb-acct-btn { padding: 7px 13px; background: rgba(255,255,255,.1); color: rgba(255,255,255,.85); border: 1px solid rgba(255,255,255,.2); border-radius: 6px; font-size: 12px; font-weight: 600; text-decoration: none; transition: all .2s; white-space: nowrap; display: inline-flex; align-items: center; gap: 5px; } 367 .pb-acct-btn:hover { background: rgba(255,255,255,.18); color: #fff; border-color: rgba(255,255,255,.45); } 368 .pb-retake { padding: 7px 13px; background: var(--accent); color: #fff; border: none; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; transition: background .2s; font-family: inherit; white-space: nowrap; } 369 .pb-retake:hover { background: var(--accent-light); } 370 @media (max-width: 768px) { .pb-sep { display: none; } .pb-label { display: none; } .pb-earn { font-size: 17px; } } 371 372 /* âââ HERO: SPEND CALCULATOR âââ */ 373 .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } 374 .hx-inner { max-width: 1280px; margin: 0 auto; } 375 /* the hand: photo tinted into the navy, small line, five real cards fanned across the seam */ 376 .hx { position: relative; overflow: hidden; } 377 .hand { position: relative; overflow: hidden; background: var(--primary-dark); height: 490px; } 378 .hand::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 44px; z-index: 2; background: linear-gradient(to bottom right, transparent 49.5%, #fff 50.5%); } 379 .hand-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 60% 40%; } 380 .hand-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(7,30,74,.8) 0, rgba(7,30,74,.8) 150px, rgba(7,30,74,.68) 230px, rgba(7,30,74,.68) 290px, var(--primary-dark) 400px); } 381 .hand-copy { position: relative; z-index: 3; padding: 36px 24px 0; } 382 .hand-copy h1 { font-size: 26px; font-weight: 800; color: #fff; line-height: 1.3; letter-spacing: -.5px; } 383 .hand-actions { display: flex; align-items: center; gap: 22px; margin-top: 16px; flex-wrap: wrap; } 384 .hand-go { display: inline-flex; align-items: center; height: 46px; padding: 0 20px; border: 1.5px solid rgba(255,255,255,.55); border-radius: 6px; color: #fff; font-size: 15px; font-weight: 600; text-decoration: none; } 385 .hand-go:hover { border-color: #fff; background: rgba(255,255,255,.08); } 386 .hand-all { display: inline-flex; align-items: center; min-height: 44px; color: #fff; font-size: 15px; font-weight: 600; } 387 /* the fan sits outside the clipped band so its lowest cards cross the seam onto the white */ 388 .hand-fan { position: absolute; left: 50%; top: 250px; width: 1060px; height: 520px; margin-left: -530px; z-index: 2; pointer-events: none; } 389 .hf { position: absolute; transform-origin: 50% 160%; } 390 .hf img { display: block; width: 460px; height: 290px; object-fit: cover; border-radius: 24px; box-shadow: 0 8px 20px rgba(0,0,0,.5); } 391 .hf1 { left: 0; top: 70px; transform: rotate(-28deg); } 392 .hf2 { left: 150px; top: 22px; transform: rotate(-14deg); }
393 .hf3 { left: 300px; top: 0; z-index: 1; } 394 .hf4 { left: 450px; top: 22px; transform: rotate(14deg); } 395 .hf5 { left: 600px; top: 70px; transform: rotate(28deg); } 396 .hx-calc-hd { padding: 290px 24px 0; } 397 .hx-cue { display: flex; align-items: center; justify-content: center; gap: 13px; margin-bottom: 20px; font-size: 15px; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase; color: var(--text-light); } 398 .hx-cue span { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border: 1.5px solid var(--border); border-radius: 50%; color: var(--accent); flex-shrink: 0; } 399 .hx-calc-hd h2 { font-size: 52px; font-weight: 800; color: var(--primary-dark); line-height: 1.13; letter-spacing: -1.5px; font-variant-numeric: tabular-nums; } 400 .hero-badge { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,.11); border: 1px solid rgba(255,255,255,.2); color: var(--accent-light); padding: 6px 14px; border-radius: 100px; font-size: 13px; font-weight: 500; margin-bottom: 18px; } 401 .hero-badge[hidden] { display: none; } 402 .hx-body { display: grid; grid-template-columns: minmax(0, 11fr) minmax(0, 13fr); gap: 72px; padding: 4px 24px 56px; align-items: start; } 403 404 .hx-head { display: flex; justify-content: space-between; padding-bottom: 10px; border-bottom: 1.5px solid var(--primary-dark); font-size: 13px; color: var(--text-gray); } 405 .hx-row { display: grid; grid-template-columns: 124px minmax(0, 1fr) 92px; align-items: center; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--border); } 406 .hx-row-label { font-size: 15px; font-weight: 600; color: var(--primary-dark); } 407 .hx-row-label small { display: block; font-size: 12px; font-weight: 400; line-height: 1.3; color: var(--text-gray); } 408 .hx-seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; border: 1px solid #CBD5E0; border-radius: 6px; overflow: hidden; } 409 .hx-seg button { height: 44px; padding: 0; border: 0; border-left: 1px solid #CBD5E0; background: #fff; color: var(--primary-dark); font: 600 14px/1 inherit; font-variant-numeric: tabular-nums; cursor: pointer; } 410 .hx-seg button:first-child { border-left: 0; } 411 .hx-seg button:hover { background: var(--bg-light); } 412 .hx-seg button[aria-pressed="true"] { background: var(--primary); color: #fff; } 413 .hx-seg button:focus-visible, .hx-tabs button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; } 414 .hx-earn { text-align: right; font-size: 14px; color: var(--text-gray); white-space: nowrap; font-variant-numeric: tabular-nums; } 415 .hx-earn strong { color: var(--primary-dark); font-weight: 700; } 416 .hx-note { margin-top: 16px; font-size: 13px; line-height: 1.6; color: var(--text-gray); } 417 .hx-note a, .hx-legal a { color: inherit; } 418 419 .hx-tabs { display: flex; gap: 24px; border-bottom: 1.5px solid var(--primary-dark); } 420 .hx-tabs button { height: 44px; padding: 0; border: 0; border-bottom: 3px solid transparent; margin-bottom: -1.5px; background: none; color: var(--text-gray); font: 600 14px/1 inherit; cursor: pointer; } 421 .hx-tabs button[aria-pressed="true"] { color: var(--primary-dark); border-bottom-color: var(--accent); } 422 .hx-stage { position: relative; height: 296px; margin-top: 8px; } 423 .hx-art { position: absolute; display: block; } 424 .hx-art img { display: block; width: 100%; height: 100%; } 425 /* Issuer art is a flat JPEG with white corners: clip them. 8.25% = 5.2% x the card's 1.586 ratio. */ 426 .hx-art.flat img, .cc-thumb.flat img { object-fit: cover; border-radius: 5.2% / 8.25%; } 427 .hx-art.flat img { box-shadow: 0 2px 3px rgba(7,30,74,.25), 14px 30px 56px rgba(7,30,74,.3); } 428 .hx-art.asis img, .cc-thumb.asis img { object-fit: contain; } 429 .hx-art.asis img { filter: drop-shadow(8px 18px 22px rgba(7,30,74,.28)); } 430 /* The Amex Gold art is two cards on a white field: blend the white away, never clip or shadow it. */ 431 .hx-art.gold img, .cc-thumb.gold img { object-fit: contain; mix-blend-mode: multiply; } 432 .hx-win { left: 0; top: 30px; width: 352px; height: 222px; transform: rotate(-8deg); z-index: 3; transition: transform .25s ease; } 433 .hx-win:hover { transform: rotate(-8deg) translateY(-6px); } 434 .hx-win.gold { transform: none; } 435 .hx-r2 { left: 206px; top: 14px; width: 280px; height: 177px; z-index: 2; }
436 .hx-r3 { left: 318px; top: 42px; width: 280px; height: 177px; transform: rotate(9deg); z-index: 1; } 437 .hx-meta { font-size: 13px; color: var(--text-gray); } 438 .hx-name { margin-top: 2px; font-size: 28px; font-weight: 800; letter-spacing: -.6px; line-height: 1.2; } 439 .hx-name a { color: var(--primary-dark); text-decoration: none; } 440 .hx-name a:hover { text-decoration: underline; } 441 .hx-figrow { display: flex; align-items: baseline; gap: 12px; margin-top: 10px; flex-wrap: wrap; } 442 .hx-fig { font-size: 64px; font-weight: 800; letter-spacing: -2.1px; line-height: 1; color: var(--primary-dark); font-variant-numeric: tabular-nums; } 443 .hx-figlabel { font-size: 15px; font-weight: 600; color: var(--text-gray); } 444 .hx-recip { margin-left: auto; font-size: 14px; color: var(--text-gray); font-variant-numeric: tabular-nums; } 445 .hx-recip strong { color: var(--primary-dark); } 446 .hx-ledger { margin-top: 14px; border-top: 1px solid var(--border); font-size: 14px; font-variant-numeric: tabular-nums; } 447 .hx-ledger div { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; border-bottom: 1px solid var(--border); color: var(--text-gray); } 448 .hx-ledger strong { color: var(--primary-dark); white-space: nowrap; } 449 .hx-worth { margin-top: 10px; font-size: 13px; line-height: 1.5; color: var(--text-gray); } 450 .hx-next { display: flex; gap: 8px 24px; flex-wrap: wrap; margin-top: 12px; font-size: 14px; color: var(--text-gray); font-variant-numeric: tabular-nums; } 451 .hx-next strong { color: var(--primary-dark); } 452 .hx-actions { display: flex; align-items: center; gap: 18px; margin-top: 16px; flex-wrap: wrap; } 453 .hx-cta { display: inline-flex; align-items: center; height: 48px; padding: 0 26px; background: var(--primary); color: #fff; border-radius: 6px; font-size: 15px; font-weight: 700; text-decoration: none; } 454 .hx-cta:hover { background: var(--primary-light); } 455 .hx-actions span { font-size: 14px; color: var(--text-gray); } 456 .hx-links { display: flex; flex-wrap: wrap; gap: 4px 22px; margin-top: 6px; font-size: 14px; font-weight: 600; } 457 .hx-links a { display: inline-flex; align-items: center; min-height: 44px; color: var(--primary); } 458 .hx-legal { margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 12px; line-height: 1.5; color: var(--text-gray); } 459 460 /* âââ HOMEPAGE SECTIONS âââ */ 461 .hp { padding: 0 24px; } 462 .hp-inner { max-width: 1280px; margin: 0 auto; padding: 56px 0 64px; border-top: 1px solid var(--border); } 463 .hp-first .hp-inner { border-top: 0; } 464 .hp-hd { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; margin-bottom: 24px; } 465 .hp-hd h2 { font-size: 36px; font-weight: 800; letter-spacing: -1px; line-height: 1.2; color: var(--primary-dark); } 466 .hp-hd > a, .hp-hd > span { font-size: 15px; white-space: nowrap; } 467 .hp-hd > a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; color: var(--primary); } 468 .hp-hd > span { color: var(--text-gray); } 469 .cc-thumb { display: block; flex-shrink: 0; } 470 .cc-thumb img { display: block; width: 100%; height: 100%; } 471 .cc-thumb.flat img { box-shadow: 0 1px 2px rgba(7,30,74,.25), 4px 8px 16px rgba(7,30,74,.2); } 472 473 .tools { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 72px; align-items: start; } 474 .cmp-row { display: grid; grid-template-columns: 150px repeat(3, minmax(0, 1fr)); column-gap: 20px; align-items: baseline; padding: 13px 0; border-bottom: 1px solid var(--border); font-size: 15px; font-variant-numeric: tabular-nums; } 475 .cmp-row > span:first-child { font-size: 14px; color: var(--text-gray); } 476 .cmp-row > span + span { font-weight: 600; color: var(--primary-dark); } 477 .cmp-head { align-items: end; padding: 0 0 14px; border-bottom: 1.5px solid var(--primary-dark); } 478 .cmp-head > span:first-child { font-size: 13px; } 479 .cmp-card { display: flex; flex-direction: column; gap: 10px; font-weight: 700; line-height: 1.3; } 480 .cmp-card .cc-thumb { width: 132px; height: 83px; } 481 .quiz-q { padding-bottom: 14px; border-bottom: 1.5px solid var(--primary-dark); font-size: 17px; font-weight: 700; line-height: 1.4; color: var(--primary-dark); } 482 .quiz-opt { display: flex; align-items: center; justify-content: space-between; min-height: 52px; border-bottom: 1px solid var(--border); font-size: 15px; font-weight: 500; color: var(--primary-dark); text-decoration: none; } 483 .quiz-opt:hover { color: var(--accent); } 484 .quiz-foot { margin-top: 14px; font-size: 13px; color: var(--text-gray); } 485 486 .off-row { display: grid; grid-template-columns: 88px minmax(0, 1.1fr) minmax(0, 1.5fr) 96px 96px 104px; column-gap: 24px; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--border); font-size: 15px; color: var(--primary-dark); text-decoration: none; font-variant-numeric: tabular-nums; } 487 a.off-row:hover .off-name { text-decoration: underline; } 488 .off-head { padding: 0 0 8px; border-bottom: 1.5px solid var(--primary-dark); font-size: 13px; color: var(--text-gray); } 489 .off-row .cc-thumb { width: 88px; height: 55px; } 490 .off-name { font-weight: 700; line-height: 1.3; } 491 .off-issuer { display: block; font-size: 13px; font-weight: 400; color: var(--text-gray); } 492 .off-desc { color: var(--text-gray); line-height: 1.4; } 493 .off-val { font-size: 22px; font-weight: 800; letter-spacing: -.5px; } 494 .off-r { text-align: right; } 495 496 .lanes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; } 497 .lane { display: flex; flex-direction: column; justify-content: space-between; gap: 20px; padding: 24px; border: 1px solid var(--border); border-radius: var(--r-sm); color: var(--primary-dark); text-decoration: none; overflow: hidden; } 498 .lane:hover { border-color: var(--primary); } 499 .lane-art { position: relative; height: 128px; } 500 .lane-art .cc-thumb { position: absolute; width: 170px; height: 107px; } 501 .lane-art .cc-thumb:first-child { left: 0; top: 18px; transform: rotate(-5deg); z-index: 1; } 502 .lane-art .cc-thumb + .cc-thumb { left: 96px; top: 0; transform: rotate(6deg); } 503 .lane-ft { display: flex; justify-content: space-between; align-items: baseline; } 504 .lane-ft strong { font-size: 22px; font-weight: 800; letter-spacing: -.4px; } 505 .lane-ft span { font-size: 14px; color: var(--text-gray); } 506 507 .issuers { padding: 20px 24px; background: var(--bg-light); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); } 508 .issuers p { max-width: 1280px; margin: 0 auto; display: flex; flex-wrap: wrap; gap: 6px 28px; align-items: baseline; font-size: 15px; font-weight: 700; color: var(--primary-dark); } 509 .issuers p span:first-child { font-size: 13px; font-weight: 400; color: var(--text-gray); margin-right: 8px; } 510 511 .guides { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 72px; } 512 .guides a { color: var(--primary-dark); text-decoration: none; } 513 .guides a:hover h3 { text-decoration: underline; } 514 .g-tag { font-size: 13px; color: var(--accent); font-weight: 600; } 515 .g-meta { font-size: 13px; color: var(--text-gray); } 516 .g-lead { display: flex; flex-direction: column; gap: 14px; padding-top: 20px; border-top: 1.5px solid var(--primary-dark); } 517 .g-lead h3 { font-size: 34px; font-weight: 800; letter-spacing: -.9px; line-height: 1.18; } 518 .g-lead p { font-size: 17px; line-height: 1.6; color: var(--text-gray); } 519 .g-item { display: flex; flex-direction: column; gap: 6px; padding: 20px 0; border-top: 1px solid var(--border); } 520 .g-item:first-child { border-top: 1.5px solid var(--primary-dark); } 521 .g-item h3 { font-size: 19px; font-weight: 700; letter-spacing: -.2px; line-height: 1.3; } 522 .g-item p { font-size: 14px; line-height: 1.55; color: var(--text-gray); } 523 524 @media (max-width: 1024px) { 525 .hx-body { grid-template-columns: minmax(0, 1fr); gap: 40px; } 526 .hx-result { order: -1; } 527 .tools, .guides { grid-template-columns: minmax(0, 1fr); gap: 48px; } 528 .off-row { grid-template-columns: 88px minmax(0, 1fr) 96px 104px; } 529 .off-desc, .off-fee { display: none; } 530 } 531 @media (max-width: 640px) { 532 .hand { height: 500px; } 533 .hand::after { height: 24px; } 534 .hand-scrim { background: linear-gradient(180deg, rgba(7,30,74,.8) 0, rgba(7,30,74,.8) 150px, rgba(7,30,74,.68) 210px, rgba(7,30,74,.68) 290px, var(--primary-dark) 400px); } 535 .hand-copy { padding: 28px 20px 0; } 536 .hand-copy h1 { font-size: 24px; line-height: 1.25; max-width: 300px; } 537 .hand-actions { gap: 4px 18px; margin-top: 12px; } 538 .hand-go { height: 44px; padding: 0 18px; font-size: 14px; }
539 .hand-fan { top: 236px; width: 390px; height: 330px; margin-left: -195px; } 540 .hf { transform-origin: 50% 130%; } 541 .hf img { width: 210px; height: 132px; border-radius: 11px; } 542 .hf1, .hf5 { display: none; } 543 .hf2 { left: 40px; top: 44px; transform: rotate(-13deg); } 544 .hf3 { left: 90px; top: 0; } 545 .hf4 { left: 140px; top: 44px; transform: rotate(13deg); } 546 .hx-calc-hd { padding: 36px 20px 0; } 547 .hx-cue { gap: 10px; margin-bottom: 14px; font-size: 13px; letter-spacing: 1.3px; } 548 .hx-cue span { width: 28px; height: 28px; } 549 .hx-calc-hd h2 { font-size: 28px; letter-spacing: -.8px; } 550 .hx-body { padding: 0 20px 40px; } 551 .hx-row { grid-template-columns: minmax(0, 1fr) auto; gap: 8px 12px; } 552 .hx-seg { grid-column: 1 / -1; grid-row: 2; } 553 .hx-stage { height: 226px; } 554 .hx-win { width: 290px; height: 183px; top: 18px; } 555 .hx-r2 { left: 150px; top: 6px; width: 200px; height: 126px; } 556 .hx-r3 { display: none; } 557 .hx-fig { font-size: 56px; letter-spacing: -1.8px; } 558 .hx-recip { margin-left: 0; flex-basis: 100%; } 559 .hx-cta { width: 100%; justify-content: center; } 560 .hp { padding: 0 20px; } 561 .hp-inner { padding: 40px 0 44px; } 562 .hp-hd h2 { font-size: 26px; letter-spacing: -.6px; } 563 .cmp-row { grid-template-columns: 96px repeat(3, minmax(0, 1fr)); column-gap: 10px; font-size: 13px; } 564 .cmp-row > span:first-child { font-size: 12px; } 565 .cmp-card .cc-thumb { width: 100%; height: auto; aspect-ratio: 1.586; } 566 .off-row { grid-template-columns: 72px minmax(0, 1fr) auto; column-gap: 14px; } 567 .off-row .cc-thumb { width: 72px; height: 45px; } 568 .off-checked { display: none; } 569 .lanes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } 570 .lane { padding: 16px; gap: 14px; } 571 .lane-art { height: 84px; } 572 .lane-art .cc-thumb { width: 112px; height: 71px; } 573 .lane-art .cc-thumb + .cc-thumb { display: none; } 574 .lane-ft { flex-direction: column; gap: 2px; } 575 .lane-ft strong { font-size: 18px; } 576 .g-lead h3 { font-size: 24px; letter-spacing: -.5px; } 577 .g-lead p { font-size: 15px; } 578 } 579 @media (max-width: 1024px) and (min-width: 641px) { 580 .hand { height: 430px; } 581 .hand-fan { top: 210px; transform: scale(.78); transform-origin: 50% 0; } 582 .hx-calc-hd { padding: 240px 24px 0; } 583 } 584 @media (prefers-reduced-motion: reduce) { 585 *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; } 586 .reveal { opacity: 1 !important; transform: none !important; } 587 } 588 </style> 589
589<script src="nav-search.js?v=2" defer></script>
589 590</head> 591<body> 592<a href="#main-content" class="skip-link">Skip to main content</a> 593 594<!-- âââââââââââ NAVBAR âââââââââââ --> 595<nav class="navbar"> 596 <div class="nav-inner"> 597 <a href="/" class="logo"> 598 <div class="logo-icon"> 599 <img src="images/logo-icon.svg" alt="CardCompass compass logo" width="40" height="40"> 600 </div> 601 <span class="logo-text">Card<span>Compass</span></span> 602 </a> 603 604 <div class="nav-links" id="nav-links"> 605 <div class="nav-item"> 606 <button class="nav-link" aria-expanded="false" aria-haspopup="true">Credit Cards <i class="fas fa-chevron-down"></i></button> 607 <div class="dropdown"> 608 <a href="/all-cards" style="font-weight:700"><i class="fas fa-th-list"></i> All Credit Cards</a> 609 <div style="height:1px;background:#E2E8F0;margin:6px 0;"></div> 610 <a href="/bonus-offers"><i class="fas fa-gift"></i> Current Bonus Offers</a> 611 <a href="/cards?cat=cashback"><i class="fas fa-star"></i> Best Cash Back Cards</a> 612 <a href="/cards?cat=travel"><i class="fas fa-plane"></i> Best Travel Cards</a> 613 <a href="/cards?cat=hotel"><i class="fas fa-hotel"></i> Best Hotel Cards</a> 614 <a href="/cards?cat=airline"><i class="fas fa-plane-departure"></i> Best Airline Cards</a> 615 <a href="/cards?cat=gas"><i class="fas fa-gas-pump"></i> Best Gas Cards</a> 616 <a href="/cards?cat=dining"><i class="fas fa-utensils"></i> Best Dining Cards</a> 617 <a href="/cards?cat=nofee"><i class="fas fa-shield-alt"></i> No Annual Fee Cards</a> 618 <a href="/cards?cat=balancetransfer"><i class="fas fa-sync-alt"></i> Balance Transfer Cards</a> 619 <a href="/cards?cat=business"><i class="fas fa-briefcase"></i> Best Business Cards</a> 620 <a href="/cards?cat=student"><i class="fas fa-graduation-cap"></i> Student Cards</a> 621 <a href="/cards?cat=secured"><i class="fas fa-lock"></i> Build Credit / Secured</a> 622 </div> 623 </div> 624 <div class="nav-item"> 625 <button class="nav-link" aria-expanded="false" aria-haspopup="true">Rewards <i class="fas fa-chevron-down"></i></button> 626 <div class="dropdown"> 627 <a href="/rewards-calculator"><i class="fas fa-calculator"></i> Points Calculator</a> 628 <a href="/rewards-partners"><i class="fas fa-exchange-alt"></i> Transfer Partners</a> 629 <a href="/rewards-maximize"><i class="fas fa-chart-line"></i> Maximize Your Points</a> 630 <a href="/rewards-award-travel"><i class="fas fa-suitcase"></i> Award Travel Guide</a> 631 <a href="/rewards-optimizer"><i class="fas fa-dollar-sign"></i> Cash Back Optimizer</a> 632 <a href="/balance-transfer-calculator"><i class="fas fa-sync-alt"></i> Balance Transfer Calc</a> 633 </div> 634 </div> 635 <div class="nav-item"> 636 <button class="nav-link" aria-expanded="false" aria-haspopup="true">Airlines <i class="fas fa-chevron-down"></i></button> 637 <div class="dropdown"> 638 <a href="/airlines" style="font-weight:700"><i class="fas fa-plane"></i> All Airlines</a> 639 <div style="height:1px;background:#E2E8F0;margin:6px 0;"></div> 640 <a href="/airline-transfer-matrix"><i class="fas fa-table"></i> Transfer Partner Matrix</a> 641 <a href="/airline-search-programs"><i class="fas fa-search"></i> Search All 23 Programs</a> 642 <div style="height:1px;background:#E2E8F0;margin:6px 0;"></div> 643 <a href="/airlines/united"><i class="fas fa-plane-departure"></i> United MileagePlus</a> 644 <a href="/airlines/delta"><i class="fas fa-plane-departure"></i> Delta SkyMiles</a> 645 <a href="/airlines/american"><i class="fas fa-plane-departure"></i> American AAdvantage</a> 646 <a href="/airlines/southwest"><i class="fas fa-plane-departure"></i> Southwest Rapid Rewards</a> 647 <a href="/airlines/alaska"><i class="fas fa-plane-departure"></i> Alaska Mileage Plan</a> 648 <a href="/airlines/jetblue"><i class="fas fa-plane-departure"></i> JetBlue TrueBlue</a> 649 <a href="/airlines/hawaiian"><i class="fas fa-plane-departure"></i> Hawaiian Miles</a> 650 </div> 651 </div> 652 <a href="/quiz" class="nav-link">Card Quiz</a> 653 <a href="/compare" class="nav-link">Compare Cards</a> 654 <a href="/my-current-wallet" class="nav-link">My Current Wallet</a> 655 <a href="/guides" class="nav-link">Guides</a> 656 </div> 657 658 <div class="nav-actions"> 659 <a href="/login" class="btn-outline">Log In</a> 660 <a href="/get-started" class="btn-primary">Get Started Free</a> 661 </div> 662 <button class="hamburger" id="hamburger" aria-label="Toggle menu">
663 <span></span><span></span><span></span> 664 </button> 665 </div> 666</nav> 667 668<!-- âââââââââââ HERO: SPEND CALCULATOR âââââââââââ --> 669<section class="hx" id="main-content"> 670 <div class="hand"> 671 <!-- The photo is the page's LCP element. Phones get a portrait crop (rect = the 60%-40% window object-fit shows at ~0.86 aspect), so they stop downloading the full-width landscape. --> 672 <picture> 673 <source media="(max-width: 640px)" sizes="100vw" srcset="https://images.unsplash.com/photo-1556740738-b6a63e27c4df?rect=2084,0,4667,5427&w=480&q=75&auto=format&fit=crop&monochrome=071e4a&con=-35&exp=8&blend=071e4a&bm=normal&blend-alpha=40 480w, https://images.unsplash.com/photo-1556740738-b6a63e27c4df?rect=2084,0,4667,5427&w=860&q=75&auto=format&fit=crop&monochrome=071e4a&con=-35&exp=8&blend=071e4a&bm=normal&blend-alpha=40 860w, https://images.unsplash.com/photo-1556740738-b6a63e27c4df?rect=2084,0,4667,5427&w=1080&q=75&auto=format&fit=crop&monochrome=071e4a&con=-35&exp=8&blend=071e4a&bm=normal&blend-alpha=40 1080w"> 674 <img class="hand-photo" src="https://images.unsplash.com/photo-1556740738-b6a63e27c4df?w=1600&q=75&auto=format&fit=crop&monochrome=071e4a&con=-35&exp=8&blend=071e4a&bm=normal&blend-alpha=40" srcset="https://images.unsplash.com/photo-1556740738-b6a63e27c4df?w=1200&q=75&auto=format&fit=crop&monochrome=071e4a&con=-35&exp=8&blend=071e4a&bm=normal&blend-alpha=40 1200w, https://images.unsplash.com/photo-1556740738-b6a63e27c4df?w=1600&q=75&auto=format&fit=crop&monochrome=071e4a&con=-35&exp=8&blend=071e4a&bm=normal&blend-alpha=40 1600w" sizes="100vw" alt="" aria-hidden="true" fetchpriority="high"> 675 </picture> 676 <div class="hand-scrim" aria-hidden="true"></div> 677 <div class="hx-inner hand-copy"> 678 <div class="hero-badge" id="hero-src-badge" hidden><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--accent-gold)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/></svg>Welcome to CardCompass. Start with a rough month of spending.</div> 679
679<script>(function(){try{var q=new URLSearchParams(location.search),s=(q.get('utm_source')||(q.get('gclid')||q.get('gbraid')||q.get('wbraid')?'google':q.get('fbclid')?'facebook':q.get('ref'))||'').toLowerCase();if(s)sessionStorage.setItem('cc_src',s);if(sessionStorage.getItem('cc_src'))document.getElementById('hero-src-badge').hidden=false;}catch(e){}})();</script>
679 680 <h1 id="hx-h1">Find the card that fits how you spend.</h1> 681 <p class="hand-actions"><a class="hand-go" href="#calc">Start with your spending</a><a class="hand-all" href="/all-cards">All <span data-card-count>113</span> cards</a></p> 682 </div> 683 </div> 684 <div class="hand-fan" aria-hidden="true"> 685 <span class="hf hf1"><img src="https://cdn.prodstatic.com/shared/images/cards/500x315/083f25c0-504f-11f0-962e-072d632f0110.png" alt="" loading="eager" decoding="async"></span> 686 <span class="hf hf2"><img src="https://cdn.prodstatic.com/shared/images/cards/500x315/8cd47490-949f-11f0-832a-f3f3309a1b0c.png" alt="" loading="eager" decoding="async"></span> 687 <span class="hf hf3"><img src="https://cdn.prodstatic.com/shared/images/cards/500x315/ae837480-ed19-11ee-b6aa-83dad90afdcd.png" alt="" loading="eager" decoding="async"></span> 688 <span class="hf hf4"><img src="https://cdn.prodstatic.com/shared/images/cards/500x315/a5c974a0-eb11-11f0-8b83-5b931725a212.png" alt="" loading="eager" decoding="async"></span> 689 <span class="hf hf5"><img src="https://cdn.prodstatic.com/shared/images/cards/500x315/4f675c90-7268-11e9-8bc5-4d4394516d65.png" alt="" loading="eager" decoding="async"></span> 690 </div> 691 <div class="hx-inner hx-calc-hd" id="calc"> 692 <p class="hx-cue"><span><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M19 12l-7 7-7-7"/></svg></span>Continue to see</p> 693 <h2 id="hx-calc-h2">What <span id="hx-total">$1,650</span> a month earns on a credit card</h2> 694 </div> 695 <div class="hx-inner hx-body"> 696 <div class="hx-inputs"> 697 <div class="hx-head"><span>Per month, roughly</span><span>Earns per year</span></div> 698 <div id="hx-rows" role="group" aria-label="Monthly spending"></div> 699 <p class="hx-note">Points counted at 1 cent each. Welcome offers counted at a conservative value, and only when this spending meets the offer's requirement. This ranks general rewards and cash back cards. Hotel, airline, business and credit-building cards are in <a href="/quiz">the quiz</a>.</p> 700 </div> 701 <div class="hx-result"> 702 <div class="hx-tabs" role="group" aria-label="Period"> 703 <button type="button" data-mode="yearOne" aria-pressed="true">Year one</button> 704 <button type="button" data-mode="after" aria-pressed="false">Every year after</button> 705 </div> 706 <div class="hx-stage" id="hx-stage"></div> 707 <div id="hx-detail"></div> 708 <p class="hx-legal">Rates and fees apply. Offers can change. We may earn a commission when you apply through our links and are approved. <a href="/advertiser-disclosure">Advertiser disclosure</a></p> 709 <div class="sr-only" role="status" id="hx-status"></div> 710 </div> 711 </div> 712</section> 713 714<!-- âââââââââââ ADVERTISER DISCLOSURE BAR âââââââââââ --> 715<div class="adv-bar"> 716 <div class="adv-bar-inner"> 717 <i class="fas fa-info-circle"></i> 718 <span><a href="/advertiser-disclosure">Advertiser Disclosure</a> â CardCompass may earn a commission when you apply through our links. This does not affect our ratings or rankings.</span> 719 </div> 720</div> 721 722<!-- âââââââââââ PERSONAL MATCH BANNER (logged-in) âââââââââââ --> 723<div class="personal-banner hidden" id="personal-banner"> 724 <div class="personal-banner-inner"> 725 <div class="pb-greeting"><i class="fas fa-hand"></i> Hey, <b id="pb-name">there</b></div> 726 <div class="pb-sep"></div> 727 <div class="pb-label">Your top match</div> 728 <div class="pb-match"> 729 <div class="pb-badge" id="pb-badge"></div> 730 <div> 731 <div class="pb-card-name" id="pb-card-name"></div> 732 <div class="pb-card-rate" id="pb-card-rate"></div> 733 </div> 734 </div> 735 <div class="pb-earn" id="pb-earn"><small>est. yearly value</small></div> 736 <div class="pb-actions"> 737 <a href="/account" class="pb-acct-btn"><i class="fas fa-user"></i> My Account</a> 738 <button class="pb-retake" onclick="location.href='/quiz'"><i class="fas fa-bullseye"></i> Update Preferences</button> 739 </div> 740 </div> 741</div> 742 743<!-- âââââââââââ TOOLS: COMPARE + QUIZ âââââââââââ --> 744<section class="hp hp-first" id="quiz"> 745 <div class="hp-inner tools"> 746 <div> 747 <div class="hp-hd" id="compare"><h2>Compare Cards</h2><a href="/compare" id="cmp-link">Compare these three</a></div> 748 <div id="cmp-table" role="table" aria-label="Your top three cards, side by side"></div> 749 </div> 750 <div> 751 <div class="hp-hd"><h2>Card Quiz</h2><span>7 questions</span></div> 752 <p class="quiz-q">What's your primary goal with a new credit card?</p> 753 <div id="quiz-opts"> 754 <a class="quiz-opt" href="/quiz?goal=travel" data-goal="travel">Earn travel rewards<i class="fas fa-chevron-right" aria-hidden="true" style="font-size:12px;color:var(--text-gray)"></i></a> 755 <a class="quiz-opt" href="/quiz?goal=cashback" data-goal="cashback">Maximize cash back<i class="fas fa-chevron-right" aria-hidden="true" style="font-size:12px;color:var(--text-gray)"></i></a> 756 <a class="quiz-opt" href="/quiz?goal=airline" data-goal="airline">Earn airline miles<i class="fas fa-chevron-right" aria-hidden="true" style="font-size:12px;color:var(--text-gray)"></i></a> 757 <a class="quiz-opt" href="/quiz?goal=hotel" data-goal="hotel">Earn hotel points<i class="fas fa-chevron-right" aria-hidden="true" style="font-size:12px;color:var(--text-gray)"></i></a> 758 <a class="quiz-opt" href="/quiz?goal=credit" data-goal="credit">Build or improve my credit<i class="fas fa-chevron-right" aria-hidden="true" style="font-size:12px;color:var(--text-gray)"></i></a> 759 <a class="quiz-opt" href="/quiz?goal=business" data-goal="business">Business card for my company<i class="fas fa-chevron-right" aria-hidden="true" style="font-size:12px;color:var(--text-gray)"></i></a> 760 </div> 761 <p class="quiz-foot">It picks up the spending you entered above.</p> 762 </div> 763 </div> 764</section> 765 766<!-- âââââââââââ CURRENT BONUS OFFERS âââââââââââ --> 767<section class="hp">
768 <div class="hp-inner"> 769 <div class="hp-hd"><h2>Current Bonus Offers</h2><a href="/bonus-offers">All bonus offers</a></div> 770 <div id="off-table" role="table" aria-label="Welcome offers, by our value"></div> 771 </div> 772</section> 773 774<!-- âââââââââââ CARDS BY TYPE âââââââââââ --> 775<section class="hp" id="categories"> 776 <div class="hp-inner"> 777 <div class="hp-hd"><h2><span data-card-count>113</span> cards, by type</h2><a href="/all-cards">All cards</a></div> 778 <div class="lanes" id="lanes"></div> 779 </div> 780</section> 781 782<!-- âââââââââââ ISSUERS âââââââââââ --> 783<div class="issuers"><p id="issuers"></p></div> 784 785<!-- âââââââââââ GUIDES âââââââââââ --> 786<section class="hp hp-first"> 787 <div class="hp-inner"> 788 <div class="hp-hd"><h2>Guides</h2><a href="/guides">All guides</a></div> 789 <div class="guides"> 790 <a class="g-lead" href="/guides/marriott-bonvoy-credit-cards-2026"> 791 <span class="g-tag">Hotel Cards</span> 792 <h3>Marriott Bonvoy Credit Cards 2026: Bold vs. Boundless vs. Brilliant vs. Business</h3> 793 <p>Four cards, two issuers, a $0 to $650 fee range. Here's which Bonvoy card's Free Night Award actually covers its own annual fee.</p> 794 <span class="g-meta">September 4, 2026 · 9 min read</span> 795 </a> 796 <div> 797 <a class="g-item" href="/guides/marriott-bonvoy-points-value-2026"> 798 <span class="g-tag">Rewards Strategy</span> 799 <h3>Marriott Bonvoy Points Value in 2026: What Your Points Are Really Worth</h3> 800 <p>A 100,000-point balance sounds like $1,000. It's closer to $750. Here's the formula to check any redemption before you book it.</p> 801 <span class="g-meta">September 4, 2026 · 7 min read</span> 802 </a> 803 <a class="g-item" href="/guides/best-business-travel-credit-cards-2026"> 804 <span class="g-tag">Business Cards</span> 805 <h3>Best Business Credit Cards for Travel in 2026</h3> 806 <p>Free employee cards, higher limits, and spend that stays off your personal credit report. Here’s which airline and hotel business co-brands are worth the fee.</p> 807 <span class="g-meta">September 3, 2026 · 10 min read</span> 808 </a> 809 <a class="g-item" href="/guides/best-hotel-credit-cards-2026"> 810 <span class="g-tag">Hotel Cards</span> 811 <h3>Best Hotel Credit Cards in 2026</h3> 812 <p>Most hotel cards are bought for the points and kept for the free night certificate. Here’s which certificates actually cover their own annual fee.</p> 813 <span class="g-meta">August 31, 2026 · 10 min read</span> 814 </a> 815 </div> 816 </div> 817 </div> 818</section> 819 820<!-- âââââââââââ NEWSLETTER âââââââââââ --> 821<section class="newsletter-section"> 822 <div class="newsletter-inner reveal"> 823 <span class="newsletter-icon"><i class="fas fa-envelope-open-text" style="color:var(--accent-gold)"></i></span> 824 <h2>Get the best card deals in your inbox</h2> 825 <p>An alert when we add a card or a welcome bonus jumps, and a roundup on the 1st of each month. No spam, ever.</p> 826 <div id="newsletter-wrap"> 827 <div class="newsletter-form"> 828 <input type="email" class="newsletter-input" id="nl-email" placeholder="[email protected]" autocomplete="email"> 829 <button class="newsletter-submit" onclick="submitNewsletter()">Subscribe Free â</button> 830 </div> 831 <div class="cf-turnstile" data-sitekey="0x4AAAAAADiVOynXw_ZhYDP0" style="margin:14px 0;"></div> 832 <div class="newsletter-error" id="nl-error">Please enter a valid email address.</div> 833 <div class="newsletter-meta" style="justify-content:center;"> 834 <span><i class="fas fa-check"></i> Unsubscribe anytime</span> 835 <span><i class="fas fa-check"></i> No spam, ever</span> 836 </div> 837 </div> 838 <div class="newsletter-success" id="nl-success"> 839 <h3><i class="fas fa-champagne-glasses"></i> You're in!</h3> 840 <p>Check your inbox to confirm your subscription. Your first deal alert is on the way.</p> 841 </div> 842 <div class="newsletter-already" id="nl-already"> 843 <i class="fas fa-check" style="color:#22c55e"></i> You're already subscribed â we'll keep sending you the best card deal
843s each month. 844 </div> 845 </div> 846</section> 847 848<!-- âââââââââââ FOOTER âââââââââââ --> 849<footer> 850 <div class="footer-inner"> 851 <div class="footer-top"> 852 <div class="footer-brand"> 853 <a href="/" class="logo"> 854 <div class="logo-icon"> 855 <img src="images/logo-icon.svg" alt="CardCompass compass logo" width="40" height="40"> 856 </div> 857 <span class="logo-text">Card<span>Compass</span></span> 858 </a> 859 <p>CardCompass helps you navigate the crowded world of credit cards with honest, data-driven recommendations â so you earn more on every dollar you spend.</p> 860 </div> 861 <div class="footer-col"> 862 <h4>Credit Cards</h4> 863 <a href="/cards?cat=cashback">Best Cash Back</a><a href="/cards?cat=travel">Best Travel Cards</a><a href="/cards?cat=hotel">Best Hotel Cards</a><a href="/cards?cat=airline">Best Airline Cards</a><a href="/cards?cat=nofee">No Annual Fee</a><a href="/cards?cat=balancetransfer">Balance Transfer</a><a href="/cards?cat=business">Business Cards</a><a href="/cards?cat=student">Student Cards</a><a href="/cards?cat=secured">Build Credit</a> 864 </div> 865 <div class="footer-col"> 866 <h4>Rewards</h4> 867 <a href="/rewards-calculator">Points Calculator</a><a href="/rewards-partners">Transfer Partners</a><a href="/rewards-award-travel">Award Travel Guide</a><a href="/rewards-maximize">Maximize Rewards</a><a href="/airlines">Airport Lounge Access</a><a href="/my-current-wallet">My Current Wallet</a> 868 </div> 869 <div class="footer-col"> 870 <h4>Company</h4> 871 <a href="/guides">Guides</a><a href="/about">About Us</a><a href="/about#methodology">How We Rate Cards</a><a href="/editorial-policy">Editorial Policy</a><a href="/advertiser-disclosure">Advertiser Disclosure</a><a href="/contact">Contact Us</a> 872 <a href="/account" id="footer-account-link" style="display:none;">My Account</a> 873 </div> 874 </div> 875 <div class="footer-bottom"> 876 <span>© 2026 CardCompass, Inc. CardCompass may receive compensation through credit card issuer partnerships. This does not influence our ratings or recommendations. <span class="footer-social" style="display:inline-flex;align-items:center;gap:14px;vertical-align:middle;margin-left:10px;"><a href="https://www.instagram.com/cardcompassapp/" target="_blank" rel="noopener" aria-label="CardCompass on Instagram" style="display:inline-flex;margin:0;"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="20" height="20" x="2" y="2" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" x2="17.51" y1="6.5" y2="6.5"/></svg></a><a href="https://www.facebook.com/cardcompassapp" target="_blank" rel="noopener" aria-label="CardCompass on Facebook" style="display:inline-flex;margin:0;"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg></a></span></span> 877 <div class="foot-links"><a href="/privacy-policy">Privacy Policy</a><a href="/terms">Terms of Service</a><a href="/advertiser-disclosure">Advertiser Disclosure</a></div> 878 </div> 879 </div> 880</footer> 881 882<button class="scroll-top" onclick="window.scrollTo({top:0,behavior:'smooth'})" aria-label="Back to top"> 883 <i class="fas fa-arrow-up"></i> 884</button> 885 886<!-- âââââââââââ DRAWER âââââââââââ --> 887<div class="drawer-overlay" id="drawer-overlay" onclick="closeDrawer()"></div> 888<div class="drawer" id="card-drawer"> 889 <div class="drawer-header"> 890 <span>Card Details</span> 891 <button class="drawer-close" onclick="closeDrawer()" aria-label="Close">×</button> 892 </div> 893 <div class="drawer-body"> 894 <div class="drawer-card-hd"> 895 <div class="drawer-card-img" id="d-img"></div> 896 <div>
896<div class="drawer-card-name" id="d-name"></div><div class="drawer-card-sub" id="d-sub"></div></div> 897 </div> 898 <div class="drawer-score-bar"> 899 <span class="drawer-stars" id="d-stars"></span> 900 <span class="drawer-score-val" id="d-score"></span> 901 <span class="drawer-score-lbl">/ 5 â CardCompass Score</span> 902 </div> 903 <div class="drawer-divider"></div> 904 <div class="drawer-details" id="d-details"></div> 905 <div class="drawer-divider"></div> 906 <div class="drawer-proscons" id="d-proscons"></div> 907 </div> 908 <div class="drawer-actions"> 909 <button class="drawer-apply" id="drawer-apply-btn">Apply Now â</button> 910 <button class="drawer-learn" onclick="closeDrawer()">Close</button> 911 </div> 912</div> 913 914<!-- âââââââââââ COOKIE CONSENT BANNER âââââââââââ --> 915<div class="cookie-banner hidden" id="cookie-banner" role="dialog" aria-label="Cookie consent"> 916 <p>We use cookies to analyze site traffic and improve your experience. See our <a href="/privacy-policy">Privacy Policy</a> for details.</p> 917 <div class="cookie-actions"> 918 <button class="cookie-accept" onclick="acceptCookies()">Accept</button> 919 <button class="cookie-decline" onclick="declineCookies()">Decline</button> 920 </div> 921</div> 922
923<script src="cards-data.js?v=15"></script>
vendor: 1 bytes, line 923
923
924<script src="recommend-engine.js?v=4"></script>
vendor: 1 bytes, line 924
924
925<script src="nav-auth.js?v=2"></script>
vendor: 1 bytes, line 925
925
926<script> 927/* âââââââââââââââââââââââââââââââââââââââââââ 928 DATA â full card pool now comes from cards-data.js / 929 recommend-engine.js (window.ALL_CARDS / window.CCEngine), not a 930 hardcoded 5-card subset. See cardBadgeHtml() below for the shared 931 image-or-issuer-badge renderer used by the quiz results, banner, and 932 drawer instead of the old per-card badgeClass/badgeText fields. 933âââââââââââââââââââââââââââââââââââââââââââ */ 934// Fills whatever sized container it's placed in (.banner-card-img, 935// .drawer-card-img, .qrc-img all already set width/height via CSS). 936function cardBadgeHtml(c) { 937 if (c.cardImage) { 938 return '<img src="' + c.cardImage + '" alt="' + c.name + '" style="width:100%;height:100%;object-fit:cover;border-radius:6px;" onerror="this.style.display=\'none\'">'; 939 } 940 const bg = (c.cardArt && c.cardArt.bg) || 'linear-gradient(135deg,#1A3F6F 0%,#0D2A4A 100%)'; 941 const label = (c.issuer || '').substring(0, 4).toUpperCase(); 942 return '<div style="width:100%;height:100%;background:' + bg + ';color:#fff;font-size:10px;font-weight:800;display:flex;align-items:center;justify-content:center;border-radius:6px;">' + label + '</div>'; 943} 944 945/* âââââââââââââââââââââââââââââââââââââââââââ 946 AFFILIATE APPLY NOW 947âââââââââââââââââââââââââââââââââââââââââââ */ 948let currentBannerCardId = null; 949 950function applyNow(cardId, source) { 951 const allCards = window.ALL_CARDS || window.CCEngine.getCardPool(); 952 const c = allCards.find(x => x.id === cardId); 953 if (!c) return; 954 if (typeof gtag === 'function') { 955 gtag('event', 'apply_click', { card_name: c.name, card_issuer: c.issuer, source: source }); 956 } 957 window.open(window.getApplyUrl ? window.getApplyUrl(c) : c.applyUrl, '_blank', 'noopener,noreferrer'); 958} 959 960const REVIEWS = [ 961 { name: 'Sarah K.', initials: 'SK', av: 'av-blue', card: 'Chase Sapphire Preferred®', rating: 5, quote: 'Earned over $1,200 in travel rewards my first year. The sign-up bonus alone paid for a round-trip flight to Europe.' }, 962 { name: 'Marcus T.', initials: 'MT', av: 'av-accent', card: 'Amex Gold Card', rating: 5, quote: 'Best dining card I\'ve ever had. The 4à points at restaurants adds up incredibly fast â I earned enough for a free flight in 6 months.' }, 963 { name: 'Priya L.', initials: 'PL', av: 'av-gold', card: 'Citi Double Cash®', rating: 4, quote: 'Perfect for someone who doesn\'t want to think about categories. Flat 2% on everything, no annual fee. Simple and effective.' }, 964 { name: 'James R.', initials: 'JR', av: 'av-red', card: 'Capital One Venture X', rating: 5, quote: 'The lounge access alone justifies the fee. Free Priority Pass plus the $300 travel credit â it basically pays for itself year after year.' }, 965 { name: 'Emily C.', initials: 'EC', av: 'av-green', card: 'Discover it® Cash Back', rating: 4, quote: 'Great starter card. The Cashback Match in year one was a game changer â I made $400 cash back my first year with zero annual fee.' }, 966 { name: 'David M.', initials: 'DM', av: 'av-blue', card: 'Chase Sapphire Preferred®', rating: 5, quote: 'CardCompass matched me to this card based on my spending. Projected $1,100/year â I\'m actually earning $1,350. Incredible tool.' }, 967 { name: 'Rachel H.', initials: 'RH', av: 'av-accent', card: 'Amex Platinum', rating: 5, quote: 'After the $200 airline fee credit, $200 hotel credit, and Centurion lounge access, I\'m paying around $300 net for benefits worth well over $2,000.' }, 968 { name: 'Tyler B.', initials: 'TB', av: 'av-purple', card: 'Chase Freedom Unlimited', rating: 5, quote: 'No annual fee and I\'m earning $580 a year. CardCompass showed me I was leaving money on the table with my old card â switched immediately.' }, 969 { name: 'Natalie W.', initials: 'NW', av: 'av-teal', card: 'World of Hyatt Card', rating: 4, quote: 'Hyatt points are worth 2â3à more than Marriott. CardCompass\'s comparison made it obvious. I got a free 5-night stay in my first year.' }, 970 { name: 'Kevin S.', initials: 'KS', av: 'av-gold', card: 'Capital One Venture', rating: 5, quote: 'Transferred 80,000 miles to Air Canada and flew business class to London. Would have been $4,800 to buy the seat outright. This card is unreal.' }, 971 { name: 'Aisha J.', initials: 'AJ', av: 'av-green', card: 'Amex Gold Card', rating: 5, quote: 'I spend a lot on groceries and restaurants. The 4à categories earn me $900+ per year in points. The $120 dining credit covers Uber Eats monthly.' }, 972 { name: 'Michael T.', initials: 'MT', av: 'av-red', card: 'Citi Custom Cash®', rating: 5, quote: 'Automatically earns 5% on my top spend category each month. I don\'t have to think about it â it just optimizes itself. Best no-fee card I\'ve had.' }, 973 { name: 'Lauren P.', initials: 'LP', av: 'av-blue', card: 'Chase Sapphire Reserve®', rating: 5, quote: 'The $300 travel credit drops the effective fee to $250. Add Priority Pass, TSA PreCheck reimbursement, and 3à on travel â it pays for itself easily.' } 974]; 975 976/* âââââââââââââââââââââââââââââââââââââââââââ 977 CARD DRAWER 978âââââââââââââââââââââââââââââââââââââââââââ */ 979let drawerTriggerEl = null; 980let drawerCardId = null; 981 982const MIN_CREDIT_LABELS = {
983 poor: 'Poor â Fair (under 640)', fair: 'Fair â Good (640+)', building: 'Building Credit', 984 good: 'Good â Excellent (700+)', verygood: 'Very Good (740+)', excellent: 'Excellent (750+)' 985}; 986 987function openDrawer(id) { 988 const allCards = window.ALL_CARDS || window.CCEngine.getCardPool(); 989 const c = allCards.find(x => x.id === id); 990 if (!c) return; 991 992 drawerCardId = id; 993 drawerTriggerEl = document.activeElement; 994 995 document.getElementById('d-img').className = 'drawer-card-img'; 996 document.getElementById('d-img').innerHTML = cardBadgeHtml(c); 997 document.getElementById('d-name').textContent = c.name; 998 document.getElementById('d-sub').textContent = 'Best for: ' + c.bestFor; 999 1000 const fullStars = Math.round(c.cardScore || 4); 1001 const dStarFull = '<i class="fas fa-star" style="color:#F5A623"></i>'; 1002 const dStarEmpty = '<i class="fas fa-star" style="color:#F5A623;opacity:.25"></i>'; 1003 document.getElementById('d-stars').innerHTML = dStarFull.repeat(fullStars) + dStarEmpty.repeat(5 - fullStars); 1004 document.getElementById('d-score').textContent = (c.cardScore || 4).toFixed(1); 1005 1006 document.getElementById('d-details').innerHTML = ` 1007 <div class="drawer-row"><span class="drawer-lbl">Welcome Bonus</span><span class="drawer-val gold">${c.bonusDesc || c.bonusShort || 'See terms'}</span></div> 1008 <div class="drawer-row"><span class="drawer-lbl">Annual Fee</span><span class="drawer-val">${c.annualFee > 0 ? '$' + c.annualFee + ' / year' : 'None'}</span></div> 1009 <div class="drawer-row"><span class="drawer-lbl">Best Rewards Rate</span><span class="drawer-val">${c.rateLabel}</span></div> 1010 <div class="drawer-row"><span class="drawer-lbl">Foreign Transaction Fee</span><span class="drawer-val">${c.foreignFee}</span></div> 1011 <div class="drawer-row"><span class="drawer-lbl">Recommended Credit</span><span class="drawer-val">${MIN_CREDIT_LABELS[c.minScore] || 'Good â Excellent (700+)'}</span></div> 1012 `; 1013 1014 const pros = c.pros.map(p => `<li><span><i class="fas fa-check-circle" style="color:#22c55e"></i></span>${p}</li>`).join(''); 1015 const cons = c.cons.map(p => `<li><span><i class="fas fa-times-circle" style="color:#ef4444"></i></span>${p}</li>`).join(''); 1016 document.getElementById('d-proscons').innerHTML = ` 1017 <div class="drawerpc pros-col"><h5 class="pros-title">Pros</h5><ul>${pros}</ul></div> 1018 <div class="drawerpc cons-col"><h5 class="cons-title">Cons</h5><ul>${cons}</ul></div> 1019 `; 1020 1021 // Wire Apply Now button 1022 const applyBtn = document.getElementById('drawer-apply-btn'); 1023 applyBtn.onclick = () => applyNow(id, 'drawer'); 1024 1025 document.getElementById('drawer-overlay').classList.add('open'); 1026 const drawer = document.getElementById('card-drawer'); 1027 drawer.classList.add('open'); 1028 document.body.style.overflow = 'hidden'; 1029 1030 // Focus the close button after transition 1031 setTimeout(() => { 1032 const closeBtn = drawer.querySelector('.drawer-close'); 1033 if (closeBtn) closeBtn.focus(); 1034 }, 350); 1035 1036 if (typeof gtag === 'function') { 1037 gtag('event', 'card_detail_view', { card_name: c.name, card_issuer: c.issuer }); 1038 } 1039 1040 // Focus trap 1041 const FOCUSABLE = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; 1042 drawer.addEventListener('keydown', trapFocus); 1043 function trapFocus(e) { 1044 if (e.key !== 'Tab') return; 1045 const focusable = Array.from(drawer.querySelectorAll(FOCUSABLE)).filter(el => !el.disabled); 1046 if (!focusable.length) { e.preventDefault(); return; } 1047 const first = focusable[0], last = focusable[focusable.length - 1]; 1048 if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last.focus(); } } 1049 else { if (document.activeElement === last) { e.preventDefault(); first.focus(); } } 1050 } 1051 drawer._trapFocus = trapFocus; 1052} 1053 1054function closeDrawer() { 1055 const drawer = document.getElementById('card-drawer'); 1056 if (drawer._trapFocus) { drawer.removeEventListener('keydown', drawer._trapFocus); delete drawer._trapFocus; } 1057 document.getElementById('drawer-overlay').classList.remove('open'); 1058 drawer.classList.remove('open'); 1059 document.body.style.overflow = ''; 1060 if (drawerTriggerEl) { drawerTriggerEl.focus(); drawerTriggerEl = null; } 1061} 1062 1063document.addEventListener('keydown', e => { if (e.key === 'Escape') closeDrawer(); }); 1064 1065/* âââââââââââââââââââââââââââââââââââââââââââ 1066 REVIEWS CAROUSEL 1067âââââââââââââââââââââââââââââââââââââââââââ */ 1068let carouselIndex = 0; 1069let carouselTimer; 1070 1071function getPerPage() { 1072 return window.innerWidth < 768 ? 1 : window.innerWidth < 1024 ? 2 : 3; 1073} 1074 1075function renderTrack() { 1076 const track = document.getElementById('reviews-track'); 1077 if (!track) return; 1078 const rvStarFull = '<i class="fas fa-star" style="color:#F5A623"></i>'; 1079 const rvStarEmpty = '<i class="fas fa-star" style="color:#F5A623;opacity:.25"></i>'; 1080 track.innerHTML = REVIEWS.map(r => { 1081 const stars = rvStarFull.repeat(r.rating) + rvStarEmpty.repeat(5 - r.rating); 1082 return `<div class="review-card"> 1083 <div class="review-stars">${stars}</div> 1084 <div class="review-quote">${r.quote}</div> 1085 <div class="review-author"> 1086 <div class="review-avatar ${r.av}">${r.initials}</div> 1087 <div><div class="review-name">${r.name}</div><div class="review-card-name">${r.card}</div></div> 1088 </div> 1089 </div>`; 1090 }).join(''); 1091} 1092 1093function slideToPage(page) { 1094 const perPage = getPerPage(); 1095 const track = document.getElementById('reviews-track'); 1096 if (!track) return; 1097 const card = track.querySelector('.review-card'); 1098 if (!card) return; 1099 const cardW = card.offsetWidth; 1100 const gap = 24;
1101 const maxPage = Math.ceil(REVIEWS.length / perPage) - 1; 1102 const safePage = Math.min(page, maxPage); 1103 track.style.transform = `translateX(-${safePage * perPage * (cardW + gap)}px)`; 1104} 1105 1106function updateDots() { 1107 const perPage = getPerPage(); 1108 const pages = Math.ceil(REVIEWS.length / perPage); 1109 const dots = document.getElementById('carousel-dots'); 1110 if (!dots) return; 1111 dots.innerHTML = ''; 1112 for (let i = 0; i < pages; i++) { 1113 const d = document.createElement('button'); 1114 d.className = 'carousel-dot' + (i === carouselIndex ? ' active' : ''); 1115 d.setAttribute('aria-label', `Page ${i + 1}`); 1116 d.onclick = () => { carouselIndex = i; slideToPage(i); updateDots(); resetTimer(); }; 1117 dots.appendChild(d); 1118 } 1119} 1120 1121function carousel(dir) { 1122 const perPage = getPerPage(); 1123 const pages = Math.ceil(REVIEWS.length / perPage); 1124 carouselIndex = (carouselIndex + dir + pages) % pages; 1125 slideToPage(carouselIndex); 1126 updateDots(); 1127 resetTimer(); 1128} 1129 1130function resetTimer() { 1131 clearInterval(carouselTimer); 1132 carouselTimer = setInterval(() => carousel(1), 5000); 1133} 1134 1135 1136/* âââââââââââââââââââââââââââââââââââââââââââ 1137 SCROLL ANIMATIONS (IntersectionObserver) 1138âââââââââââââââââââââââââââââââââââââââââââ */ 1139const revealObserver = new IntersectionObserver((entries) => { 1140 entries.forEach(el => { 1141 if (el.isIntersecting) { el.target.classList.add('visible'); revealObserver.unobserve(el.target); } 1142 }); 1143}, { threshold: 0.12 }); 1144 1145document.querySelectorAll('.reveal').forEach(el => revealObserver.observe(el)); 1146 1147 1148/* âââââââââââââââââââââââââââââââââââââââââââ 1149 COUNTER ANIMATION 1150âââââââââââââââââââââââââââââââââââââââââââ */ 1151function animateCount(el) { 1152 const target = parseInt(el.dataset.target); 1153 const divisor = parseInt(el.dataset.divisor) || 1; 1154 const suffix = el.dataset.suffix || ''; 1155 const prefix = el.dataset.prefix || ''; 1156 const duration = 1500; 1157 const start = performance.now(); 1158 1159 function step(now) { 1160 const progress = Math.min((now - start) / duration, 1); 1161 const ease = 1 - Math.pow(1 - progress, 3); 1162 const val = Math.round(target * ease / divisor); 1163 el.textContent = prefix + val.toLocaleString() + suffix; 1164 if (progress < 1) requestAnimationFrame(step); 1165 else el.textContent = el.dataset.final; 1166 } 1167 1168 el.dataset.final = el.textContent; 1169 requestAnimationFrame(step); 1170} 1171 1172const counterObserver = new IntersectionObserver((entries) => { 1173 entries.forEach(e => { 1174 if (e.isIntersecting) { animateCount(e.target); counterObserver.unobserve(e.target); } 1175 }); 1176}, { threshold: 0.5 }); 1177 1178document.querySelectorAll('[data-card-count]').forEach(el => { 1179 el.textContent = window.ALL_CARDS.length; 1180 if (el.dataset.target) el.dataset.target = window.ALL_CARDS.length; 1181}); 1182document.querySelectorAll('.trust-item .num[data-target]').forEach(el => counterObserver.observe(el)); 1183 1184/* âââââââââââââââââââââââââââââââââââââââââââ 1185 HAMBURGER (MOBILE NAV) 1186âââââââââââââââââââââââââââââââââââââââââââ */ 1187const hamburger = document.getElementById('hamburger'); 1188const navLinks = document.getElementById('nav-links'); 1189 1190hamburger.addEventListener('click', () => { 1191 const open = hamburger.classList.toggle('open'); 1192 navLinks.classList.toggle('mobile-open', open); 1193 hamburger.setAttribute('aria-expanded', open ? 'true' : 'false'); 1194}); 1195 1196document.addEventListener('click', e => { 1197 if (!hamburger.contains(e.target) && !navLinks.contains(e.target)) { 1198 hamburger.classList.remove('open'); 1199 navLinks.classList.remove('mobile-open'); 1200 } 1201}); 1202 1203/* âââââââââââââââââââââââââââââââââââââââââââ 1204 NEWSLETTER 1205âââââââââââââââââââââââââââââââââââââââââââ */ 1206async function submitNewsletter() { 1207 const emailEl = document.getElementById('nl-email'); 1208 const errEl = document.getElementById('nl-error'); 1209 const btn = document.querySelector('.newsletter-submit'); 1210 const email = emailEl.value.trim(); 1211 errEl.style.display = 'none'; 1212 1213 if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { 1214 errEl.style.display = 'block'; 1215 emailEl.focus(); 1216 return; 1217 } 1218 1219 const token = window.turnstile?.getResponse(); 1220 if (!token) { 1221 errEl.textContent = 'Please complete the verification below.'; 1222 errEl.style.display = 'block'; 1223 return; 1224 } 1225 1226 btn.disabled = true; 1227 btn.textContent = 'Subscribingâ¦'; 1228 1229 try { 1230 const res = await fetch('/api/subscribe', { 1231 method: 'POST', 1232 headers: { 'Content-Type': 'application/json' }, 1233 body: JSON.stringify({ email, cfToken: token }) 1234 }); 1235 const data = await res.json(); 1236 1237 if (data.status === 'subscribed') { 1238 localStorage.setItem('cc_subscribed', email); 1239 document.getElementById('newsletter-wrap').style.display = 'none'; 1240 document.getElementById('nl-success').style.display = 'block'; 1241 if (typeof gtag === 'function') gtag('event', 'newsletter_signup'); 1242 } else { 1243 throw new Error('bad response'); 1244 } 1245 } catch { 1246 errEl.textContent = 'Something went wrong â please try again.'; 1247 errEl.style.display = 'block'; 1248 btn.disabled = false;
1249 btn.textContent = 'Subscribe Free â'; 1250 window.turnstile?.reset(); 1251 } 1252} 1253 1254(function checkSubscribed() { 1255 if (localStorage.getItem('cc_subscribed')) { 1256 document.getElementById('newsletter-wrap').style.display = 'none'; 1257 document.getElementById('nl-already').style.display = 'block'; 1258 } 1259})(); 1260 1261/* âââââââââââââââââââââââââââââââââââââââââââ 1262 NAV SCROLL HELPERS 1263âââââââââââââââââââââââââââââââââââââââââââ */ 1264 1265/* âââââââââââââââââââââââââââââââââââââââââââ 1266 WELCOME TOAST (post-login redirect) 1267âââââââââââââââââââââââââââââââââââââââââââ */ 1268(function () { 1269 const msg = sessionStorage.getItem('cc_welcome'); 1270 if (!msg) return; 1271 sessionStorage.removeItem('cc_welcome'); 1272 const t = document.createElement('div'); 1273 t.style.cssText = 'position:fixed;bottom:28px;left:50%;transform:translateX(-50%) translateY(80px);background:#1A1A2E;color:#fff;padding:13px 22px;border-radius:12px;font-size:14px;font-weight:500;z-index:9999;opacity:0;transition:all .35s cubic-bezier(.4,0,.2,1);max-width:calc(100vw - 32px);width:max-content;text-align:center;box-shadow:0 8px 28px rgba(0,0,0,.25);font-family:Inter,sans-serif;'; 1274 // msg carries the user's name, so it goes in as text, not HTML. 1275 t.innerHTML = '<i class="fas fa-hand" style="margin-right:6px"></i>'; 1276 t.append(msg); 1277 document.body.appendChild(t); 1278 setTimeout(() => { t.style.opacity = '1'; t.style.transform = 'translateX(-50%) translateY(0)'; }, 100); 1279 setTimeout(() => { t.style.opacity = '0'; t.style.transform = 'translateX(-50%) translateY(80px)'; }, 4000); 1280 setTimeout(() => t.remove(), 4500); 1281})(); 1282 1283/* âââââââââââââââââââââââââââââââââââââââââââ 1284 BEST CARDS TABLE â DYNAMIC CATEGORY TABS 1285âââââââââââââââââââââââââââââââââââââââââââ */ 1286const ISSUER_BADGE = { 1287 'Chase': 'ci-chase', 1288 'American Express': 'ci-amex', 1289 'Citi': 'ci-citi', 1290 'Capital One': 'ci-cap', 1291 'Discover': 'ci-disc', 1292}; 1293function issuerBadge(issuer) { 1294 return ISSUER_BADGE[issuer] || 'ci-chase'; 1295} 1296 1297const COMP_PAGE_SIZE = 6; 1298let _compCat = 'bestoffers'; 1299 1300function _getFilteredCards(cat) { 1301 const allCards = window.ALL_CARDS || window.CCEngine.getCardPool(); 1302 let filtered = [...allCards] 1303 .filter(c => (c.categories || []).includes(cat)) 1304 .sort((a, b) => (b.cardScore || 0) - (a.cardScore || 0)); 1305 // Fallback: if no cards match, show top-rated overall 1306 if (!filtered.length) { 1307 filtered = [...allCards].sort((a, b) => (b.cardScore || 0) - (a.cardScore || 0)); 1308 } 1309 return filtered; 1310} 1311 1312function renderCompTable(cat, page) { 1313 page = (page === undefined) ? 0 : page; 1314 _compCat = cat; 1315 1316 const filtered = _getFilteredCards(cat); 1317 const totalPages = Math.max(1, Math.ceil(filtered.length / COMP_PAGE_SIZE)); 1318 const safePage = Math.min(Math.max(0, page), totalPages - 1); 1319 const pageCards = filtered.slice(safePage * COMP_PAGE_SIZE, (safePage + 1) * COMP_PAGE_SIZE); 1320 1321 const tbody = document.getElementById('comp-table-body'); 1322 if (!tbody) return; 1323 const header = `<div class="comp-header"> 1324 <span></span><span>Card</span><span>Welcome Bonus</span><span>Best Rate</span><span>Annual Fee</span><span></span> 1325 </div>`; 1326 1327 const rows = pageCards.map(c => { 1328 const badge = issuerBadge(c.issuer); 1329 const imgHtml = c.cardImage 1330 ? `<img src="${c.cardImage}" alt="${c.name}" loading="lazy" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:8px;" onerror="this.style.display='none'">` 1331 : ''; 1332 const bonusShort = c.bonusShort || (c.bonusDesc ? c.bonusDesc.split(' after')[0] : 'â'); 1333 const bonusVal = c.bonusValue ? `â $${c.bonusValue.toLocaleStr
1333ing()} value` : ' '; 1334 const feeLabel = c.annualFee === 0 1335 ? '<span style="color:#10B981;font-weight:700;">$0/yr</span>' 1336 : `$${c.annualFee}/yr`; 1337 return `<div class="comp-row" onclick="openDrawer('${c.id}')"> 1338 <div class="comp-info"> 1339 <div class="comp-card-img ${badge}" style="position:relative;">${imgHtml}</div> 1340 <div> 1341 <div class="comp-name">${c.name}</div> 1342 <div class="comp-sub">${c.bestFor || ''}</div> 1343 </div> 1344 </div> 1345 <div class="comp-cell"><span class="hi">${bonusShort}</span><br><span class="tiny">${bonusVal}</span></div> 1346 <div class="comp-cell"><span class="badge">${c.rateLabel || 'â'}</span></div> 1347 <div class="comp-cell">${feeLabel}</div> 1348 <div class="comp-cell"><button class="comp-btn" onclick="event.stopPropagation();applyNow('${c.id}','comp_table')">Apply Now</button></div> 1349 </div>`; 1350 }).join(''); 1351 1352 tbody.innerHTML = header + rows; 1353 tbody.classList.add('visible'); 1354 1355 // Render pagination controls 1356 const pg = document.getElementById('comp-pagination'); 1357 if (pg) { 1358 if (totalPages <= 1) { 1359 pg.innerHTML = ''; 1360 } else { 1361 let pgHtml = `<button class="comp-pager-btn" onclick="renderCompTable('${cat}',${safePage - 1})" ${safePage === 0 ? 'disabled' : ''}><i class="fas fa-chevron-left"></i></button>`; 1362 for (let i = 0; i < totalPages; i++) { 1363 pgHtml += `<button class="comp-pager-num${i === safePage ? ' active' : ''}" onclick="renderCompTable('${cat}',${i})">${i + 1}</button>`; 1364 } 1365 pgHtml += `<button class="comp-pager-btn" onclick="renderCompTable('${cat}',${safePage + 1})" ${safePage === totalPages - 1 ? 'disabled' : ''}><i class="fas fa-chevron-right"></i></button>`; 1366 pg.innerHTML = pgHtml; 1367 } 1368 } 1369} 1370 1371// Wire up the category tabs â resets to page 0 on every tab switch 1372(function initCompTabs() { 1373 const tabs = document.querySelectorAll('.comp-tab'); 1374 renderCompTable('bestoffers', 0);
1375 tabs.forEach(btn => { 1376 btn.addEventListener('click', () => { 1377 tabs.forEach(t => t.classList.remove('active')); 1378 btn.classList.add('active'); 1379 renderCompTable(btn.dataset.cat, 0); 1380 }); 1381 }); 1382})(); 1383 1384/* âââââââââââââââââââââââââââââââââââââââââââ 1385 AUTH â PERSONAL BANNER (scores the full window.ALL_CARDS pool via 1386 window.CCEngine â this used to only ever consider 5 hardcoded cards) 1387âââââââââââââââââââââââââââââââââââââââââââ */ 1388(function updateBannerForAuth() { 1389 if (!localStorage.getItem('cc_token')) return; 1390 let user = null; 1391 try { user = JSON.parse(localStorage.getItem('cc_user') || 'null'); } catch(e) {} 1392 if (!user || !user.prefs) return; 1393 1394 const prefs = user.prefs; 1395 const profile = window.CCEngine.profileFromQuickForm(prefs.goal, prefs.spend, prefs.credit, prefs.maxFee); 1396 const maxFee = parseFloat(prefs.maxFee) || 9999; 1397 1398 const scored = window.CCEngine.rankCards(profile, { limit: 20 }) 1399 .filter(c => c.annualFee <= maxFee) 1400 .map(c => { 1401 const yearly = Object.keys(profile.spend).reduce( 1402 (sum, cat) => sum + window.CCEngine.estimateEarn(c, cat, profile.spend[cat]) * 12, 0); 1403 const net = Math.round(yearly + (c.bonusValue / 2) - c.annualFee); 1404 return Object.assign({}, c, { yearly: Math.round(yearly), net: net }); 1405 }) 1406 .sort((a, b) => b.net - a.net); 1407 1408 if (!scored.length) return; 1409 const top = scored[0]; 1410 const fname = user.fname || 'there'; 1411 document.getElementById('pb-name').textContent = fname; 1412 const badge = document.getElementById('pb-badge'); 1413 badge.className = 'pb-badge'; 1414 badge.innerHTML = cardBadgeHtml(top); 1415 document.getElementById('pb-card-name').textContent = top.name; 1416 document.getElementById('pb-card-rate').textContent = top.rateLabel + (top.annualFee > 0 ? ' · $' + top.annualFee + '/yr' : ' · No fee'); 1417 document.getElementById('pb-earn').innerHTML = '$' + top.net.toLocaleString() + '<small>est. yearly value</small>'; 1418 document.getElementById('personal-banner').classList.remove('hidden'); 1419})(); 1420 1421function toggleUserDropdown() { 1422 const nav = document.getElementById('user-nav'); 1423 if (!nav) return; 1424 const open = nav.classList.toggle('open'); 1425 document.getElementById('user-btn').setAttribute('aria-expanded', open ? 'true' : 'false'); 1426} 1427 1428/* Close dropdown on outside click */ 1429document.addEventListener('click', function(e) { 1430 const nav = document.getElementById('user-nav'); 1431 if (nav && !nav.contains(e.target)) { 1432 nav.classList.remove('open'); 1433 const btn = document.getElementById('user-btn'); 1434 if (btn) btn.setAttribute('aria-expanded', 'false'); 1435 } 1436}); 1437 1438// signOut() is provided by nav-auth.js (window.signOut) â it clears cc_token/cc_user 1439// and calls /api/auth/logout. Not redefined here so that shared implementation wins. 1440 1441/* âââââââââââââââââââââââââââââââââââââââââââ 1442 COOKIE CONSENT 1443âââââââââââââââââââââââââââââââââââââââââââ */ 1444function acceptCookies() { 1445 localStorage.setItem('cc_consent', 'granted'); 1446 document.getElementById('cookie-banner').classList.add('hidden'); 1447 if (typeof gtag === 'function') { 1448 gtag('consent', 'update', { analytics_storage: 'granted', ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted' }); 1449 } 1450} 1451 1452function declineCookies() { 1453 localStorage.setItem('cc_consent', 'denied'); 1454 document.getElementById('cookie-banner').classList.add('hidden'); 1455 if (typeof gtag === 'function') { 1456 gtag('consent', 'update', { analytics_storage: 'denied', ad_storage: 'denied', ad_user_data: 'denied', ad_personalization: 'denied' }); 1457 } 1458} 1459 1460(function initCookieBanner() { 1461 if (localStorage.getItem('cc_consent')) return; 1462 const eu = /^Europe\//.test(Intl.DateTimeFormat().resolvedOptions().timeZone); 1463 const banner = document.getElementById('cookie-banner'); 1464 if (!eu) { 1465 banner.querySelector('p').innerHTML = 'We use analytics cookies to understand site traffic and improve CardCompass. You can opt out anytime â see our <a href="/privacy-policy">Privacy Policy</a>.'; 1466 banner.querySelector('.cookie-accept').textContent = 'OK'; 1467 banner.querySelector('.cookie-decline').textContent = 'Opt out'; 1468 }
1469 // Show banner after a short delay so page renders first 1470 setTimeout(() => banner.classList.remove('hidden'), 1500); 1471})(); 1472 1473/* âââââââââââââââââââââââââââââââââââââââââââ 1474 HOMEPAGE: SPEND CALCULATOR, COMPARE, OFFERS, LANES 1475 All ranking is window.CCEngine.rankBySpend (recommend-engine.js). 1476 Spend options are quiz.html's own buckets, so /quiz?grocery=450... 1477 pre-fills its spending question exactly. 1478âââââââââââââââââââââââââââââââââââââââââââ */ 1479(function homepage() { 1480 const cards = window.ALL_CARDS || []; 1481 const E = window.CCEngine; 1482 if (!cards.length || !E || !E.rankBySpend) return; 1483 1484 const CATS = [ 1485 ['grocery', 'Groceries', [0, 200, 450, 800]], 1486 ['dining', 'Dining out', [0, 200, 450, 800]], 1487 ['travel', 'Travel', [0, 200, 450, 800], 'booked through the cardâs portal'], 1488 ['gas', 'Gas', [0, 200, 450, 800]], 1489 ['other', 'Everything else', [200, 450, 800, 1500]] 1490 ]; 1491 const spend = { grocery: 450, dining: 200, travel: 0, gas: 200, other: 800 }; 1492 1493 // Ads land here promising a specific amount ($500/mo groceries, $400/mo dining, 1494 // $2,000/mo mixed for the debit call-out), so that amount is preselected; 1495 // ?grocery=500 etc. works too. An amount that isn't a button replaces the nearest 1496 // one, and the quiz accepts it as an exact amount. 1497 const DEBIT = { grocery: 500, dining: 300, travel: 0, gas: 200, other: 1000 }; 1498 const AD_SPEND = { groceries: { grocery: 500 }, dining: { dining: 400 }, debit: DEBIT, 'debit-static': DEBIT }; 1499 const params = new URLSearchParams(location.search); 1500 const preset = Object.assign({}, AD_SPEND[params.get('utm_content')]); 1501 CATS.forEach(([k]) => { if (/^\d{1,5}$/.test(params.get(k) || '')) preset[k] = +params.get(k); }); 1502 Object.entries(preset).forEach(([k, v]) => { 1503 const opts = CATS.find(x => x[0] === k)[2]; 1504 if (!opts.includes(v)) { 1505 opts[opts.reduce((best, o, i) => Math.abs(o - v) < Math.abs(opts[best] - v) ? i : best, 0)] = v; 1506 opts.sort((a, b) => a - b); 1507 } 1508 spend[k] = v; 1509 }); 1510 // The debit call-out quotes the whole mix net of fees with welcome offers excluded, 1511 // which is the "every year after" figure, so its visitors start on that tab. 1512 let mode = /^debit/.test(params.get('utm_content') || '') ? 'after' : 'yearOne'; 1513 let touched = false; 1514 1515 const $ = id => document.getElementById(id); 1516 const esc = s => String(s).replace(/[&<>"]/g, ch => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[ch])); 1517 const money = n => (n < 0 ? 'â$' : '$') + Math.abs(Math.round(n)).toLocaleString('en-US'); 1518 const short = c => c.name.replace(/[®â¢â ]/g, '').replace(/\s+(Credit )?Card( from .*)?$/i, '').trim(); 1519 const rate = (r, k) => (r.rates[k] || 0) + ((r.card.categories || []).includes('cashback') ? '%' : 'Ã'); 1520 const day = iso => new Date(iso + 'T12:00:00').toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); 1521 const track = cta => { if (typeof gtag === 'function') gtag('event', 'hero_cta_click', { cta: cta }); }; 1522 1523 // The site stores 219px issuer art; the same host serves 500px. Art from anywhere 1524 // else is shown as supplied (it is often angled or transparent). 1525 const COMPOSITE = ['amex-gold']; 1526 function art(c, cls, tag) { 1527 const src = (c.cardImage || '').replace('/cards/219x139/', '/cards/500x315/'); 1528 const kind = COMPOSITE.includes(c.id) ? 'gold' : /cdn\.prodstatic\.com/.test(src) ? 'flat' : 'asis'; 1529 const img = '<img src="' + esc(src) + '" alt="' + (tag === 'a' ? esc(c.name) : '') + '" loading="lazy">'; 1530 return tag === 'a' 1531 ? '<a class="' + cls + ' ' + kind + '" href="/card/' + c.id + '" data-cta="card">' + img + '</a>' 1532 : '<span class="' + cls + ' ' + kind + '" aria-hidden="true">' + img + '</span>'; 1533 } 1534 1535 function render() { 1536 const ranked = E.rankBySpend(spend, { mode: mode }); 1537 const top = ranked[0], c = top.card; 1538 const total = CATS.reduce((s, x) => s + spend[x[0]], 0); 1539 const after = mode === 'after'; 1540 $('hx-total').textContent = money(total); 1541 1542 $('hx-rows').innerHTML = CATS.map(([k, label, opts, sub]) => 1543 '<div class="hx-row"><span class="hx-row-label">' + label + (sub ? '<small>' + sub + '</small>' : '') + '</span>' + 1544 '<div class="hx-seg" role="group" aria-label="' + label + ' per month">' + 1545 opts.map(v => '<button type="button" data-cat="' + k + '" data-val="' + v + '" aria-pressed="' + (spend[k] === v) + '">' + money(v) + '</button>').join('') + 1546 '</div><span class="hx-earn">' + rate(top, k) + ' <strong>' + money(top.perCat[k]) + '</strong></span></div>').join(''); 1547 1548 // Two cards fan out behind the winner. Composite art cannot sit in a fan, so the 1549 // fan skips it; "Next best" below still names the true second and third. 1550 const fan = COMPOSITE.includes(c.id) ? [] : ranked.slice(1).filter(r => !COMPOSITE.includes(r.card.id)).slice(0, 2); 1551 $('hx-stage').innerHTML = art(c, 'hx-art hx-win', 'a') + 1552 (fan[0] ? art(fan[0].card, 'hx-art hx-r2') : '') + (fan[1] ? art(fan[1].card, 'hx-art hx-r3') : ''); 1553 1554 const q = new URLSearchParams(spend).toString(); 1555 const three = ranked.slice(0, 3).map(r => r.card); 1556 const cmpHref = '/compare?' + three.map((x, i) => 'abc'[i] + '=' + x.id).join('&'); 1557 const welcome = top.met 1558 ? '<div><span>Welcome offer: ' + esc(c.bonusDesc || c.bonusShort || '') + '</span><strong>+' + money(top.welcome) + '</strong></div>' 1559 : '<div><span>Welcome offer needs ' + money(top.req.amount) + ' in ' + top.req.months + ' months, not counted at this spending</span><strong>$0</strong></div>'; 1560 $('hx-detail').innerHTML = 1561 '<div class="hx-meta">' + esc(c.issuer) + (top.checked ? ' · Offer checked ' + day(top.checked) : '') + '</div>' + 1562 '<h2 class="hx-name"><a href="/card/' + c.id + '" data-cta="card">' + esc(c.name) + '</a></h2>' + 1563 '<div class="hx-figrow"><span class="hx-fig">' + money(after ? top.after : top.yearOne) + '</span>' + 1564 '<span class="hx-figlabel">' + (after ? 'every year after' : 'in year one') + '</span>' + 1565 '<span class="hx-recip"><strong>' + money(after ? top.yearOne : top.after) + '</strong> ' + (after ? 'in year one' : 'every year after') + '</span></div>' + 1566 '<div class="hx-actions"><a class="hx-cta" href="/quiz?' + q + '" data-cta="quiz">Continue in the quiz</a>' + 1567 '<span>Your numbers carry over. It ranks all ' + cards.length + ' cards.<
1567/span></div>' + 1568 '<div class="hx-ledger"><div><span>Rewards on this spending</span><strong>' + money(top.rewards) + '</strong></div>' + 1569 (after ? '' : welcome) + 1570 '<div><span>Annual fee</span><strong>' + (top.fee ? 'â' + money(top.fee) : '$0') + '</strong></div></div>' + 1571 (c.note ? '<p class="hx-worth">' + esc(c.note) + '</p>' : '') + 1572 '<div class="hx-next"><span>Next best</span>' + ranked.slice(1, 3).map(r => 1573 '<span>' + esc(short(r.card)) + ' <strong>' + money(after ? r.after : r.yearOne) + '</strong></span>').join('') + '</div>' + 1574 '<p class="hx-links"><a href="' + cmpHref + '" data-cta="compare">Compare these three side by side</a>' + 1575 '<a href="/card/' + c.id + '" data-cta="card">Card details</a>' + 1576 (c.guideUrl ? '<a href="' + esc(c.guideUrl) + '" data-cta="guide">Our guide</a>' : '') + '</p>'; 1577 $('hx-status').textContent = c.name + ', ' + money(after ? top.after : top.yearOne) + (after ? ' every year after.' : ' in year one.'); 1578 1579 // Compare Cards follows the same three. 1580 const row = (label, f) => '<div class="cmp-row" role="row"><span role="rowheader">' + label + '</span>' + three.map(x => '<span role="cell">' + f(x) + '</span>').join('') + '</div>'; 1581 const floor = x => { const r = ranked.find(y => y.card === x); return r.offer ? money(r.offer) : 'None'; }; 1582 $('cmp-table').innerHTML = 1583 '<div class="cmp-row cmp-head" role="row"><span role="columnheader">Your top three, from the numbers above</span>' + 1584 three.map(x => '<span class="cmp-card" role="columnheader">' + art(x, 'cc-thumb') + esc(short(x)) + '</span>').join('') + '</div>' + 1585 row('Welcome offer', x => esc(x.bonusShort || 'None')) + row('Our value', floor) + 1586 row('Annual fee', x => money(x.annualFee || 0)) + 1587 CATS.map(([k, label]) => row(label, x => rate(ranked.find(y => y.card === x), k))).join(''); 1588 $('cmp-link').href = cmpHref; 1589 document.querySelectorAll('#quiz-opts a').forEach(a => { a.href = '/quiz?goal=' + a.dataset.goal + '&' + q; }); 1590 } 1591 1592 $('hx-rows').addEventListener('click', e => { 1593 const b = e.target.closest('button[data-cat]'); 1594 if (!b) return; 1595 spend[b.dataset.cat] = +b.dataset.val; 1596 if (!touched && typeof gtag === 'function') gtag('event', 'hero_calc_use', { category: b.dataset.cat }); 1597 touched = true; 1598 render(); 1599 const again = $('hx-rows').querySelector('[data-cat="' + b.dataset.cat + '"][data-val="' + b.dataset.val + '"]'); 1600 if (again) again.focus(); // the rows re-render; keep the keyboard user's place 1601 }); 1602 document.querySelectorAll('.hx-tabs button').forEach(b => b.addEventListener('click', () => { 1603 mode = b.dataset.mode; 1604 document.querySelectorAll('.hx-tabs button').forEach(x => x.setAttribute('aria-pressed', String(x === b))); 1605 render(); 1606 })); 1607 document.querySelectorAll('.hx-tabs button').forEach(x => x.setAttribute('aria-pressed', String(x.dataset.mode === mode))); 1608 document.querySelector('.hx').addEventListener('click', e => { 1609 const a = e.target.closest('a[data-cta]'); 1610 if (a) track(a.dataset.cta); 1611 }); 1612 render(); 1613 1614 /* ââ Current Bonus Offers: the conservative floor, never the headline; stale offers are skipped ââ */ 1615 const F = window.CARD_FRESHNESS || {}; 1616 const offers = cards 1617 .filter(x => F[x.id] && F[x.id].bonusFloor > 0 && !(window.getBonusDisplay && window.getBonusDisplay(x).stale)) 1618 .sort((a, b) => F[b.id].bonusFloor - F[a.id].bonusFloor || (a.annualFee || 0) - (b.annualFee || 0)) 1619 .slice(0, 7); 1620 $('off-table').innerHTML = 1621 '<div class="off-row off-head" role="row"><span></span><span role="columnheader">Card</span><span class="off-desc" role="columnheader">Welcome offer</span>' + 1622 '<span class="off-r" role="columnheader">Our value</span><span class="off-r off-fee" role="columnheader">Annual fee</span><span class="off-r off-checked" role="columnheader">Last checked</span></div>' + 1623 offers.map(x => '<a class="off-row" role="row" href="/card/' + x.id + '">' + art(x, 'cc-thumb') + 1624 '<span class="off-name" role="cell">' + esc(x.name) + '<span class="off-issuer">
1624' + esc(x.issuer) + '</span></span>' + 1625 '<span class="off-desc" role="cell">' + esc(x.bonusDesc || x.bonusShort || '') + '</span>' + 1626 '<span class="off-val off-r" role="cell">' + money(F[x.id].bonusFloor) + '</span>' + 1627 '<span class="off-r off-fee" role="cell">' + money(x.annualFee || 0) + '</span>' + 1628 '<span class="off-r off-checked" role="cell">' + day(F[x.id].bonusUpdated) + '</span></a>').join(''); 1629 1630 /* ââ Cards by type: live counts, the two best-scored cards of each lane as art ââ */ 1631 const LANES = [['travel', 'Travel'], ['cashback', 'Cash back'], ['dining', 'Dining'], ['nofee', 'No annual fee'], ['secured', 'Build credit'], ['business', 'Business']]; 1632 const used = new Set(); 1633 $('lanes').innerHTML = LANES.map(([k, label]) => { 1634 const inLane = cards.filter(x => (x.categories || []).includes(k)); 1635 const pics = inLane.filter(x => /cdn\.prodstatic\.com/.test(x.cardImage || '') && !COMPOSITE.includes(x.id) && !used.has(x.id) 1636 && (k === 'business' || !(x.categories || []).includes('business'))) // consumer lanes show consumer cards 1637 .sort((a, b) => (b.cardScore || 0) - (a.cardScore || 0)).slice(0, 2); 1638 pics.forEach(x => used.add(x.id)); 1639 return '<a class="lane" href="/cards?cat=' + k + '"><div class="lane-art">' + pics.map(x => art(x, 'cc-thumb')).join('') + '</div>' + 1640 '<div class="lane-ft"><strong>' + label + '</strong><span>' + inLane.length + ' cards</span></div></a>'; 1641 }).join(''); 1642 1643 /* ââ Issuers ââ */ 1644 const byIssuer = {}; 1645 cards.forEach(x => { byIssuer[x.issuer] = (byIssuer[x.issuer] || 0) + 1; }); 1646 const names = Object.keys(byIssuer).sort((a, b) => byIssuer[b] - byIssuer[a]); 1647 $('issuers').innerHTML = '<span>' + cards.length + ' cards from ' + names.length + ' issuers</span>' + names.slice(0, 9).map(n => '<span>' + esc(n) + '</span>').join(''); 1648})(); 1649</script>
vendor: 1 bytes, line 1649
1649
1650<script src="/newsletter-popup.js" defer></script>
vendor: 1 bytes, line 1650
1650
1651<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"e79d8b0449fb411087aba9b1640e3938","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 1651
1651
1652<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a43fd73e19fa4e06',t:'MTc5MDkwMzU2Nw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
1652</body> 1653</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.