1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <title>Pricing | AssetMarketCap.com</title> 5 <!-- Google tag (gtag.js) -->
vendor: 64 bytes, lines 5-6
5 6<script async src="https://www.googletagmanager.com/gtag/js?id=
6G-0Y08EV6WYH
vendor: 12 bytes, line 6
6"></script>
7<script> 8
vendor: 134 bytes, lines 8-12
8window.dataLayer = window.dataLayer || []; 9 function gtag(){dataLayer.push(arguments);} 10 gtag('js', new Date()); 11 12 gtag('config', '
12G-0Y08EV6WYH
vendor: 4 bytes, line 12
12');
13</script>
13 14
15<script> 16(function () { 17 const key = 'prefers_dark_mode'; 18 // Server-side preference for logged-in users (null if not logged in or not set) 19 var serverDark = null; 20 21 var shouldBeDark; 22 if (serverDark !== null) { 23 // Use server preference and sync localStorage 24 shouldBeDark = serverDark; 25 localStorage.setItem(key, shouldBeDark ? '1' : '0'); 26 } else { 27 // Fall back to localStorage 28 shouldBeDark = (localStorage.getItem(key) === '1'); 29 } 30 31 if (shouldBeDark) document.documentElement.classList.add('dark-mode'); 32})(); 33</script>
33 34 35<meta charset="UTF-8"> 36<meta name="viewport" content="width=980, viewport-fit=cover"> 37
38<script> 39/* Native-app (Capacitor webview) detection - the bridge is injected at 40 documentStart, so this runs before first paint and the safe-area CSS 41 below applies immediately. iap.js also adds this class on /pro and 42 /account; this is the global copy. */ 43(function () { 44 try { 45 if (window.Capacitor && Capacitor.isNativePlatform && Capacitor.isNativePlatform()) { 46 document.documentElement.classList.add('is-native-app'); 47 } 48 } catch (e) { /* never block paint */ } 49})(); 50</script>
50 51<style> 52 /* ââ Safe-area handling inside the mobile apps âââââââââââââââââââââ 53 The iOS webview is edge-to-edge (the native inset in 54 MainViewController.swift never worked: Capacitor 8 makes the webview 55 the root view, so its safe-area constraints are self-referential 56 no-ops). Fix is web-side: viewport-fit=cover (meta above) activates 57 env(safe-area-inset-*), then we pad. In desktop/mobile browsers and 58 in the Android app the insets are 0, so all of this no-ops. 59 The bottom tab bar (mobile_nav.html) already carries its own 60 env(safe-area-inset-bottom) padding. */ 61 html.is-native-app body { 62 padding-top: env(safe-area-inset-top, 0px); 63 } 64 /* No third-party sign-in inside the apps. Offering Google or Facebook 65 login in the iOS app obliges us to also offer Sign in with Apple 66 (App Store guideline 4.8), and Google blocks OAuth in embedded 67 webviews anyway. Email and password sign-in is unaffected, and the 68 website keeps social login. */ 69 html.is-native-app .social-auth { 70 display: none !important; 71 } 72 /* Opaque strip behind the status bar so scrolled content doesn't 73 collide with the clock/battery text. Sits under the auth modal 74 (z-index 99999) but over page content. */ 75 html.is-native-app body::before { 76 content: ""; 77 position: fixed; 78 top: 0; left: 0; right: 0; 79 height: env(safe-area-inset-top, 0px); 80 background: var(--color-surface, #ffffff); 81 z-index: 99990; 82 pointer-events: none; 83 } 84 /* Full-screen mobile drawer opens from the very top - pad its inner 85 content down too. */ 86 html.is-native-app .m-drawer { 87 padding-top: env(safe-area-inset-top, 0px); 88 } 89</style> 90
91<script> 92/* Early-init: density + sidebar prefs before first paint. Narrow viewports 93 default to Comfortable until the user picks a density (see table.js). */ 94(function () { 95 var NARROW_DENSITY_MAX = 1100; /* matches global.css sidebar/mobile breakpoint */ 96 window.__amcNARROW_DENSITY_MAX = NARROW_DENSITY_MAX; 97 window.__amcDensityUserHasChosen = function () { 98 try { return localStorage.getItem('table_density') !== null; } catch (e) { return true; } 99 }; 100 window.__amcIsNarrowViewport = function () { 101 try { 102 if (window.matchMedia && window.matchMedia('(max-width: ' + NARROW_DENSITY_MAX + 'px)').matches) { 103 return true; 104 } 105 } catch (e) { /* ignore */ } 106 return window.innerWidth <= NARROW_DENSITY_MAX; 107 }; 108 window.__amcApplyNarrowDensity = function () { 109 if (window.__amcDensityUserHasChosen()) return; 110 if (window.__amcIsNarrowViewport()) { 111 document.documentElement.setAttribute('data-density', 'comfortable'); 112 } else { 113 document.documentElement.removeAttribute('data-density'); 114 } 115 }; 116 try { 117 var d = localStorage.getItem('table_density'); 118 if (d === 'compact' || d === 'comfortable') { 119 document.documentElement.setAttribute('data-density', d); 120 } else if (d === 'normal') { 121 document.documentElement.removeAttribute('data-density'); 122 } else { 123 window.__amcApplyNarrowDensity(); 124 } 125 if (localStorage.getItem('sidebar_collapsed') === '1') { 126 document.documentElement.classList.add('sidebar-collapsed'); 127 } 128 } catch (e) { /* localStorage blocked â silently fall back to defaults */ } 129 function syncNarrowDensityAfterLayout() { 130 if (window.__amcApplyNarrowDensity) window.__amcApplyNarrowDensity(); 131 } 132 if (document.readyState === 'loading') { 133 document.addEventListener('DOMContentLoaded', syncNarrowDensityAfterLayout); 134 } else { 135 syncNarrowDensityAfterLayout(); 136 } 137})(); 138</script>
138 139
140<script> 141/* Back-to-top button â injects a fixed-position circle into <body> 142 once the DOM is ready. Becomes visible after the user has scrolled 143 ~300px and smooth-scrolls back to the top on click. */ 144(function () { 145 function init() { 146 if (document.getElementById('backToTop')) return; 147 var btn = document.createElement('button'); 148 btn.id = 'backToTop'; 149 btn.type = 'button'; 150 btn.setAttribute('aria-label', 'Back to top'); 151 btn.title = 'Back to top'; 152 btn.innerHTML = 153 '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" ' + 154 'stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + 155 '<line x1="12" y1="19" x2="12" y2="5"/>' + 156 '<polyline points="5 12 12 5 19 12"/>' + 157 '</svg>'; 158 document.body.appendChild(btn); 159 btn.addEventListener('click', function () { 160 window.scrollTo({ top: 0, behavior: 'smooth' }); 161 }); 162 var threshold = 300; 163 function onScroll() { 164 var y = window.pageYOffset || document.documentElement.scrollTop || 0; 165 btn.classList.toggle('is-visible', y > threshold); 166 } 167 window.addEventListener('scroll', onScroll, { passive: true }); 168 onScroll(); 169 } 170 if (document.readyState === 'loading') { 171 document.addEventListener('DOMContentLoaded', init); 172 } else { 173 init(); 174 } 175})(); 176</script>
176 177 178
179<script> 180 // Single-flight pre-fetch of the basic leaderboard JSON. 181 // Both the flippenings ticker and the main table need this 60KB 182 // payload; wrapping it once here means a single network request 183 // is in flight before DataTables even loads from CDN. Both 184 // consumers await window.__basicDataPromise. 185 window.__basicDataPromise = fetch("/data/basic/daily.json", { credentials: 'same-origin' }) 186 .then(function (r) { return r.ok ? r.json() : null; }) 187 .then(function (json) { 188 if (json && window.__amcApplyNarrowDensity) window.__amcApplyNarrowDensity(); 189 return json; 190 }) 191 .catch(function () { return null; }); 192 193 // Always-live basic snapshot. In Time-Machine mode the table (above) loads a 194 // historical archive, but the header totals, H2 narrative, dominance bar and 195 // the Market Overview / Highlights sidebar must keep showing CURRENT data. 196 // In live mode this is the same payload, so just alias it (no extra fetch). 197 198 window.__liveBasicPromise = window.__basicDataPromise; 199 200</script>
200 201 202<meta name="robots" content="index, follow"> 203 204 205 206 <meta name="description" content="Track 35,000+ assets ranked by market cap. Live prices, charts and data for stocks, crypto, commodities and currencies." /> 207 208 <meta property="og:type" content="website" /> 209 <meta property="og:site_name" content="AssetMarketCap"/> 210 <meta property="og:url" content="https://assetmarketcap.com/" /> 211 <meta property="og:title" content="Assets Ranked by Market Cap | AssetMarketCap" /> 212 <meta property="og:description" content="Track 35,000+ assets ranked by market capitalization. Live prices, charts and data for stocks, crypto, commodities and currencies." /> 213 <meta property="og:image" content="https://assetmarketcap.com/static/preview.jpg" /> 214 <meta property="og:image:width" content="1200" /> 215 <meta property="og:image:height" content="630" /> 216 217 <meta name="twitter:card" content="summary_large_image" /> 218 <meta name="twitter:site" content="@assetmarketcap" /> 219 <meta name="twitter:title" content="Assets Ranked by Market Cap | AssetMarketCap" /> 220 <meta name="twitter:description" content="Track 35,000+ assets ranked by market capitalization. Live prices, charts and data for stocks, crypto, commodities and currencies." /> 221 <meta name="twitter:image" content="https://assetmarketcap.com/static/preview.jpg" /> 222 223 224<!-- Apple touch icon --> 225<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png?v=3"> 226 227<meta http-equiv='cache-control' content='no-cache'> 228<meta http-equiv='expires' content='0'> 229<meta http-equiv='pragma' content='no-cache'> 230
231<script type="application/ld+json"> 232{ 233 "@context": "https://schema.org", 234 "@type": "WebSite", 235 "name": "AssetMarketCap", 236 "alternateName": "Asset Market Cap", 237 "url": "https://assetmarketcap.com/", 238 "description": "Track 35,000+ assets ranked by market capitalization. Live prices, charts and data for stocks, crypto, commodities and currencies.", 239 "potentialAction": { 240 "@type": "SearchAction", 241 "target": "https://assetmarketcap.com/?q={search_term_string}", 242 "query-input": "required name=search_term_string" 243 } 244} 245</script>
vendor: 1 bytes, line 245
245
246<script type="application/ld+json"> 247{ 248 "@context": "https://schema.org", 249 "@type": "Organization", 250 "name": "AssetMarketCap", 251 "url": "https://assetmarketcap.com", 252 "logo": "https://assetmarketcap.com/static/logo-dark.png", 253 "sameAs": [ 254 "https://x.com/assetmarketcap", 255 "https://www.youtube.com/@assetmarketcap", 256 "https://www.facebook.com/assetmarketcap", 257 "https://instagram.com/assetmarketcap", 258 "https://discord.gg/KKDAMNbStQ", 259 "https://reddit.com/r/assetmarketcap" 260 ] 261} 262</script>
vendor: 1 bytes, line 262
262
263<script type="application/ld+json"> 264{ 265 "@context": "https://schema.org", 266 "@type": "SiteNavigationElement", 267 "name": ["News", "Learn", "Deals", "Community", "Merch", "Newsletter", "Pricing", "Pro", "API", "Support"], 268 "url": [ 269 "https://assetmarketcap.com/news", 270 "https://assetmarketcap.com/learn", 271 "https://assetmarketcap.com/deals", 272 "https://discord.gg/KKDAMNbStQ", 273 "https://merch.assetmarketcap.com", 274 "https://assetmarketcap.com/newsletter", 275 "https://assetmarketcap.com/pricing", 276 "https://assetmarketcap.com/pro", 277 "https://assetmarketcap.com/api-info", 278 "https://assetmarketcap.com/support" 279 ] 280} 281</script>
281 282 283<!------ Google Fonts ----------> 284<link rel="preconnect" href="https://fonts.googleapis.com"> 285<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 286<link rel="preconnect" href="https://cdn.datatables.net" crossorigin> 287<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin> 288<link rel="preload" href="https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" as="style"> 289<link href="https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" rel="stylesheet"> 290 291<!------ Favicon & icons ----------> 292<link rel="icon" type="image/x-icon" href="/favicon.ico?v=2"> 293<link rel="icon" type="image/png" sizes="192x192" href="/static/favicon-192.png?v=3"> 294<link rel="canonical" href="https://assetmarketcap.com/pricing" /> 295 296 297 298<!------ for the footer ----------> 299<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> 300<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> 301 302 303 304<!-- DataTables CSS --> 305<link rel="stylesheet" href="https://cdn.datatables.net/1.13.11/css/jquery.dataTables.min.css"> 306<!------ CSS - must come after Bootstrap to avoid overrides ----------> 307<link rel="stylesheet" href="/static/css/global.css?v=2026062201"> 308 309<!-- Responsive extension --> 310<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.5.0/css/responsive.dataTables.min.css"> 311 312<!-- ColReorder extension (column drag-reorder) --> 313<link rel="stylesheet" href="https://cdn.datatables.net/colreorder/1.7.0/css/colReorder.dataTables.min.css"> 314
315<script defer src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
vendor: 1 bytes, line 315
315
316<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
vendor: 1 bytes, line 316
316
317<script defer src="https://cdn.jsdelivr.net/npm/luxon@3/build/global/luxon.min.js"></script>
vendor: 1 bytes, line 317
317
318<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-adapter-luxon.umd.min.js"></script>
318 319 320 321 322 323 324<!-- Trustpilot invitation script (integration key registered site-wide, 325 2026-07-22). Loads async from invitejs.trustpilot.com; used to send 326 review invitations via tp('trigger', ...) events. -->
327<script> 328 (function(w,d,s,r,n){w.TrustpilotObject=n;w[n]=w[n]||function(){(w[n].q=w[n].q||[]).push(arguments)}; 329 a=d.createElement(s);a.async=1;a.src=r;a.type='text/java'+s;f=d.getElementsByTagName(s)[0]; 330 f.parentNode.insertBefore(a,f)})(window,document,'script', 'https://invitejs.trustpilot.com/tp.min.js', 'tp'); 331 tp('register', 'HfViG6gtA0OwevMi'); 332</script>
332 333 334<!-- TrustBox script (widget bootstrap - per TP docs this belongs in <head>; 335 the Review Collector TrustBox on the homepage banner depends on it) -->
336<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async></script>
336 337<!-- End TrustBox script --> 338 339<!-- Native in-app purchases. No-ops in a normal browser; inside the Capacitor 340 webview it hides the web billing UI and replaces the Stripe checkout forms 341 with the App Store / Play Store purchase sheet. Loaded globally because the 342 subscribe forms appear on /pro, /pricing, /account AND in the auth modal 343 that header.html includes on every page (App Store guideline 3.1.1). -->
344<script src="/static/js/iap.js?v=3" defer></script>
344 345
345<script type="application/ld+json"> 346 { 347 "@context": "https://schema.org", 348 "@type": "BreadcrumbList", 349 "itemListElement": [ 350 { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://assetmarketcap.com/" }, 351 { "@type": "ListItem", "position": 2, "name": "Pricing", "item": "https://assetmarketcap.com/pricing" } 352 ] 353 } 354 </script>
354 355 <meta name="description" content="AssetMarketCap pricing - Free, Pro and Pro Plus plans. The full 35,000+ asset dataset is free. Upgrade for ad-free browsing, premium news, the Net Worth Tracker with live prices, Time Machine archive back to 2010, JSON/CSV downloads, and unlimited tools."> 356 <style> 357 /* ============================================================ 358 PRICING 2026 - Apple monochrome (shares token system with /pro) 359 ============================================================ */ 360 .x-page{ 361 --x-bg: #ffffff; 362 --x-bg-soft: #f5f5f7; 363 --x-bg-card: #ffffff; 364 365 --x-text: #1d1d1f; 366 --x-text-muted: #6e6e73; 367 --x-text-faint: #86868b; 368 369 --x-border: rgba(0,0,0,0.10); 370 --x-border-soft: rgba(0,0,0,0.06); 371 --x-divider: rgba(0,0,0,0.10); 372 373 --x-cta-fill: #1d1d1f; 374 --x-cta-fill-h: #000000; 375 --x-cta-on: #ffffff; 376 377 background: var(--x-bg); 378 color: var(--x-text); 379 font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Nunito", "Helvetica Neue", Arial, sans-serif; 380 -webkit-font-smoothing: antialiased; 381 -moz-osx-font-smoothing: grayscale; 382 } 383 html.dark-mode .x-page{ 384 --x-bg: #000000; 385 --x-bg-soft: #161617; 386 --x-bg-card: #1d1d1f; 387 388 --x-text: #f5f5f7; 389 --x-text-muted: #a1a1a6; 390 --x-text-faint: #86868b; 391 392 --x-border: rgba(255,255,255,0.12); 393 --x-border-soft: rgba(255,255,255,0.08); 394 --x-divider: rgba(255,255,255,0.12); 395 396 --x-cta-fill: #f5f5f7; 397 --x-cta-fill-h: #ffffff; 398 --x-cta-on: #1d1d1f; 399 } 400 .x-page *{ box-sizing: border-box; } 401 .x-wrap{ max-width: 1180px; margin: 0 auto; padding: 0 22px; } 402 .x-band{ background: var(--x-bg); } 403 .x-band--soft{ background: var(--x-bg-soft); } 404 405 /* Typography ----------------------------------------- */ 406 .x-eyebrow{ text-align: center; font-size: 0.82rem; font-weight: 600; color: var(--x-text-muted); margin: 0 0 14px; } 407 .x-eyebrow--left{ text-align: left; } 408 .x-eyebrow strong{ color: var(--x-text); font-weight: 600; } 409 .x-h1{ 410 text-align: center; max-width: 920px; margin: 0 auto 18px; 411 font-size: clamp(2.6rem, 6vw, 4.6rem); font-weight: 600; 412 letter-spacing: -0.035em; line-height: 1.04; color: var(--x-text); 413 } 414 .x-h2{ 415 text-align: center; max-width: 800px; margin: 0 auto 14px; 416 font-size: clamp(2rem, 4vw, 3.2rem); font-weight: 600; 417 letter-spacing: -0.025em; line-height: 1.08; color: var(--x-text); 418 } 419 .x-lede{ 420 text-align: center; max-width: 660px; margin: 0 auto 32px; 421 font-size: clamp(1.08rem, 1.5vw, 1.28rem); line-height: 1.45; 422 color: var(--x-text-muted); 423 } 424 .x-sub{ 425 text-align: center; color: var(--x-text-muted); 426 max-width: 640px; margin: 0 auto 48px; 427 font-size: 1.08rem; line-height: 1.5; 428 } 429 .x-sec{ padding: 110px 0; } 430 .x-sec--md{ padding: 80px 0; } 431 .x-hero{ text-align: center; padding: 120px 0 50px; } 432 @media (max-width: 720px){ 433 .x-sec{ padding: 70px 0; } 434 .x-hero{ padding: 80px 0 30px; } 435 } 436 437 /* CTAs ----------------------------------------------- */ 438 .x-cta{ 439 display: inline-flex; align-items: center; justify-content: center; gap: 6px; 440 padding: 12px 22px; border-radius: 980px; 441 border: 1px solid var(--x-cta-fill); background: var(--x-cta-fill); 442 color: var(--x-cta-on); 443 font: 600 1rem/1.2 inherit; letter-spacing: -0.005em; 444 cursor: pointer; text-decoration: none; 445 transition: background 0.18s, border-color 0.18s, color 0.18s; 446 white-space: nowrap; 447 } 448 .x-cta:hover{ background: var(--x-cta-fill-h); border-color: var(--x-cta-fill-h); color: var(--x-cta-on); } 449 .x-cta--lg{ padding: 14px 28px; font-size: 1.06rem; } 450 .x-cta--ghost{ background: transparent; border-color: var(--x-text); color: var(--x-text); } 451 .x-cta--ghost:hover{ background: var(--x-text); color: var(--x-bg); border-color: var(--x-text); } 452 .x-link{ 453 display: inline-flex; align-items: center; gap: 4px; 454 color: var(--x-text); font: 600 1rem/1 inherit; 455 text-decoration: none; padding: 12px 0; letter-spacing: -0.005em; 456 } 457 .x-link::after{ content: "\203A"; font-weight: 400; font-size: 1.15em; transition: transform 0.18s; display: inline-block; } 458 .x-link:hover::after{ transform: translateX(3px); } 459 460 /* Billing toggle ------------------------------------- */ 461 .x-bill{ 462 display: inline-flex; align-items: center; 463 padding: 4px; border-radius: 999px; 464 background: var(--x-bg-card); 465 border: 1px solid var(--x-border-soft); 466 position: relative; 467 } 468 .x-bill button{ 469 appearance: none; border: 0; background: transparent; 470 color: var(--x-text-muted); 471 font: 600 0.9rem/1 inherit; letter-spacing: -0.005em; 472 padding: 10px 22px; border-radius: 999px; cursor: pointer; 473 transition: color 0.2s; 474 position: relative; z-index: 2; 475 } 476 .x-bill button.is-on{ color: var(--x-cta-on); } 477 .x-bill-thumb{ 478 position: absolute; top: 4px; bottom: 4px; 479 border-radius: 999px; 480 background: var(--x-cta-fill); 481 transition: left 0.24s cubic-bezier(.5,1.4,.4,1), width 0.24s cubic-bezier(.5,1.4,.4,1); 482 z-index: 1; 483 } 484 .x-bill-save{ color: var(--x-text-muted); font-size: 0.78rem; font-weight: 500; } 485 486 /* Plan cards (3-up) ---------------------------------- */ 487 .x-plans{ display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 22px; max-width: 1100px; margin: 0 auto; } 488 @media (max-width: 980px){ .x-plans{ grid-template-columns: 1fr; max-width: 460px; } } 489 .x-plan{ 490 position: relative; padding: 36px 32px; 491 border-radius: 22px; 492 background: var(--x-bg-card); 493 border: 1px solid var(--x-border); 494 display: flex; flex-direction: column; 495 } 496 .x-plan--featured{ border-color: var(--x-text); border-width: 2px; padding: 35px 31px; } 497 .x-plan-tag{ 498 position: absolute; top: -14px; left: 24px; 499 padding: 5px 12px; border-radius: 999px; 500 background: var(--x-cta-fill); color: var(--x-cta-on); 501 font: 600 0.72rem/1 inherit; letter-spacing: 0.04em; text-transform: uppercase; 502 white-space: nowrap; 503 } 504 .x-plan-name{ font-size: 0.82rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--x-text-faint); margin-bottom: 14px; } 505 .x-plan-price{ display: flex; align-items: baseline; gap: 3px; margin-bottom: 8px; min-height: 56px; } 506 .x-plan-cur{ font-size: 1.4rem; font-weight: 600; color: var(--x-text); align-self: flex-start; margin-top: 8px; } 507 .x-plan-amt{ font-size: clamp(2.8rem, 4.5vw, 3.4rem); font-weight: 600; letter-spacing: -0.025em; line-height: 1; color: var(--x-text); } 508 .x-plan-per{ font-size: 1rem; color: var(--x-text-muted); margin-left: 6px; } 509 .x-plan-tag-line{ color: var(--x-text-muted); font-size: 0.96rem; line-height: 1.5; margin: 6px 0 24px 0; min-height: 48px; } 510 .x-plan-feats{ list-style: none; padding: 0; margin: 0 0 28px 0; display: flex; flex-direction: column; gap: 12px; font-size: 0.95rem; } 511 .x-plan-feats li{ display: flex; gap: 11px; align-items: flex-start; line-height: 1.45; color: var(--x-text); } 512 .x-plan-feats li::before{ 513 content: ""; flex: 0 0 16px; height: 16px; margin-top: 2px; 514 background: var(--x-text); 515 mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center / 80% no-repeat; 516 -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center / 80% no-repeat; 517 } 518 .x-plan-cta-wrap{ margin-top: auto; } 519 .x-plan-cta-wrap .x-cta{ width: 100%; } 520 521 /* Personas ------------------------------------------- */ 522 .x-personas{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--x-divider); border: 1px solid var(--x-divider); border-radius: 18px; overflow: hidden; } 523 @media (max-width: 920px){ .x-personas{ grid-template-columns: 1fr; } } 524 .x-persona{ padding: 32px 28px; background: var(--x-bg); } 525 .x-persona-ico{ width: 40px; height: 40px; margin-bottom: 22px; color: var(--x-text); } 526 .x-persona-ico svg{ width: 100%; height: 100%; display: block; } 527 .x-persona-label{ font-size: 0.75rem; font-weight: 600; letter-spacing: 0.10em; text-transform: uppercase; color: var(--x-text-muted); margin-bottom: 6px; } 528 .x-persona h3{ margin: 0 0 10px 0; font-size: 1.22rem; font-weight: 600; color: var(--x-text); letter-spacing: -0.005em; } 529 .x-persona p{ margin: 0 0 16px 0; color: var(--x-text-muted); font-size: 0.98rem; line-height: 1.5; } 530 531 /* Compare table -------------------------------------- */ 532 .x-compare{ 533 border-radius: 18px; 534 background: var(--x-bg-card); 535 border: 1px solid var(--x-border); 536 overflow: hidden; 537 } 538 .x-ct{ width: 100%; border-collapse: collapse; font-size: 0.98rem; color: var(--x-text); } 539 .x-ct thead th{ 540 padding: 24px 16px; 541 font: 600 0.78rem/1 inherit; text-transform: uppercase; letter-spacing: 0.08em; 542 color: var(--x-text-muted); text-align: center; 543 border-bottom: 1px solid var(--x-divider); 544 background: var(--x-bg-soft); 545 } 546 .x-ct thead th:first-child{ text-align: left; padding-left: 28px; color: var(--x-text); } 547 .x-ct thead th.is-feat{ color: var(--x-text); position: relative; } 548 .x-ct thead th.is-feat::after{ 549 content: ""; position: absolute; bottom: -1px; left: 0; right: 0; height: 2px; 550 background: var(--x-text); 551 } 552 .x-ct td{ 553 padding: 16px; 554 text-align: center; 555 border-bottom: 1px solid var(--x-border-soft); 556 } 557 .x-ct td:first-child{ text-align: left; padding-left: 28px; font-weight: 500; } 558 .x-ct td.is-feat{ background: var(--x-bg-soft); } 559 .x-ct tbody tr:last-child td{ border-bottom: none; } 560 .x-tick{ 561 display: inline-flex; width: 22px; height: 22px; 562 align-items: center; justify-content: center; 563 color: var(--x-text); 564 } 565 .x-tick svg{ width: 18px; height: 18px; display: block; } 566 .x-cross{ color: var(--x-text-faint); font-size: 0.95rem; } 567 .x-pill-cell{ color: var(--x-text-muted); font-size: 0.92rem; } 568 .x-pill-cell--good{ 569 display: inline-block; padding: 4px 12px; border-radius: 999px; 570 background: var(--x-cta-fill); color: var(--x-cta-on); 571 font: 600 0.84rem/1.4 inherit; 572 } 573 @media (max-width: 760px){ 574 .x-ct{ font-size: 0.84rem; } 575 .x-ct thead th, .x-ct td{ padding: 12px 8px; } 576 .x-ct thead th:first-child, .x-ct td:first-child{ padding-left: 14px; } 577 } 578 579 /* FAQ ------------------------------------------------- */ 580 .x-faq{ max-width: 820px; margin: 0 auto; border-top: 1px solid var(--x-divider); } 581 .x-faq-item{ border-bottom: 1px solid var(--x-divider); } 582 .x-faq-item summary{ 583 list-style: none; cursor: pointer; 584 padding: 26px 52px 26px 4px; position: relative; 585 font: 600 1.12rem/1.3 inherit; 586 color: var(--x-text); 587 letter-spacing: -0.005em; 588 } 589 .x-faq-item summary::-webkit-details-marker{ display: none; } 590 .x-faq-item summary i.x-faq-icon{ 591 position: absolute; right: 8px; top: 50%; width: 14px; height: 14px; 592 margin-top: -7px; display: block; pointer-events: none; 593 } 594 .x-faq-item summary i.x-faq-icon::before, 595 .x-faq-item summary i.x-faq-icon::after{ 596 content: ""; position: absolute; left: 0; top: 50%; width: 14px; height: 1.5px; 597 margin-top: -0.75px; background: var(--x-text); border-radius: 1px; 598 transition: opacity 0.2s, transform 0.2s; 599 } 600 .x-faq-item summary i.x-faq-icon::after{ transform: rotate(90deg); } 601 .x-faq-item[open] summary i.x-faq-icon::after{ transform: rotate(0deg); opacity: 0; } 602 .x-faq-body{ padding: 0 52px 28px 4px; color: var(--x-text-muted); font-size: 1rem; line-height: 1.55; } 603 604 /* Final ---------------------------------------------- */ 605 .x-final{ max-width: 880px; margin: 0 auto; padding: 100px 30px 110px; text-align: center; } 606 .x-final h2{ font-size: clamp(2.2rem, 4.5vw, 3.4rem); font-weight: 600; letter-spacing: -0.03em; margin: 0 0 18px 0; line-height: 1.05; color: var(--x-text); } 607 .x-final p{ color: var(--x-text-muted); font-size: 1.18rem; line-height: 1.5; max-width: 540px; margin: 0 auto 36px; } 608 .x-final-cta{ display: inline-flex; gap: 20px; flex-wrap: wrap; justify-content: center; align-items: center; } 609 .x-disclaimer{ max-width: 820px; margin: 0 auto; padding: 0 22px 80px; color: var(--x-text-faint); font-size: 0.82rem; line-height: 1.55; text-align: center; } 610 </style> 611</head> 612<body> 613 614 615 616<div class="top-bar"> 617 <div class="col-lg-10 col-lg-offset-1 top-bar-inner"> 618 619 620 <div class="upper-header stats-strip top-bar-stats"> 621 <div class="upper-categories"> 622 <div class="upper-block"> 623 <a href="#" class="category-link" data-table-category=""> 624 total assets (<span id="noOfAssets_All">38,592</span>):
625 <span class="marketcap-value" id="MarketCaps_All">$1,083.80T</span> 626 </a> 627 </div> 628 <div class="upper-block"> 629 630 <a href="#" class="category-link" data-table-category="real-estate"> 631 real estate: 632 <span class="marketcap-value" id="MarketCaps_RealEstate">$448.91T</span> 633 </a> 634 </div> 635 <div class="upper-block"> 636 <a href="#" class="category-link" data-table-category="Commodity"> 637 commodities (<span id="noOfAssets_Comms">7</span>): 638 <span class="marketcap-value" id="MarketCaps_Comms">$225.33T</span> 639 </a> 640 </div> 641 <div class="upper-block"> 642 <a href="#" class="category-link" data-table-category="Company"> 643 companies (<span id="noOfAssets_Companies">20,655</span>): 644 <span class="marketcap-value" id="MarketCaps_Companies">$149.86T</span> 645 </a> 646 </div> 647 <div class="upper-block"> 648 <a href="#" class="category-link" data-table-category="Currency"> 649 currencies (<span id="noOfAssets_Currs">113</span>): 650 <span class="marketcap-value" id="MarketCaps_Currs">$139.77T</span> 651 </a> 652 </div> 653 <div class="upper-block"> 654 <a href="#" class="category-link" data-table-category="Bond"> 655 govt bonds (<span id="noOfAssets_Bonds">177</span>): 656 <span class="marketcap-value" id="MarketCaps_Bonds">$116.98T</span> 657 </a> 658 </div> 659 <div class="upper-block"> 660 <a href="#" class="category-link" data-table-category="Cryptocurrency"> 661 cryptos (<span id="noOfAssets_Cryptos">12,154</span>): 662 <span class="marketcap-value" id="MarketCaps_Cryptos">$2.95T</span> 663 </a> 664 </div> 665 <span class="stats-scroll-hint" aria-hidden="true">›</span> 666 </div> 667 </div> 668
668<script> 669 (function () { 670 // Toggle a right-edge chevron on the assets strip whenever there's more 671 // content to scroll to (and hide it once scrolled to the end). 672 var strip = document.querySelector('.top-bar-stats'); 673 if (!strip) return; 674 function sync() { 675 var more = strip.scrollWidth - strip.clientWidth - strip.scrollLeft > 4; 676 strip.classList.toggle('can-scroll-right', more); 677 } 678 strip.addEventListener('scroll', sync, { passive: true }); 679 window.addEventListener('resize', sync); 680 // Re-check after the market-cap numbers fill in (they load async). 681 sync(); 682 setTimeout(sync, 800); 683 setTimeout(sync, 2500); 684 })(); 685 </script>
685 686 687 688 <div class="top-bar-actions"> 689 690 691 <a href="/advertise" class="nav-pill nav-pill--ghost nav-advertise-pill" title="Advertise with us"> 692 <svg class="nav-pill-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 693 <path d="m3 11 18-5v12L3 14v-3z"/> 694 <path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/> 695 </svg> 696 <span class="nav-pill-label">Advertise</span> 697 </a> 698 699 700 <div class="unit-dropdown nav-currency"> 701 <label for="unit-select" class="sr-only">Currency</label> 702 <select id="unit-select" required class="unit-select"> 703 <option value="USD" selected>USD ($)</option> 704 <option value="GOLD">Gold (oz)</option> 705 <option value="BTC">Bitcoin (â¿)</option> 706 <option value="SATS">Bitcoin (sats)</option> 707 708 <option value="SILVER" hidden disabled>Silver (oz)</option> 709 <option value="mBTC" hidden disabled>mBitcoin (mâ¿)</option> 710 <option value="uBTC" hidden disabled>μBitcoin (μâ¿)</option> 711 </select> 712 <img id="unit-logo" 713 src="/static/icons/flags/us.png" 714 alt="Selected currency icon" 715 class="unit-logo-icon"> 716 </div> 717 718 719 <div class="nav-dropdown nav-help-dropdown"> 720 <button type="button" class="nav-icon-btn nav-dropdown-trigger nav-help-trigger" 721 aria-haspopup="true" aria-expanded="false" aria-label="Help"> 722 <i class="fa-solid fa-question"></i> 723 </button> 724 <ul class="nav-dropdown-menu nav-help-menu" role="menu"> 725 <li role="none"><a href="/faqs" role="menuitem"><i class="fa-regular fa-circle-question"></i> FAQs</a></li> 726 <li role="none"><a href="/support" role="menuitem"><i class="fa-regular fa-life-ring"></i> Support</a></li> 727 </ul> 728 </div> 729 730 731 <div class="nav-dropdown nav-mobile-dropdown"> 732 <button type="button" id="openAppPopup" class="nav-icon-btn nav-dropdown-trigger js-open-app-popup nav-mobile-trigger" 733 aria-haspopup="true" aria-expanded="false" aria-label="Get the app"> 734 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 735 <rect x="5" y="2" width="14" height="20" rx="2.5" ry="2.5"/> 736 <line x1="12" y1="18" x2="12" y2="18"/> 737 </svg> 738 </button> 739 <div class="nav-dropdown-menu nav-mobile-menu" role="menu" aria-label="Scan to download the app"> 740 <div class="nav-mobile-title">Scan to get the app</div> 741 <img class="nav-mobile-qr" 742 src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&margin=0&data=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.assetmarketcap.app" 743 alt="Google Play QR code" width="160" height="160" loading="lazy"> 744 <div class="nav-mobile-sub">Android available now. iOS coming soon.</div> 745 </div> 746 </div> 747 748 749 <button id="darkModeBtn" class="dark-mode-btn nav-icon-btn" type="button" aria-label="Toggle dark mode"> 750 <svg class="dm-icon dm-icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 751 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 752 <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/> 753 </svg> 754 <svg class="dm-icon dm-icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 755 fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"> 756 <circle cx="12" cy="12" r="4.5"/> 757 <line x1="12" y1="2" x2="12" y2="5"/> 758 <line x1="12" y1="19" x2="12" y2="22"/> 759 <line x1="4.22" y1="4.22" x2="6.34" y2="6.34"/> 760 <line x1="17.66" y1="17.66" x2="19.78" y2="19.78"/> 761 <line x1="2" y1="12" x2="5" y2="12"/> 762 <line x1="19" y1="12" x2="22" y2="12"/> 763 <line x1="4.22" y1="19.78" x2="6.34" y2="17.66"/> 764 <line x1="17.66" y1="6.34" x2="19.78" y2="4.22"/> 765 </svg> 766 </button> 767 768 769 <div class="nav-dropdown nav-settings-dropdown"> 770 <button type="button" class="nav-icon-btn nav-dropdown-trigger nav-settings-trigger" 771 aria-haspopup="true" aria-expanded="false" aria-label="Settings"> 772 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 773 <circle cx="12" cy="12" r="3"/> 774 <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2
7740 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/> 775 </svg> 776 </button> 777 <div class="nav-dropdown-menu nav-settings-menu" role="menu"> 778 <div class="nav-settings-section"> 779 <div class="nav-settings-label">Language</div> 780 <div class="nav-settings-row"> 781 <select class="nav-settings-select" id="settingsLanguage" aria-label="Language"> 782 <option value="en" selected>English</option> 783 <option value="es" disabled>Español (soon)</option> 784 <option value="de" disabled>Deutsch (soon)</option> 785 <option value="fr" disabled>Français (soon)</option> 786 <option value="zh" disabled>䏿 (soon)</option> 787 </select> 788 </div> 789 </div> 790 <div class="nav-settings-divider"></div> 791 <div class="nav-settings-section"> 792 <div class="nav-settings-label">Theme</div> 793 <div class="nav-theme-toggle" role="radiogroup" aria-label="Theme"> 794 <button type="button" class="nav-theme-btn" data-theme="dark" role="radio" aria-checked="false"> 795 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg> 796 <span>Dark</span> 797 </button> 798 <button type="button" class="nav-theme-btn" data-theme="light" role="radio" aria-checked="false"> 799 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4.5"/><line x1="12" y1="2" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="22"/><line x1="4.22" y1="4.22" x2="6.34" y2="6.34"/><line x1="17.66" y1="17.66" x2="19.78" y2="19.78"/><line x1="2" y1="12" x2="5" y2="12"/><line x1="19" y1="12" x2="22" y2="12"/><line x1="4.22" y1="19.78" x2="6.34" y2="17.66"/><line x1="17.66" y1="6.34" x2="19.78" y2="4.22"/></svg> 800 <span>Light</span> 801 </button> 802 <button type="button" class="nav-theme-btn" data-theme="system" role="radio" aria-checked="true"> 803 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg> 804 <span>System</span> 805 </button> 806 </div> 807 </div> 808 </div> 809 </div> 810 811 812 <input id="darkModeToggle" type="checkbox" style="display:none;"> 813 814 <div class="auth-links"> 815 816 <a href="/login" id="openAuth" class="auth-link auth-link-login">Log in</a> 817 <a href="/login?view=register" id="openSignup" class="auth-link auth-link-primary">Sign up</a> 818 819 </div> 820 821 </div> 822 </div> 823</div> 824 825 826<div class="navbar-wrapper navbar-wrapper--compact"> 827<nav class="navbar-header" aria-label="Main navigation"> 828 <div class="col-lg-10 col-lg-offset-1"> 829 <div class="navbar"> 830 831 <a href="/" class="nav-logo" aria-label="AssetMarketCap home"> 832 <picture> 833 <img src="/static/logo.png" class="nav-logo-img nav-logo-img--light" width="180" height="30" alt="AssetMarketCap"> 834 <img src="/static/logo-dark.png" class="nav-logo-img nav-logo-img--dark" width="180" height="30" alt="" aria-hidden="true"> 835 </picture> 836 837 </a> 838 839 <div class="nav-left"> 840 <ul> 841 <li class="nav-dropdown"> 842 <a href="#" class="nav-dropdown-trigger" aria-haspopup="true" aria-expanded="false">Learn <i class="fa-solid fa-chevron-down nav-dropdown-caret" aria-hidden="true"></i></a> 843 <ul class="nav-dropdown-menu" role="menu"> 844 <li role="none"><a href="/learn" role="menuitem">Education Articles</a></li> 845 <li role="none"><a href="/news" role="menuitem">Latest News</a></li> 846 <li role="none"><a href="/deals" role="menuitem">Deals</a></li> 847 </ul> 848 </li> 849 <li class="nav-dropdown"> 850 <a href="#" class="nav-dropdown-trigger" aria-haspopup="true" aria-expanded="false">Products <i class="fa-solid fa-chevron-down nav-dropdown-caret" aria-hidden="true"></i></a> 851 <ul class="nav-dropdown-menu" role="menu"> 852 <li role="none"><a href="/pro" role="menuitem">Pro</a></li> 853 <li role="none"><a href="https://merch.assetmarketcap.com" role="menuitem">Merchandise</a></li> 854 <li role="none"><a href="/advertise" role="menuitem">Advertise</a></li> 855 </ul> 856 </li> 857 <li class="nav-dropdown"> 858 <a href="#" class="nav-dropdown-trigger" aria-haspopup="true" aria-expanded="false">Tools <i class="fa-solid fa-chevron-down nav-dropdown-caret" aria-hidden="true"></i></a> 859 <ul class="nav-dropdown-menu" role="menu"> 860 <li role="none"><a href="/flippener" role="menuitem">Flippener Tool</a></li> 861 <li role="none"><a href="/compare" role="menuitem">Comparator Tool</a></li> 862 <li role="none"><a href="/net-worth-tracker" role="menuitem">Net-Worth Tracker</a></li> 863 864 865 </ul> 866 </li> 867 <li><a href="/pricing" id="openPricing">Pricing</a></li> 868 <li><a href="/api-info" id="openApi">API</a></li> 869 </ul> 870 </div> 871 872 873 <div class="navbar-right"> 874 <a href="/net-worth-tracker" class="nav-pill nav-pill--ghost" title="Net-Worth Tracker">
875 <svg class="nav-pill-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 876 <path d="M3 17l5-5 4 4 8-8"/> 877 <path d="M14 8h6v6"/> 878 </svg> 879 <span class="nav-pill-label">Net-Worth</span> 880 </a> 881 882 <a href="/login?view=register" 883 class="nav-pill nav-pill--ghost nav-favourites-trigger" title="Your favourites"> 884 <svg class="nav-pill-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 885 <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/> 886 </svg> 887 <span class="nav-pill-label">Favourites</span> 888 </a> 889 890 <form class="nav-search" role="search" action="/search" method="GET" autocomplete="off"> 891 <i class="fa-solid fa-magnifying-glass nav-search-icon" aria-hidden="true"></i> 892 <input type="search" name="q" id="navGlobalSearch" 893 class="nav-search-input" 894 placeholder="Search assets, articles, newsâ¦" 895 aria-label="Search"> 896 <kbd class="nav-search-kbd" aria-hidden="true">/</kbd> 897 </form> 898 899 </div> 900 901 </div> 902 </div> 903</nav> 904</div> 905 906 907<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 908 PRICING MODAL 909 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 910<div id="pricingOverlay" class="auth-overlay" aria-hidden="true"> 911 <div class="auth-backdrop" data-close-modal="pricingOverlay"></div> 912 <section class="auth-modal" role="dialog" aria-modal="true"> 913 <button class="auth-close" data-close-modal="pricingOverlay" type="button" aria-label="Close">×</button> 914 915 <div class="auth-modal-left"> 916 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 917 <div class="auth-badge">Pro Plans</div> 918 <h2 class="auth-hero-title">Go beyond the data</h2> 919 <p class="auth-hero-copy">Everyone gets the full 35k+ dataset. Pro adds premium content and an ad-free experience.</p> 920 <div class="auth-feature-list"> 921 <div class="auth-feature-item">Full 35,000+ asset dataset, free for everyone</div> 922 <div class="auth-feature-item">Education library on sign-up</div> 923 <div class="auth-feature-item">Filter by Country & Sector on sign-up</div> 924 <div class="auth-feature-item">Ad-Free Mobile App Experience</div> 925 <div class="auth-feature-item">News Article Access</div> 926 <div class="auth-feature-item">Favourites, Flippener Tool & Comparator Tool</div> 927 </div> 928 </div> 929 </div> 930 931 <div class="auth-modal-right"> 932 933 934 <h3 class="auth-modal-heading">Pricing</h3> 935 <p class="auth-subtitle">The full 35k+ dataset is free for everyone. Sign up for favourites, or upgrade to Pro for premium features.</p> 936 937 <table class="auth-compare-table"> 938 <thead> 939 <tr> 940 <th class="auth-compare-feature-col">Feature</th> 941 <th>Free</th> 942 <th>Sign Up</th> 943 <th>Pro</th> 944 <th class="auth-compare-highlight">Pro+</th> 945 </tr> 946 </thead> 947 <tbody> 948 <tr><td>35,000+ assets</td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 949 <tr><td>Education library</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td>
949<span class="auth-compare-tick">✓</span></td></tr> 950 <tr><td>Filter by Country & Sector</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 951 <tr><td>Ad-Free Mobile App Experience</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 952 <tr><td>News Article Access</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 953 <tr><td>Favourites</td><td class="auth-compare-text">3</td><td class="auth-compare-text">15</td><td class="auth-compare-text">50/mo</td><td class="auth-compare-text">Unlimited</td></tr> 954 <tr><td>Flippener Tool</td><td class="auth-compare-text">3</td><td class="auth-compare-text">10</td><td class="auth-compare-text">30/mo</td><td class="auth-compare-text">Unlimited</td></tr> 955 <tr><td>Comparator Tool</td><td class="auth-compare-text">3</td><td class="auth-compare-text">5</td><td class="auth-compare-text">15/mo</td><td class="auth-compare-text">Unlimited</td></tr> 956 <tr class="auth-compare-price-row"> 957 <td>Price</td> 958 <td class="auth-compare-text">Free</td> 959 <td class="auth-compare-text">Sign Up</td> 960 <td class="auth-compare-text"><span data-info-pro-price>$3.99<small>/mo</small></span></td> 961 <td class="auth-compare-text"><span data-info-proplus-price>$9.99<small>/mo</small></span></td> 962 </tr> 963 </tbody> 964 </table> 965 966 <a href="/login?view=register" class="auth-submit" style="margin-top:18px; display:block; text-align:center; text-decoration:none;">Create a free account</a> 967 <div style="margin-top:12px; text-align:center;"> 968 <a href="/login" class="auth-inline-link">Already have an account? Log in</a> 969 </div> 970 971 </div> 972 </section> 973</div> 974 975 976<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 977 API MODAL 978 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 979<div id="apiOverlay" class="auth-overlay" aria-hidden="true"> 980 <div class="auth-backdrop" data-close-modal="apiOverlay"></div> 981 <section class="auth-modal" role="dialog" aria-modal="true"> 982 <button class="auth-close" data-close-modal="apiOverlay" type="button" aria-label="Close">×</button> 983 984 <div class="auth-modal-left"> 985 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 986 <div class="auth-badge">REST API</div> 987 <h2 class="auth-hero-title">Build with the API</h2> 988 <p class="auth-hero-copy"> 989 Programmatic access for apps, scripts, and integrations. Every account includes a free API key with the top 100 assets. Upgrade for the full 35k+ dataset, fundamentals, and historical data. 990 </p> 991 <div class="auth-feature-list"> 992 <div class="auth-feature-item">Top 100 assets free with every account</div> 993 <div class="auth-feature-item">Full 35,000+ asset dataset</div> 994 <div class="auth-feature-item">Individual asset fundamentals</div> 995 <div class="auth-feature-item">Historical market cap & price data</div> 996 <div class="auth-feature-item">Up to 1 million API calls per month</div> 997 <div class="auth-feature-item">Plans from $19.99/mo</div> 998 </div> 999 </div> 1000 </div> 1001 1002 <div class="auth-modal-right"> 1003 1004 1005 <h3 class="auth-modal-heading">API access</h3> 1006 <p class="auth-subtitle">Register for your free API key (top 100 assets included). Subscribe for the full 35k+ dataset, fundamentals, and historical data.</p> 1007 1008 <table class="auth-compare-table"> 1009 <thead> 1010 <tr> 1011 <th class="auth-compare-feature-col">Feature</th> 1012 <th>Free</th> 1013 <th>Starter</th> 1014 <th>Growth</th> 1015 <th class="auth-compare-highlight">Scale</th> 1016 </tr> 1017 </thead> 1018 <tbody> 1019 <tr><td>Top 100 assets</td><td>
1019<span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 1020 <tr><td>Full 35k+ asset list</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 1021 <tr><td>Fundamentals</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 1022 <tr><td>Historical data</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 1023 <tr><td>Calls / month</td><td class="auth-compare-text">1,000</td><td class="auth-compare-text">10k</td><td class="auth-compare-text">100k</td><td class="auth-compare-text">1M</td></tr> 1024 <tr class="auth-compare-price-row"> 1025 <td>Price</td> 1026 <td class="auth-compare-text">Free</td> 1027 <td class="auth-compare-text">$19.99<small>/mo</small></td> 1028 <td class="auth-compare-text">$49.99<small>/mo</small></td> 1029 <td class="auth-compare-text">$99.99<small>/mo</small></td> 1030 </tr> 1031 </tbody> 1032 </table> 1033 1034 <a href="/login?view=register" class="auth-submit" style="margin-top:18px; display:block; text-align:center; text-decoration:none;">Register for your Free API Key</a> 1035 <div style="margin-top:14px;"> 1036 <a href="/API" class="auth-inline-link">View API documentation →</a> 1037 </div> 1038 1039 </div> 1040 </section> 1041</div> 1042 1043 1044<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1045 NEWSLETTER MODAL 1046 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 1047<div id="newsletterOverlay" class="auth-overlay" aria-hidden="true"> 1048 <div class="auth-backdrop" data-close-modal="newsletterOverlay"></div> 1049 <section class="auth-modal" role="dialog" aria-modal="true"> 1050 <button class="auth-close" data-close-modal="newsletterOverlay" type="button" aria-label="Close">×</button> 1051 1052 <div class="auth-modal-left"> 1053 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 1054 <div class="auth-badge">Newsletter</div> 1055 <h2 class="auth-hero-title">Stay ahead of the market</h2> 1056 <p class="auth-hero-copy"> 1057 Weekly insights on market caps, asset rankings, and macro trends, straight to your inbox. 1058 </p> 1059 <div class="auth-feature-list"> 1060 <div class="auth-feature-item">Weekly market cap rankings recap</div> 1061 <div class="auth-feature-item">Asset class breakdowns & analysis</div> 1062 <div class="auth-feature-item">Bitcoin & crypto market updates</div> 1063 <div class="auth-feature-item">Unsubscribe any time</div> 1064 </div> 1065 </div> 1066 </div> 1067 1068 <div class="auth-modal-right"> 1069 1070 <h3 class="auth-modal-heading">Newsletter</h3> 1071 <p class="auth-subtitle">Get weekly market insights delivered to your inbox. No account required.</p> 1072 <form method="POST" action="/subscribe-newsletter" class="auth-form" id="newsletterForm"> 1073 <input name="email" type="email" placeholder="Email address" required autocomplete="email"> 1074 <button type="submit" class="auth-submit">Subscribe</button> 1075 </form> 1076 1077 <p style="font-size:11px; color:rgba(255,255,255,0.4); line-height:1.45; margin:14px 0 0; text-align:center;"> 1078 We'll never share your email. Unsubscribe any time. 1079 </p> 1080 </div> 1081 </section> 1082</div> 1083 1084 1085<!-- =========================================================== 1086 SIGNUP GATE MODAL - unclosable, fires after activity threshold 1087 Only rendered for logged-out users; controlled by JS below. 1088 =========================================================== --> 1089
1090<div id="signupGateOverlay" class="auth-overlay signup-gate" aria-hidden="true"> 1091 <div class="auth-backdrop signup-gate-backdrop" data-close-modal="signupGateOverlay"></div> 1092 <section class="auth-modal signup-gate-modal" role="dialog" aria-modal="true" aria-labelledby="signupGateTitle"> 1093 1094 <button class="auth-close signup-gate-close" data-close-modal="signupGateOverlay" type="button" aria-label="Close">×</button> 1095 1096 <div class="auth-modal-left"> 1097 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 1098 <div class="auth-badge">Free Account</div> 1099 <h2 class="auth-hero-title" id="signupGateTitle">Keep exploring 35,000+ assets</h2> 1100 <p class="auth-hero-copy">You're clearly getting value out of the data. Create a free account in seconds to keep going - no credit card required.</p> 1101 <div class="auth-feature-list"> 1102 <div class="auth-feature-item">Free access to the full 35,000+ dataset</div> 1103 <div class="auth-feature-item">Education library & news article access</div> 1104 <div class="auth-feature-item">Filter by Country & Sector</div> 1105 <div class="auth-feature-item">Save favourites & build watchlists</div> 1106 <div class="auth-feature-item">Free API key included</div> 1107 </div> 1108 </div> 1109 </div> 1110 1111 <div class="auth-modal-right"> 1112 <h3 class="auth-modal-heading">Create your free account</h3> 1113 <p class="auth-subtitle">It takes about 15 seconds. No card, no spam - just full access to everything on AssetMarketCap.</p> 1114 1115 <a href="/login?view=register" class="auth-submit signup-gate-cta" style="margin-top:18px; display:block; text-align:center; text-decoration:none;">Create a free account</a> 1116 1117 <div style="margin-top:14px; text-align:center;"> 1118 <a href="/login" class="auth-inline-link">Already have an account? Log in</a> 1119 </div> 1120 1121 <p style="font-size:11px; color:rgba(255,255,255,0.4); line-height:1.45; margin:22px 0 0; text-align:center;"> 1122 By creating an account you agree to our <a href="/privacy" class="auth-inline-link">Privacy Policy</a>. 1123 </p> 1124 </div> 1125 </section> 1126</div> 1127 1128 1129 1130<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 1131 MODAL JS - shared open/close + feature-specific logic 1132 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ -->
1133<script> 1134(function () { 1135 // ââ Generic open/close for all modals ââââââââââââââââââââââ 1136 function openModal(id) { 1137 var el = document.getElementById(id); 1138 if (!el) return; 1139 el.classList.add("open"); 1140 el.setAttribute("aria-hidden", "false"); 1141 document.body.style.overflow = "hidden"; 1142 // Load API usage when API modal opens 1143 if (id === "apiOverlay") loadApiUsage(); 1144 } 1145 1146 function closeModal(id) { 1147 var el = document.getElementById(id); 1148 if (!el) return; 1149 el.classList.remove("open"); 1150 el.setAttribute("aria-hidden", "true"); 1151 document.body.style.overflow = ""; 1152 } 1153 1154 // Close buttons (backdrop + X button) 1155 document.querySelectorAll("[data-close-modal]").forEach(function (el) { 1156 el.addEventListener("click", function () { 1157 closeModal(el.getAttribute("data-close-modal")); 1158 }); 1159 }); 1160 1161 // Escape key closes any open modal 1162 document.addEventListener("keydown", function (e) { 1163 if (e.key === "Escape") { 1164 ["pricingOverlay", "apiOverlay", "newsletterOverlay"].forEach(closeModal); 1165 } 1166 }); 1167 1168 // ââ Nav link triggers ââââââââââââââââââââââââââââââââââââââ 1169 1170 var isAuthed = false; 1171 1172 1173 var openPro = document.getElementById("openPro"); 1174 var openPricing = document.getElementById("openPricing"); 1175 var openApi = document.getElementById("openApi"); 1176 var openNewsletterNav = document.getElementById("openNewsletterNav"); 1177 1178 // Pro / Pricing nav links navigate to the full pages now (we 1179 // converted these from modal overlays to standalone marketing 1180 // pages). The only special-case left: logged-in users clicking 1181 // "Pro" go straight to their account subscriptions section 1182 // instead of the marketing page â they already have the info. 1183 if (openPro) { 1184 openPro.addEventListener("click", function (e) { 1185 if (isAuthed) { 1186 e.preventDefault(); 1187 window.location.href = "/account#subscriptions"; 1188 } 1189 // Otherwise let the <a href="/pro"> navigate normally. 1190 }); 1191 } 1192 // Pricing link: no interceptor â the <a href="/pricing"> handles it. 1193 1194 // API: route to the standalone /api-info documentation page (no modal). 1195 // The <a href="/api-info" id="openApi"> in header.html handles navigation 1196 // natively now that the docs live on a full page. 1197 1198 // Newsletter 1199 if (openNewsletterNav) { 1200 openNewsletterNav.addEventListener("click", function (e) { 1201 e.preventDefault(); 1202 openModal("newsletterOverlay"); 1203 }); 1204 } 1205 1206 // Footer newsletter button (loaded after this script, so use delegation) 1207 document.addEventListener("click", function (e) { 1208 var el = e.target.closest("#openNewsletter"); 1209 if (el) { e.preventDefault(); openModal("newsletterOverlay"); } 1210 }); 1211 1212 // Login/Register link: go to /login page 1213 var openAuth = document.getElementById("openAuth"); 1214 if (openAuth) { 1215 openAuth.addEventListener("click", function (e) { 1216 e.preventDefault(); 1217 window.location.href = "/login"; 1218 }); 1219 } 1220 1221 // Auto-open modal when returning from a flow that asked for it. 1222 // The pricing/pro auto-open was dropped after those moved to full 1223 // pages â Stripe cancel now redirects to /pricing directly. 1224 var modalParam = new URLSearchParams(window.location.search).get("modal"); 1225 if (modalParam === "api" || modalParam === "api-info") openModal("apiOverlay"); 1226 if (modalParam === "newsletter") openModal("newsletterOverlay"); 1227 1228 // ââ Billing toggle helper ââââââââââââââââââââââââââââââââââ 1229 function setupBillingToggle(toggleEl, onChange) { 1230 if (!toggleEl) return; 1231 var sw = toggleEl.querySelector(".billing-toggle-switch"); 1232 var labels = toggleEl.querySelectorAll(".billing-toggle-label"); 1233 var isAnnual = false; 1234 1235 function update() { 1236 toggleEl.classList.toggle("billing-toggle--annual", isAnnual); 1237 labels.forEach(function (l) { 1238 l.classList.toggle("billing-toggle-label--active", l.dataset.interval === (isAnnual ? "annual" : "monthly")); 1239 }); 1240 onChange(isAnnual ? "annual" : "monthly"); 1241 } 1242 1243 if (sw) sw.addEventListener("click", function () { isAnnual = !isAnnual; update(); });
1244 labels.forEach(function (l) { 1245 l.addEventListener("click", function () { isAnnual = l.dataset.interval === "annual"; update(); }); 1246 }); 1247 } 1248 1249 // ââ Pro plan card selection ââââââââââââââââââââââââââââââââ 1250 var proCards = document.querySelectorAll("#pricingOverlay .auth-plan-option[data-pro-plan]"); 1251 var proForm = document.getElementById("proSubscribeForm"); 1252 var proBtn = document.getElementById("proSubscribeBtn"); 1253 var proIntervalInput = document.getElementById("proInterval"); 1254 1255 function updateProCardPrices(interval) { 1256 proCards.forEach(function (card) { 1257 var amount = card.querySelector("[data-price-amount]"); 1258 var cents = card.querySelector("[data-price-cents]"); 1259 var period = card.querySelector("[data-price-period]"); 1260 if (interval === "annual") { 1261 if (amount) amount.textContent = card.dataset.annualAmount; 1262 if (cents) cents.textContent = card.dataset.annualCents; 1263 if (period) period.textContent = card.dataset.annualPeriod; 1264 card.dataset.proLabel = card.dataset.proLabelAnnual; 1265 } else { 1266 if (amount) amount.textContent = card.dataset.monthlyAmount; 1267 if (cents) cents.textContent = card.dataset.monthlyCents; 1268 if (period) period.textContent = card.dataset.monthlyPeriod; 1269 card.dataset.proLabel = card.dataset.proLabelMonthly; 1270 } 1271 }); 1272 var selectedCard = document.querySelector("#pricingOverlay .auth-plan-option.selected"); 1273 if (selectedCard && proBtn) proBtn.textContent = selectedCard.dataset.proLabel; 1274 } 1275 1276 setupBillingToggle(document.getElementById("proBillingToggle"), function (interval) { 1277 if (proIntervalInput) proIntervalInput.value = interval; 1278 updateProCardPrices(interval); 1279 }); 1280 1281 proCards.forEach(function (card) { 1282 card.addEventListener("click", function () { 1283 proCards.forEach(function (c) { c.classList.remove("selected"); }); 1284 card.classList.add("selected"); 1285 if (proForm) proForm.setAttribute("action", card.dataset.proAction); 1286 if (proBtn) proBtn.textContent = card.dataset.proLabel; 1287 }); 1288 }); 1289 1290 // ââ Pro upgrade toggle âââââââââââââââââââââââââââââââââââââ 1291 var proUpgradeBtn = document.getElementById("proUpgradeBtn"); 1292 var proUpgradeBadge = document.getElementById("proUpgradeBadge"); 1293 var proUpgradeInterval = document.getElementById("proUpgradeInterval"); 1294 1295 setupBillingToggle(document.getElementById("proUpgradeBillingToggle"), function (interval) { 1296 if (proUpgradeInterval) proUpgradeInterval.value = interval; 1297 if (interval === "annual") { 1298 if (proUpgradeBadge) proUpgradeBadge.textContent = "Pro Plus - $99.99/yr"; 1299 if (proUpgradeBtn) proUpgradeBtn.textContent = "Upgrade to Pro Plus - $99.99/yr"; 1300 } else { 1301 if (proUpgradeBadge) proUpgradeBadge.textContent = "Pro Plus - $9.99/mo"; 1302 if (proUpgradeBtn) proUpgradeBtn.textContent = "Upgrade to Pro Plus - $9.99/mo"; 1303 } 1304 }); 1305 1306 // ââ API plan card selection ââââââââââââââââââââââââââââââââ 1307 var apiAllCards = document.querySelectorAll("#apiPlanCards .api-plan-card[data-plan]"); 1308 var selectedPlanInput = document.getElementById("apiSelectedPlan"); 1309 var apiSignupForm = document.getElementById("apiSignupForm"); 1310 var apiSubscribeBtn = document.getElementById("apiSubscribeBtn"); 1311 var apiEnterpriseBtn = document.getElementById("apiEnterpriseBtn"); 1312 var apiIntervalInput = document.getElementById("apiInterval"); 1313 var currentApiInterval = "monthly"; 1314 1315 var apiPlanLabels = { 1316 starter: { monthly: "Subscribe - Starter $19.99/mo", annual: "Subscribe - Starter $99.99/yr" }, 1317 growth: { monthly: "Subscribe - Growth $49.99/mo", annual: "Subscribe - Growth $499.99/yr" }, 1318 scale: { monthly: "Subscribe - Scale $99.99/mo", annual: "Subscribe - Scale $999.99/yr" } 1319 }; 1320 1321 function updateApiCardLimits(interval) { 1322 apiAllCards.forEach(function (card) { 1323 if (card.dataset.plan === "enterprise") return; 1324 var limitEl = card.querySelector("[data-api-limit]"); 1325 if (limitEl) limitEl.textContent = card.dataset["limit" + interval.charAt(0).toUpperCase() + interval.slice(1)] || ""; 1326 }); 1327 var selectedCard = document.querySelector("#apiPlanCards .api-plan-card--selected"); 1328 if (selectedCard && selectedCard.dataset.plan !== "enterprise" && apiSubscribeBtn) { 1329 var plan = selectedCard.dataset.plan; 1330 apiSubscribeBtn.textContent = (apiPlanLabels[plan] && apiPlanLabels[plan][interval]) || "Subscribe to API plan"; 1331 } 1332 } 1333 1334 setupBillingToggle(document.getElementById("apiBillingToggle"), function (interval) { 1335 currentApiInterval = interval; 1336 if (apiIntervalInput) apiIntervalInput.value = interval; 1337 updateApiCardLimits(interval); 1338 }); 1339 1340 apiAllCards.forEach(function (card) { 1341 card.addEventListener("click", function () { 1342 apiAllCards.forEach(function (c) { c.classList.remove("api-plan-card--selected"); }); 1343 card.classList.add("api-plan-card--selected"); 1344 var plan = card.dataset.plan; 1345 if (plan === "enterprise") { 1346 if (apiSignupForm) apiSignupForm.style.display = "none"; 1347 if (apiEnterpriseBtn) apiEnterpriseBtn.style.display = "block"; 1348 } else {
1349 if (selectedPlanInput) selectedPlanInput.value = plan; 1350 if (apiSubscribeBtn) apiSubscribeBtn.textContent = (apiPlanLabels[plan] && apiPlanLabels[plan][currentApiInterval]) || "Subscribe to API plan"; 1351 if (apiSignupForm) apiSignupForm.style.display = ""; 1352 if (apiEnterpriseBtn) apiEnterpriseBtn.style.display = "none"; 1353 } 1354 }); 1355 }); 1356 1357 // ââ Copy API key ââââââââââââââââââââââââââââââââââââââââââ 1358 // 2026-07-05: apiKeyText now shows only the key PREFIX (keys are stored 1359 // hashed, plaintext is shown once at creation on the account page), so 1360 // the old click-to-copy handler was removed - copying a prefix would 1361 // only produce broken curl commands. 1362 1363 // ââ API usage meter âââââââââââââââââââââââââââââââââââââââ 1364 function loadApiUsage() { 1365 var block = document.getElementById("apiUsageBlock"); 1366 if (!block) return; 1367 fetch("/api-usage") 1368 .then(function (r) { return r.ok ? r.json() : null; }) 1369 .then(function (data) { 1370 if (!data) return; 1371 var label = document.getElementById("apiUsageLabel"); 1372 var fill = document.getElementById("apiUsageFill"); 1373 var reset = document.getElementById("apiUsageReset"); 1374 if (label) label.textContent = data.used.toLocaleString() + " / " + data.limit.toLocaleString() + " calls this month"; 1375 if (fill) { 1376 var pct = Math.min(100, Math.round((data.used / data.limit) * 100)); 1377 fill.style.width = pct + "%"; 1378 fill.classList.toggle("api-usage-bar-fill--warning", pct >= 80); 1379 fill.classList.toggle("api-usage-bar-fill--danger", pct >= 95); 1380 } 1381 if (reset && data.reset_date) { 1382 var d = new Date(data.reset_date + "T00:00:00"); 1383 reset.textContent = "Resets " + d.toLocaleDateString(undefined, { month: "short", day: "
1383numeric", year: "numeric" }); 1384 } 1385 }) 1386 .catch(function () {}); 1387 } 1388 1389 // ââ Stripe loading spinner ââââââââââââââââââââââââââââââââ 1390 var stripeActions = [ 1391 "/create-checkout-session", "/create-pro-plus-checkout-session", 1392 "/create-customer-portal-session", "/create-pro-plus-customer-portal-session", 1393 "/create-api-checkout-session", "/create-api-customer-portal-session", 1394 "/cancel-subscription", "/cancel-pro-plus-subscription", 1395 "/cancel-api-subscription", "/switch-api-plan" 1396 ]; 1397 1398 document.querySelectorAll(".auth-form").forEach(function (form) { 1399 if (!stripeActions.includes(form.getAttribute("action"))) return; 1400 form.addEventListener("submit", function () { 1401 var btn = form.querySelector(".auth-submit, .api-plan-card"); 1402 if (btn) { btn.classList.add("auth-submit--loading"); btn.disabled = true; } 1403 }); 1404 }); 1405 1406 window.addEventListener("pageshow", function (event) { 1407 if (event.persisted) { 1408 document.querySelectorAll(".auth-submit--loading").forEach(function (btn) { 1409 btn.classList.remove("auth-submit--loading"); 1410 btn.disabled = false; 1411 }); 1412 } 1413 }); 1414 1415 1416 // == Signup gate (unclosable, 24hr cooldown) ================== 1417 (function setupSignupGate() { 1418 var overlay = document.getElementById("signupGateOverlay"); 1419 if (!overlay) return; // not rendered (user is authenticated) 1420 1421 var STORAGE_KEY = "amc_signup_gate_v1"; 1422 var CLICK_THRESHOLD = 5; 1423 var SCROLL_THRESHOLD = Infinity; // scroll trigger disabled for now (click trigger still active) 1424 var COOLDOWN_MS = 24 * 60 * 60 * 1000; 1425 var SCROLL_BURST_GAP_MS = 700; // collapse rapid wheel events into one "scroll" 1426 1427 // Skip on the login page itself - the modal's CTAs already point there 1428 var path = window.location.pathname || ""; 1429 if (path === "/login" || path.indexOf("/login/") === 0 || path.indexOf("/login?") === 0) return; 1430 1431 // Skip for crawlers, bots and link-preview fetchers - keeps the modal out 1432 // of Googlebot's rendered DOM and out of social link previews (which fetch 1433 // pages to extract og:image etc). 1434 var ua = navigator.userAgent || ""; 1435 if (/bot|crawl|spider|slurp|mediapartners|googlebot|bingbot|baiduspider|yandex|duckduckbot|applebot|petalbot|semrushbot|ahrefsbot|facebookexternalhit|embedly|preview|skypeuripreview|telegrambot|whatsapp|discordbot|linkedinbot|twitterbot|slackbot/i.test(ua)) return; 1436 1437 // Skip when the user arrived from a search engine - avoids Google's 1438 // intrusive-interstitial penalty on the landing page. (Once they 1439 // navigate to a second page, document.referrer is our own domain and 1440 // counting resumes normally.) 1441 var ref = document.referrer || ""; 1442 if (ref) { 1443 try { 1444 var refHost = new URL(ref).hostname.toLowerCase(); 1445 if (/(^|\.)(google|bing|duckduckgo|yahoo|yandex|baidu|ecosia|qwant|brave|kagi|startpage)\./.test(refHost)) return; 1446 } catch (e) { /* malformed referrer - ignore */ } 1447 } 1448 1449 function readState() { 1450 try { 1451 var raw = localStorage.getItem(STORAGE_KEY); 1452 if (!raw) return { clicks: 0, scrolls: 0, seenAt: 0 }; 1453 var s = JSON.parse(raw); 1454 return { 1455 clicks: s.clicks | 0, 1456 scrolls: s.scrolls | 0, 1457 seenAt: parseInt(s.seenAt, 10) || 0 1458 }; 1459 } catch (e) { 1460 return { clicks: 0, scrolls: 0, seenAt: 0 }; 1461 } 1462 } 1463 1464 function writeState(s) { 1465 try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch (e) {} 1466 } 1467 1468 var state = readState(); 1469 var now = Date.now(); 1470 1471 // Within 24hr cooldown from a prior show? Stay quiet. 1472 if (state.seenAt && (now - state.seenAt) < COOLDOWN_MS) return; 1473 1474 // Cooldown expired - reset counters so the gate has to be re-earned. 1475 if (state.seenAt && (now - state.seenAt) >= COOLDOWN_MS) { 1476 state = { clicks: 0, scrolls: 0, seenAt: 0 }; 1477 writeState(state); 1478 } 1479 1480 function closeGate() { 1481 overlay.classList.remove("open"); 1482 overlay.setAttribute("aria-hidden", "true"); 1483 document.body.style.overflow = ""; 1484 document.documentElement.style.overflow = ""; 1485 } 1486 1487 function showGate() { 1488 overlay.classList.add("open"); 1489 overlay.setAttribute("aria-hidden", "false"); 1490 document.body.style.overflow = "hidden"; 1491 document.documentElement.style.overflow = "hidden"; 1492 1493 // Stamp seenAt so subsequent navigations within 24hr stay quiet. 1494 state.seenAt = Date.now(); 1495 writeState(state); 1496
1497 // Wire close button + backdrop click + Escape key. 1498 overlay.querySelectorAll('[data-close-modal="signupGateOverlay"]').forEach(function (el) { 1499 if (!el.dataset.closeBound) { 1500 el.dataset.closeBound = "1"; 1501 el.addEventListener("click", closeGate); 1502 } 1503 }); 1504 document.addEventListener("keydown", function onEsc(e) { 1505 if (e.key === "Escape") { 1506 closeGate(); 1507 document.removeEventListener("keydown", onEsc); 1508 } 1509 }); 1510 1511 removeListeners(); 1512 } 1513 1514 function checkAndMaybeShow() { 1515 if (state.clicks >= CLICK_THRESHOLD || state.scrolls >= SCROLL_THRESHOLD) { 1516 showGate(); 1517 } 1518 } 1519 1520 function onLinkClick(e) { 1521 var a = e.target.closest && e.target.closest("a"); 1522 if (!a) return; 1523 if (overlay.contains(a)) return; // ignore links inside the gate 1524 var href = a.getAttribute("href"); 1525 if (!href) return; 1526 if (href.charAt(0) === "#") return; 1527 if (/^(javascript:|mailto:|tel:)/i.test(href)) return; 1528 state.clicks++; 1529 writeState(state); 1530 checkAndMaybeShow(); 1531 } 1532 1533 var lastScrollBurst = 0; 1534 function onScroll() { 1535 var t = Date.now(); 1536 if (t - lastScrollBurst < SCROLL_BURST_GAP_MS) return; 1537 lastScrollBurst = t; 1538 state.scrolls++; 1539 writeState(state); 1540 checkAndMaybeShow(); 1541 } 1542 1543 function addListeners() { 1544 document.addEventListener("click", onLinkClick, true); 1545 // scroll trigger disabled for now: 1546 // window.addEventListener("scroll", onScroll, { passive: true }); 1547 } 1548 function removeListeners() { 1549 document.removeEventListener("click", onLinkClick, true); 1550 window.removeEventListener("scroll", onScroll); 1551 } 1552 1553 // If counters already crossed the threshold on a previous page-load, 1554 // open immediately (e.g. they hit it just before navigating). 1555 if (state.clicks >= CLICK_THRESHOLD || state.scrolls >= SCROLL_THRESHOLD) { 1556 showGate(); 1557 } else { 1558 addListeners(); 1559 } 1560 })(); 1561 1562})(); 1563</script>
1563 1564 1565 1566 1567<style> 1568/* ââ Flippenings row âââââââââââââââââââââââââââââââââââââ */ 1569.flippenings-wrap { 1570 width: 100%; 1571 background: var(--color-background2); 1572 padding: 12px 0; 1573 margin-top: 6px; 1574 margin-bottom: clamp(14px, 1.5vw, 24px); 1575 transition: background-color .25s ease; 1576} 1577/* Six equal containers always on one horizontal row â 5 asset cells + 1578 1 tool cell. The tool cell never wraps below the asset row. Content 1579 that doesn't fit (e.g. "Real Estate $634.9T" at a narrow viewport) 1580 is handled by font-size scaling in the media queries below, and by 1581 hiding the mcap on the narrowest phones where even small fonts 1582 can't squeeze it in. */ 1583.flippenings-inner { 1584 display: grid; 1585 grid-template-columns: repeat(6, minmax(0, 1fr)); 1586 gap: 8px 10px; 1587 align-items: center; 1588 box-sizing: border-box; 1589} 1590.header-press-strip { 1591 clear: both; 1592 margin-top: 0; 1593 margin-bottom: 12px; 1594 padding: 9px 15px 12px; 1595 background: var(--color-background); 1596 box-sizing: border-box; 1597} 1598.header-press-strip__title { 1599 margin: 0 auto 6px; 1600 color: var(--color-neutral-500); 1601 font-size: 0.78rem; 1602 font-weight: 700; 1603 letter-spacing: 0; 1604 line-height: 1; 1605 text-align: center; 1606 text-transform: uppercase; 1607} 1608.header-press-row { 1609 display: block; 1610 margin: 0 auto; 1611} 1612.header-press-carousel { 1613 width: 100%; 1614 overflow: hidden; 1615 -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); 1616 mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); 1617} 1618.header-press-track { 1619 display: flex; 1620 align-items: center; 1621 width: max-content; 1622 animation: headerPressScroll 54s linear infinite; 1623} 1624.header-press-set { 1625 display: flex; 1626 align-items: center; 1627 gap: clamp(24px, 4vw, 54px); 1628 padding-right: clamp(24px, 4vw, 54px); 1629} 1630.header-press-logo { 1631 display: block; 1632 width: auto; 1633 max-width: 150px; 1634 height: 34px; 1635 object-fit: contain; 1636 opacity: 0.86; 1637} 1638.header-press-logo:hover { 1639 opacity: 1; 1640} 1641@keyframes headerPressScroll { 1642 from { transform: translateX(0); } 1643 to { transform: translateX(-25%); } 1644} 1645.flip-row { 1646 display: flex; 1647 align-items: center; 1648 justify-content: center; 1649 gap: 6px; 1650 min-width: 0; 1651 overflow: hidden; /* last-resort guard: clip at cell edge instead of 1652 bleeding into the next cell if content overflows */ 1653} 1654/* Two-row cell: head (label + mcap) on top, meter (bar + pct) below. 1655 Cells never overflow because the grid uses auto-fit with a 160px 1656 minimum (wide enough for "Real Estate $634.9T") â on narrow viewports 1657 fewer cells fit per row and the rest wrap to a new row. */ 1658.flip-cell { 1659 display: flex; 1660 flex-direction: column; 1661 align-items: center; 1662 row-gap: 2px; 1663 min-width: 0; 1664} 1665.flip-head { 1666 display: inline-flex; 1667 align-items: baseline; 1668 gap: 4px; 1669 white-space: nowrap; 1670} 1671.flip-meter { 1672 display: inline-flex; 1673 align-items: center; 1674 gap: 6px; 1675 white-space: nowrap; 1676} 1677a.flip-row-link { 1678 text-decoration: none; 1679 color: inherit; 1680 border-radius: 6px; 1681 padding: 4px 6px; 1682 margin: -4px -6px; 1683 transition: background 0.15s; 1684} 1685a.flip-row-link:hover { 1686 background: rgba(99,102,241,0.08); 1687} 1688a.flip-row-link:hover .flip-label { 1689 color: var(--color-primary); 1690} 1691.flip-logo { 1692 width: 22px; 1693 height: 22px; 1694 border-radius: 50%; 1695 flex-shrink: 0; 1696 object-fit: cover; 1697} 1698.flip-label { 1699 font-size: 0.95rem; 1700 font-weight: 700; 1701 white-space: nowrap; 1702 color: var(--color-text); 1703} 1704.flip-bar { 1705 display: inline-block; 1706 width: 60px; 1707 height: 8px; 1708 background: var(--color-neutral-200); 1709 border-radius: 4px; 1710 overflow: hidden; 1711 flex-shrink: 0; 1712} 1713.flip-fill { 1714 display: block; 1715 height: 100%; 1716 border-radius: 4px; 1717 background: var(--color-neutral-400); 1718 transition: width 0.6s ease; 1719 width: 0%; 1720} 1721.flip-fill-btc { 1722 background: #f7931a; 1723} 1724.flip-mcap { 1725 font-size: 0.88rem; 1726 font-weight: 600; 1727 color: var(--color-neutral-500); 1728 white-space: nowrap; 1729} 1730.flip-pct { 1731 font-size: 0.82rem; 1732 font-weight: 700; 1733 color: var(--color-text); 1734 min-width: 34px; 1735 text-align: right; 1736} 1737/* Sixth grid cell â side-by-side buttons when the cell is wide enough, 1738 one-under-the-other when it isn't. The tool CELL itself stays in its 1739 grid column (never wraps below the asset row); only its contents 1740 wrap internally. */ 1741.flip-tools { 1742 display: flex; 1743 flex-wrap: wrap; 1744 gap: 4px 6px; 1745 align-items: center; 1746 justify-content: center; 1747 min-width: 0; 1748} 1749.flip-tool-btn { 1750 padding: 4px 10px; 1751 font-size: 0.72rem; 1752 font-weight: 700; 1753 border: 2px solid var(--color-primary); 1754 border-radius: var(--radius-full); 1755 background: transparent; 1756 color: var(--color-primary); 1757 cursor: pointer; 1758 white-space: nowrap; 1759 transition: all 0.15s ease; 1760} 1761.flip-tool-btn:hover { 1762 background: var(--color-primary); 1763 color: #fff; 1764} 1765 1766/* Font-size scaling so all 6 cells keep fitting on one row as the 1767 viewport narrows. The grid stays fixed at 6 columns â only the content 1768 inside each cell shrinks. On the narrowest phones the mcap is hidden 1769 entirely (label + meter still visible) because no sensible font size 1770 would keep it readable in the cell width. */ 1771@media (max-width: 1100px) { 1772 .flip-label { font-size: 0.78rem; } 1773 .flip-mcap { font-size: 0.72rem; } 1774 .flip-pct { font-size: 0.72rem; } 1775 .flip-bar { width: 45px; } 1776} 1777@media (max-width: 900px) { 1778 .flippenings-wrap { 1779 padding: 4px 0 2px; 1780 margin-top: 2px; 1781 margin-bottom: 12px; 1782 } 1783 .flip-label { font-size: 0.72rem; } 1784 .flip-mcap { font-size: 0.64rem; } 1785 .flip-pct { font-size: 0.64rem; } 1786 .flip-bar { width: 38px; }
1787 .flip-logo { width: 20px; height: 20px; } 1788 .flip-tool-btn { padding: 3px 8px; font-size: 0.64rem; border-width: 2px; } 1789} 1790@media (max-width: 700px) { 1791 .flip-label { font-size: 0.65rem; } 1792 .flip-mcap { font-size: 0.58rem; } 1793 .flip-pct { font-size: 0.58rem; } 1794 .flip-bar { width: 28px; height: 6px; } 1795 .flip-logo { width: 18px; height: 18px; } 1796 .flippenings-inner { gap: 6px 6px; } 1797} 1798@media (max-width: 500px) { 1799 .header-press-strip { 1800 margin-top: 0; 1801 margin-bottom: 10px; 1802 padding: 8px 12px 10px; 1803 } 1804 .header-press-strip__title { 1805 font-size: 0.68rem; 1806 margin-bottom: 6px; 1807 } 1808 .header-press-track { 1809 animation-duration: 42s; 1810 } 1811 .header-press-set { 1812 gap: 26px; 1813 padding-right: 26px; 1814 } 1815 .header-press-logo { 1816 max-width: 116px; 1817 height: 26px; 1818 } 1819 .flip-mcap { display: none; } /* no room at this width */ 1820 .flip-label { font-size: 0.6rem; } 1821 .flip-pct { font-size: 0.55rem; } 1822 .flip-bar { width: 22px; height: 5px; } 1823 .flip-logo { width: 16px; height: 16px; } 1824 .flip-tool-btn { padding: 2px 6px; font-size: 0.55rem; border-width: 1px; } 1825 .flippenings-inner { gap: 4px 4px; } 1826} 1827 1828/* ââ Shared dropdown styles ââââââââââââââââââââââââââââ */ 1829.flippener-drop-sep { height: 1px; background: var(--color-neutral-200); margin: 2px 0; } 1830.cmp-drop-sep, .fp-drop-sep { height: 1px; background: var(--color-neutral-200); margin: 2px 0; } 1831.cmp-drop-item.cdi-class, .fp-drop-item.fpdi-class { background: rgba(99,102,241,0.04); } 1832.cmp-drop-item.cdi-class .cdi-name, .fp-drop-item.fpdi-class .fpdi-name { color: var(--color-primary); } 1833.flip-tool-btn--link { 1834 text-decoration: none; display: inline-flex; align-items: center; 1835} 1836 1837/* Free usage badge */ 1838.free-usage-badge { 1839 display: inline-block; 1840 font-size: 0.72rem; 1841 font-weight: 600; 1842 color: var(--color-neutral-500); 1843 background: var(--color-neutral-50); 1844 border: 1px solid var(--color-neutral-200); 1845 border-radius: 20px; 1846 padding: 4px 12px; 1847 margin: 8px auto 0; 1848 text-align: center; 1849} 1850.free-usage-badge .fub-count { color: var(--color-primary); font-weight: 700; } 1851.free-usage-badge.fub-warn { border-color: #f59e0b; color: #b45309; background: #fef3c7; } 1852.free-usage-badge.fub-limit { border-color: #ef4444; color: #dc2626; background: #fef2f2; } 1853</style> 1854 1855
1856<script> 1857/* Flippenings bar loader moved to templates/html/flippenings.html so 1858 it ships alongside the markup â guarantees the #flippenings element 1859 exists in the DOM by the time the loader runs, no matter where the 1860 include lands on the page. */ 1861 1862// Global auth flag (used by dark mode sync, favourites, etc.) 1863if (typeof window.__isLoggedIn === 'undefined') { 1864 window.__isLoggedIn = false; 1865} 1866// Global user tier: 'free' | 'signup' | 'pro' | 'proplus' 1867window.__userTier = (function() { 1868 1869 return 'free'; 1870 1871})(); 1872 1873// Asset class synthetic rows for Flippener/Comparator dropdowns 1874(function() { 1875 function _pm(s) { 1876 if (!s) return 0; 1877 s = String(s).replace(/[$,]/g, ''); 1878 var m = 1; 1879 if (s.endsWith('T')) { m = 1e12; s = s.slice(0, -1); } 1880 else if (s.endsWith('B')) { m = 1e9; s = s.slice(0, -1); } 1881 else if (s.endsWith('M')) { m = 1e6; s = s.slice(0, -1); } 1882 return parseFloat(s) * m || 0; 1883 } 1884 window.__assetClasses = [ 1885 { Name: 'All Assets', Ticker: '__all__', MarketCap: _pm('$1,083.80T'), Logo: '/static/logo.png', AssetType: 'Asset Class', _isClass: true }, 1886 { Name: 'All Commodities', Ticker: '__commodities__', MarketCap: _pm('$225.33T'), Logo: '/static/icons/commodities/gc=f.png', AssetType: 'Asset Class', _isClass: true }, 1887 { Name: 'All Currencies', Ticker: '__currencies__', MarketCap: _pm('$139.77T'), Logo: '/static/icons/flags/us.png', AssetType: 'Asset Class', _isClass: true }, 1888 { Name: 'All Companies', Ticker: '__companies__', MarketCap: _pm('$149.86T'), Logo: '/static/icons/companies/AAPL.png', AssetType: 'Asset Class', _isClass: true }, 1889 { Name: 'All Crypto', Ticker: '__crypto__', MarketCap: _pm('$2.95T'), Logo: '/static/icons/cryptos/BTC-USD.png', AssetType: 'Asset Class', _isClass: true } 1890 ]; 1891})(); 1892// ââ Usage limits per tier âââââââââââââââââââââââââââââââââ 1893// Limits: { type: { free, signup, pro, proplus } } 1894// -1 means unlimited, positive = total (free/signup) or per month (pro) 1895window.__freeUsage = (function() { 1896 var TIER_LIMITS = { 1897 favourite: { free: 3, signup: 15, pro: 50, proplus: -1 }, 1898 flippener: { free: 3, signup: 10, pro: 30, proplus: -1 }, 1899 comparator: { free: 3, signup: 5, pro: 15, proplus: -1 } 1900 }; 1901 var KEY = 'amc_usage'; 1902 var tier = window.__userTier || 'free'; 1903 1904 function _load() { 1905 try { return JSON.parse(localStorage.getItem(KEY)) || {}; } catch(e) { return {}; } 1906 } 1907 function _save(d) { try { localStorage.setItem(KEY, JSON.stringify(d)); } catch(e) {} } 1908 1909 // Monthly reset key: "2026-04" etc 1910 function _monthKey() { 1911 var d = new Date(); 1912 return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0'); 1913 } 1914 1915 // For pro/proplus, counts reset monthly. For free/signup, counts are lifetime. 1916 function _getStorageKey(type) { 1917 if (tier === 'pro' || tier === 'proplus') return type + ':' + _monthKey(); 1918 return type; 1919 } 1920 1921 function getCount(type) { 1922 var d = _load(); 1923 return d[_getStorageKey(type)] || 0; 1924 } 1925 1926 function getLimit(type) { 1927 var limits = TIER_LIMITS[type]; 1928 if (!limits) return 3; 1929 var lim = limits[tier]; 1930 return (lim === undefined) ? 3 : lim; 1931 } 1932 1933 function getRemaining(type) { 1934 var lim = getLimit(type); 1935 if (lim === -1) return -1; // unlimited 1936 return Math.max(0, lim - getCount(type)); 1937 } 1938 1939 function increment(type) { 1940 var d = _load(); 1941 var sk = _getStorageKey(type); 1942 d[sk] = (d[sk] || 0) + 1; 1943 _save(d); 1944 // Also notify server for logged-in users 1945 if (window.__isLoggedIn) { 1946 fetch('/api/usage/increment', { 1947 method: 'POST', 1948 headers: { 'Content-Type': 'application/json' }, 1949 body: JSON.stringify({ type: type }) 1950 }).catch(function(){}); 1951 } 1952 return d[sk]; 1953 } 1954 1955 function isLimitReached(type) { 1956 var lim = getLimit(type); 1957 if (lim === -1) return false; 1958 return getCount(type) >= lim; 1959 } 1960 1961 // Reason-driven CTA: the title changes to WHY the user was gated, e.g. 1962 // 'Save your Favourites with a Free Account'. Uses the closable signup-gate 1963 // overlay (free-account CTA) if present, else the pricing overlay, else /login.
1964 var GATE_TITLES = { 1965 favourite: 'Save your Favourites with a Free Account', 1966 networth: 'Track your Net Worth with a Free Account', 1967 comparator: 'Keep Comparing with a Free Account', 1968 flippener: 'Keep using the Flippener with a Free Account', 1969 article: 'Read this article with a Free Account', 1970 'default': 'Create your Free Account' 1971 }; 1972 function openSignupGate(reason) { 1973 var title = GATE_TITLES[reason] || GATE_TITLES['default']; 1974 var overlay = document.getElementById('pricingOverlay'); 1975 if (overlay) { 1976 var th = overlay.querySelector('.auth-hero-title'); 1977 if (th) th.textContent = title; // retitle to WHY they were gated 1978 overlay.classList.add('open'); 1979 overlay.setAttribute('aria-hidden', 'false'); 1980 document.body.style.overflow = 'hidden'; 1981 } else { 1982 window.location.href = '/login?view=register'; 1983 } 1984 } 1985 1986 function tryUse(type) { 1987 if (tier === 'proplus') return true; // unlimited 1988 if (isLimitReached(type)) { 1989 openSignupGate(type); // pass the feature so the CTA title matches 1990 return false; 1991 } 1992 increment(type); 1993 return true; 1994 } 1995 1996 // Whether counts are monthly (vs lifetime total) 1997 function isMonthly() { return tier === 'pro' || tier === 'proplus'; } 1998 1999 return { 2000 getCount: getCount, getLimit: getLimit, getRemaining: getRemaining, 2001 increment: increment, isLimitReached: isLimitReached, 2002 tryUse: tryUse, openSignupGate: openSignupGate, isMonthly: isMonthly, 2003 TIER_LIMITS: TIER_LIMITS 2004 }; 2005})(); 2006 2007// Render usage badges for all tiers 2008window.__updateUsageBadge = function(elId, type, label) { 2009 var el = document.getElementById(elId); 2010 if (!el) return; 2011 var fu = window.__freeUsage; 2012 var limit = fu.getLimit(type); 2013 2014 // Pro Plus = unlimited, hide badge 2015 if (limit === -1) { el.style.display = 'none'; return; } 2016 2017 var used = fu.getCount(type); 2018 var remaining = Math.max(0, limit - used); 2019 var period = fu.isMonthly() ? '/month' : ''; 2020 var tier = window.__userTier; 2021 2022 var upgradeText; 2023 if (tier === 'free') upgradeText = '<a href="/login?view=register" style="color:#dc2626;font-weight:700;text-decoration:underline;">sign up</a> for more'; 2024 else if (tier === 'signup') upgradeText = '<a href="/login?view=pro" style="color:#dc2626;font-weight:700;text-decoration:underline;">upgrade to Pro</a> for more'; 2025 else upgradeText = '<a href="/login?view=pro-plus" style="color:#dc2626;font-weight:700;text-decoration:underline;">upgrade to Pro Plus</a> for unlimited'; 2026 2027 if (remaining <= 0) { 2028 el.className = 'free-usage-badge fub-limit'; 2029 el.innerHTML = label + ' limit reached â ' + upgradeText; 2030 } else if (remaining <= Math.ceil(limit * 0.25)) { 2031 el.className = 'free-usage-badge fub-warn'; 2032 el.innerHTML = '<span class="fub-count">' + remaining + '</span> of ' + limit + ' ' + label + period + ' remaining'; 2033 } else { 2034 el.className = 'free-usage-badge'; 2035 el.innerHTML = '<span class="fub-count">' + remaining + '</span> of ' + limit + ' ' + label + period + ' remaining'; 2036 } 2037 el.style.display = ''; 2038}; 2039 2040// Sync server-side usage counts for logged-in users 2041if (window.__isLoggedIn && window.__userTier !== 'free') { 2042 fetch('/api/usage') 2043 .then(function(r) { return r.ok ? r.json() : null; }) 2044 .then(function(data) { 2045 if (!data) return; 2046 // Overwrite localStorage with authoritative server counts 2047 var KEY = 'amc_usage'; 2048 var month = (function(){ var d=new Date(); return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0'); })(); 2049 var stored = {}; 2050 try { stored = JSON.parse(localStorage.getItem(KEY)) || {}; } catch(e) {} 2051 var isMonthly = data.tier === 'pro' || data.tier === 'proplus'; 2052 ['flippener','comparator'].forEach(function(k) { 2053 if (data[k]) { 2054 var sk = isMonthly ? (k+':'+month) : k; 2055 stored[sk] = data[k].used; 2056 } 2057 }); 2058 if (data.favourite) { 2059 var sk = isMonthly ? ('favourite:'+month) : 'favourite'; 2060 stored[sk] = data.favourite.used; 2061 } 2062 try { localStorage.setItem(KEY, JSON.stringify(stored)); } catch(e) {} 2063 }) 2064 .catch(function(){}); 2065} 2066 2067const DARK_KEY = 'prefers_dark_mode'; // legacy: '0' / '1' 2068const THEME_KEY = 'theme_mode';
2068 // new: 'dark' / 'light' / 'system' 2069 2070// Resolve which theme should currently be applied given the stored mode. 2071// 'system' -> follows prefers-color-scheme; 'dark'/'light' force. 2072function resolveTheme(mode) { 2073 if (mode === 'dark') return 'dark'; 2074 if (mode === 'light') return 'light'; 2075 // 'system' is now an EXPLICIT opt-in only (no longer the default) - follow the OS. 2076 return (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; 2077} 2078 2079function getStoredThemeMode() { 2080 try { 2081 var v = localStorage.getItem(THEME_KEY); 2082 if (v === 'dark' || v === 'light' || v === 'system') return v; 2083 // Migration: respect legacy DARK_KEY if no new key set yet 2084 var legacy = localStorage.getItem(DARK_KEY); 2085 if (legacy === '1') return 'dark'; 2086 if (legacy === '0') return 'light'; 2087 } catch(e) {} 2088 // DEFAULT = light. Previously 'system', which followed prefers-color-scheme and 2089 // silently gave dark-OS visitors a dark site on first load. Dark/System are now 2090 // opt-in only (via the quick toggle or the Settings 3-way picker). 2091 return 'light'; 2092} 2093 2094function setTheme(mode) { 2095 try { localStorage.setItem(THEME_KEY, mode); } catch(e) {} 2096 var resolved = resolveTheme(mode); 2097 applyDarkMode(resolved === 'dark', { skipModeStore: true }); 2098 // Reflect the picker state in the Settings UI 2099 document.querySelectorAll('.nav-theme-btn').forEach(function (b) { 2100 var on = b.dataset.theme === mode; 2101 b.classList.toggle('is-active', on); 2102 b.setAttribute('aria-checked', on ? 'true' : 'false'); 2103 }); 2104} 2105 2106function applyDarkMode(isDark, opts) { 2107 document.documentElement.classList.toggle('dark-mode', isDark); 2108 // Legacy key kept for any code that still reads it 2109 localStorage.setItem(DARK_KEY, isDark ? '1' : '0'); 2110 2111 // When the user clicks the quick toggle (not the Settings 3-way), 2112 // promote to an explicit dark/light selection so it doesn't snap back 2113 // when System changes. 2114 if (!opts || !opts.skipModeStore) { 2115 try { localStorage.setItem(THEME_KEY, isDark ? 'dark' : 'light'); } catch(e) {} 2116 } 2117 2118 const toggle = document.getElementById('darkModeToggle'); 2119 if (toggle) toggle.checked = isDark; 2120 2121 // Persist to server for logged-in users 2122 if (window.__isLoggedIn) { 2123 fetch('/account/dark-mode', { 2124 method: 'POST', 2125 headers: { 'Content-Type': 'application/json' }, 2126 body: JSON.stringify({ dark: isDark }) 2127 }).catch(function () {}); 2128 } 2129} 2130 2131function darkModeFunction() { 2132 const isDark = !document.documentElement.classList.contains('dark-mode'); 2133 applyDarkMode(isDark); 2134} 2135 2136// Initial paint: apply the stored mode immediately (before DOMContentLoaded 2137// so there's no flash). Re-resolves if user is on System and the OS theme 2138// changes during the session. 2139(function bootTheme() { 2140 var mode = getStoredThemeMode(); 2141 var resolved = resolveTheme(mode); 2142 document.documentElement.classList.toggle('dark-mode', resolved === 'dark'); 2143 if (window.matchMedia) { 2144 var mql = window.matchMedia('(prefers-color-scheme: dark)'); 2145 var onChange = function () { 2146 if (getStoredThemeMode() === 'system') { 2147 document.documentElement.classList.toggle('dark-mode', mql.matches); 2148 } 2149 }; 2150 if (mql.addEventListener) mql.addEventListener('change', onChange); 2151 else if (mql.addListener) mql.addListener(onChange); 2152 } 2153})(); 2154 2155document.addEventListener('DOMContentLoaded', () => { 2156 const toggle = document.getElementById('darkModeToggle'); 2157 const btn = document.getElementById('darkModeBtn'); 2158 2159 // Sync hidden checkbox to current mode 2160 if (toggle) toggle.checked = document.documentElement.classList.contains('dark-mode'); 2161 2162 // Icon button click â toggle 2163 if (btn) btn.addEventListener('click', darkModeFunction); 2164 2165 // Settings â Theme picker (Dark / Light / System) 2166 document.querySelectorAll('.nav-theme-btn').forEach(function (b) { 2167 var on = b.dataset.theme === getStoredThemeMode(); 2168 b.classList.toggle('is-active', on); 2169 b.setAttribute('aria-checked', on ? 'true' : 'false'); 2170 b.addEventListener('click', function (e) { 2171 e.preventDefault(); 2172 e.stopPropagation(); 2173 setTheme(b.dataset.theme); 2174 }); 2175 }); 2176 2177 // Overflow auth triggers in logo bar â proxy to real buttons (skip avatar links, they're direct <a href>) 2178 document.querySelectorAll('.overflow-auth-trigger:not(.nav-avatar-link)').forEach(function (el) { 2179 el.addEventListener('click', function (e) { 2180 e.preventDefault(); 2181 var target = el.dataset.open; 2182 var real = document.getElementById(target === 'account' ? 'openAccount' : 'openAuth'); 2183 if (real) real.click(); 2184 }); 2185 }); 2186 2187 // Detect navbar overflow â swap buttons to logo bar 2188 var wrapper = document.querySelector('.navbar-wrapper'); 2189 var navbar = document.querySelector('.navbar'); 2190 2191 function checkNavOverflow() { 2192 // Collapse-into-logo-bar behavior disabled: FAQs / Support / dark-mode 2193 // should always live in .nav-right, regardless of width. 2194 if (wrapper) wrapper.classList.remove('nav-overflowed'); 2195 } 2196 2197 checkNavOverflow(); 2198 window.addEventListener('resize', checkNavOverflow); 2199 2200 // ââ Tap-to-toggle for nav dropdowns (Learn, Products, Help) âââââââââ 2201 // Covers navbar-row dropdowns AND the top-bar Help dropdown. Selector
2202 // is broad: any .nav-dropdown inside .navbar-header or .top-bar-actions. 2203 var DD_SCOPE = '.navbar-header .nav-dropdown, .top-bar-actions .nav-dropdown'; 2204 document.querySelectorAll(DD_SCOPE).forEach(function (dd) { 2205 var trigger = dd.querySelector(':scope > .nav-dropdown-trigger'); 2206 if (!trigger) return; 2207 trigger.addEventListener('click', function (e) { 2208 e.preventDefault(); 2209 e.stopPropagation(); 2210 document.querySelectorAll(DD_SCOPE).forEach(function (other) { 2211 if (other !== dd && other.classList.contains('open')) { 2212 other.classList.remove('open'); 2213 var t = other.querySelector(':scope > .nav-dropdown-trigger'); 2214 if (t) t.setAttribute('aria-expanded', 'false'); 2215 } 2216 }); 2217 var isOpen = dd.classList.toggle('open'); 2218 trigger.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); 2219 }); 2220 }); 2221 document.addEventListener('click', function (e) { 2222 document.querySelectorAll(DD_SCOPE).forEach(function (dd) { 2223 if (!dd.classList.contains('open')) return; 2224 if (!dd.contains(e.target)) { 2225 dd.classList.remove('open'); 2226 var t = dd.querySelector(':scope > .nav-dropdown-trigger'); 2227 if (t) t.setAttribute('aria-expanded', 'false'); 2228 } 2229 }); 2230 }); 2231 document.addEventListener('keydown', function (e) { 2232 if (e.key !== 'Escape') return; 2233 document.querySelectorAll(DD_SCOPE).forEach(function (dd) { 2234 dd.classList.remove('open'); 2235 var t = dd.querySelector(':scope > .nav-dropdown-trigger'); 2236 if (t) t.setAttribute('aria-expanded', 'false'); 2237 }); 2238 }); 2239 2240 // ââ App download popup ââââââââââââââââââââââââââââââââââââ 2241 var appPopup = document.getElementById('appDownloadPopup'); 2242 var appClose = document.getElementById('closeAppPopup'); 2243 var appBackdrop = document.getElementById('appPopupBackdrop'); 2244 var appPopupTitle = document.getElementById('appPopupTitle'); 2245 var appPopupCopy = document.getElementById('appPopupCopy'); 2246 2247 function openApp(e) { 2248 if (e) e.preventDefault(); 2249 var appleSoon = e && e.currentTarget && e.currentTarget.dataset.appleSoon === 'true'; 2250 if (appPopupTitle) appPopupTitle.textContent = appleSoon ? 'App Store Coming Soon' : 'Get the App'; 2251 if (appPopupCopy) appPopupCopy.textContent = appleSoon ? 'AssetMarketCap for iPhone is coming soon.' : 'Track 35,000+ assets on the go.'; 2252 if (appPopup) appPopup.classList.add('open'); 2253 } 2254 function closeApp() { 2255 if (appPopup) appPopup.classList.remove('open'); 2256 } 2257 2258 // Mobile button click routing: 2259 // Android â straight to Play Store 2260 // iOS â "App Store coming soon" popup (uses the existing modal) 2261 // Desktop â open the popup with QR (existing behavior) 2262 function detectPlatform() { 2263 var ua = navigator.userAgent || ''; 2264 if (/android/i.test(ua)) return 'android'; 2265 if (/iPad|iPhone|iPod/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) return 'ios'; 2266 return 'desktop'; 2267 } 2268 document.querySelectorAll('.js-open-app-popup').forEach(function (el) { 2269 el.addEventListener('click', function (e) { 2270 // If this was triggered by an actual tap/click (not the dropdown's 2271 // own click-to-toggle), route by platform. 2272 var plat = detectPlatform(); 2273 if (plat === 'android') { 2274 e.preventDefault(); 2275 window.location.href = 'https://play.google.com/store/apps/details?id=com.assetmarketcap.app'; 2276 return; 2277 } 2278 if (plat === 'ios') { 2279 e.preventDefault(); 2280 if (appPopupTitle) appPopupTitle.textContent = 'App Store Coming Soon'; 2281 if (appPopupCopy) appPopupCopy.textContent = 'AssetMarketCap for iPhone is on the way. Use the web app for now â it works great on iOS.'; 2282 if (appPopup) appPopup.classList.add('open'); 2283 return; 2284 } 2285 // Desktop: keep the QR popover (hover) behavior. The dropdown JS 2286 // already opens the menu on click for tap devices. Open the big 2287 // modal too as a fallback for users who want a larger QR. 2288 openApp(e); 2289 }); 2290 }); 2291 if (appClose) appClose.addEventListener('click', closeApp); 2292 if (appBackdrop) appBackdrop.addEventListener('click', closeApp); 2293 document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeApp(); }); 2294 2295 // ââ "/" keyboard shortcut focuses the global search ââââââââ 2296 // CoinGecko-style. Ignore if user is already in an input/textarea 2297 // so it doesn't hijack typing in form fields. 2298 var navSearch = document.getElementById('navGlobalSearch'); 2299 if (navSearch) { 2300 document.addEventListener('keydown', function (e) { 2301 if (e.key !== '/') return; 2302 var t = e.target; 2303 var inField = t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.isContentEditable); 2304 if (inField) return; 2305 e.preventDefault(); 2306 navSearch.focus(); 2307 navSearch.select(); 2308 }); 2309 2310 // On the homepage, mirror typing into the DataTables filter so the 2311 // table updates as the user types in the global search. Form submit 2312 // still goes to /search for a full-text search across pages. 2313 var dtFilter = document.querySelector('.dataTables_filter input[type="search"], .dataTables_filter input'); 2314 if (dtFilter) { 2315 navSearch.addEventListener('input', function () { 2316 var v = navSearch.value; 2317 dtFilter.value = v; 2318 // Fire the events DataTables listens for 2319 dtFilter.dispatchEvent(new Event('keyup', { bubbles: true })); 2320 dtFilter.dispatchEvent(new Event('input', { bubbles: true })); 2321 }); 2322 } 2323 } 2324 2325 // ââ Favourites pill: toggle the table's "favourites-only" filter ââ 2326 // Logged-in users get the same behaviour as clicking the header 2327 // star (toggleStarFilter is exposed at top level of table.js). The 2328 // header star and the pill share state via syncStarHeaderUI, which 2329 // also paints the pill icon yellow when the filter is active. 2330 // Logged-out users keep the original anchor (â /login?view=register). 2331 var favPill = document.querySelector('.nav-favourites-trigger'); 2332 if (favPill && favPill.getAttribute('href') === '#favourites') { 2333 favPill.addEventListener('click', function (e) { 2334 if (typeof window.toggleStarFilter === 'function') { 2335 e.preventDefault(); 2336 window.toggleStarFilter(); 2337 } 2338 }); 2339 } 2340 2341 // ââ Sidebar toggle ââââââââââââââââââââââââââââââââââââââââââââ 2342 // Two trigger points share one handler â visually they look like a 2343 // single button that "jumps" between two homes: 2344 // #sidebarCloseBtn â sidebar's top-right corner (visible while 2345 // the sidebar is expanded) 2346 // #sidebarShowBtn â right side of the dominance bar (visible 2347 // only when the sidebar is collapsed) 2348 // Both flip html.sidebar-collapsed which hides .page-side and
2349 // collapses .page-grid to a single column. Preference persists. 2350 // We tag html.has-sidebar when a .page-side actually exists so the 2351 // dominance-bar Show button only ever appears on pages where it 2352 // would do something meaningful. 2353 if (document.querySelector('.page-side')) { 2354 document.documentElement.classList.add('has-sidebar'); 2355 } 2356 var sidebarClose = document.getElementById('sidebarCloseBtn'); 2357 var sidebarShow = document.getElementById('sidebarShowBtn'); 2358 function toggleSidebar() { 2359 var collapsed = document.documentElement.classList.toggle('sidebar-collapsed'); 2360 try { localStorage.setItem('sidebar_collapsed', collapsed ? '1' : '0'); } catch (e) {} 2361 // Let DataTables re-measure now that the main column changed width, then 2362 // re-run the responsive column fit so collapsing the sidebar re-shows the 2363 // columns that now fit (e.g. Country), and expanding re-hides as needed. 2364 function _reflow() { 2365 if (window.__assetTable && typeof window.__assetTable.columns === 'function') { 2366 try { window.__assetTable.columns.adjust(); } catch (e) {} 2367 } 2368 if (typeof window.__amcRefitColumns === 'function') { 2369 try { window.__amcRefitColumns(); } catch (e) {} 2370 } 2371 } 2372 _reflow(); 2373 // The grid width transition takes a moment; re-fit again once it settles. 2374 setTimeout(_reflow, 260); 2375 } 2376 if (sidebarClose) sidebarClose.addEventListener('click', toggleSidebar); 2377 if (sidebarShow) sidebarShow .addEventListener('click', toggleSidebar); 2378}); 2379</script>
2379 2380 2381<!-- App download popup --> 2382<div id="appDownloadPopup" class="app-popup-overlay"> 2383 <div class="app-popup-backdrop" id="appPopupBackdrop"></div> 2384 <div class="app-popup"> 2385 <button class="app-popup-close" id="closeAppPopup" type="button" aria-label="Close">×</button> 2386 <div class="app-popup-icon"> 2387 <i class="fa-solid fa-mobile-screen-button"></i> 2388 </div> 2389 <h3 class="app-popup-title" id="appPopupTitle">Get the App</h3> 2390 <p class="app-popup-copy" id="appPopupCopy">Scan with your phone to download. 35,000+ assets in your pocket.</p> 2391 2392 2393 <div class="app-popup-qrs"> 2394 <div class="app-popup-qr"> 2395 <a href="https://play.google.com/store/apps/details?id=com.assetmarketcap.app" 2396 target="_blank" rel="noopener" class="app-popup-qr-link"> 2397 <img class="app-popup-qr-img" 2398 src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&margin=0&data=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.assetmarketcap.app" 2399 alt="Google Play QR code" width="160" height="160" loading="lazy"> 2400 <span class="app-popup-qr-label"> 2401 <i class="fa-brands fa-google-play"></i> 2402 Google Play 2403 </span> 2404 </a> 2405 </div> 2406 <div class="app-popup-qr app-popup-qr--soon"> 2407 <div class="app-popup-qr-placeholder" aria-hidden="true"> 2408 <i class="fa-brands fa-apple"></i> 2409 <span>Coming soon</span> 2410 </div> 2411 <span class="app-popup-qr-label app-popup-qr-label--muted"> 2412 <i class="fa-brands fa-apple"></i> 2413 App Store 2414 </span> 2415 </div> 2416 </div> 2417 </div> 2418</div> 2419 2420<style> 2421.app-popup-overlay { 2422 position: fixed; 2423 inset: 0; 2424 z-index: 99998; 2425 display: none; 2426 align-items: center; 2427 justify-content: center; 2428 padding: 24px; 2429} 2430.app-popup-overlay.open { 2431 display: flex; 2432} 2433.app-popup-backdrop { 2434 position: fixed; 2435 inset: 0; 2436 background: rgba(4, 2, 20, 0.65); 2437 backdrop-filter: blur(12px); 2438 -webkit-backdrop-filter: blur(12px); 2439} 2440.app-popup { 2441 position: relative; 2442 z-index: 2; 2443 width: min(400px, 92vw); 2444 padding: 36px 32px 32px; 2445 border-radius: 24px; 2446 background: rgba(14, 8, 40, 0.88); 2447 backdrop-filter: blur(40px) saturate(180%); 2448 -webkit-backdrop-filter: blur(40px) saturate(180%); 2449 border: 1px solid rgba(255, 255, 255, 0.1); 2450 box-shadow: 2451 0 0 0 1px rgba(255, 255, 255, 0.05) inset, 2452 0 40px 80px rgba(0, 0, 0, 0.5), 2453 0 0 80px rgba(99, 102, 241, 0.1); 2454 text-align: center; 2455} 2456.app-popup-close { 2457 position: absolute; 2458 top: 12px; 2459 right: 12px; 2460 width: 32px; 2461 height: 32px; 2462 border: 1px solid rgba(255, 255, 255, 0.12); 2463 border-radius: 50%; 2464 background: rgba(255, 255, 255, 0.08); 2465 color: rgba(255, 255, 255, 0.7); 2466 font-size: 1.15rem; 2467 cursor: pointer; 2468 display: flex; 2469 align-items: center; 2470 justify-content: center; 2471 line-height: 1; 2472 transition: background 0.15s; 2473} 2474.app-popup-close:hover { 2475 background: rgba(255, 255, 255, 0.16); 2476 color: white; 2477} 2478.app-popup-icon { 2479 font-size: 2.2rem; 2480 color: #a78bfa; 2481 margin-bottom: 14px; 2482} 2483.app-popup-title { 2484 margin: 0 0 6px; 2485 font-size: 1.25rem; 2486 font-weight: 800; 2487 color: white; 2488 letter-spacing: -0.01em; 2489} 2490.app-popup-copy { 2491 margin: 0 0 24px; 2492 font-size: 0.82rem; 2493 color: rgba(255, 255, 255, 0.55); 2494} 2495.app-popup-buttons { 2496 display: flex; 2497 flex-direction: column; 2498 gap: 10px; 2499} 2500.app-popup-btn { 2501 display: flex; 2502 align-items: center; 2503 gap: 12px; 2504 padding: 14px 20px; 2505 border-radius: 14px; 2506 text-decoration: none; 2507 color: white; 2508 font-family: "Nunito", system-ui, sans-serif; 2509 transition: all 0.18s ease; 2510} 2511.app-popup-btn i { 2512 font-size: 1.6rem; 2513 flex-shrink: 0; 2514 width: 28px; 2515 text-align: center; 2516} 2517.app-popup-btn span { 2518 display: flex; 2519 flex-direction: column; 2520 text-align: left; 2521 line-height: 1.2; 2522 font-size: 1rem; 2523 font-weight: 700; 2524} 2525.app-popup-btn span small { 2526 font-size: 0.65rem; 2527 font-weight: 500; 2528 color: rgba(255, 255, 255, 0.6); 2529 letter-spacing: 0.02em; 2530} 2531.app-popup-btn--apple { 2532 background: rgba(255, 255, 255, 0.1); 2533 border: 1px solid rgba(255, 255, 255, 0.15); 2534} 2535.app-popup-btn--apple:hover { 2536 background: rgba(255, 255, 255, 0.16); 2537 transform: translateY(-1px); 2538 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); 2539} 2540.app-popup-btn--google { 2541 background: rgba(255, 255, 255, 0.1); 2542 border: 1px solid rgba(255, 255, 255, 0.15); 2543} 2544.app-popup-btn--google:hover { 2545 background: rgba(255, 255, 255, 0.16); 2546 transform: translateY(-1px); 2547 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); 2548} 2549.app-popup-btn.is-disabled {
2550 cursor: not-allowed; 2551 opacity: 0.55; 2552 pointer-events: none; 2553 user-select: none; 2554} 2555.app-popup-btn.is-disabled:hover { 2556 background: rgba(255, 255, 255, 0.1); 2557 transform: none; 2558 box-shadow: none; 2559} 2560 2561/* Make the active nav link's indigo background fill the entire navbar 2562 height including the 1px bottom border, so there's no dark strip 2563 below it. */ 2564.navbar-header .navbar { align-items: stretch; } 2565.navbar-header .nav-left, 2566.navbar-header .nav-left ul, 2567.navbar-header .nav-left li, 2568.navbar-header .nav-left li a { 2569 height: 100%; 2570 align-self: stretch; 2571} 2572.navbar-header .nav-left li a { 2573 box-sizing: border-box; 2574} 2575.navbar-header .active { 2576 margin-bottom: -1px; /* overlap the 1px bottom border on the nav row */ 2577} 2578 2579/* AssetMarketCap logo â bump removed in v5 refresh; logo now sits 2580 naturally in a compact 72px-height navbar instead of the old 180px 2581 black hero block. Margin handled by .logo-top rule in global.css. */ 2582 2583/* ââ Navbar overflow collapse â DISABLED ââââââââââââââââââââââââââââââââââ 2584 FAQs / Support / Dark-mode always live in .nav-right. The overflow copy 2585 in .navbar-wrapper-actions stays hidden at every width. */ 2586.navbar-wrapper-actions, 2587.nav-overflowed .navbar-wrapper-actions { display: none !important; } 2588.nav-overflowed .nav-right { display: flex !important; } 2589 2590/* ââ Navbar hover dropdowns (Learn, Products) âââââââââââââââââââââââââââ */ 2591/* Allow dropdowns to escape the nav ul, which sets overflow:hidden. */ 2592.navbar-header ul { overflow: visible !important; } 2593.navbar-header .navbar { overflow: visible; } 2594.navbar-header .nav-left { overflow: visible; } 2595 2596.navbar-header li.nav-dropdown { position: relative; } 2597 2598.navbar-header li.nav-dropdown > .nav-dropdown-trigger { 2599 display: flex; 2600 align-items: center; 2601 gap: 6px; 2602 cursor: pointer; 2603} 2604.nav-dropdown-caret { 2605 font-size: 0.65em; 2606 opacity: 0.75; 2607 transition: transform 0.15s ease; 2608} 2609.navbar-header li.nav-dropdown:hover > .nav-dropdown-trigger .nav-dropdown-caret, 2610.navbar-header li.nav-dropdown:focus-within > .nav-dropdown-trigger .nav-dropdown-caret, 2611.navbar-header li.nav-dropdown.open > .nav-dropdown-trigger .nav-dropdown-caret { 2612 transform: rotate(180deg); 2613} 2614 2615.navbar-header .nav-dropdown-menu { 2616 position: absolute; 2617 top: 100%; 2618 left: 0; 2619 min-width: 220px; 2620 margin: 0; 2621 padding: 6px 0; 2622 list-style: none; 2623 display: none; 2624 /* Earlier rule `.navbar-header .nav-left ul { height: 100%; align-self: stretch }` 2625 pins the dropdown to the nav row's height â kill it so the menu grows 2626 to fit its items. */ 2627 height: auto !important; 2628 align-self: auto !important; 2629 /* Force a vertical stack â .navbar-header ul sets flex-direction:row 2630 which would line items up horizontally with no background fill. */ 2631 flex-direction: column !important; 2632 align-items: stretch !important; 2633 z-index: 1000; 2634 overflow: hidden; /* keep rounded corners on hover backgrounds */ 2635 /* Background / border / shadow handled by v5 refresh rules in global.css */ 2636} 2637.navbar-header li.nav-dropdown:hover > .nav-dropdown-menu, 2638.navbar-header li.nav-dropdown:focus-within > .nav-dropdown-menu, 2639.navbar-header li.nav-dropdown.open > .nav-dropdown-menu { 2640 display: flex !important; 2641} 2642/* Inner LIs are block-level, not stretched flex children. 2643 Clobber the `.nav-left li { height: 100% }` rule from earlier so each 2644 item is just tall enough for its own anchor â otherwise every item is 2645 stretched to the row's height and the menu only shows the first one. */ 2646.navbar-header .nav-dropdown-menu > li { 2647 display: block !important; 2648 width: 100%; 2649 height: auto !important; 2650 align-self: auto !important; 2651 align-items: initial; 2652 background: transparent; 2653} 2654.navbar-header .nav-dropdown-menu > li > a { 2655 display: block !important; 2656 width: 100%; 2657 height: auto !important; 2658 align-self: auto !important; 2659 font-size: 0.85rem !important; 2660 white-space: nowrap; 2661 text-align: left !important; 2662 /* Colors / padding / hover-bg handled by v5 refresh in global.css */ 2663} 2664 2665/* Login / Register pill â visual styling moved to v5 refresh in 2666 global.css (Bitcoin orange). Inline rule kept minimal so the 2667 tap-highlight reset (mobile-specific) still applies. */ 2668a.auth-link-primary { 2669 outline: none !important; 2670 text-decoration: none !important; 2671 -webkit-tap-highlight-color: transparent !important; 2672} 2673</style> 2674 2675 2676<style> 2677 .m-tabbar { display: none; } 2678 html.m-nav-on .m-tabbar { display: flex; } 2679 html.m-nav-on body { padding-bottom: 150px; } 2680 @media (max-width: 640px) { 2681 .m-tabbar { display: flex; } 2682 body { padding-bottom: 150px; } 2683 } 2684 /* Cookie banner is hidden entirely on phones (it covered the tab bar). */ 2685 html.m-nav-on .cookie-banner { display: none !important; } 2686 2687 .m-tabbar { 2688 position: fixed; left: 0; right: 0; bottom: 0; z-index: 9000; 2689 background: var(--color-background, #ffffff); 2690 border-top: 1px solid var(--color-neutral-200, #e7eaee); 2691 padding-bottom: env(safe-area-inset-bottom, 0px); 2692 box-shadow: 0 -2px 16px rgba(0,0,0,0.12); 2693 } 2694 .m-tab { 2695 flex: 1; display: flex; flex-direction: column; align-items: center; 2696 justify-content: center; gap: 9px; padding: 22px 4px 18px; 2697 background: none; border: 0; cursor: pointer; text-decoration: none; 2698 color: var(--color-neutral-600, #555555); font-size: 1.45rem; 2699 font-weight: 600; font-family: inherit; line-height: 1; 2700 } 2701 .m-tab i { font-size: 3rem; } 2702 .m-tab.is-active { color: var(--color-primary, #6366f1); } 2703 html.dark-mode .m-tabbar { background: #1c1d22; border-top-color: #2a2d35; } 2704 html.dark-mode .m-tab { color: #a9adb6; } 2705 2706 .m-drawer-backdrop { 2707 display: none; position: fixed; inset: 0; z-index: 9500; 2708 background: rgba(0,0,0,0.5); opacity: 0; pointer-events: none; 2709 transition: opacity .2s ease; 2710 } 2711 .m-drawer-backdrop.open { display: block; opacity: 1; pointer-events: auto; } 2712 .m-drawer { 2713 display: flex; flex-direction: column; position: fixed; inset: 0; 2714 width: 100vw; z-index: 9600; overflow-y: auto; 2715 -webkit-overflow-scrolling: touch; background: var(--color-background, #ffffff); 2716 color: var(--color-text, #1d1d1f); transform: translateX(100%); 2717 transition: transform .25s ease; 2718 } 2719 .m-drawer.open { transform: translateX(0); } 2720 html.dark-mode .m-drawer { background: #1c1d22; color: #eeeeee; } 2721 .m-drawer-head { 2722 display: flex; align-items: center; justify-content: space-between; 2723 padding: 26px 28px; border-bottom: 1px solid var(--color-neutral-200, #e7eaee); 2724 } 2725 .m-drawer-head img { height: 56px; width: auto; } 2726 .m-drawer-head .nav-logo-img--dark { display: none; } 2727 html.dark-mode .m-drawer-head .nav-logo-img--light { display: none; } 2728 html.dark-mode .m-drawer-head .nav-logo-img--dark { display: inline; } 2729 .m-drawer-close { 2730 background: none; border: 0; font-size: 3.6rem; line-height: 1; color: inherit; 2731 cursor: pointer; padding: 0 12px; 2732 } 2733 .m-drawer-body { padding: 4px 0 60px; } 2734 2735 /* Accordion main menus (Learn / Products / Tools / Help) â tap to expand, 2736 mirroring the homepage nav dropdowns. */ 2737 .m-acc { 2738 display: flex; align-items: center; justify-content: space-between; width: 100%; 2739 background: none; border: 0; cursor: pointer; font-family: inherit; text-align: left; 2740 color: inherit; padding: 24px 30px; font-size: 1.95rem; font-weight: 600; 2741 border-bottom: 1px solid var(--color-neutral-100, #eef0f2); 2742 } 2743 html.dark-mode .m-acc { border-bottom-color: #2a2d35; } 2744 .m-acc-caret { font-size: 1.4rem; opacity: .55; transition: transform .2s ease; margin-left: 14px; } 2745 .m-acc.open .m-acc-caret { transform: rotate(180deg); } 2746 .m-acc-panel { display: none; background: var(--color-neutral-50, #f7f8fa); } 2747 .m-acc-panel.open { display: block; } 2748 html.dark-mode .m-acc-panel { background: rgba(255,255,255,0.035); } 2749 .m-acc-panel .m-link { padding: 18px 30px 18px 48px; font-size: 1.65rem; } 2750 2751 /* Top-level standalone links (Pricing, API) styled like the accordion rows. */ 2752 .m-link-top { 2753 display: block; padding: 24px 30px; color: inherit; text-decoration: none; 2754 font-size: 1.95rem; font-weight: 600; 2755 border-bottom: 1px solid var(--color-neutral-100, #eef0f2); 2756 } 2757 html.dark-mode .m-link-top { border-bottom-color: #2a2d35; } 2758 2759 .m-link { display: block; padding: 22px 30px; color: inherit; text-decoration: none; font-size: 1.9rem; } 2760 .m-link:active, .m-link:hover { background: var(--color-neutral-100, #f3f4f6); } 2761 html.dark-mode .m-link:active, html.dark-mode .m-link:hover { background: #26282f; } 2762 .m-sec-title { 2763 font-size: 1.3rem; text-transform: uppercase; letter-spacing: .05em; 2764 color: var(--color-neutral-500, #888888); padding: 30px 30px 10px; font-weight: 700; 2765 } 2766 .m-auth { display: flex; flex-direction: column; gap: 14px; padding: 26px 30px 10px; } 2767 .m-btn { display: block; text-align: center; padding: 22px; border-radius: 12px; 2768 font-weight: 700; text-decoration: none; font-size: 1.8rem; } 2769 .m-btn-primary { background: var(--color-primary, #6366f1); color: #ffffff; } 2770 .m-btn-ghost { border: 1px solid var(--color-neutral-300, #cccccc); color: inherit; } 2771 .m-util { display: flex; flex-wrap: wrap; gap: 12px; padding: 12px 30px 0; align-items: center; } 2772 .m-util select { flex: 1; min-width: 150px; padding: 16px; border-radius: 8px; font-family: inherit; font-size: 1.4rem; 2773 border: 1px solid var(--color-neutral-300, #cccccc); background: var(--color-background, #fff); color: inherit; } 2774 .m-theme { display: flex; gap: 10px; width: 100%; } 2775 .m-theme button { flex: 1; padding: 16px; border-radius: 8px; cursor: pointer; font-size: 1.3rem; 2776 border: 1px solid var(--color-neutral-300, #cccccc); background: none; color: inherit; font-family: inherit; } 2777 .m-theme button.active { border-color: var(--color-primary, #6366f1); color: var(--color-primary, #6366f1); } 2778 2779 .m-search { display: none; position: fixed; inset: 0; z-index: 9700; background: var(--color-background, #fff); } 2780 .m-search.open { display: block; } 2781 html.dark-mode .m-search { background: #1c1d22; } 2782 .m-search-bar { display: flex; align-items: center; gap: 14px; padding: 24px 24px; 2783 border-bottom: 1px solid var(--color-neutral-200, #e7eaee); } 2784 .m-search-bar input { flex: 1; padding: 20px; font-size: 1.7rem; border-radius: 12px; font-family: inherit; 2785 border: 1px solid var(--color-neutral-300, #cccccc); background: var(--color-background, #fff); color: inherit; } 2786 .m-search-cancel { background: none; border: 0; color: var(--color-primary, #6366f1); font-weight: 600; cursor: pointer; font-size: 1.6rem; } 2787</style> 2788 2789<nav class="m-tabbar" aria-label="Mobile navigation"> 2790 <a class="m-tab " href="/"><i class="fa-solid fa-house" aria-hidden="true"></i><span>Home</span></a> 2791 <a class="m-tab " href="/net-worth-tracker"><i class="fa-solid fa-chart-line" aria-hidden="true"></i><span>Net-Worth</span></a> 2792 <button class="m-tab" type="button" id="mTabSearch"><i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i><span>Search</span></button> 2793 2794 <a class="m-tab" href="/login?view=register"><i class="fa-solid fa-user-plus" aria-hidden="true"></i><span>Sign Up</span></a> 2795 2796 <button class="m-tab" type="button" id="mTabMenu"><i class="fa-solid fa-bars" aria-hidden="true"></i><span>Menu</span></button> 2797</nav> 2798 2799<div class="m-drawer-backdrop" id="mDrawerBackdrop"></div> 2800<aside class="m-drawer" id="mDrawer" aria-label="Menu" aria-hidden="true"> 2801 <div class="m-drawer-head"> 2802 <a href="/" aria-label="AssetMarketCap home"> 2803 <img src="/static/logo.png" class="nav-logo-img--light" alt="AssetMarketCap"> 2804 <img src="/static/logo-dark.png" class="nav-logo-img--dark" alt="" aria-hidden="true"> 2805 </a> 2806 <button class="m-drawer-close" id="mDrawerClose" type="button" aria-label="Close menu">×</button> 2807 </div> 2808 <div class="m-drawer-body"> 2809 <button class="m-acc" type="button" aria-expanded="false">Learn <i class="fa-solid fa-chevron-down m-acc-caret" aria-hidden="true"></i></button> 2810 <div class="m-acc-panel"> 2811 <a class="m-link" href="/learn">Education Articles</a> 2812 <a class="m-link" href="/news">Latest News</a> 2813 <a class="m-link" href="/deals">Deals</a> 2814 </div> 2815 2816 <button class="m-acc" type="button" aria-expanded="false">Products <i class="fa-solid fa-chevron-down m-acc-caret" aria-hidden="true"></i></button> 2817 <div class="m-acc-panel"> 2818 <a class="m-link" href="/pro">Pro</a> 2819 <a class="m-link" href="https://merch.assetmarketcap.com">Merchandise</a> 2820 </div> 2821 2822 <button class="m-acc" type="button" aria-expanded="false">Tools <i class="fa-solid fa-chevron-down m-acc-caret" aria-hidden="true"></i></button> 2823 <div class="m-acc-panel"> 2824 <a class="m-link" href="/flippener">Flippener Tool</a> 2825 <a class="m-link" href="/compare">Comparator Tool</a> 2826 <a class="m-link" href="/net-worth-tracker">Net-Worth Tracker</a> 2827 </div> 2828 2829 <a class="m-link-top" href="/pricing">Pricing</a> 2830 <a class="m-link-top" href="/api-info">API</a> 2831 <a class="m-link-top" href="/advertise">Advertise</a> 2832 2833 <button class="m-acc" type="button" aria-expanded="false">Help <i class="fa-solid fa-chevron-down m-acc-caret" aria-hidden="true"></i></button> 2834 <div class="m-acc-panel"> 2835 <a class="m-link" href="/faqs">FAQs</a> 2836 <a class="m-link" href="/support">Support</a> 2837 <a class="m-link" href="https://play.google.com/store/apps/details?id=com.assetmarketcap.app">Get the App</a> 2838 </div> 2839 2840 2841 <div class="m-auth"> 2842 <a class="m-btn m-btn-primary" href="/login?view=register">Create account</a> 2843 <a class="m-btn m-btn-ghost" href="/login">Log in</a> 2844 </div> 2845 2846 2847 <div class="m-sec-title">Settings</div> 2848 <div class="m-util"> 2849 <select id="mCurrency" aria-label="Currency"> 2850 <option value="USD">USD ($)</option> 2851 <option value="GOLD">Gold (oz)</option> 2852 <option value="BTC">Bitcoin</option> 2853 <option value="SATS">Bitcoin (sats)</option> 2854 </select> 2855 <select aria-label="Language"><option selected>English</option></select> 2856 </div> 2857 <div class="m-util"> 2858 <div class="m-theme" id="mTheme"> 2859 <button type="button" data-theme="light">Light</button> 2860 <button type="button" data-theme="dark">Dark</button> 2861 <button type="button" data-theme="system">System</button> 2862 </div> 2863 </div> 2864 </div> 2865</aside> 2866 2867<div class="m-search" id="mSearch"> 2868 <form class="m-search-bar" role="search" action="/search" method="GET"> 2869 <input type="search" name="q" id="mSearchInput" placeholder="Search assets, articles, news..." aria-label="Search" autocomplete="off"> 2870 <button class="m-search-cancel" type="button" id="mSearchCancel">Cancel</button> 2871 </form> 2872</div> 2873
2874<script> 2875(function () { 2876 try { 2877 var touch = ("ontouchstart" in window) || (navigator.maxTouchPoints || 0) > 0; 2878 var minDim = Math.min(window.screen.width || 9999, window.screen.height || 9999); 2879 if (touch && minDim <= 700) document.documentElement.classList.add("m-nav-on"); 2880 } catch (e) {} 2881 2882 var drawer = document.getElementById('mDrawer'), 2883 backdrop = document.getElementById('mDrawerBackdrop'); 2884 function openDrawer() { 2885 drawer.classList.add('open'); backdrop.classList.add('open'); 2886 drawer.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; 2887 } 2888 function closeDrawer() { 2889 drawer.classList.remove('open'); backdrop.classList.remove('open'); 2890 drawer.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; 2891 } 2892 var menuBtn = document.getElementById('mTabMenu'); 2893 if (menuBtn) menuBtn.addEventListener('click', openDrawer); 2894 var closeBtn = document.getElementById('mDrawerClose'); 2895 if (closeBtn) closeBtn.addEventListener('click', closeDrawer); 2896 if (backdrop) backdrop.addEventListener('click', closeDrawer); 2897 2898 // Accordion main menus (Learn / Products / Tools / Help). 2899 Array.prototype.forEach.call(document.querySelectorAll('#mDrawer .m-acc'), function (btn) { 2900 btn.addEventListener('click', function () { 2901 var panel = btn.nextElementSibling; 2902 var willOpen = !btn.classList.contains('open'); 2903 btn.classList.toggle('open', willOpen); 2904 btn.setAttribute('aria-expanded', willOpen ? 'true' : 'false'); 2905 if (panel && panel.classList.contains('m-acc-panel')) panel.classList.toggle('open', willOpen); 2906 }); 2907 }); 2908 2909 var srch = document.getElementById('mSearch'), 2910 sBtn = document.getElementById('mTabSearch'), 2911 sCancel = document.getElementById('mSearchCancel'), 2912 sInput = document.getElementById('mSearchInput'); 2913 if (sBtn) sBtn.addEventListener('click', function () { 2914 srch.classList.add('open');
2915 if (sInput) setTimeout(function () { sInput.focus(); }, 50); 2916 }); 2917 if (sCancel) sCancel.addEventListener('click', function () { srch.classList.remove('open'); }); 2918 2919 var mc = document.getElementById('mCurrency'), 2920 real = document.getElementById('unit-select'); 2921 if (mc && real) { 2922 mc.value = real.value || 'USD'; 2923 mc.addEventListener('change', function () { 2924 real.value = mc.value; 2925 real.dispatchEvent(new Event('change', { bubbles: true })); 2926 }); 2927 } 2928 2929 var mt = document.getElementById('mTheme'); 2930 if (mt) { 2931 mt.addEventListener('click', function (e) { 2932 var b = e.target.closest('button[data-theme]'); 2933 if (!b) return; 2934 var t = b.dataset.theme; 2935 try { 2936 // Record the choice in theme_mode (the key header.html's bootTheme reads); 2937 // the legacy prefers_dark_mode key is kept in sync for older code paths. 2938 // Without the explicit 'system' write, picking System would fall through 2939 // to the default on reload - which is now 'light', not 'system'. 2940 if (t === 'dark') { localStorage.setItem('theme_mode', 'dark'); localStorage.setItem('prefers_dark_mode', '1'); document.documentElement.classList.add('dark-mode'); } 2941 else if (t === 'light') { localStorage.setItem('theme_mode', 'light'); localStorage.setItem('prefers_dark_mode', '0'); document.documentElement.classList.remove('dark-mode'); } 2942 else { localStorage.setItem('theme_mode', 'system'); localStorage.removeItem('prefers_dark_mode'); 2943 var d = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; 2944 document.documentElement.classList.toggle('dark-mode', !!d); } 2945 } catch (_) {} 2946 mt.querySelectorAll('button').forEach(function (x) { x.classList.toggle('active', x === b); }); 2947 }); 2948 } 2949})(); 2950</script>
2950 2951 2952 <main class="x-page"> 2953 2954 <!-- BILLING TOGGLE (above plan cards) =============== --> 2955 <section class="x-band" aria-label="Billing interval" style="padding: 100px 0 36px;"> 2956 <div class="x-wrap" style="display: flex; align-items: center; gap: 14px; justify-content: center; flex-wrap: wrap;"> 2957 <div class="x-bill" id="xBilling" role="tablist" aria-label="Billing interval"> 2958 <div class="x-bill-thumb" id="xBillThumb"></div> 2959 <button type="button" class="is-on" data-interval="monthly" role="tab" aria-selected="true">Monthly</button> 2960 <button type="button" data-interval="annual" role="tab" aria-selected="false">Annual</button> 2961 </div> 2962 <span class="x-bill-save">Save ~17% annually</span> 2963 </div> 2964 </section> 2965 2966 <!-- PLAN CARDS ======================================== --> 2967 <section class="x-band" aria-label="Plan options" style="padding: 0 0 90px;"> 2968 <div class="x-wrap"> 2969 <div class="x-plans"> 2970 2971 <article class="x-plan"> 2972 <div class="x-plan-name">Free</div> 2973 <div class="x-plan-price"> 2974 <span class="x-plan-amt">$0</span> 2975 <span class="x-plan-per">forever</span> 2976 </div> 2977 <p class="x-plan-tag-line">The full dataset, no strings. Browse 35,000+ assets, no card required.</p> 2978 <ul class="x-plan-feats"> 2979 <li>35,000+ assets, live</li> 2980 <li>Full market cap rankings</li> 2981 <li>Flippener & comparator (limited)</li> 2982 <li>Up to 3 favourites</li> 2983 <li>Net Worth Tracker - 5 assets, daily prices</li> 2984 <li>Top 100 JSON / CSV downloads (signed in)</li> 2985 </ul> 2986 <div class="x-plan-cta-wrap"> 2987 <a href="/" class="x-cta x-cta--ghost">Browse free</a> 2988 </div> 2989 </article> 2990 2991 <article class="x-plan x-plan--featured" 2992 data-plan="pro" 2993 data-monthly-amount="3.99" data-monthly-period="/mo" 2994 data-annual-amount="39.99" data-annual-period="/yr" 2995 data-cta-monthly="Subscribe to Pro - $3.99/mo" 2996 data-cta-annual="Subscribe to Pro - $39.99/yr"> 2997 <span class="x-plan-tag">Most popular</span> 2998 <div class="x-plan-name">Pro</div> 2999 <div class="x-plan-price"> 3000 <span class="x-plan-cur">$</span> 3001 <span class="x-plan-amt" data-price-amount>3.99</span> 3002 <span class="x-plan-per" data-price-period>/mo</span> 3003 </div> 3004 <p class="x-plan-tag-line">Ad-free, premium news, premium education. The sweet spot for everyday use.</p> 3005 <ul class="x-plan-feats"> 3006 <li>Everything in Free</li> 3007 <li>Ad-free mobile app experience</li> 3008 <li>Filter by Country & Sector</li> 3009 <li>Premium news articles & videos</li> 3010 <li>Premium education guides</li> 3011 <li>Up to 50 favourites & 30 flippenings / mo</li> 3012 <li>Net Worth Tracker - 10 assets, live prices</li> 3013 <li>Time Machine - rewind the table up to 1 year</li> 3014 <li>1 table screenshot per day</li> 3015 <li>Top 100 JSON / CSV downloads</li> 3016 </ul> 3017 <div class="x-plan-cta-wrap"> 3018 3019 <a href="/login?view=register&next=/pricing" class="x-cta">Get Pro</a> 3020 3021 </div> 3022 </article> 3023 3024 <article class="x-plan" 3025 data-plan="pro-plus" 3026 data-monthly-amount="9.99" data-monthly-period="/mo" 3027 data-annual-amount="99.99" data-annual-period="/yr" 3028 data-cta-monthly="Subscribe to Pro Plus - $9.99/mo" 3029 data-cta-annual="Subscribe to Pro Plus - $99.99/yr"> 3030 <div class="x-plan-name">Pro Plus</div> 3031 <div class="x-plan-price"> 3032 <span class="x-plan-cur">$</span> 3033 <span class="x-plan-amt" data-price-amount>9.99</span>
3034 <span class="x-plan-per" data-price-period>/mo</span> 3035 </div> 3036 <p class="x-plan-tag-line">Everything in Pro plus unlimited tools and exclusive research. For power users.</p> 3037 <ul class="x-plan-feats"> 3038 <li>Everything in Pro</li> 3039 <li>Unlimited favourites</li> 3040 <li>Unlimited flippener & comparator</li> 3041 <li>Net Worth Tracker - unlimited assets, live prices</li> 3042 <li>Time Machine - full archive back to 2010 (15+ years)</li> 3043 <li>Unlimited table screenshots</li> 3044 <li>Full 35k+ JSON / CSV downloads</li> 3045 <li>Exclusive research reports</li> 3046 <li>Priority support</li> 3047 <li>Early access to new features</li> 3048 </ul> 3049 <div class="x-plan-cta-wrap"> 3050 3051 <a href="/login?view=register&next=/pricing" class="x-cta">Get Pro Plus</a> 3052 3053 </div> 3054 </article> 3055 3056 </div> 3057 3058 <p style="text-align:center; margin-top:18px;"> 3059 <button type="button" class="x-link iap-restore" hidden 3060 style="background:none; border:none; cursor:pointer; display:none;">Restore purchases</button> 3061 </p> 3062 3063 <p class="x-sub-legal" style="text-align:center; margin-top:18px; font-size:0.82rem; line-height:1.5; opacity:.72;"> 3064 <span data-native-billing-only hidden> 3065 Payment is charged to your Apple Account at confirmation of purchase. 3066 The subscription renews automatically for the same period unless it is 3067 cancelled at least 24 hours before the end of the current period. Manage 3068 or cancel it in Settings › Apple Account › Subscriptions. 3069 <br> 3070 <a href="https://www.apple.com/legal/internet-services/itunes/dev/stdeula/">Terms of Use (EULA)</a> 3071 · 3072 <a href="/privacy">Privacy Policy</a> 3073 </span> 3074 <span data-web-billing-only> 3075 Subscriptions renew automatically until cancelled. Cancel anytime from your account. 3076 · 3077 <a href="/privacy">Privacy Policy</a> 3078 </span> 3079 </p> 3080 </div> 3081 </section> 3082 3083 <!-- PRICING INTRO (moved below the plan cards) ====== --> 3084 <section class="x-band--soft x-sec" aria-label="Pricing intro"> 3085 <div class="x-wrap"> 3086 <p class="x-eyebrow"><strong>Plans & pricing.</strong></p> 3087 <h1 class="x-h1">Simple pricing.<br>Made for serious investors.</h1> 3088 <p class="x-lede" style="margin-bottom: 0;">The full 35,000+ asset dataset is free, forever. Upgrade for ad-free browsing, premium news, premium research, and unlimited tools - cancel anytime.</p> 3089 </div> 3090 </section> 3091 3092 <!-- PERSONAS ========================================== --> 3093 <section class="x-band x-sec" aria-label="Which plan fits you"> 3094 <div class="x-wrap"> 3095 <p class="x-eyebrow">Not sure which to pick?</p> 3096 <h2 class="x-h2">Choose the plan that matches how you actually use the app.</h2> 3097 <p class="x-sub">Three quick personas. Pick the one that sounds most like you.</p> 3098 3099 <div class="x-personas"> 3100 <div class="x-persona"> 3101 <div class="x-persona-ico"> 3102 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> 3103 </div> 3104 <div class="x-persona-label">Free</div> 3105 <h3>The casual checker</h3> 3106 <p>You glance at Bitcoin and a handful of stocks a few times a month. You don't need filters or research - just a clean ranked list.</p> 3107 <a href="/" class="x-link">Browse free</a> 3108 </div> 3109 <div class="x-persona"> 3110 <div class="x-persona-ico"> 3111 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 13l3-3 4 4 5-5"/></svg> 3112 </div> 3113 <div class="x-persona-label">Pro</div> 3114 <h3>The everyday investor</h3> 3115 <p>You're in the app weekly. You want ads gone, country/sector filters, and premium news that doesn't waste your time.</p> 3116 <a href="#plans" class="x-link">Get Pro</a> 3117 </div> 3118 <div class="x-persona"> 3119 <div class="x-persona-ico"> 3120 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg> 3121 </div> 3122 <div class="x-persona-label">Pro Plus</div> 3123 <h3>The power user</h3> 3124 <p>You're running flippenings and comparisons daily. You want exclusive monthly research and a human on priority support.</p> 3125 <a href="#plans" class="x-link">Get Pro Plus</a> 3126 </div> 3127 </div> 3128 </div> 3129 </section> 3130 3131 <!-- COMPARE TABLE ==================================== --> 3132 <section class="x-band--soft x-sec" aria-label="Feature comparison"> 3133 <div class="x-wrap"> 3134 <p class="x-eyebrow">Side by side</p>
3135 <h2 class="x-h2">Every feature, every plan.</h2> 3136 <p class="x-sub">No surprises at checkout. Everything that's in each plan, laid out cleanly.</p> 3137 3138 <div class="x-compare"> 3139 <table class="x-ct"> 3140 <thead> 3141 <tr> 3142 <th>Feature</th> 3143 <th>Free</th> 3144 <th class="is-feat">Pro</th> 3145 <th>Pro Plus</th> 3146 </tr> 3147 </thead> 3148 <tbody> 3149 <tr><td>35,000+ assets</td> <td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3150 <tr><td>Live market cap rankings</td> <td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3151 <tr><td>Education library</td> <td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3152 <tr><td>Country & sector filters</td><td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3153 <tr><td>Ad-free mobile app</td> <td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3154 <tr><td>Premium news articles & videos</td><td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3155 <tr><td>Premium education & research</td><td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td>
3155<span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3156 <tr><td>Exclusive research reports</td><td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-cross">—</span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3157 <tr><td>Priority support</td> <td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-cross">—</span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3158 <tr><td>Favourites</td> <td class="x-pill-cell">3</td> <td class="is-feat x-pill-cell">50 / mo</td> <td><span class="x-pill-cell--good">Unlimited</span></td></tr> 3159 <tr><td>Flippener tool</td> <td class="x-pill-cell">3</td> <td class="is-feat x-pill-cell">30 / mo</td> <td><span class="x-pill-cell--good">Unlimited</span></td></tr> 3160 <tr><td>Comparator tool</td> <td class="x-pill-cell">3</td> <td class="is-feat x-pill-cell">15 / mo</td> <td><span class="x-pill-cell--good">Unlimited</span></td></tr> 3161 <tr><td>Net Worth Tracker</td><td class="x-pill-cell">5 assets, daily</td><td class="is-feat x-pill-cell">10 assets, live</td><td><span class="x-pill-cell--good">Unlimited, live</span></td></tr> 3162 <tr><td>Time Machine</td><td><span class="x-cross">—</span></td><td class="is-feat x-pill-cell">1 year back</td><td><span class="x-pill-cell--good">Back to 2010 (15+ years)</span></td></tr> 3163 <tr><td>Table screenshots</td><td><span class="x-cross">—</span></td><td class="is-feat x-pill-cell">1 / day</td><td><span class="x-pill-cell--good">Unlimited</span></td></tr> 3164 <tr><td>Top 100 JSON / CSV download</td><td class="x-pill-cell">Signed in</td><td class="is-feat"><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3165 <tr><td>Full 35k JSON / CSV download</td><td><span class="x-cross">—</span></td><td class="is-feat"><span class="x-cross">—</span></td><td><span class="x-tick"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span></td></tr> 3166 <tr> 3167 <td><strong>Price</strong></td> 3168 <td class="x-pill-cell">Free</td> 3169 <td class="is-feat x-pill-cell"><span data-compare-pro>$3.99/mo</span></td> 3170 <td class="x-pill-cell"><span data-compare-proplus>$9.99/mo</span></td> 3171 </tr> 3172 </tbody> 3173 </table> 3174 </div> 3175 </div> 3176 </section> 3177 3178 <!-- FAQ =============================================== --> 3179 <section class="x-band x-sec" aria-label="Questions answered"> 3180 <div class="x-wrap"> 3181 <p class="x-eyebrow">Questions</p> 3182 <h2 class="x-h2">Everything you might want to know.</h2> 3183 <p class="x-sub">Quick answers to the most common questions before subscribing.</p> 3184 3185 <div class="x-faq"> 3186 <details class="x-faq-item"> 3187 <summary>Is the free plan actually free?<i class="x-faq-icon"></i></summary> 3188 <div class="x-faq-body">Yes. All 35,000+ assets, full rankings, and the core tools (Flippener, Comparator) are free with no credit card. The free plan caps a few power-user features so you can decide whether Pro is worth it.</div> 3189 </details> 3190 <details class="x-faq-item"> 3191 <summary>Can I cancel any time?<i class="x-faq-icon"></i></summary> 3192 <div class="x-faq-body">Of course. Subscriptions are managed through Stripe - open your account page and hit "Manage billing" to cancel, pause, or change plan. No emails, no friction.</div> 3193 </details> 3194 <details class="x-faq-item"> 3195 <summary>Monthly vs. annual - what's the difference?<i class="x-faq-icon"></i></summary> 3196 <div class="x-faq-body">Annual billing saves about 17% vs. paying monthly. Same features, just tw
3196o months free if you commit for a year.</div> 3197 </details> 3198 <details class="x-faq-item"> 3199 <summary>What's the difference between Pro and Pro Plus?<i class="x-faq-icon"></i></summary> 3200 <div class="x-faq-body">Pro removes ads, unlocks premium news + education, and unlocks country/sector filters with generous monthly limits on favourites, flippenings, and comparisons. Pro Plus removes those limits entirely and adds exclusive research reports + priority support.</div> 3201 </details> 3202 <details class="x-faq-item"> 3203 <summary>Do you offer student or non-profit pricing?<i class="x-faq-icon"></i></summary> 3204 <div class="x-faq-body">Not formally yet - but if you're a student, journalist, or non-profit and want to use the data, email us through the contact form and we'll figure something out.</div> 3205 </details> 3206 <details class="x-faq-item"> 3207 <summary>How is my data handled?<i class="x-faq-icon"></i></summary> 3208 <div class="x-faq-body">Payments are processed by Stripe - we never see your card details. Your account stores an email and a hashed password and nothing else. See the privacy page for the full picture.</div> 3209 </details> 3210 </div> 3211 </div> 3212 </section> 3213 3214 <!-- FINAL ============================================ --> 3215 <section class="x-band" aria-label="Sign up"> 3216 <div class="x-final"> 3217 <h2>Start with Free.<br>Upgrade when it pays off.</h2> 3218 <p>Create a free account in under 30 seconds - no card needed. Pro and Pro Plus are one click away.</p> 3219 <div class="x-final-cta"> 3220 3221 <a href="/login?view=register&next=/pricing" class="x-cta x-cta--lg">Create free account</a> 3222 <a href="/login" class="x-link">Log in</a> 3223 3224 </div> 3225 </div> 3226 <p class="x-disclaimer"> 3227 Not financial advice. Market data is provided for informational purposes only. 3228 Prices shown in USD. Taxes may apply based on your billing region. 3229 </p> 3230 </section> 3231 3232 </main> 3233 3234 3235 3236<style> 3237 .upper-footer-press { 3238 clear: both; 3239 display: flow-root; 3240 float: none; 3241 width: 100%; 3242 margin-top: 0; 3243 margin-bottom: 0; 3244 } 3245 .upper-footer-press::after { 3246 content: ""; 3247 display: table; 3248 clear: both; 3249 } 3250 .upper-footer-press .header-press-strip { 3251 float: none; 3252 clear: both; 3253 width: calc(83.33333333% - 30px); 3254 max-width: 1558px; 3255 margin-left: auto; 3256 margin-right: auto; 3257 margin-bottom: 15px !important; 3258 background: transparent; 3259 } 3260 .upper-footer-press + .webintern-footer { 3261 margin-top: 0; 3262 } 3263 @media (max-width: 700px) { 3264 .upper-footer-press { 3265 margin-top: 0; 3266 } 3267 .upper-footer-press .header-press-strip { 3268 width: calc(100% - 24px); 3269 } 3270 } 3271 3272/* Subscribe button visual styling moved to v5 refresh in global.css 3273 (Bitcoin orange to match Login/Register pill and cookie Accept). */ 3274 3275/* Inline subscribe form â email input + Subscribe button side by side, 3276 replacing the old modal trigger. */ 3277.footer-subscribe-form { 3278 display: flex; 3279 align-items: stretch; 3280 gap: 8px; 3281 margin-top: 10px; 3282 max-width: 420px; 3283} 3284.footer-subscribe-input { 3285 flex: 1 1 auto; 3286 min-width: 0; 3287 padding: 10px 14px; 3288 border-radius: 999px; 3289 border: 1px solid rgba(255, 255, 255, 0.18); 3290 background: rgba(255, 255, 255, 0.06); 3291 color: #fff; 3292 font-size: 0.85rem; 3293 outline: none; 3294 transition: border-color 0.15s ease, background 0.15s ease; 3295} 3296.footer-subscribe-input::placeholder { color: rgba(255,255,255,0.30); } 3297.footer-subscribe-input:focus { 3298 border-color: rgba(124, 58, 237, 0.7); 3299 background: rgba(255, 255, 255, 0.10); 3300} 3301.footer-subscribe-btn { 3302 flex: 0 0 auto; 3303 padding: 10px 22px !important; 3304 border-radius: 999px !important; 3305 font-size: 0.85rem; 3306 cursor: pointer; 3307 display: inline-flex; 3308 align-items: center; 3309 justify-content: center; 3310 line-height: 1; 3311} 3312</style> 3313<div class="upper-footer-press"> 3314 <div class="header-press-strip" role="region" aria-label="As seen on"> 3315 <div class="header-press-strip__title">—— As Seen On ——</div> 3316 <div class="header-press-row" aria-hidden="true"> 3317 <div class="header-press-carousel"> 3318 <div class="header-press-track"> 3319 3320 <div class="header-press-set"> 3321 3322 3323 <img src="/static/asSeenOn/abc-news.png" alt="abc news" class="header-press-logo" decoding="async"> 3324 3325 3326 <img src="/static/asSeenOn/AP-news.png" alt="AP news" class="header-press-logo" decoding="async"> 3327 3328 3329 <img src="/static/asSeenOn/CBS-news-logo.png" alt="CBS news logo" class="header-press-logo" decoding="async"> 3330 3331 3332 <img src="/static/asSeenOn/fox-news-logo.png" alt="fox news logo" class="header-press-logo" decoding="async"> 3333 3334 3335 <img src="/static/asSeenOn/international-business-times-uk-logo.jpg" alt="international business times uk logo" class="header-press-logo" decoding="async"> 3336 3337 3338 <img src="/static/asSeenOn/MorningNews-logo.png" alt="MorningNews logo" class="header-press-logo" decoding="async"> 3339 3340 3341 <img src="/static/asSeenOn/NBC-logo.png" alt="NBC logo" class="header-press-logo" decoding="async"> 3342 3343 3344 <img src="/static/asSeenOn/USA-News-logo.jpg" alt="USA News logo" class="header-press-logo" decoding="async"> 3345 3346 </div> 3347 3348 <div class="header-press-set"> 3349 3350 3351 <img src="/static/asSeenOn/abc-news.png" alt="abc news" class="header-press-logo" decoding="async"> 3352 3353 3354 <img src="/static/asSeenOn/AP-news.png" alt="AP news" class="header-press-logo" decoding="async"> 3355 3356 3357 <img src="/static/asSeenOn/CBS-news-logo.png" alt="CBS news logo" class="header-press-logo" decoding="async"> 3358 3359 3360 <img src="/static/asSeenOn/fox-news-logo.png" alt="fox news logo" class="header-press-logo" decoding="async"> 3361 3362 3363 <img src="/static/asSeenOn/international-business-times-uk-logo.jpg" alt="international business times uk logo" class="header-press-logo" decoding="async"> 3364 3365 3366 <img src="/static/asSeenOn/MorningNews-logo.png" alt="MorningNews logo" class="header-press-logo" decoding="async"> 3367 3368 3369 <img src="/static/asSeenOn/NBC-logo.png" alt="NBC logo" class="header-press-logo" decoding="async"> 3370 3371 3372 <img src="/static/asSeenOn/USA-News-logo.jpg" alt="USA News logo" class="header-press-logo" decoding="async"> 3373 3374 </div> 3375 3376 <div class="header-press-set"> 3377 3378 3379 <img src="/static/asSeenOn/abc-news.png" alt="abc news" class="header-press-logo" decoding="async"> 3380 3381 3382 <img src="/static/asSeenOn/AP-news.png" alt="AP news" class="header-press-logo" decoding="async"> 3383 3384 3385 <img src="/static/asSeenOn/CBS-news-logo.png" alt="CBS news logo" class="header-press-logo" decoding="async"> 3386 3387 3388 <img src="/static/asSeenOn/fox-news-logo.png" alt="fox news logo" class="header-press-logo" decoding="async"> 3389 3390 3391 <img src="/static/asSeenOn/international-business-times-uk-logo.jpg" alt="international business times uk logo" class="header-press-logo" decoding="async"> 3392 3393 3394 <img src="/static/asSeenOn/MorningNews-logo.png" alt="MorningNews logo" class="header-press-logo" decoding="async"> 3395 3396 3397 <img src="/static/asSeenOn/NBC-logo.png" alt="NBC logo" class="header-press-logo" decoding="async"> 3398 3399 3400 <img src="/static/asSeenOn/USA-News-logo.jpg" alt="USA News logo" class="header-press-logo" decoding="async"> 3401 3402 </div> 3403 3404 <div class="header-press-set"> 3405 3406 3407 <img src="/static/asSeenOn/abc-news.png" alt="abc news" class="header-press-logo" decoding="async"> 3408 3409 3410 <img src="/static/asSeenOn/AP-news.png" alt="AP news" class="header-press-logo" decoding="async"> 3411 3412 3413 <img src="/static/asSeenOn/CBS-news-logo.png" alt="CBS news logo" class="header-press-logo" decoding="async"> 3414 3415 3416 <img src="/static/asSeenOn/fox-news-logo.png" alt="fox news logo" class="header-press-logo" decoding="async"> 3417 3418 3419 <img src="/static/asSeenOn/international-business-times-uk-logo.jpg" alt="international business times uk logo" class="header-press-logo" decoding="async"> 3420 3421 3422 <img src="/static/asSeenOn/MorningNews-logo.png" alt="MorningNews logo" class="header-press-logo" decoding="async"> 3423 3424 3425 <img src="/static/asSeenOn/NBC-logo.png" alt="NBC logo" class="header-press-logo" decoding="async"> 3426 3427 3428 <img src="/static/asSeenOn/USA-News-logo.jpg" alt="USA News logo" class="header-press-logo" decoding="async"> 3429 3430 </div> 3431 3432 </div> 3433 </div> 3434 </div> 3435 </div> 3436</div> 3437 3438 3439<footer class="webintern-footer"> 3440 3441 <div class="webintern-footer-inner"> 3442 <div class="footer-grid"> 3443 3444 <!-- Column 1: Logo + methodology --> 3445 <div class="footer-col footer-brand"> 3446 <a href="/"> 3447 <img src="/static/logo-dark.png" class="logo-bottom" width="200" height="33" alt="AssetMarketCap" loading="lazy"> 3448 </a> 3449 <p class="footer-blurb">Market capitalization is typically calculated by multiplying an asset's cir
3449culating supply by its current market price. For commodities, circulating supply is estimated using proven reserves (for oil and gas) or above-ground stock estimates (for precious metals). For currencies, supply is based on M2 money supply data sourced from TradingEconomics.com. The real estate market value is an inflation-adjusted estimate derived from a historical baseline, with data sourced from Statista.com. Values denoted by ~ are best estimates based on the most recent available data and may not reflect exact figures.</p> 3450 </div> 3451 3452 <!-- Column 2: Quick links (two sub-columns) --> 3453 <div class="footer-col footer-col-center"> 3454 <p class="footer-heading">Quick Links</p> 3455 <div class="footer-links-grid"> 3456 3457 <ul class="footer-links"> 3458 <li><a href="#" class="footer-open-app">App</a></li> 3459 <li><a href="/deals">Deals</a></li> 3460 <li><a href="/learn">Education Articles</a></li> 3461 <li><a href="/news">Latest News</a></li> 3462 <li><a href="/faqs">FAQs</a></li> 3463 <li><a href="/support">Support</a></li> 3464 <li><a href="https://merch.assetmarketcap.com">Merchandise</a></li> 3465 </ul> 3466 <ul class="footer-links"> 3467 <li><a href="/pricing" class="footer-open-pricing">Pricing</a></li> 3468 <li><a href="/pro" class="footer-open-pro">Pro</a></li> 3469 <li><a href="/api-info" class="footer-open-api">API</a></li> 3470 <li><a href="/flippener">Flippener Tool</a></li> 3471 <li><a href="/compare">Comparator Tool</a></li> 3472 <li><a href="/net-worth-tracker">Net-Worth Tracker</a></li> 3473 <li><a href="/advertise">Advertise</a></li> 3474 </ul> 3475 </div> 3476 </div> 3477 3478 <!-- Column 3: Partners --> 3479 <div class="footer-col footer-col-center"> 3480 <p class="footer-heading">Our Partners</p> 3481 <ul class="footer-links"> 3482 <li><a href="https://kraken.pxf.io/EE69A4">Kraken</a></li> 3483 <li><a href="https://www.bybit.com/invite?ref=EPBGD1">ByBit</a></li> 3484 <li><a href="https://shop.ledger.com/pages/referral-program?referral_code=GZ8J1XRHRK32N">Ledger</a></li> 3485 <li><a href="https://ref.nordvpn.com/UzltTVopBvG">NordVPN</a></li> 3486 <li><a href="https://www.tradingview.com/?aff_id=135662">TradingView</a></li> 3487 </ul> 3488 </div> 3489 3490 <!-- Column 4: Newsletter + socials --> 3491 <div class="footer-col footer-newsletter"> 3492 <p class="footer-heading">Newsletter</p> 3493 <p class="footer-newsletter-text">Stay informed with market insights, asset analysis, and the latest developments across crypto, equities, commodities, and more, delivered straight to your inbox.</p> 3494 <form class="footer-subscribe-form" action="/subscribe-newsletter" method="POST" aria-label="Subscribe to newsletter"> 3495 <input type="email" name="email" class="footer-subscribe-input" placeholder="Enter your email address" required aria-label="Email address"> 3496 <button type="submit" class="subscribe-button footer-subscribe-btn">Subscribe</button> 3497 </form> 3498 3499 <p class="footer-heading footer-heading--community">Community</p> 3500 <div class="footer-socials"> 3501 <a href="https://www.youtube.com/@assetmarketcap" aria-label="YouTube"><i class="fa-brands fa-youtube"></i></a> 3502 <a href="https://www.facebook.com/assetmarketcap" aria-label="Facebook"><i class="fa-brands fa-facebook"></i></a> 3503 <a href="https://instagram.com/assetmarketcap" aria-label="Instagram"><i class="fa-brands fa-instagram"></i></a> 3504 <a href="https://tiktok.com/@assetmarketcap" aria-label="TikTok"><i class="fa-brands fa-tiktok"></i></a> 3505 <a href="https://x.com/assetmarketcap" aria-label="X"><i class="fa-solid fa-x"></i></a> 3506 <a href="https://telegram.me/assetmarketcap" aria-label="Telegram"><i class="fa-brands fa-telegram"></i></a> 3507 <a href="https://discord.gg/KKDAMNbStQ" aria-label="Discord"><i class="fa-brands fa-discord"></i></a> 3508 <a href="https://reddit.com/r/assetmarketcap" aria-label="Reddit"><i class="fa-brands fa-reddit"></i></a> 3509 </div> 3510 3511 <!-- App store badges sit under the social links inside Community --> 3512 <div class="app-store-badges" aria-label="Download AssetMarketCap app"> 3513 <a href="https://play.google.com/store/apps/details?id=com.assetmarketcap.app" 3514 class="app-store-badge" 3515 target="_blank" 3516 rel="noopener" 3517 aria-label="Get AssetMarketCap on Google Play"> 3518 <img src="/static/App-Store-Logo-Google.svg" alt="Get it on Google Play" loading="lazy"> 3519 </a>
3520 <span class="app-store-badge app-store-badge--coming-soon" 3521 role="img" 3522 aria-disabled="true" 3523 aria-label="AssetMarketCap on the App Store - coming soon"> 3524 <span class="app-store-badge__crop"> 3525 <img src="/static/App-Store-Logo-Apple.svg" alt="Download on the App Store" loading="lazy"> 3526 <span class="app-store-badge__ribbon" aria-hidden="true">Coming Soon</span> 3527 </span> 3528 </span> 3529 </div> 3530 </div> 3531 3532 </div> 3533 </div> 3534 3535 <!-- Disclaimer --> 3536 <div class="footer-disclaimer"> 3537 <div class="footer-disclaimer-inner"> 3538 <p><strong>Disclaimer:</strong> The content on AssetMarketCap is for informational and educational purposes only and does not constitute financial, investment, tax, or legal advice. Trading and investing in financial instruments, including cryptocurrencies, carries a high level of risk and may not be suitable for all investors. Always conduct your own research and consult a qualified financial advisor before making any investment decisions. Past performance is not indicative of future results. <a href="/privacy" class="footer-privacy-link">Privacy</a></p> 3539 </div> 3540 </div> 3541 3542 <!-- Copyright --> 3543 <div class="footer-copyright"> 3544 <span class="copy-left">© AssetMarketCap.com
3544<script>document.write(new Date().getFullYear());</script>
3544. All rights reserved.</span> 3545 <span class="copy-right">Bitcoin Donations: bc1q5dsmgwd5nl4g33jkf7sh5r8r6n3exncteesadd</span> 3546 </div> 3547 3548 <!-- Wire footer modal links to navbar triggers --> 3549
3549<script> 3550 document.addEventListener("DOMContentLoaded", function () { 3551 var map = { 3552 // .footer-open-pricing and .footer-open-pro removed â the 3553 // pricing and pro routes are full pages now, so the footer 3554 // links navigate via their own href without proxying through 3555 // a navbar trigger. 3556 ".footer-open-api": "openApi", 3557 ".footer-open-app": "openAppPopup" 3558 }; 3559 for (var sel in map) { 3560 (function (selector, targetId) { 3561 var el = document.querySelector(selector); 3562 if (el) { 3563 el.addEventListener("click", function (e) { 3564 e.preventDefault(); 3565 var target = document.getElementById(targetId); 3566 if (target) target.click(); 3567 }); 3568 } 3569 })(sel, map[sel]); 3570 } 3571 }); 3572 </script>
3572 3573</footer> 3574 3575 <!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 3576 PRICING MODAL 3577 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 3578<div id="pricingOverlay" class="auth-overlay" aria-hidden="true"> 3579 <div class="auth-backdrop" data-close-modal="pricingOverlay"></div> 3580 <section class="auth-modal" role="dialog" aria-modal="true"> 3581 <button class="auth-close" data-close-modal="pricingOverlay" type="button" aria-label="Close">×</button> 3582 3583 <div class="auth-modal-left"> 3584 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 3585 <div class="auth-badge">Pro Plans</div> 3586 <h2 class="auth-hero-title">Go beyond the data</h2> 3587 <p class="auth-hero-copy">Everyone gets the full 35k+ dataset. Pro adds premium content and an ad-free experience.</p> 3588 <div class="auth-feature-list"> 3589 <div class="auth-feature-item">Full 35,000+ asset dataset, free for everyone</div> 3590 <div class="auth-feature-item">Education library on sign-up</div> 3591 <div class="auth-feature-item">Filter by Country & Sector on sign-up</div> 3592 <div class="auth-feature-item">Ad-Free Mobile App Experience</div> 3593 <div class="auth-feature-item">News Article Access</div> 3594 <div class="auth-feature-item">Favourites, Flippener Tool & Comparator Tool</div> 3595 </div> 3596 </div> 3597 </div> 3598 3599 <div class="auth-modal-right"> 3600 3601 3602 <h3 class="auth-modal-heading">Pricing</h3> 3603 <p class="auth-subtitle">The full 35k+ dataset is free for everyone. Sign up for favourites, or upgrade to Pro for premium features.</p> 3604 3605 <table class="auth-compare-table"> 3606 <thead> 3607 <tr> 3608 <th class="auth-compare-feature-col">Feature</th> 3609 <th>Free</th> 3610 <th>Sign Up</th> 3611 <th>Pro</th> 3612 <th class="auth-compare-highlight">Pro+</th> 3613 </tr> 3614 </thead> 3615 <tbody> 3616 <tr><td>35,000+ assets</td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3617 <tr><td>Education library</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3618 <tr><td>Filter by Country & Sector</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3619 <tr><td>Ad-Free Mobile App Experience</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3620 <tr><td>News Article Access</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3621 <tr><td>Favourites</td><td class="auth-compare-text">3</td><td class="auth-compare-text">15</td><td class="auth-compare-text">50/mo</td><td class="auth-compare-text">Unlimited</td></tr> 3622 <tr><td>Flippener Tool</td><td class="auth-compare-text">3</td><td class="auth-compare-text">10</td><td class="auth-compare-text">30/mo</td><td class="auth-compare-text">Unlimited</td></tr> 3623 <tr><td>Comparator Tool</td><td class="auth-compare-text">3</td><td class="auth-compare-text">5</td><td class="auth-compare-text">15/mo</td><td class="auth-compare-text">Unlimited</td></tr> 3624 <tr class="auth-compare-price-row"> 3625 <td>Price</td> 3626 <td class="auth-compare-text">Free</td> 3627 <td class="auth-compare-text">Sign Up</td> 3628 <td class="auth-compare-text"><span data-info-pro-price>$3.99<small>/mo</small></span></td> 3629 <td class="auth-compare-text"><span data-info-proplus-price>$9.99<small>/mo</small></span></td> 3630 </tr> 3631 </tbody> 3632 </table> 3633 3634 <a href="/login?view=register" class="auth-submit" style="margin-top:18px; display:block; text-align:center; text-decoration:none;">Create a free account</a> 3635 <div style="margin-top:12px; text-align:center;"> 3636 <a href="/login" class="auth-inline-link">Already have an account? Log in</a> 3637 </div> 3638 3639 </div> 3640 </section> 3641</div> 3642 3643 3644<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 3645 API MODAL 3646 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 3647<div id="apiOverlay" class="auth-overlay" aria-hidden="true"> 3648 <div class="auth-backdrop" data-close-modal="apiOverlay"></div> 3649 <section class="auth-modal" role="dialog" aria-modal="true"> 3650 <button class="auth-close" data-close-modal="apiOverlay" type="button" aria-label="Close">×</button> 3651 3652 <div class="auth-modal-left"> 3653 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 3654 <div class="auth-badge">REST API</div> 3655 <h2 class="auth-hero-title">Build with the API</h2> 3656 <p class="auth-hero-copy"> 3657 Programmatic access for apps, scripts, and integrations. Every account includes a free API key with the top 100 assets. Upgrade for the full 35k+ dataset, fundamentals, and historical data. 3658 </p> 3659 <div class="auth-feature-list"> 3660 <div class="auth-feature-item">Top 100 assets free with every account</div> 3661 <div class="auth-feature-item">Full 35,000+ asset dataset</div> 3662 <div class="auth-feature-item">Individual asset fundamentals</div> 3663 <div class="auth-feature-item">Historical market cap & price data</div> 3664 <div class="auth-feature-item">Up to 1 million API calls per month</div> 3665 <div class="auth-feature-item">Plans from $19.99/mo</div> 3666 </div> 3667 </div> 3668 </div> 3669 3670 <div class="auth-modal-right"> 3671 3672 3673 <h3 class="auth-modal-heading">API access</h3> 3674 <p class="auth-subtitle">Register for your free API key (top 100 assets included). Subscribe for the full 35k+ dataset, fundamentals, and historical data.</p> 3675 3676 <table class="auth-compare-table"> 3677 <thead> 3678 <tr> 3679 <th class="auth-compare-feature-col">Feature</th> 3680 <th>Free</th> 3681 <th>Starter</th> 3682 <th>Growth</th> 3683 <th class="auth-compare-highlight">Scale</th> 3684 </tr> 3685 </thead> 3686 <tbody> 3687 <tr><td>Top 100 assets</td><td>
3687<span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3688 <tr><td>Full 35k+ asset list</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3689 <tr><td>Fundamentals</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3690 <tr><td>Historical data</td><td><span class="auth-compare-cross">✗</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td><td><span class="auth-compare-tick">✓</span></td></tr> 3691 <tr><td>Calls / month</td><td class="auth-compare-text">1,000</td><td class="auth-compare-text">10k</td><td class="auth-compare-text">100k</td><td class="auth-compare-text">1M</td></tr> 3692 <tr class="auth-compare-price-row"> 3693 <td>Price</td> 3694 <td class="auth-compare-text">Free</td> 3695 <td class="auth-compare-text">$19.99<small>/mo</small></td> 3696 <td class="auth-compare-text">$49.99<small>/mo</small></td> 3697 <td class="auth-compare-text">$99.99<small>/mo</small></td> 3698 </tr> 3699 </tbody> 3700 </table> 3701 3702 <a href="/login?view=register" class="auth-submit" style="margin-top:18px; display:block; text-align:center; text-decoration:none;">Register for your Free API Key</a> 3703 <div style="margin-top:14px;"> 3704 <a href="/API" class="auth-inline-link">View API documentation →</a> 3705 </div> 3706 3707 </div> 3708 </section> 3709</div> 3710 3711 3712<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 3713 NEWSLETTER MODAL 3714 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 3715<div id="newsletterOverlay" class="auth-overlay" aria-hidden="true"> 3716 <div class="auth-backdrop" data-close-modal="newsletterOverlay"></div> 3717 <section class="auth-modal" role="dialog" aria-modal="true"> 3718 <button class="auth-close" data-close-modal="newsletterOverlay" type="button" aria-label="Close">×</button> 3719 3720 <div class="auth-modal-left"> 3721 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 3722 <div class="auth-badge">Newsletter</div> 3723 <h2 class="auth-hero-title">Stay ahead of the market</h2> 3724 <p class="auth-hero-copy"> 3725 Weekly insights on market caps, asset rankings, and macro trends, straight to your inbox. 3726 </p> 3727 <div class="auth-feature-list"> 3728 <div class="auth-feature-item">Weekly market cap rankings recap</div> 3729 <div class="auth-feature-item">Asset class breakdowns & analysis</div> 3730 <div class="auth-feature-item">Bitcoin & crypto market updates</div> 3731 <div class="auth-feature-item">Unsubscribe any time</div> 3732 </div> 3733 </div> 3734 </div> 3735 3736 <div class="auth-modal-right"> 3737 3738 <h3 class="auth-modal-heading">Newsletter</h3> 3739 <p class="auth-subtitle">Get weekly market insights delivered to your inbox. No account required.</p> 3740 <form method="POST" action="/subscribe-newsletter" class="auth-form" id="newsletterForm"> 3741 <input name="email" type="email" placeholder="Email address" required autocomplete="email"> 3742 <button type="submit" class="auth-submit">Subscribe</button> 3743 </form> 3744 3745 <p style="font-size:11px; color:rgba(255,255,255,0.4); line-height:1.45; margin:14px 0 0; text-align:center;"> 3746 We'll never share your email. Unsubscribe any time. 3747 </p> 3748 </div> 3749 </section> 3750</div> 3751 3752 3753<!-- =========================================================== 3754 SIGNUP GATE MODAL - unclosable, fires after activity threshold 3755 Only rendered for logged-out users; controlled by JS below. 3756 =========================================================== --> 3757
3758<div id="signupGateOverlay" class="auth-overlay signup-gate" aria-hidden="true"> 3759 <div class="auth-backdrop signup-gate-backdrop" data-close-modal="signupGateOverlay"></div> 3760 <section class="auth-modal signup-gate-modal" role="dialog" aria-modal="true" aria-labelledby="signupGateTitle"> 3761 3762 <button class="auth-close signup-gate-close" data-close-modal="signupGateOverlay" type="button" aria-label="Close">×</button> 3763 3764 <div class="auth-modal-left"> 3765 <div style="position:relative; z-index:1; display:flex; flex-direction:column; height:100%;"> 3766 <div class="auth-badge">Free Account</div> 3767 <h2 class="auth-hero-title" id="signupGateTitle">Keep exploring 35,000+ assets</h2> 3768 <p class="auth-hero-copy">You're clearly getting value out of the data. Create a free account in seconds to keep going - no credit card required.</p> 3769 <div class="auth-feature-list"> 3770 <div class="auth-feature-item">Free access to the full 35,000+ dataset</div> 3771 <div class="auth-feature-item">Education library & news article access</div> 3772 <div class="auth-feature-item">Filter by Country & Sector</div> 3773 <div class="auth-feature-item">Save favourites & build watchlists</div> 3774 <div class="auth-feature-item">Free API key included</div> 3775 </div> 3776 </div> 3777 </div> 3778 3779 <div class="auth-modal-right"> 3780 <h3 class="auth-modal-heading">Create your free account</h3> 3781 <p class="auth-subtitle">It takes about 15 seconds. No card, no spam - just full access to everything on AssetMarketCap.</p> 3782 3783 <a href="/login?view=register" class="auth-submit signup-gate-cta" style="margin-top:18px; display:block; text-align:center; text-decoration:none;">Create a free account</a> 3784 3785 <div style="margin-top:14px; text-align:center;"> 3786 <a href="/login" class="auth-inline-link">Already have an account? Log in</a> 3787 </div> 3788 3789 <p style="font-size:11px; color:rgba(255,255,255,0.4); line-height:1.45; margin:22px 0 0; text-align:center;"> 3790 By creating an account you agree to our <a href="/privacy" class="auth-inline-link">Privacy Policy</a>. 3791 </p> 3792 </div> 3793 </section> 3794</div> 3795 3796 3797 3798<!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 3799 MODAL JS - shared open/close + feature-specific logic 3800 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ -->
3801<script> 3802(function () { 3803 // ââ Generic open/close for all modals ââââââââââââââââââââââ 3804 function openModal(id) { 3805 var el = document.getElementById(id); 3806 if (!el) return; 3807 el.classList.add("open"); 3808 el.setAttribute("aria-hidden", "false"); 3809 document.body.style.overflow = "hidden"; 3810 // Load API usage when API modal opens 3811 if (id === "apiOverlay") loadApiUsage(); 3812 } 3813 3814 function closeModal(id) { 3815 var el = document.getElementById(id); 3816 if (!el) return; 3817 el.classList.remove("open"); 3818 el.setAttribute("aria-hidden", "true"); 3819 document.body.style.overflow = ""; 3820 } 3821 3822 // Close buttons (backdrop + X button) 3823 document.querySelectorAll("[data-close-modal]").forEach(function (el) { 3824 el.addEventListener("click", function () { 3825 closeModal(el.getAttribute("data-close-modal")); 3826 }); 3827 }); 3828 3829 // Escape key closes any open modal 3830 document.addEventListener("keydown", function (e) { 3831 if (e.key === "Escape") { 3832 ["pricingOverlay", "apiOverlay", "newsletterOverlay"].forEach(closeModal); 3833 } 3834 }); 3835 3836 // ââ Nav link triggers ââââââââââââââââââââââââââââââââââââââ 3837 3838 var isAuthed = false; 3839 3840 3841 var openPro = document.getElementById("openPro"); 3842 var openPricing = document.getElementById("openPricing"); 3843 var openApi = document.getElementById("openApi"); 3844 var openNewsletterNav = document.getElementById("openNewsletterNav"); 3845 3846 // Pro / Pricing nav links navigate to the full pages now (we 3847 // converted these from modal overlays to standalone marketing 3848 // pages). The only special-case left: logged-in users clicking 3849 // "Pro" go straight to their account subscriptions section 3850 // instead of the marketing page â they already have the info. 3851 if (openPro) { 3852 openPro.addEventListener("click", function (e) { 3853 if (isAuthed) { 3854 e.preventDefault(); 3855 window.location.href = "/account#subscriptions"; 3856 } 3857 // Otherwise let the <a href="/pro"> navigate normally. 3858 }); 3859 } 3860 // Pricing link: no interceptor â the <a href="/pricing"> handles it. 3861 3862 // API: route to the standalone /api-info documentation page (no modal). 3863 // The <a href="/api-info" id="openApi"> in header.html handles navigation 3864 // natively now that the docs live on a full page. 3865 3866 // Newsletter 3867 if (openNewsletterNav) { 3868 openNewsletterNav.addEventListener("click", function (e) { 3869 e.preventDefault(); 3870 openModal("newsletterOverlay"); 3871 }); 3872 } 3873 3874 // Footer newsletter button (loaded after this script, so use delegation) 3875 document.addEventListener("click", function (e) { 3876 var el = e.target.closest("#openNewsletter"); 3877 if (el) { e.preventDefault(); openModal("newsletterOverlay"); } 3878 }); 3879 3880 // Login/Register link: go to /login page 3881 var openAuth = document.getElementById("openAuth"); 3882 if (openAuth) { 3883 openAuth.addEventListener("click", function (e) { 3884 e.preventDefault(); 3885 window.location.href = "/login"; 3886 }); 3887 } 3888 3889 // Auto-open modal when returning from a flow that asked for it. 3890 // The pricing/pro auto-open was dropped after those moved to full 3891 // pages â Stripe cancel now redirects to /pricing directly. 3892 var modalParam = new URLSearchParams(window.location.search).get("modal"); 3893 if (modalParam === "api" || modalParam === "api-info") openModal("apiOverlay"); 3894 if (modalParam === "newsletter") openModal("newsletterOverlay"); 3895 3896 // ââ Billing toggle helper ââââââââââââââââââââââââââââââââââ 3897 function setupBillingToggle(toggleEl, onChange) { 3898 if (!toggleEl) return; 3899 var sw = toggleEl.querySelector(".billing-toggle-switch"); 3900 var labels = toggleEl.querySelectorAll(".billing-toggle-label"); 3901 var isAnnual = false; 3902 3903 function update() { 3904 toggleEl.classList.toggle("billing-toggle--annual", isAnnual); 3905 labels.forEach(function (l) { 3906 l.classList.toggle("billing-toggle-label--active", l.dataset.interval === (isAnnual ? "annual" : "monthly")); 3907 }); 3908 onChange(isAnnual ? "annual" : "monthly"); 3909 } 3910 3911 if (sw) sw.addEventListener("click", function () { isAnnual = !isAnnual; update(); });
3912 labels.forEach(function (l) { 3913 l.addEventListener("click", function () { isAnnual = l.dataset.interval === "annual"; update(); }); 3914 }); 3915 } 3916 3917 // ââ Pro plan card selection ââââââââââââââââââââââââââââââââ 3918 var proCards = document.querySelectorAll("#pricingOverlay .auth-plan-option[data-pro-plan]"); 3919 var proForm = document.getElementById("proSubscribeForm"); 3920 var proBtn = document.getElementById("proSubscribeBtn"); 3921 var proIntervalInput = document.getElementById("proInterval"); 3922 3923 function updateProCardPrices(interval) { 3924 proCards.forEach(function (card) { 3925 var amount = card.querySelector("[data-price-amount]"); 3926 var cents = card.querySelector("[data-price-cents]"); 3927 var period = card.querySelector("[data-price-period]"); 3928 if (interval === "annual") { 3929 if (amount) amount.textContent = card.dataset.annualAmount; 3930 if (cents) cents.textContent = card.dataset.annualCents; 3931 if (period) period.textContent = card.dataset.annualPeriod; 3932 card.dataset.proLabel = card.dataset.proLabelAnnual; 3933 } else { 3934 if (amount) amount.textContent = card.dataset.monthlyAmount; 3935 if (cents) cents.textContent = card.dataset.monthlyCents; 3936 if (period) period.textContent = card.dataset.monthlyPeriod; 3937 card.dataset.proLabel = card.dataset.proLabelMonthly; 3938 } 3939 }); 3940 var selectedCard = document.querySelector("#pricingOverlay .auth-plan-option.selected"); 3941 if (selectedCard && proBtn) proBtn.textContent = selectedCard.dataset.proLabel; 3942 } 3943 3944 setupBillingToggle(document.getElementById("proBillingToggle"), function (interval) { 3945 if (proIntervalInput) proIntervalInput.value = interval; 3946 updateProCardPrices(interval); 3947 }); 3948 3949 proCards.forEach(function (card) { 3950 card.addEventListener("click", function () { 3951 proCards.forEach(function (c) { c.classList.remove("selected"); }); 3952 card.classList.add("selected"); 3953 if (proForm) proForm.setAttribute("action", card.dataset.proAction); 3954 if (proBtn) proBtn.textContent = card.dataset.proLabel; 3955 }); 3956 }); 3957 3958 // ââ Pro upgrade toggle âââââââââââââââââââââââââââââââââââââ 3959 var proUpgradeBtn = document.getElementById("proUpgradeBtn"); 3960 var proUpgradeBadge = document.getElementById("proUpgradeBadge"); 3961 var proUpgradeInterval = document.getElementById("proUpgradeInterval"); 3962 3963 setupBillingToggle(document.getElementById("proUpgradeBillingToggle"), function (interval) { 3964 if (proUpgradeInterval) proUpgradeInterval.value = interval; 3965 if (interval === "annual") { 3966 if (proUpgradeBadge) proUpgradeBadge.textContent = "Pro Plus - $99.99/yr"; 3967 if (proUpgradeBtn) proUpgradeBtn.textContent = "Upgrade to Pro Plus - $99.99/yr"; 3968 } else { 3969 if (proUpgradeBadge) proUpgradeBadge.textContent = "Pro Plus - $9.99/mo"; 3970 if (proUpgradeBtn) proUpgradeBtn.textContent = "Upgrade to Pro Plus - $9.99/mo"; 3971 } 3972 }); 3973 3974 // ââ API plan card selection ââââââââââââââââââââââââââââââââ 3975 var apiAllCards = document.querySelectorAll("#apiPlanCards .api-plan-card[data-plan]"); 3976 var selectedPlanInput = document.getElementById("apiSelectedPlan"); 3977 var apiSignupForm = document.getElementById("apiSignupForm"); 3978 var apiSubscribeBtn = document.getElementById("apiSubscribeBtn"); 3979 var apiEnterpriseBtn = document.getElementById("apiEnterpriseBtn"); 3980 var apiIntervalInput = document.getElementById("apiInterval"); 3981 var currentApiInterval = "monthly"; 3982 3983 var apiPlanLabels = { 3984 starter: { monthly: "Subscribe - Starter $19.99/mo", annual: "Subscribe - Starter $99.99/yr" }, 3985 growth: { monthly: "Subscribe - Growth $49.99/mo", annual: "Subscribe - Growth $499.99/yr" }, 3986 scale: { monthly: "Subscribe - Scale $99.99/mo", annual: "Subscribe - Scale $999.99/yr" } 3987 }; 3988 3989 function updateApiCardLimits(interval) { 3990 apiAllCards.forEach(function (card) { 3991 if (card.dataset.plan === "enterprise") return; 3992 var limitEl = card.querySelector("[data-api-limit]"); 3993 if (limitEl) limitEl.textContent = card.dataset["limit" + interval.charAt(0).toUpperCase() + interval.slice(1)] || ""; 3994 }); 3995 var selectedCard = document.querySelector("#apiPlanCards .api-plan-card--selected"); 3996 if (selectedCard && selectedCard.dataset.plan !== "enterprise" && apiSubscribeBtn) { 3997 var plan = selectedCard.dataset.plan; 3998 apiSubscribeBtn.textContent = (apiPlanLabels[plan] && apiPlanLabels[plan][interval]) || "Subscribe to API plan"; 3999 } 4000 } 4001 4002 setupBillingToggle(document.getElementById("apiBillingToggle"), function (interval) { 4003 currentApiInterval = interval; 4004 if (apiIntervalInput) apiIntervalInput.value = interval; 4005 updateApiCardLimits(interval); 4006 }); 4007 4008 apiAllCards.forEach(function (card) { 4009 card.addEventListener("click", function () { 4010 apiAllCards.forEach(function (c) { c.classList.remove("api-plan-card--selected"); }); 4011 card.classList.add("api-plan-card--selected"); 4012 var plan = card.dataset.plan; 4013 if (plan === "enterprise") { 4014 if (apiSignupForm) apiSignupForm.style.display = "none"; 4015 if (apiEnterpriseBtn) apiEnterpriseBtn.style.display = "block"; 4016 } else {
4017 if (selectedPlanInput) selectedPlanInput.value = plan; 4018 if (apiSubscribeBtn) apiSubscribeBtn.textContent = (apiPlanLabels[plan] && apiPlanLabels[plan][currentApiInterval]) || "Subscribe to API plan"; 4019 if (apiSignupForm) apiSignupForm.style.display = ""; 4020 if (apiEnterpriseBtn) apiEnterpriseBtn.style.display = "none"; 4021 } 4022 }); 4023 }); 4024 4025 // ââ Copy API key ââââââââââââââââââââââââââââââââââââââââââ 4026 // 2026-07-05: apiKeyText now shows only the key PREFIX (keys are stored 4027 // hashed, plaintext is shown once at creation on the account page), so 4028 // the old click-to-copy handler was removed - copying a prefix would 4029 // only produce broken curl commands. 4030 4031 // ââ API usage meter âââââââââââââââââââââââââââââââââââââââ 4032 function loadApiUsage() { 4033 var block = document.getElementById("apiUsageBlock"); 4034 if (!block) return; 4035 fetch("/api-usage") 4036 .then(function (r) { return r.ok ? r.json() : null; }) 4037 .then(function (data) { 4038 if (!data) return; 4039 var label = document.getElementById("apiUsageLabel"); 4040 var fill = document.getElementById("apiUsageFill"); 4041 var reset = document.getElementById("apiUsageReset"); 4042 if (label) label.textContent = data.used.toLocaleString() + " / " + data.limit.toLocaleString() + " calls this month"; 4043 if (fill) { 4044 var pct = Math.min(100, Math.round((data.used / data.limit) * 100)); 4045 fill.style.width = pct + "%"; 4046 fill.classList.toggle("api-usage-bar-fill--warning", pct >= 80); 4047 fill.classList.toggle("api-usage-bar-fill--danger", pct >= 95); 4048 } 4049 if (reset && data.reset_date) { 4050 var d = new Date(data.reset_date + "T00:00:00"); 4051 reset.textContent = "Resets " + d.toLocaleDateString(undefined, { month: "short", day: "
4051numeric", year: "numeric" }); 4052 } 4053 }) 4054 .catch(function () {}); 4055 } 4056 4057 // ââ Stripe loading spinner ââââââââââââââââââââââââââââââââ 4058 var stripeActions = [ 4059 "/create-checkout-session", "/create-pro-plus-checkout-session", 4060 "/create-customer-portal-session", "/create-pro-plus-customer-portal-session", 4061 "/create-api-checkout-session", "/create-api-customer-portal-session", 4062 "/cancel-subscription", "/cancel-pro-plus-subscription", 4063 "/cancel-api-subscription", "/switch-api-plan" 4064 ]; 4065 4066 document.querySelectorAll(".auth-form").forEach(function (form) { 4067 if (!stripeActions.includes(form.getAttribute("action"))) return; 4068 form.addEventListener("submit", function () { 4069 var btn = form.querySelector(".auth-submit, .api-plan-card"); 4070 if (btn) { btn.classList.add("auth-submit--loading"); btn.disabled = true; } 4071 }); 4072 }); 4073 4074 window.addEventListener("pageshow", function (event) { 4075 if (event.persisted) { 4076 document.querySelectorAll(".auth-submit--loading").forEach(function (btn) { 4077 btn.classList.remove("auth-submit--loading"); 4078 btn.disabled = false; 4079 }); 4080 } 4081 }); 4082 4083 4084 // == Signup gate (unclosable, 24hr cooldown) ================== 4085 (function setupSignupGate() { 4086 var overlay = document.getElementById("signupGateOverlay"); 4087 if (!overlay) return; // not rendered (user is authenticated) 4088 4089 var STORAGE_KEY = "amc_signup_gate_v1"; 4090 var CLICK_THRESHOLD = 5; 4091 var SCROLL_THRESHOLD = Infinity; // scroll trigger disabled for now (click trigger still active) 4092 var COOLDOWN_MS = 24 * 60 * 60 * 1000; 4093 var SCROLL_BURST_GAP_MS = 700; // collapse rapid wheel events into one "scroll" 4094 4095 // Skip on the login page itself - the modal's CTAs already point there 4096 var path = window.location.pathname || ""; 4097 if (path === "/login" || path.indexOf("/login/") === 0 || path.indexOf("/login?") === 0) return; 4098 4099 // Skip for crawlers, bots and link-preview fetchers - keeps the modal out 4100 // of Googlebot's rendered DOM and out of social link previews (which fetch 4101 // pages to extract og:image etc). 4102 var ua = navigator.userAgent || ""; 4103 if (/bot|crawl|spider|slurp|mediapartners|googlebot|bingbot|baiduspider|yandex|duckduckbot|applebot|petalbot|semrushbot|ahrefsbot|facebookexternalhit|embedly|preview|skypeuripreview|telegrambot|whatsapp|discordbot|linkedinbot|twitterbot|slackbot/i.test(ua)) return; 4104 4105 // Skip when the user arrived from a search engine - avoids Google's 4106 // intrusive-interstitial penalty on the landing page. (Once they 4107 // navigate to a second page, document.referrer is our own domain and 4108 // counting resumes normally.) 4109 var ref = document.referrer || ""; 4110 if (ref) { 4111 try { 4112 var refHost = new URL(ref).hostname.toLowerCase(); 4113 if (/(^|\.)(google|bing|duckduckgo|yahoo|yandex|baidu|ecosia|qwant|brave|kagi|startpage)\./.test(refHost)) return; 4114 } catch (e) { /* malformed referrer - ignore */ } 4115 } 4116 4117 function readState() { 4118 try { 4119 var raw = localStorage.getItem(STORAGE_KEY); 4120 if (!raw) return { clicks: 0, scrolls: 0, seenAt: 0 }; 4121 var s = JSON.parse(raw); 4122 return { 4123 clicks: s.clicks | 0, 4124 scrolls: s.scrolls | 0, 4125 seenAt: parseInt(s.seenAt, 10) || 0 4126 }; 4127 } catch (e) { 4128 return { clicks: 0, scrolls: 0, seenAt: 0 }; 4129 } 4130 } 4131 4132 function writeState(s) { 4133 try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch (e) {} 4134 } 4135 4136 var state = readState(); 4137 var now = Date.now(); 4138 4139 // Within 24hr cooldown from a prior show? Stay quiet. 4140 if (state.seenAt && (now - state.seenAt) < COOLDOWN_MS) return; 4141 4142 // Cooldown expired - reset counters so the gate has to be re-earned. 4143 if (state.seenAt && (now - state.seenAt) >= COOLDOWN_MS) { 4144 state = { clicks: 0, scrolls: 0, seenAt: 0 }; 4145 writeState(state); 4146 } 4147 4148 function closeGate() { 4149 overlay.classList.remove("open"); 4150 overlay.setAttribute("aria-hidden", "true"); 4151 document.body.style.overflow = ""; 4152 document.documentElement.style.overflow = ""; 4153 } 4154 4155 function showGate() { 4156 overlay.classList.add("open"); 4157 overlay.setAttribute("aria-hidden", "false"); 4158 document.body.style.overflow = "hidden"; 4159 document.documentElement.style.overflow = "hidden"; 4160 4161 // Stamp seenAt so subsequent navigations within 24hr stay quiet. 4162 state.seenAt = Date.now(); 4163 writeState(state); 4164
4165 // Wire close button + backdrop click + Escape key. 4166 overlay.querySelectorAll('[data-close-modal="signupGateOverlay"]').forEach(function (el) { 4167 if (!el.dataset.closeBound) { 4168 el.dataset.closeBound = "1"; 4169 el.addEventListener("click", closeGate); 4170 } 4171 }); 4172 document.addEventListener("keydown", function onEsc(e) { 4173 if (e.key === "Escape") { 4174 closeGate(); 4175 document.removeEventListener("keydown", onEsc); 4176 } 4177 }); 4178 4179 removeListeners(); 4180 } 4181 4182 function checkAndMaybeShow() { 4183 if (state.clicks >= CLICK_THRESHOLD || state.scrolls >= SCROLL_THRESHOLD) { 4184 showGate(); 4185 } 4186 } 4187 4188 function onLinkClick(e) { 4189 var a = e.target.closest && e.target.closest("a"); 4190 if (!a) return; 4191 if (overlay.contains(a)) return; // ignore links inside the gate 4192 var href = a.getAttribute("href"); 4193 if (!href) return; 4194 if (href.charAt(0) === "#") return; 4195 if (/^(javascript:|mailto:|tel:)/i.test(href)) return; 4196 state.clicks++; 4197 writeState(state); 4198 checkAndMaybeShow(); 4199 } 4200 4201 var lastScrollBurst = 0; 4202 function onScroll() { 4203 var t = Date.now(); 4204 if (t - lastScrollBurst < SCROLL_BURST_GAP_MS) return; 4205 lastScrollBurst = t; 4206 state.scrolls++; 4207 writeState(state); 4208 checkAndMaybeShow(); 4209 } 4210 4211 function addListeners() { 4212 document.addEventListener("click", onLinkClick, true); 4213 // scroll trigger disabled for now: 4214 // window.addEventListener("scroll", onScroll, { passive: true }); 4215 } 4216 function removeListeners() { 4217 document.removeEventListener("click", onLinkClick, true); 4218 window.removeEventListener("scroll", onScroll); 4219 } 4220 4221 // If counters already crossed the threshold on a previous page-load, 4222 // open immediately (e.g. they hit it just before navigating). 4223 if (state.clicks >= CLICK_THRESHOLD || state.scrolls >= SCROLL_THRESHOLD) { 4224 showGate(); 4225 } else { 4226 addListeners(); 4227 } 4228 })(); 4229 4230})(); 4231</script>
4231 4232 <!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 4233 Buy Bitcoin Modal 4234 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 4235<div id="btcOverlay" class="auth-overlay" aria-hidden="true"> 4236 <div class="auth-backdrop" id="closeBtcBackdrop"></div> 4237 4238 <section class="btc-modal" role="dialog" aria-modal="true" aria-labelledby="btcModalTitle"> 4239 <button class="auth-close" id="closeBtcBtn" type="button" aria-label="Close">×</button> 4240 4241 <!-- ââ Left panel ââ --> 4242 <div class="btc-modal-left"> 4243 <div class="auth-badge">Buy Bitcoin</div> 4244 <h2 class="auth-hero-title">Purchase Bitcoin instantly</h2> 4245 <p class="auth-hero-copy"> 4246 Buy BTC securely through our Stripe-powered on-ramp. Your card is saved for one-time use and a small commission is applied after purchase. 4247 </p> 4248 4249 <div class="auth-feature-list"> 4250 <div class="auth-feature-item"> 4251 <div class="auth-feature-text"> 4252 <strong>No sign-up required</strong> 4253 <span class="auth-feature-sub">private and secure</span> 4254 </div> 4255 </div> 4256 <div class="auth-feature-item"> 4257 <div class="auth-feature-text"> 4258 <strong>Non-custodian service</strong> 4259 <span class="auth-feature-sub">no 3rd parties</span> 4260 </div> 4261 </div> 4262 <div class="auth-feature-item"> 4263 <div class="auth-feature-text"> 4264 <strong>Funds stay in your wallet</strong> 4265 <span class="auth-feature-sub">stay in control</span> 4266 </div> 4267 </div> 4268 <div class="auth-feature-item"> 4269 <div class="auth-feature-text"> 4270 <strong>No additional KYC required</strong> 4271 <span class="auth-feature-sub">as long as you've signed up to a Stripe service before (AI subscription etc.)</span> 4272 </div> 4273 </div> 4274 </div> 4275 4276 4277 <div class="btc-commission-note"> 4278 AssetMarketCap charges a commission of <strong>2.99%</strong> on your completed purchase amount, in addition to any Stripe processing fees and Bitcoin network fees. Total fees will vary depending on network conditions and payment method. 4279 </div> 4280 </div> 4281 4282 <!-- ââ Right panel ââ --> 4283 <div class="btc-modal-right"> 4284 <h2 class="auth-form-title" id="btcModalTitle">Buy Bitcoin</h2> 4285 <p class="btc-subtitle">Enter the amount you'd like to spend in EUR.</p> 4286 4287 <div class="btc-form-group"> 4288 <label class="btc-label" for="btcAmount">Amount (EUR)</label> 4289 <input 4290 id="btcAmount" 4291 class="auth-input" 4292 type="number" 4293 min="1" 4294 step="0.01" 4295 placeholder="e.g. 100.00" 4296 /> 4297 </div> 4298 4299 <div id="btcFee" class="btc-fee-text"></div> 4300 4301 <button id="btcBuyBtn" class="auth-submit" type="button"> 4302 Continue 4303 </button> 4304 4305 <p style="font-size:10px; color:rgba(255,255,255,0.35); line-height:1.5; margin:12px 0 0; text-align:center;"> 4306 This service is not financial advice. AssetMarketCap does not provide investment, tax, or legal recommendations. Cryptocurrency is volatile and you may lose some or all of your investment. 4307 </p> 4308 4309 <p class="btc-powered">Secure checkout powered by Stripe</p> 4310 </div> 4311 </section> 4312</div> 4313
4314<script> 4315(function () { 4316 const overlay = document.getElementById("btcOverlay"); 4317 const backdrop = document.getElementById("closeBtcBackdrop"); 4318 const closeBtn = document.getElementById("closeBtcBtn"); 4319 const buyBtn = document.getElementById("btcBuyBtn"); 4320 const amtInput = document.getElementById("btcAmount"); 4321 const feeText = document.getElementById("btcFee"); 4322 4323 const COMMISSION = 2.99; 4324 4325 /* ââ open / close helpers ââ */ 4326 window.openBtcModal = function () { 4327 overlay.classList.add("open"); 4328 overlay.setAttribute("aria-hidden", "false"); 4329 document.body.style.overflow = "hidden"; 4330 amtInput.value = ""; 4331 feeText.textContent = ""; 4332 setTimeout(function () { amtInput.focus(); }, 120); 4333 }; 4334 4335 function closeBtcModal() { 4336 overlay.classList.remove("open"); 4337 overlay.setAttribute("aria-hidden", "true"); 4338 document.body.style.overflow = ""; 4339 } 4340 4341 backdrop.addEventListener("click", closeBtcModal); 4342 closeBtn.addEventListener("click", closeBtcModal); 4343 document.addEventListener("keydown", function (e) { 4344 if (e.key === "Escape" && overlay.classList.contains("open")) closeBtcModal(); 4345 }); 4346 4347 /* ââ live fee preview ââ */ 4348 amtInput.addEventListener("input", function () { 4349 var value = parseFloat(amtInput.value || "0"); 4350 if (!value || value <= 0) { feeText.textContent = ""; return; } 4351 var fee = (value * COMMISSION / 100).toFixed(2); 4352 feeText.textContent = "Estimated commission: \u20AC" + fee; 4353 }); 4354 4355 /* ââ submit ââ */ 4356 buyBtn.addEventListener("click", async function () { 4357 var value = parseFloat(amtInput.value || "0"); 4358 if (!value || value <= 0) { alert("Please enter a valid amount"); return; } 4359 4360 var amount = Math.round(value * 100); 4361 buyBtn.disabled = true; 4362 buyBtn.classList.add("auth-submit--loading"); 4363 buyBtn.textContent = "Processing\u2026"; 4364 4365 var params = new URLSearchParams(window.location.search); 4366 var country = params.get("country"); 4367 var qs = country ? "?country=" + encodeURIComponent(country) : ""; 4368 4369 try { 4370 var res = await fetch("/create-card-setup" + qs, { 4371 method: "POST", 4372 headers: { "Content-Type": "application/json" }, 4373 body: JSON.stringify({ amount: amount }) 4374 }); 4375 var data = await res.json(); 4376 if (!res.ok) { alert(data.error || "Failed"); return; } 4377 window.location.href = data.checkoutUrl; 4378 } catch (err) { 4379 alert("Something went wrong. Please try again."); 4380 } finally { 4381 buyBtn.disabled = false; 4382 buyBtn.classList.remove("auth-submit--loading"); 4383 buyBtn.textContent = "Continue"; 4384 } 4385 }); 4386})(); 4387</script>
4387 4388 <!-- âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 4389 Cookie Consent Banner + Settings Modal 4390 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ --> 4391<style> 4392 /* ââ Cookie banner (compact, bottom-right corner) ââââââââ */ 4393 .cookie-banner { 4394 position: fixed; 4395 right: 16px; 4396 bottom: 16px; 4397 left: auto; 4398 z-index: 99998; 4399 display: none; 4400 animation: cookieSlideUp 0.32s cubic-bezier(0.16, 1, 0.3, 1); 4401 } 4402 .cookie-banner.show { display: block; } 4403 4404 .cookie-card { 4405 width: 320px; 4406 max-width: calc(100vw - 32px); 4407 background: rgba(14, 8, 40, 0.82); 4408 color: white; 4409 border: 1px solid rgba(255, 255, 255, 0.1); 4410 border-radius: 16px; 4411 backdrop-filter: blur(60px) saturate(200%); 4412 -webkit-backdrop-filter: blur(60px) saturate(200%); 4413 box-shadow: 4414 0 0 0 1px rgba(255, 255, 255, 0.05) inset, 4415 0 20px 40px rgba(0, 0, 0, 0.45), 4416 0 8px 16px rgba(0, 0, 0, 0.3), 4417 0 0 40px rgba(99, 102, 241, 0.08); 4418 padding: 14px 16px; 4419 display: flex; 4420 flex-direction: column; 4421 gap: 12px; 4422 font-family: "Nunito", system-ui, sans-serif; 4423 } 4424 4425 .cookie-text { flex: 1 1 auto; min-width: 0; } 4426 4427 .cookie-title { 4428 font-size: 13px; 4429 font-weight: 800; 4430 margin: 0 0 3px; 4431 color: white; 4432 letter-spacing: -0.01em; 4433 } 4434 4435 .cookie-description { 4436 margin: 0; 4437 font-size: 12px; 4438 line-height: 1.45; 4439 color: rgba(255, 255, 255, 0.55); 4440 } 4441 4442 .cookie-description a { 4443 color: rgba(167, 139, 250, 0.9); 4444 text-decoration: none; 4445 font-weight: 600; 4446 transition: color 0.15s; 4447 } 4448 .cookie-description a:hover { 4449 color: #a78bfa; 4450 text-decoration: underline; 4451 } 4452 4453 .cookie-actions { 4454 display: flex; 4455 gap: 6px; 4456 flex-wrap: wrap; 4457 } 4458 4459 .cookie-btn { 4460 border: 0; 4461 border-radius: 10px; 4462 padding: 7px 12px; 4463 font-size: 12px; 4464 font-weight: 700; 4465 font-family: "Nunito", system-ui, sans-serif; 4466 cursor: pointer; 4467 transition: all 0.18s ease; 4468 letter-spacing: 0.01em; 4469 flex: 1 1 auto; 4470 white-space: nowrap; 4471 } 4472 4473 .cookie-btn.accept { 4474 background: linear-gradient(135deg, #6366f1 0%, #7c3aed 100%); 4475 color: white; 4476 box-shadow: 0 8px 24px rgba(99, 102, 241, 0.35), 4477 inset 0 1px 0 rgba(255, 255, 255, 0.2); 4478 } 4479 .cookie-btn.accept:hover { 4480 transform: translateY(-1px); 4481 box-shadow: 0 12px 32px rgba(99, 102, 241, 0.45), 4482 inset 0 1px 0 rgba(255, 255, 255, 0.2); 4483 } 4484 4485 .cookie-btn.reject, 4486 .cookie-btn.settings { 4487 background: rgba(255, 255, 255, 0.07); 4488 color: rgba(255, 255, 255, 0.75); 4489 border: 1px solid rgba(255, 255, 255, 0.12); 4490 } 4491 .cookie-btn.reject:hover, 4492 .cookie-btn.settings:hover { 4493 background: rgba(255, 255, 255, 0.12); 4494 border-color: rgba(255, 255, 255, 0.2); 4495 color: white; 4496 } 4497 4498 @keyframes cookieSlideUp { 4499 from { opacity: 0; transform: translateY(18px) scale(0.98); } 4500 to { opacity: 1; transform: translateY(0) scale(1); } 4501 } 4502 4503 @media (max-width: 480px) { 4504 .cookie-banner { right: 8px; bottom: 8px; left: 8px; } 4505 .cookie-card { width: auto; padding: 12px 14px; border-radius: 14px; } 4506 .cookie-btn { padding: 6px 10px; font-size: 11.5px; } 4507 } 4508 4509 /* ââ Cookie settings modal âââââââââââââââââââââââââââââââ */
4510 .cookie-settings-overlay { 4511 position: fixed; 4512 inset: 0; 4513 z-index: 99999; 4514 display: none; 4515 overflow-y: auto; 4516 padding: 24px 16px 40px; 4517 } 4518 .cookie-settings-overlay.open { display: block; } 4519 4520 .cookie-settings-backdrop { 4521 position: fixed; 4522 inset: 0; 4523 background: rgba(0, 0, 0, 0.6); 4524 backdrop-filter: blur(6px); 4525 -webkit-backdrop-filter: blur(6px); 4526 } 4527 4528 .cookie-settings-modal { 4529 position: relative; 4530 z-index: 2; 4531 width: min(560px, 96vw); 4532 margin: 60px auto; 4533 background: rgba(18, 18, 34, 0.92); 4534 backdrop-filter: blur(48px) saturate(1.6); 4535 -webkit-backdrop-filter: blur(48px) saturate(1.6); 4536 border: 1px solid rgba(255, 255, 255, 0.08); 4537 border-radius: 24px; 4538 box-shadow: 0 32px 72px rgba(0, 0, 0, 0.5), 4539 inset 0 1px 0 rgba(255, 255, 255, 0.06); 4540 padding: 36px 32px 28px; 4541 color: white; 4542 font-family: "Nunito", system-ui, sans-serif; 4543 animation: cookieSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); 4544 } 4545 4546 .cookie-settings-close { 4547 position: absolute; 4548 top: 16px; 4549 right: 18px; 4550 width: 36px; 4551 height: 36px; 4552 border-radius: 50%; 4553 border: 1px solid rgba(255, 255, 255, 0.1); 4554 background: rgba(255, 255, 255, 0.06); 4555 color: rgba(255, 255, 255, 0.5); 4556 font-size: 20px; 4557 cursor: pointer; 4558 display: flex; 4559 align-items: center; 4560 justify-content: center; 4561 transition: all 0.15s ease; 4562 }
4563 .cookie-settings-close:hover { 4564 background: rgba(255, 255, 255, 0.12); 4565 color: white; 4566 } 4567 4568 .cookie-settings-heading { 4569 margin: 0 0 6px; 4570 font-size: 22px; 4571 font-weight: 800; 4572 letter-spacing: -0.02em; 4573 } 4574 4575 .cookie-settings-modal > p { 4576 margin: 0 0 24px; 4577 font-size: 14px; 4578 color: rgba(255, 255, 255, 0.5); 4579 line-height: 1.6; 4580 } 4581 4582 /* Category rows */ 4583 .cookie-category { 4584 display: flex; 4585 align-items: flex-start; 4586 justify-content: space-between; 4587 gap: 16px; 4588 padding: 16px 0; 4589 border-top: 1px solid rgba(255, 255, 255, 0.07); 4590 } 4591 4592 .cookie-category-info { flex: 1; } 4593 4594 .cookie-category-name { 4595 font-size: 15px; 4596 font-weight: 700; 4597 margin: 0 0 4px; 4598 color: white; 4599 } 4600 4601 .cookie-category-desc { 4602 font-size: 13px; 4603 color: rgba(255, 255, 255, 0.45); 4604 line-height: 1.5; 4605 margin: 0; 4606 } 4607 4608 /* Toggle switch */ 4609 .cookie-toggle { 4610 position: relative; 4611 flex-shrink: 0; 4612 width: 44px; 4613 height: 24px; 4614 margin-top: 2px; 4615 } 4616 4617 .cookie-toggle input { 4618 opacity: 0; 4619 width: 0; 4620 height: 0; 4621 position: absolute; 4622 } 4623 4624 .cookie-toggle-track { 4625 position: absolute; 4626 inset: 0; 4627 border-radius: 12px; 4628 background: rgba(255, 255, 255, 0.1); 4629 border: 1px solid rgba(255, 255, 255, 0.12); 4630 cursor: pointer; 4631 transition: background 0.2s ease, border-color 0.2s ease; 4632 } 4633 4634 .cookie-toggle-track::after { 4635 content: ""; 4636 position: absolute; 4637 top: 2px; 4638 left: 2px; 4639 width: 18px; 4640 height: 18px; 4641 border-radius: 50%; 4642 background: rgba(255, 255, 255, 0.5); 4643 transition: transform 0.2s ease, background 0.2s ease; 4644 } 4645 4646 .cookie-toggle input:checked + .cookie-toggle-track { 4647 background: linear-gradient(135deg, #6366f1, #7c3aed); 4648 border-color: rgba(99, 102, 241, 0.4); 4649 } 4650 4651 .cookie-toggle input:checked + .cookie-toggle-track::after { 4652 transform: translateX(20px); 4653 background: white; 4654 } 4655 4656 .cookie-toggle input:disabled + .cookie-toggle-track { 4657 opacity: 0.5; 4658 cursor: not-allowed; 4659 } 4660 4661 .cookie-always-on { 4662 font-size: 12px; 4663 font-weight: 700; 4664 color: rgba(52, 211, 153, 0.8); 4665 text-transform: uppercase; 4666 letter-spacing: 0.06em; 4667 margin-top: 6px; 4668 flex-shrink: 0; 4669 } 4670 4671 /* Save button */
4672 .cookie-settings-save { 4673 width: 100%; 4674 margin-top: 24px; 4675 border: 0; 4676 border-radius: 14px; 4677 padding: 14px 18px; 4678 font-size: 15px; 4679 font-weight: 700; 4680 font-family: "Nunito", system-ui, sans-serif; 4681 background: linear-gradient(135deg, #6366f1 0%, #7c3aed 100%); 4682 color: white; 4683 cursor: pointer; 4684 transition: all 0.2s ease; 4685 box-shadow: 0 8px 24px rgba(99, 102, 241, 0.3), 4686 inset 0 1px 0 rgba(255, 255, 255, 0.2); 4687 } 4688 .cookie-settings-save:hover { 4689 transform: translateY(-1px); 4690 box-shadow: 0 12px 32px rgba(99, 102, 241, 0.45), 4691 inset 0 1px 0 rgba(255, 255, 255, 0.2); 4692 } 4693 4694 /* ââ Full-width compact cookie bar âââââââââââââââââââââââââââ */ 4695 .cookie-banner { 4696 right: 0 !important; 4697 bottom: 0 !important; 4698 left: 0 !important; 4699 } 4700 .cookie-card { 4701 width: 100% !important; 4702 max-width: none !important; 4703 border-radius: 0 !important; 4704 border-left: 0 !important; 4705 border-right: 0 !important; 4706 border-bottom: 0 !important; 4707 padding: 8px 24px !important; 4708 flex-direction: row !important; 4709 align-items: center !important; 4710 gap: 16px !important; 4711 } 4712 .cookie-card .cookie-text { flex: 1 1 auto; min-width: 0; } 4713 .cookie-card .cookie-title { display: none; } /* save vertical space */ 4714 .cookie-card .cookie-description { 4715 font-size: 12px !important; 4716 line-height: 1.35 !important; 4717 } 4718 .cookie-card .cookie-actions { 4719 flex: 0 0 auto !important; 4720 flex-wrap: nowrap !important; 4721 gap: 8px !important; 4722 } 4723 .cookie-card .cookie-btn { 4724 flex: 0 0 auto !important; 4725 padding: 6px 14px !important; 4726 font-size: 12px !important; 4727 } 4728 @media (max-width: 600px) { 4729 .cookie-card { 4730 padding: 8px 12px !important; 4731 gap: 10px !important; 4732 } 4733 .cookie-card .cookie-description { font-size: 11.5px !important; } 4734 .cookie-card .cookie-btn { padding: 5px 10px !important; font-size: 11.5px !important; } 4735 } 4736 4737 /* ââ Always-black cookie banner âââââââââââââââââââââââââââââââââ 4738 The original .cookie-card uses rgba(14,8,40,0.82) with a heavy 4739 backdrop-filter glass effect â designed for a corner card over 4740 varied content. As a full-width bottom strip with a white page 4741 background behind it, the glass picked up white and the bar 4742 looked pale. Force a solid black background + drop the blur so 4743 the bar reads as black regardless of dark/light mode. */ 4744 .cookie-banner .cookie-card { 4745 background: #0b0a14 !important; 4746 backdrop-filter: none !important; 4747 -webkit-backdrop-filter: none !important; 4748 border: 1px solid rgba(255, 255, 255, 0.08) !important; 4749 color: #ffffff !important; 4750 } 4751 .cookie-banner .cookie-title { color: #ffffff !important; } 4752 .cookie-banner .cookie-description { color: rgba(255, 255, 255, 0.65) !important; } 4753 .cookie-banner .cookie-description a { 4754 color: rgba(167, 139, 250, 0.9) !important; 4755 text-decoration: none !important; 4756 } 4757 .cookie-banner .cookie-btn.reject, 4758 .cookie-banner .cookie-btn.settings { 4759 background: rgba(255, 255, 255, 0.07) !important; 4760 color: rgba(255, 255, 255, 0.85) !important; 4761 border: 1px solid rgba(255, 255, 255, 0.14) !important; 4762 } 4763 .cookie-banner .cookie-btn.reject:hover, 4764 .cookie-banner .cookie-btn.settings:hover { 4765 background: rgba(255, 255, 255, 0.14) !important; 4766 color: #ffffff !important; 4767 } 4768 4769 /* ââ Accept Cookies button â white âââââââââââââââââââââââââââ */ 4770 .cookie-banner .cookie-btn.accept { 4771 background: #ffffff !important; 4772 color: #0f172a !important; 4773 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25), 4774 inset 0 1px 0 rgba(255, 255, 255, 0.4) !important; 4775 border: 1px solid rgba(255, 255, 255, 0.5) !important; 4776 } 4777 .cookie-banner .cookie-btn.accept:hover { 4778 background: #f1f5f9 !important; 4779 box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35), 4780 inset 0 1px 0 rgba(255, 255, 255, 0.4) !important; 4781 } 4782</style> 4783 4784<!-- ââ Banner ââ --> 4785<div id="cookieBanner" class="cookie-banner" role="dialog" aria-live="polite" aria-label="Cookie consent banner"> 4786 <div class="cookie-card"> 4787 <div class="cookie-text"> 4788 <p class="cookie-title">We use cookies</p> 4789 <p class="cookie-description"> 4790 We use essential cookies to make this site work and optional cookies to improve your experience. 4791 Read our <a href="/privacy">Privacy Policy</a>. 4792 </p> 4793 </div> 4794 <div class="cookie-actions"> 4795 <button class="cookie-btn settings" type="button" id="cookieSettingsOpen">Settings</button> 4796 <button class="cookie-btn reject" type="button" id="cookieRejectBtn">Reject all</button> 4797 <button class="cookie-btn accept" type="button" id="cookieAcceptBtn">Accept all</button> 4798 </div> 4799 </div> 4800</div> 4801 4802<!-- ââ Settings modal ââ -->
4803<div id="cookieSettingsOverlay" class="cookie-settings-overlay" aria-hidden="true"> 4804 <div class="cookie-settings-backdrop" id="cookieSettingsBackdrop"></div> 4805 4806 <div class="cookie-settings-modal" role="dialog" aria-modal="true" aria-labelledby="cookieSettingsTitle"> 4807 <button class="cookie-settings-close" id="cookieSettingsClose" type="button" aria-label="Close">×</button> 4808 4809 <p id="cookieSettingsTitle" class="cookie-settings-heading">Cookie settings</p> 4810 <p>Choose which cookies you'd like to allow. Essential cookies cannot be disabled as they are required for the site to function.</p> 4811 4812 <!-- Essential - always on --> 4813 <div class="cookie-category"> 4814 <div class="cookie-category-info"> 4815 <p class="cookie-category-name">Essential</p> 4816 <p class="cookie-category-desc">Required for core site functionality such as login, security, and session management. These cannot be turned off.</p> 4817 </div> 4818 <span class="cookie-always-on">Always on</span> 4819 </div> 4820 4821 <!-- Analytics --> 4822 <div class="cookie-category"> 4823 <div class="cookie-category-info"> 4824 <p class="cookie-category-name">Analytics</p> 4825 <p class="cookie-category-desc">Help us understand how visitors interact with the site so we can improve the experience. Includes Google Analytics.</p> 4826 </div> 4827 <label class="cookie-toggle"> 4828 <input type="checkbox" id="cookieAnalytics" checked> 4829 <span class="cookie-toggle-track"></span> 4830 </label> 4831 </div> 4832 4833 <!-- Functional --> 4834 <div class="cookie-category"> 4835 <div class="cookie-category-info"> 4836 <p class="cookie-category-name">Functional</p> 4837 <p class="cookie-category-desc">Enable personalised features like remembering your preferences, dark mode setting, and favourites.</p> 4838 </div> 4839 <label class="cookie-toggle"> 4840 <input type="checkbox" id="cookieFunctional" checked> 4841 <span class="cookie-toggle-track"></span> 4842 </label> 4843 </div> 4844 4845 <!-- Marketing --> 4846 <div class="cookie-category"> 4847 <div class="cookie-category-info"> 4848 <p class="cookie-category-name">Marketing</p> 4849 <p class="cookie-category-desc">Used by our advertising partners to show you relevant ads and measure campaign effectiveness.</p> 4850 </div> 4851 <label class="cookie-toggle"> 4852 <input type="checkbox" id="cookieMarketing"> 4853 <span class="cookie-toggle-track"></span> 4854 </label> 4855 </div> 4856 4857 <button class="cookie-settings-save" type="button" id="cookieSettingsSave">Save preferences</button> 4858 </div> 4859</div> 4860
4861<script> 4862(function () { 4863 var CONSENT_KEY = "cookie_consent"; 4864 var PREFS_KEY = "cookie_preferences"; 4865 4866 var banner = document.getElementById("cookieBanner"); 4867 var overlay = document.getElementById("cookieSettingsOverlay"); 4868 var backdrop = document.getElementById("cookieSettingsBackdrop"); 4869 4870 /* Inside the Capacitor apps we never grant the Google advertising signals. 4871 ad_user_data / ad_personalization would be "tracking" under Apple's 4872 definition and would require an App Tracking Transparency prompt plus a 4873 tracking declaration in App Privacy. Analytics stays consent-driven; only 4874 the advertising half is forced off, and the Marketing choice is hidden so 4875 we are not offering a toggle that does nothing. */ 4876 var IS_NATIVE = !!(window.Capacitor && window.Capacitor.isNativePlatform && 4877 window.Capacitor.isNativePlatform()); 4878 if (IS_NATIVE) { 4879 var mkt = document.getElementById("cookieMarketing"); 4880 var cat = mkt && mkt.closest(".cookie-category"); 4881 if (cat) cat.style.display = "none"; 4882 } 4883 4884 /* ââ Helpers ââ */ 4885 function showBanner() { if (banner) banner.classList.add("show"); } 4886 function hideBanner() { if (banner) banner.classList.remove("show"); } 4887 4888 function openSettings() { 4889 overlay.classList.add("open"); 4890 overlay.setAttribute("aria-hidden", "false"); 4891 document.body.style.overflow = "hidden"; 4892 4893 // Pre-fill toggles from saved prefs 4894 var prefs = getPrefs(); 4895 document.getElementById("cookieAnalytics").checked = prefs.analytics; 4896 document.getElementById("cookieFunctional").checked = prefs.functional; 4897 document.getElementById("cookieMarketing").checked = prefs.marketing; 4898 } 4899 4900 function closeSettings() { 4901 overlay.classList.remove("open"); 4902 overlay.setAttribute("aria-hidden", "true"); 4903 document.body.style.overflow = ""; 4904 } 4905 4906 function getPrefs() { 4907 try { 4908 var raw = localStorage.getItem(PREFS_KEY); 4909 if (raw) return JSON.parse(raw); 4910 } catch (e) {} 4911 return { analytics: true, functional: true, marketing: false }; 4912 } 4913 4914 function savePrefs(prefs) { 4915 localStorage.setItem(PREFS_KEY, JSON.stringify(prefs)); 4916 } 4917 4918 function setConsent(value, prefs) { 4919 localStorage.setItem(CONSENT_KEY, value); 4920 if (prefs) savePrefs(prefs); 4921 hideBanner(); 4922 closeSettings(); 4923 applyConsent(); 4924 } 4925 4926 /* ââ Apply consent - enable/disable scripts based on prefs ââ */ 4927 function applyConsent() { 4928 var consent = localStorage.getItem(CONSENT_KEY); 4929 if (!consent) return; 4930 4931 var prefs = getPrefs(); 4932 4933 // Google Analytics (gtag) - already loaded in head.html 4934 // Respect analytics consent via gtag consent mode 4935 if (typeof gtag === "function") { 4936 if (consent === "accepted" && prefs.analytics) { 4937 gtag("consent", "update", { 4938 analytics_storage: "granted" 4939 }); 4940 } else { 4941 gtag("consent", "update", { 4942 analytics_storage: "denied" 4943 }); 4944 } 4945 } 4946 4947 // Marketing 4948 if (typeof gtag === "function") { 4949 if (!IS_NATIVE && consent === "accepted" && prefs.marketing) { 4950 gtag("consent", "update", { 4951 ad_storage: "granted", 4952 ad_user_data: "granted", 4953 ad_personalization: "granted" 4954 }); 4955 } else { 4956 gtag("consent", "update", { 4957 ad_storage: "denied", 4958 ad_user_data: "denied", 4959 ad_personalization: "denied" 4960 }); 4961 } 4962 } 4963 } 4964 4965 /* ââ Button handlers ââ */ 4966 document.getElementById("cookieAcceptBtn").addEventListener("click", function () { 4967 setConsent("accepted", { analytics: true, functional: true, marketing: !IS_NATIVE }); 4968 }); 4969 4970 document.getElementById("cookieRejectBtn").addEventListener("click", function () { 4971 setConsent("rejected", { analytics: false, functional: false, marketing: false }); 4972 }); 4973 4974 document.getElementById("cookieSettingsOpen").addEventListener("click", openSettings); 4975 document.getElementById("cookieSettingsClose").addEventListener("click", closeSettings); 4976 backdrop.addEventListener("click", closeSettings); 4977 4978 document.addEventListener("keydown", function (e) { 4979 if (e.key === "Escape" && overlay.classList.contains("open")) closeSettings(); 4980 }); 4981 4982 document.getElementById("cookieSettingsSave").addEventListener("click", function () { 4983 var prefs = { 4984 analytics: document.getElementById("cookieAnalytics").checked, 4985 functional: document.getElementById("cookieFunctional").checked, 4986 marketing: IS_NATIVE ? false : document.getElementById("cookieMarketing").checked 4987 }; 4988 setConsent("accepted", prefs); 4989 }); 4990 4991 /* ââ Init ââ */ 4992 // Set default consent mode to denied until user acts 4993 if (typeof gtag === "function") { 4994 gtag("consent", "default", { 4995 analytics_storage: "denied", 4996 ad_storage: "denied", 4997 ad_user_data: "denied", 4998 ad_personalization: "denied" 4999 }); 5000 } 5001 5002 var consent = localStorage.getItem(CONSENT_KEY); 5003 if (!consent) { 5004 showBanner(); 5005 } else { 5006 applyConsent(); 5007 } 5008})(); 5009</script>
5009 5010 5011
5011<script> 5012 window.__isPro = false; 5013 window.__isLoggedIn = false; 5014 </script>
5014 5015
5016<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
vendor: 1 bytes, line 5016
5016
5017<script src="https://cdn.datatables.net/1.13.11/js/jquery.dataTables.min.js"></script>
vendor: 1 bytes, line 5017
5017
5018<script src="https://cdn.datatables.net/responsive/2.5.0/js/dataTables.responsive.min.js"></script>
5018 5019<!-- ColReorder (column drag-reorder API) â build matched to DataTables 1.13 -->
5020<script src="https://cdn.datatables.net/colreorder/1.7.0/js/dataTables.colReorder.min.js"></script>
5020 5021<!-- SortableJS â drag-and-drop for the Columns manager panel -->
5022<script src="https://cdn.jsdelivr.net/npm/[email protected]/Sortable.min.js"></script>
5022 5023 5024 5025<!-- Dark mode toggle is handled by header.html (uses document.documentElement + prefers_dark_mode key) --> 5026 5027 5028 5029
5029<script> 5030 (function(){ 5031 var billing = document.getElementById("xBilling"); 5032 if (!billing) return; 5033 var thumb = document.getElementById("xBillThumb"); 5034 var buttons = billing.querySelectorAll("button[data-interval]"); 5035 var planCards = document.querySelectorAll(".x-plan[data-plan]"); 5036 var planForms = document.querySelectorAll(".x-plan-form"); 5037 var comparePro = document.querySelector("[data-compare-pro]"); 5038 var compareProPlus = document.querySelector("[data-compare-proplus]"); 5039 5040 function moveThumb(activeBtn){ 5041 if (!thumb || !activeBtn) return; 5042 var bRect = billing.getBoundingClientRect(); 5043 var aRect = activeBtn.getBoundingClientRect(); 5044 thumb.style.left = (aRect.left - bRect.left) + "px"; 5045 thumb.style.width = aRect.width + "px"; 5046 } 5047 function setBillingInterval(interval){ 5048 var activeBtn = null; 5049 buttons.forEach(function(b){ 5050 var on = b.dataset.interval === interval; 5051 b.classList.toggle("is-on", on); 5052 b.setAttribute("aria-selected", on ? "true" : "false"); 5053 if (on) activeBtn = b; 5054 }); 5055 moveThumb(activeBtn); 5056 planCards.forEach(function(card){ 5057 var amount = card.querySelector("[data-price-amount]"); 5058 var period = card.querySelector("[data-price-period]"); 5059 var cta = card.querySelector("[data-cta-text]"); 5060 if (amount) amount.textContent = card.dataset[interval + "Amount"]; 5061 if (period) period.textContent = card.dataset[interval + "Period"]; 5062 if (cta) cta.textContent = card.dataset["cta" + (interval === "annual" ? "Annual" : "Monthly")]; 5063 }); 5064 planForms.forEach(function(f){ 5065 var i = f.querySelector("input[name=interval]"); 5066 if (i) i.value = interval; 5067 }); 5068 if (comparePro) comparePro.textContent = interval === "annual" ? "$39.99/yr" : "$3.99/mo"; 5069 if (compareProPlus) compareProPlus.textContent = interval === "annual" ? "$99.99/yr" : "$9.99/mo"; 5070 } 5071 buttons.forEach(function(b){ 5072 b.addEventListener("click", function(){ setBillingInterval(b.dataset.interval); }); 5073 }); 5074 requestAnimationFrame(function(){ moveThumb(billing.querySelector("button.is-on")); }); 5075 window.addEventListener("resize", function(){ moveThumb(billing.querySelector("button.is-on")); }); 5076 if (document.fonts && document.fonts.ready) { 5077 document.fonts.ready.then(function(){ moveThumb(billing.querySelector("button.is-on")); }); 5078 } 5079 document.querySelectorAll(".x-plan-form").forEach(function(form){ 5080 form.addEventListener("submit", function(){ 5081 var btn = form.querySelector("button[type=submit]"); 5082 if (btn){ btn.disabled = true; btn.textContent = "Loading..."; } 5083 }); 5084 }); 5085 })(); 5086 </script>
5086 5087</body> 5088</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.