/* ==========================================================================
   HEED.ONL — Shared Design System
   Light Apple-glass UI. Accent used sparingly (logo, one primary CTA,
   live dot, the candle chart, focus rings). Money green/red are muted
   TEXT ONLY, never backgrounds/pills.
   Reused by every page — only add page-specific tweaks in a page's own
   <style> block, never redefine these tokens/classes.
   ========================================================================== */

:root{
  /* ---- surfaces & ink ---- */
  --canvas:#F4F4F2;
  --panel:#FFFFFF;
  --panel-2:#EEEEEC;
  --ink:#141414;
  --ink-muted:#5C5C58;
  --ink-faint:#6E6E68;
  --border:#E2E2DE;
  --border-soft:rgba(20,20,20,0.06);

  /* ---- accent: lime-cyan. Use rarely. ---- */
  --accent:#38E1B0;
  --accent-hover:#22D1A0;
  --accent-soft:#DFF9F0;
  --accent-deep:#12A47D;
  --accent-ink:#0C6E54;
  --accent-bar:#0B7A5C;

  /* ---- muted money colors: text only, never fills ---- */
  --up:#2E7D4F;
  --down:#B24A3E;

  /* ---- chart-only candle colors: deliberately vivid, unlike --up/--down
     above. The muted-text doctrine is correct for a "+12.6%" label (reads
     expensive, not shouty) but wrong for a candlestick fill, where instant
     up/down legibility at ~4px width is the entire job — every serious
     trading UI (this chart is TradingView's own Lightweight Charts) uses
     saturated candles for exactly that reason. Chosen to sit outside the
     brand accent's hue family (~162°, cyan-mint) so a chart full of green
     candles never reads as "the accent," which stays rare on purpose.
     Text/pills/table rows must keep reading --up/--down, never these. ---- */
  --chart-up:#16A34A;
  --chart-down:#E5484D;

  /* ---- glass ---- */
  --glass-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.85),
    0 10px 32px 0 rgba(18,164,125,0.10),
    0 1px 2px 0 rgba(20,20,20,0.04),
    0 8px 24px 0 rgba(20,20,20,0.06),
    0 24px 48px 0 rgba(20,20,20,0.04);
  --shadow-sm:0 1px 2px rgba(20,20,20,0.04), 0 2px 6px rgba(20,20,20,0.03);

  /* ---- radii ---- */
  --r-panel:24px;
  --r-card:20px;
  --r-pill:999px;

  /* ---- type ---- */
  /* Inter is banned by house design rules; Instrument Sans is the approved
     swap (verified live on Google Fonts) and pairs with Instrument Serif below. */
  --font-ui:'Instrument Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --font-serif:'Instrument Serif',Georgia,serif;
  --font-mono:'IBM Plex Mono',monospace;

  /* ---- read by the canvas hero JS each frame ---- */
  --chart-grid:20,20,20;
  --chart-fade-1:244,244,242;
  --fade-strength:0.55;
}

/* The HTML `hidden` attribute is applied by the UA as a low-specificity
   display:none. Any component rule like `.step-panel{display:flex}` beats it,
   which silently breaks every show/hide on the site. This guard makes `hidden`
   authoritative everywhere. */
[hidden]{display:none !important;}


/* ================= DARK THEME ================= */
html[data-theme="dark"]{
/* Contrast pass (foreman ticket, "black color has readability issue"):
   --panel was #16191C and --panel-2 was #1C2023 — panel-2 was LIGHTER than
   panel, backwards from the light theme's own hierarchy (panel=white/raised,
   panel-2=grey/recessed) and only ~6 RGB steps apart from --canvas besides,
   so every coin/viral card (panel) sat almost flush with the page (canvas)
   and the active-tab pill (panel) barely separated from its track
   (panel-2). Restored panel as the brighter/raised surface, panel-2 as the
   darker/recessed one, and lifted both off canvas. --border's alpha went
   0.10 -> 0.36 because at these near-black lightnesses a WCAG 3:1 boundary
   is out of reach from background-lightness alone without turning the UI
   grey — the crisp hairline is what actually carries the "visible edge"
   requirement now (see .coin-card/.viral-card/.badge-dark below, and
   .stat/.step/.trust-card/.input/.modal/.faq-item/.mobile-nav which already
   referenced var(--border) and get it for free). --ink-faint raised
   #9A9FA5 -> #ADB3B9 since every low-emphasis label (kbd, panel-sub,
   stat-label, table th, footer-note...) used it and sat under 4.5:1 on
   --panel. Light theme's :root block above is untouched — these vars only
   exist inside this [data-theme="dark"] selector. */
  --canvas:#0C0E10;
  --panel:#242930;
  --panel-2:#15181C;
  --ink:#EDEFF0;
  --ink-muted:#A6ABB0;
  --ink-faint:#ADB3B9;
  --border:rgba(255,255,255,0.36);
  --border-soft:rgba(255,255,255,0.12);

  --accent-soft:rgba(56,225,176,0.14);
  --accent-ink:#5FE9C0;

  --up:#3FD08A;
  --down:#E06A5C;

  /* own pair for the dark panel (#242930) — the light values above read a
     touch flat against near-black, so both are nudged brighter/warmer to
     hold the same instant legibility, same reasoning as the light pair. */
  --chart-up:#22C55E;
  --chart-down:#F0555A;

  --glass-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.06),
    0 10px 32px 0 rgba(56,225,176,0.10),
    0 8px 24px 0 rgba(0,0,0,0.5),
    0 24px 48px 0 rgba(0,0,0,0.4);
  --shadow-sm:0 1px 2px rgba(0,0,0,0.3), 0 2px 8px rgba(0,0,0,0.24);

  --chart-grid:255,255,255;
  --chart-fade-1:12,14,16;
  --fade-strength:0.35;
}

/* ================= RESET / BASE ================= */
*{margin:0;padding:0;box-sizing:border-box;}
/* overflow-x:clip belongs on both html and body (see body's comment below
   for why `clip` and not `hidden`) — body-only clip depends on the UA
   propagating body's overflow up to the viewport, which is the correct
   spec behaviour but is exactly the kind of edge case that leaves a gap
   for a fixed-position element (see #chartCanvas/.canvas-fade below) to
   slip past. Belt-and-braces only — the real fix for this ticket's 390px
   overflow was the 100vw width on those two fixed layers, not this rule. */
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;overflow-x:clip;}
html,body{transition:background .4s ease,color .4s ease;}
body{
  background:var(--canvas);
  color:var(--ink);
  font-family:var(--font-ui);
  /* `clip`, not `hidden`. `html` stays overflow-x:visible, and per spec
     overflow set on body alone propagates up to the viewport instead of
     clipping there — so `hidden` here silently did nothing. `clip` clips
     without creating a scroll container, which `hidden` would, and a scroll
     container here breaks the site's position:sticky nav. Do not "fix" this
     back to hidden and do not add overflow-x:hidden to html either. */
  overflow-x:clip;
  position:relative;
  min-height:100vh;
  cursor:crosshair; /* trading-terminal feel; interactive elements override below */
}
a,button,input,textarea,select,.no-crosshair,.no-crosshair *{cursor:auto;}
a,button,summary{cursor:pointer;}

a{color:inherit;text-decoration:none;}
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit;}
img{max-width:100%;display:block;}

::selection{background:var(--accent-soft);color:var(--accent-ink);}

:focus-visible{
  outline:2px solid var(--accent-deep);
  outline-offset:3px;
}

.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
.serif{font-family:var(--font-serif);}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

/* ================= LAYOUT ================= */
.wrap{max-width:1320px;margin:0 auto;padding:0 32px;}
.page{position:relative;z-index:2;}
.section{padding:64px 0;}

@media (max-width:768px){
  .wrap{padding:0 20px;}
  .section{padding:44px 0;}
}
@media (max-width:390px){
  .wrap{padding:0 16px;}
}

/* ================= CANVAS HERO BACKGROUND ================= */
/* Present on any page that wants the cursor-paints-candles engine. */
/* THE 390px OVERFLOW SOURCE (foreman ticket): both layers below were sized
   with `100vw`, not `100%`. `vw` is 1% of the initial containing block,
   which on a normal (non-touch, non-overlay-scrollbar) browser window
   does NOT subtract the reserved width of the page's own vertical
   scrollbar — and this page is always taller than one viewport, so that
   scrollbar is always present. That made both fixed, full-page layers
   render ~15-17px wider than the actually-visible viewport. Because
   they're position:fixed, they sit outside body's normal-flow box (fixed
   elements are positioned against the initial containing block directly,
   not clipped by an ancestor's overflow the way an in-flow descendant
   would be), so body's own overflow-x:clip never touched them — and their
   overshoot into the scrollbar gutter was enough to force the whole
   document's scrollable width past 390px, which is what pushed every
   other component's right edge (nav's Connect Wallet button, the intro
   line, the filter pills, the coin card values) a few pixels past the
   visible edge. `100%` on a position:fixed element resolves against its
   containing block (the viewport) the same way `vw` does EXCEPT it
   correctly excludes the scrollbar gutter, matching what's actually
   visible. Pure bugfix, no visual change: these are background layers,
   never meant to render into the scrollbar's own space. */
#chartCanvas{
  position:fixed;top:0;left:0;
  width:100%;height:100vh;
  z-index:0;
  pointer-events:none;
}
.canvas-fade{
  position:fixed;top:0;left:0;
  width:100%;height:100vh;
  z-index:1;
  pointer-events:none;
  background:
    linear-gradient(180deg, rgba(var(--chart-fade-1),0.10) 0%, rgba(var(--chart-fade-1),var(--fade-strength)) 62%, var(--canvas) 94%),
    linear-gradient(90deg, rgba(var(--chart-fade-1),0.85) 0%, rgba(var(--chart-fade-1),0) 14%, rgba(var(--chart-fade-1),0) 86%, rgba(var(--chart-fade-1),0.85) 100%);
  transition:background .4s ease;
}
.cross-tag{
  position:fixed;z-index:5;pointer-events:none;
  right:14px;transform:translateY(-50%);
  font-family:var(--font-mono);font-size:11px;font-weight:600;
  color:#04140F;background:var(--accent);padding:2px 7px;border-radius:5px;
  opacity:0;transition:opacity .18s ease;white-space:nowrap;
  box-shadow:0 2px 8px rgba(18,164,125,0.3);
}

/* ================= NAV (identical markup every page) ================= */
.nav{position:sticky;top:0;z-index:50;padding:18px 0;}
.nav-inner{
  max-width:1320px;margin:0 auto;
  padding:10px 14px 10px 18px;
  display:flex;align-items:center;gap:28px;
  background:rgba(255,255,255,0.72);
  backdrop-filter:blur(20px) saturate(1.4);
  -webkit-backdrop-filter:blur(20px) saturate(1.4);
  border-radius:var(--r-pill);
  box-shadow:var(--glass-shadow);
  border:1px solid rgba(255,255,255,0.6);
}
html[data-theme="dark"] .nav-inner{
  background:rgba(22,25,28,0.68);
  border-color:rgba(255,255,255,0.08);
}
.brand{
  display:flex;align-items:center;gap:9px;
  /* Instrument Sans tops out at 700 (Inter's 800 doesn't exist in this
     family) — 800 was silently faux-bolding. 700 is the real weight. */
  font-weight:700;font-size:18px;letter-spacing:-0.02em;
  padding-right:6px;margin-right:2px;flex-shrink:0;
}
.brand-logo{
  height:34px;width:auto;display:block;
  filter:drop-shadow(0 2px 6px rgba(18,164,125,.35)) drop-shadow(0 1px 2px rgba(20,20,20,.15));
}
.nav-links{display:flex;align-items:center;gap:4px;flex:1;}
.nav-links a{
  font-size:14px;font-weight:500;color:var(--ink-muted);
  padding:8px 14px;border-radius:var(--r-pill);
  transition:background .18s ease,color .18s ease;
}
.nav-links a:hover{background:rgba(20,20,20,0.045);color:var(--ink);}
.nav-links a.active{color:var(--ink);background:var(--panel-2);}
html[data-theme="dark"] .nav-links a:hover{background:rgba(255,255,255,0.06);}

.nav-search{
  display:flex;align-items:center;gap:8px;
  background:var(--panel-2);
  border:1px solid var(--border);
  border-radius:var(--r-pill);
  padding:8px 12px 8px 14px;
  width:230px;color:var(--ink-faint);
}
.nav-search svg{width:14px;height:14px;flex-shrink:0;}
.nav-search input{
  border:none;background:none;outline:none;
  font-family:var(--font-mono);font-size:12.5px;color:var(--ink);width:100%;
}
.nav-search input::placeholder{color:var(--ink-faint);}
/* --ink-faint on dark --panel-2 measures ~6.9:1 on its own (see the dark
   token comment above), but placeholder text also renders at reduced
   opacity in most engines beyond the CSS color itself — bumping to
   --ink-muted (~7.7:1 on this field) keeps the header search hint from
   reading as blank in dark mode. */
html[data-theme="dark"] .nav-search input::placeholder{color:var(--ink-muted);}
.kbd{
  font-family:var(--font-mono);font-size:10.5px;color:var(--ink-faint);
  border:1px solid var(--border);background:var(--panel);
  border-radius:6px;padding:2px 6px;flex-shrink:0;
}

.theme-toggle{
  display:flex;align-items:center;justify-content:center;
  width:38px;height:38px;border-radius:var(--r-pill);
  background:var(--panel-2);border:1px solid var(--border);
  color:var(--ink-muted);flex-shrink:0;
  transition:background .18s ease,color .18s ease,border-color .18s ease;
}
.theme-toggle:hover{color:var(--ink);border-color:var(--ink-faint);}
.theme-toggle svg{width:16px;height:16px;}
html[data-theme="dark"] .theme-toggle .icon-sun{display:none;}
html:not([data-theme="dark"]) .theme-toggle .icon-moon{display:none;}

/* Right-edge control cluster (GMGN / axiom.trade pattern): theme toggle +
   Connect Wallet read as one tight unit anchored at the wrap edge, not two
   icons floating at the nav's uniform 28px rhythm. Desktop-only (min-width
   901px matches the .nav-links/.nav-search breakpoint below) so the mobile
   one-line nav (heed.js:80 inserts .nav-toggle before .theme-toggle here,
   and @media max-width:480px already tightens .nav-inner's gap to fit that
   line) is untouched. margin-left:auto is redundant with .nav-links'
   flex:1 (that flex-grow item still consumes all free space first, so the
   auto margin resolves to 0) but is kept so the cluster still lands right
   even if flex:1 is ever dropped from .nav-links. margin-right:-16px only
   tightens the gap to the very next sibling, .btn-connect (28px gap minus
   16px = 12px), leaving every other nav gap at its original rhythm. */
@media (min-width:901px){
  .theme-toggle{margin-left:auto;margin-right:-16px;}
}

/* DEFECT 3a (owner ~1050px screenshot, foreman ticket): nav-links were
   already visible at every width down to 901px — nothing was collapsing
   early there. The actual problem: nothing between 901-1100px earned the
   row any breathing room, so it sat at the same 28px nav-inner gap and
   full-size link padding as 1320px desktop, one step from crowding right
   up against the hamburger's own 900px hide threshold (exactly the ~1050
   width the owner's screenshot was taken at). Tighten instead of staying
   cramped: smaller link gap/padding/font, and the search field (⌘K still
   reaches it from anywhere, hint or no hint) steps aside first so the
   five real nav links keep clean room the whole way down to 901px. */
@media (max-width:1100px) and (min-width:901px){
  .nav-search{display:none;}
  .nav-inner{gap:14px;}
  .nav-links{gap:1px;}
  .nav-links a{padding:7px 10px;font-size:13px;}
}

@media (max-width:900px){
  .nav-links{display:none;}
  .nav-search{display:none;}
  /* DEFECT 3b: nav-inner's base 28px gap spaced every visible child the
     same wide amount — logo, hamburger, theme toggle, Connect Wallet all
     floating at one uniform rhythm reads as scattered, not "logo left,
     controls clustered right". Tighten the row gap and push the first
     item of the right-hand cluster (the hamburger, injected by heed.js
     right before .theme-toggle) out to the wrap's right edge with an
     auto margin — the same brand-left/controls-right pattern the >=901px
     desktop row already gets from .theme-toggle's own margin-left:auto
     above, just one control earlier once links/search are gone. */
  .nav-inner{gap:8px;}
  /* Owner-specified control order (standard mobile UI): theme toggle,
     then Connect, then the hamburger at the absolute right edge.
     DOM order is toggle-inserted-before-theme, so reorder via flex:
     theme starts the right cluster (auto margin), burger flexes last. */
  .theme-toggle{margin-left:auto;margin-right:0;}
  .nav-toggle{order:10;}
  /* NAV REBUILD (foreman ticket): hamburger + theme toggle are both fixed
     38x38 circles; .btn-connect's own height comes from its padding/font
     instead and lands a couple px short — pin it to the same 38px so the
     right-hand cluster reads as one aligned row of controls, ophion-style,
     not three different heights. */
  .btn-connect{height:38px;}
}

/* ---- FOREMAN TICKET: "Connect Wallet" -> "Connect" now starts at 640px
   (was 480px) so the right cluster (hamburger + theme + connect) stays
   tidy through the whole <=900px tier, not just the last 480px of it.
   Selectors are scoped to .btn-connect (not the bare .btn-connect-label-*
   classes the BUTTONS section uses below) purely so this always wins on
   specificity regardless of source order — .btn-connect-label-short's
   unconditional display:none sits later in the file, in the BUTTONS
   section, and a same-specificity single-class rule that appears later
   in the cascade beats an earlier media-scoped one even when both match,
   which is exactly the bug that made the button render as a bare dot
   with no "Connect" text at <=480px before this ticket. */
@media (max-width:640px){
  .btn-connect .btn-connect-label-full{display:none;}
  .btn-connect .btn-connect-label-short{display:inline;}
}

/* ---- very small screens: keep the nav on ONE line ----
   At 390px the brand + hamburger + theme toggle + "Connect Wallet" overflowed
   and the connect button was clipped off the right edge. Tighten the gaps and
   drop the wordmark (the cut-out glass H carries the brand on its own). */
@media (max-width:480px){
  .nav{padding:12px 0;}
  .nav-inner{gap:10px;padding:8px 10px 8px 12px;}
  .brand{gap:7px;padding-right:0;margin-right:0;font-size:16px;}
  .brand-logo{height:28px;}
  .btn-connect{padding:9px 13px;font-size:12.5px;}
}
@media (max-width:400px){
  .brand span{display:none;}
}

/* ---- mobile nav toggle + dropdown panel (injected by heed.js) ---- */
.nav-toggle{
  display:none;
  align-items:center;justify-content:center;
  width:38px;height:38px;border-radius:var(--r-pill);
  background:var(--panel-2);border:1px solid var(--border);
  color:var(--ink-muted);flex-shrink:0;
  transition:background .18s ease,color .18s ease,border-color .18s ease;
}
.nav-toggle:hover{color:var(--ink);border-color:var(--ink-faint);}
.nav-toggle svg{width:17px;height:17px;}
@media (max-width:900px){
  .nav-toggle{display:flex;}
}

.mobile-nav{
  display:none;
  position:absolute;top:calc(100% + 10px);left:14px;right:14px;
  z-index:60;
  background:var(--panel);
  border-radius:var(--r-card);
  box-shadow:var(--glass-shadow);
  border:1px solid var(--border);
  padding:10px;
  flex-direction:column;gap:2px;
}
.mobile-nav.open{display:flex;}
.mobile-nav a{
  font-size:15px;font-weight:500;color:var(--ink-muted);
  padding:14px 14px;border-radius:14px;
  min-height:44px;box-sizing:border-box;display:flex;align-items:center;
  transition:background .18s ease,color .18s ease;
}
.mobile-nav a:hover,
.mobile-nav a:focus-visible{background:var(--panel-2);color:var(--ink);}
.mobile-nav a.active{color:var(--ink);background:var(--panel-2);}
.mobile-nav .nav-search{
  display:flex;width:100%;margin-top:6px;
}
@media (min-width:901px){
  .mobile-nav{display:none !important;}
}

/* ================= BUTTONS ================= */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--font-ui);font-size:14.5px;font-weight:600;
  padding:14px 24px;border-radius:var(--r-pill);
  white-space:nowrap;
  transition:transform .18s ease,box-shadow .18s ease,background .18s ease,border-color .18s ease,color .18s ease;
}
.btn svg{width:14px;height:14px;flex-shrink:0;}
.btn-primary{
  background:var(--ink);color:#fff;
  box-shadow:0 8px 20px rgba(18,164,125,0.18), 0 2px 6px rgba(20,20,20,0.1);
}
.btn-primary:hover{
  transform:translateY(-2px);
  background:var(--accent-ink);
  box-shadow:0 12px 28px rgba(18,164,125,0.26), 0 2px 6px rgba(20,20,20,0.1);
}
/* foreman ticket, defect 1's root cause: .btn-primary's rest-state fill is
   var(--ink), which is the correct near-black in light theme but flips to
   near-white (#EDEFF0) in dark theme — that made the button (and, on the
   same rule, .btn-connect below) render as white-on-white. Pin the fill to
   a fixed dark tint that doesn't flip with the theme (was 1.15:1, now
   17.99:1 for the white label). The :hover swap to --accent-ink has the
   same failure on its own axis: --accent-ink is a bright mint in dark
   theme, so the button text (still #fff, untouched by the hover rule)
   went from illegible-white-on-white to illegible-white-on-mint (1.51:1).
   Dark text on hover matches the same accent/dark-text pairing already
   used by .cta-strip .btn-primary and .viral-launch elsewhere in this
   file (8.29:1). */
html[data-theme="dark"] .btn-primary{background:#14171A;}
html[data-theme="dark"] .btn-primary:hover{color:#0A3B2E;}
.btn-ghost{
  background:rgba(255,255,255,0.7);
  border:1px solid var(--border);
  color:var(--ink);
  backdrop-filter:blur(10px);
}
html[data-theme="dark"] .btn-ghost{background:rgba(255,255,255,0.04);}
.btn-ghost:hover{background:var(--panel);border-color:var(--ink-faint);}
.btn-sm{padding:10px 16px;font-size:13px;}

.btn-connect{
  background:var(--ink);color:#fff;
  font-size:13.5px;font-weight:600;
  padding:10px 18px;border-radius:var(--r-pill);
  display:flex;align-items:center;gap:8px;flex-shrink:0;
}
.btn-connect:hover{background:var(--accent-ink);transform:translateY(-1px);}
/* DEFECT 3c (foreman ticket): "Connect Wallet" at <=480px next to the
   hamburger + theme toggle was the widest thing in an already-tight row.
   heed.js only ever overwrites this button's innerHTML with a literal
   "Connect Wallet" / short address string on an actual connect/disconnect
   event (initWallet's setConnectedUI/setDisconnectedUI) — never on plain
   page load — so the static two-span markup below survives for every
   visitor until they interact with a wallet. A CSS `content:` text swap
   was avoided per the ticket (brittle with the dot+icon layout); this is
   a second short-label span, hidden by default, shown only <=640px (nav
   rebuild ticket moved this from <=480px — see the NAV section's
   .btn-connect .btn-connect-label-short rule, which scopes to
   .btn-connect specifically so it always outranks this plain-class rule
   on specificity no matter which one the cascade hits second) —
   .btn-connect itself, its class, and heed.js's own querySelectorAll('.btn-connect')
   binding are all untouched. */
.btn-connect-label-short{display:none;}
.btn-connect .dot{
  width:6px;height:6px;border-radius:50%;
  background:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft);
}
/* foreman ticket, defect 1 (WORST): same var(--ink) background bug as
   .btn-primary above — this is the "Connect Wallet" pill that read as pale
   text on a near-white pill in dark mode (1.15:1). Same fix, same fixed
   dark fill and dark hover-text; see the comment on .btn-primary:hover. */
html[data-theme="dark"] .btn-connect{background:#14171A;}
html[data-theme="dark"] .btn-connect:hover{color:#0A3B2E;}

/* ================= PANEL / CARD ================= */
.panel{
  background:var(--panel);
  border-radius:var(--r-panel);
  box-shadow:var(--glass-shadow);
  padding:22px 24px 24px;
  /* .panel is a grid item on portfolio's two-column layout (.pf-columns).
     Grid/flex items default min-width to their content's min-content, so
     without this the panel (and the table inside it) refuses to shrink
     below the un-wrapped table width and blows the page out sideways.
     min-width:0 lets it shrink; its own .table-scroll's overflow-x:auto
     then does the actual clipping/scrolling. */
  min-width:0;
  /* Some panels wrap an unbreakable monospace string (e.g. token.html's
     contract address). overflow-wrap is inherited, so setting it here lets
     that text break instead of forcing the panel wide; it's a no-op for
     ordinary prose, which still wraps at spaces first. */
  overflow-wrap:anywhere;
}
.panel-head{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:12px;flex-wrap:wrap;margin-bottom:16px;
}
.panel-title{
  font-family:var(--font-serif);font-size:24px;
  display:flex;align-items:center;gap:10px;
}
.panel-title .count{
  font-family:var(--font-mono);font-size:11px;font-weight:600;
  color:var(--ink-faint);background:var(--panel-2);
  border-radius:var(--r-pill);padding:3px 9px;
}
.panel-sub{font-size:12.5px;color:var(--ink-faint);}

.card{
  background:var(--panel);
  border:1px solid var(--border);
  border-radius:var(--r-card);
  padding:16px;
  transition:transform .2s ease,border-color .2s ease,box-shadow .2s ease;
}
.card:hover{transform:translateY(-3px);border-color:#D6D6D1;box-shadow:0 10px 26px rgba(20,20,20,0.06);}

/* ---- section head (used above card grids across pages) ---- */
.section-head{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:20px;flex-wrap:wrap;margin-bottom:32px;
  /* position:relative is a no-op everywhere this rule already applied —
     it only starts doing something on the few pages that add an
     absolutely-positioned .fx-accent illustration as a child (see the FX
     ILLUSTRATIONS block near the end of this file). */
  position:relative;
}
.section-title{
  font-family:var(--font-serif);font-size:36px;font-weight:400;letter-spacing:-0.01em;
}
.section-sub{font-size:14.5px;color:var(--ink-faint);margin-top:6px;}
.link-all{
  display:flex;align-items:center;gap:6px;
  font-size:14px;font-weight:600;color:var(--accent-ink);padding:10px 4px;
}
.link-all svg{width:14px;height:14px;transition:transform .2s ease;}
.link-all:hover svg{transform:translateX(3px);}

@media (max-width:768px){
  .section-title{font-size:28px;}
}

/* ================= STAT ================= */
.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;}
.stat{
  background:var(--panel);
  border:1px solid var(--border);
  border-radius:var(--r-card);
  padding:16px 18px;
  display:flex;flex-direction:column;gap:6px;
  box-shadow:var(--shadow-sm);
}
.stat-label{
  font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-faint);display:flex;align-items:center;gap:6px;
}
.stat-value{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:20px;font-weight:600;color:var(--ink);}
.stat-sub{font-size:11.5px;color:var(--ink-faint);}
.stat-sub.up{color:var(--up);}
.stat-sub.down{color:var(--down);}

.live-dot{
  width:6px;height:6px;border-radius:50%;
  background:var(--accent-deep);
  box-shadow:0 0 0 3px var(--accent-soft);
  animation:pulse 2s ease-in-out infinite;
}
@keyframes pulse{0%,100%{opacity:1;}50%{opacity:.5;}}

@media (max-width:1080px){.stats{grid-template-columns:repeat(3,1fr);}}
@media (max-width:560px){.stats{grid-template-columns:repeat(2,1fr);}}

/* ================= TABS ================= */
.tabs{
  display:flex;align-items:center;gap:2px;
  background:var(--panel-2);
  border-radius:var(--r-pill);padding:3px;
  width:fit-content;
  overflow-x:auto;
  /* width:fit-content sizes .tabs to its own min-content, which — because
     the .tab buttons are nowrap flex children that refuse to shrink below
     their label width — already includes the full unshrunk row. That
     min-content floor wins over the available space, so overflow-x:auto
     never got a chance to engage and the row pushed the page wide.
     max-width:100% clamps the box down to whatever space it actually has
     (in flow, or as a flex item of a toolbar row), which is what makes the
     existing overflow-x:auto finally scroll instead of overflow. min-width:0
     additionally removes the same refusal-to-shrink default on the rare
     pages where .tabs itself sits inside another flex row (e.g. token.html's
     .panel-head timeframe tabs). */
  max-width:100%;
  min-width:0;
}
.tab{
  font-size:13px;font-weight:600;color:var(--ink-muted);
  padding:8px 16px;border-radius:var(--r-pill);
  white-space:nowrap;
  transition:background .18s ease,color .18s ease;
}
.tab.active{background:var(--panel);color:var(--ink);box-shadow:var(--shadow-sm);}
.tab:not(.active):hover{color:var(--ink);}
/* foreman ticket, defect 3: New/Viral/Graduating and the 1H/7D range chips
   are marked disabled with an inline style="opacity:.4" (explore.html) on
   top of --ink-muted text — composited, that's ~2.3:1 against --panel-2 in
   dark theme, effectively invisible. The inline opacity can only be beaten
   by an !important rule at the same specificity tier; this keeps the
   "obviously disabled" dimmed look (still lighter opacity than the enabled
   tabs) while landing at ~3.7:1, legible instead of vanished. Dark-scoped
   only — the same opacity reads fine against the light theme's --panel-2. */
html[data-theme="dark"] .tab:disabled{opacity:.62 !important;color:var(--ink-muted);}

/* ================= BADGE ================= */
.badge{
  display:inline-flex;align-items:center;gap:5px;
  font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;
  color:var(--ink-muted);
  background:var(--panel-2);
  border:1px solid var(--border);
  border-radius:var(--r-pill);
  padding:4px 9px;
  white-space:nowrap;
}
.badge svg{width:11px;height:11px;}
.badge-accent{color:var(--accent-ink);background:var(--accent-soft);border-color:transparent;}
.badge-dark{color:#fff;background:rgba(20,20,20,0.82);border-color:transparent;}
/* .badge-dark ("Live" pill) is a hardcoded near-black chip, deliberate in
   light theme (pops on a white card) but in dark theme that near-black
   fill sits on a near-black card (--panel) and the shape itself nearly
   disappears (composited ~1.05:1 against --panel), even though its own
   white text stays perfectly legible. Same fix as the card boundary below:
   a light-tinted fill plus the hairline var(--border) carries the shape. */
html[data-theme="dark"] .badge-dark{background:rgba(255,255,255,0.10);color:var(--ink);border:1px solid var(--border);}

/* ================= BOND BAR ================= */
.bond-bar{display:flex;flex-direction:column;gap:6px;}
.bond-track{height:6px;background:var(--panel-2);border-radius:var(--r-pill);overflow:hidden;}
.bond-fill{height:100%;background:var(--accent-bar);border-radius:var(--r-pill);}
.bond-labels{
  display:flex;justify-content:space-between;
  font-size:11px;color:var(--ink-faint);font-family:var(--font-mono);
}
.bond-labels b{color:var(--ink-muted);font-weight:600;}

/* ================= COIN CARD ================= */
.coin-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;}
.coin-card{
  background:var(--panel);
  border-radius:var(--r-panel);
  box-shadow:var(--glass-shadow);
  padding:22px;
  display:flex;flex-direction:column;gap:14px;
  transition:transform .28s cubic-bezier(.2,.8,.2,1),box-shadow .28s ease;
}
.coin-card:hover{
  transform:translateY(-5px);
  box-shadow:
    inset 0 1px 0 0 rgba(255,255,255,0.9),
    0 14px 36px 0 rgba(18,164,125,0.14),
    0 2px 4px 0 rgba(20,20,20,0.05),
    0 10px 28px 0 rgba(20,20,20,0.07),
    0 30px 56px 0 rgba(20,20,20,0.05);
}
/* foreman ticket, defect 2: unlike .card, .coin-card never had a border —
   it relied entirely on --glass-shadow for separation, which works in
   light theme (the shadow is a real shadow against a light page) but in
   dark theme the shadow's own color is black-on-black and does nothing,
   leaving the card's --panel fill to separate from --canvas on lightness
   alone. Even after this ticket's --panel raise, that background-only gap
   can't reach a 3:1 boundary without turning the surface grey, so the
   hairline border (var(--border), tuned to ~3.2:1 against --panel above)
   is what actually makes the card edge legible in dark mode. Dark-scoped
   only — light theme's borderless glass-card look is untouched. */
html[data-theme="dark"] .coin-card{border:1px solid var(--border);}
.coin-top{display:flex;align-items:center;gap:12px;}
/* BUG 2 (foreman ticket, "monogram reads as a zero"): a capital O/I/l set in
   Instrument Serif inside a dark rounded square is unmistakable for a digit
   at a glance — confirmed on "Ohio Drift" ("a weird 0 icon", flagged twice).
   Instrument Sans has none of that ambiguity at this weight/size, and its
   business is legibility, not display flourish, so it wins for any monogram
   fallback. font-weight bumped 400 -> 700 (Instrument Sans' regular cut
   reads thin as a badge glyph at 19-34px). overflow:hidden is new too — it
   clips a real coin logo <img> (when one exists) to the tile's own rounded
   corners; a bare monogram never overflowed the box so this is a no-op for
   every tile that stays text-only. Shared by every page via this one class,
   so the fix reaches leaderboard/portfolio/explore's tiles too, not just
   token.html's — same rule, same box, no page needs its own override. */
.coin-tile{
  width:44px;height:44px;border-radius:13px;flex-shrink:0;
  display:flex;align-items:center;justify-content:center;
  overflow:hidden;
  position:relative; /* anchors the ::after glass-H mark below */
  font-family:var(--font-ui);font-size:19px;font-weight:700;color:#fff;
}
.coin-tile img{width:100%;height:100%;object-fit:cover;display:block;}
/* ---- glass-H fallback (foreman ticket, "replace the letter-tile fallback")
   Every page that builds a .coin-tile (this file's own portfolio.html/
   token.html inline scripts, plus js/board.js on explore.html/leaderboard.html
   — out of this ticket's write set, reached here on purpose, see the ticket
   report) renders EITHER a real coin <img> (js/board.js's tileMarkup, or an
   inline style.backgroundImage set directly on the tile) OR a bare letter/
   monogram text node as the no-logo fallback. :has(img) is the one reliable,
   JS-independent signal for "a real logo is present" across all of those call
   sites, so the glass-H mark only ever paints when there is genuinely no
   image child. The deterministic per-coin gradient (each page's own
   gradientFor()/avatarBackgroundFor(), set inline as `background` on this
   same element) is untouched — this only covers what was the bare letter,
   never the gradient underneath it. The letter/monogram text itself is not
   removed (no JS touched), only made invisible via color:transparent, so a
   change to any of those pages' fallback logic later still degrades safely. */
/* .has-logo — portfolio.html's own buildCoinTile() swaps in a real logo via
   style.backgroundImage on the tile itself, not a child <img> (see that
   page's inline script for why), so :has(img) alone can't see it — this
   class is portfolio.html's explicit signal for that one case. Every other
   .coin-tile builder (js/board.js, token.html) adds a real <img> child, so
   :has(img) alone already covers them and never sets this class. */
.coin-tile:not(:has(img)):not(.has-logo){color:transparent;}
.coin-tile:not(:has(img)):not(.has-logo) span{text-shadow:none;} /* board.js's explore-grid richAvatar span sets its own inline text-shadow — neutralize so no ghost outline shows through the transparent glyph */
.coin-tile:not(:has(img)):not(.has-logo)::after{
  content:'';
  position:absolute;inset:16%;
  background-image:url('../assets/mark-h.webp');
  background-size:contain;background-position:center;background-repeat:no-repeat;
  pointer-events:none;
}

/* ---- RESOLVING state (foreman ticket, "there a lag its how the
   placeholder image before it has to be perfect" — the glass-H above means
   "this coin genuinely has no artwork", so it must only ever paint once
   that is actually known, never as a default shown while still finding
   out). token.html's #tokenTile carries this class from its very first
   HTML frame (baked into the markup itself, not added by JS, so there is
   no JS-timing gap where the mark above could flash on before this state
   even exists) and only ever loses it once the real answer is known — see
   that page's own settleAvatar(). Scoped to is-resolving on purpose:
   js/board.js's explore.html/leaderboard.html tiles and portfolio.html's
   buildCoinTile() never set this class, so they are completely unaffected
   by anything below (out of this ticket's write set — see the ticket
   report).
   Equal CSS specificity to the ::after rule directly above (both are
   (0,2,2): two classes + :has()'s inner type selector + the pseudo-element)
   — this relies on appearing LATER in the file to win the tie and suppress
   the mark while resolving; keep it after the rule above if this block is
   ever moved. */
.coin-tile.is-resolving:not(:has(img))::after{content:none;}
.coin-tile.is-resolving::before{
  content:'';
  position:absolute;inset:0;
  /* a calm sweep over the tile's own deterministic gradient (already
     painted as this element's inline `background`) — not a separate flat
     "loading" tile, not a spinner, nothing that reads as broken or urgent */
  background:linear-gradient(100deg,transparent 35%,rgba(255,255,255,.16) 50%,transparent 65%);
  background-size:220% 100%;
  animation:coinTileShimmer 1.4s ease-in-out infinite;
  pointer-events:none;
}
@keyframes coinTileShimmer{
  0%{background-position:120% 0;}
  100%{background-position:-20% 0;}
}
.coin-id{flex:1;min-width:0;}
.coin-name{font-size:15px;font-weight:700;letter-spacing:-0.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.coin-ticker{font-family:var(--font-mono);font-size:12px;color:var(--ink-faint);}
.coin-chg{
  font-family:var(--font-mono);font-size:13px;font-weight:600;
  display:flex;align-items:center;gap:3px;flex-shrink:0;
}
.coin-chg svg{width:10px;height:10px;}
.coin-chg.up{color:var(--up);}
.coin-chg.down{color:var(--down);}

/* BUG 1 (foreman ticket, "Seeded"/"Curve" split mid-letter): this row's
   three wrapper divs (Liquidity/Kind/Creator) are unlabeled plain <div>s
   with no explicit width. As a flex row (justify-content:space-between)
   their default min-width:auto means their floor is the CONTENT's min-
   content size, not the whole word — and .panel two levels up sets
   overflow-wrap:anywhere (see PANEL/CARD above), which is an inherited
   property. That inheritance is what shrank a plain word's min-content
   size down to a single character, so the flex item happily shrank past
   the word boundary and the renderer sliced "Seeded" into "Seede"/"d" to
   fit. Grid with minmax(0,1fr) tracks (never bare 1fr — see the coin-grid
   column comment above) sidesteps the whole auto-min-content problem: each
   third gets an explicit, predictable width instead of shrinking to fit
   content. overflow-wrap:normal on the row explicitly breaks the
   inheritance chain from .panel for this text (label/value words should
   never split mid-letter); white-space:nowrap + ellipsis on .k/.v is the
   correct fallback if a real value is still too wide for its third —
   trims at a word boundary via "…", never mid-letter.

   BUG 2 (foreman ticket, "1.3558 E..." — Liquidity's ETH unit clipped):
   equal thirds gave Liquidity's longer value ("1.3558 ETH", 10 mono
   chars) the same width as Kind ("Seeded"/"Curve", 5-6 chars) and
   Creator's short address ("0x7d4d…94e3", 11 mono chars at 11px). Tracks
   are now uneven minmax(0,Nfr) sized to each column's real content
   instead of a bare 1fr split — Liquidity gets the most (42%), Creator
   next (34%, it already carries the sanctioned ellipsis fallback above
   for the rare long-address case), Kind the least (24%, its words are
   short). Still minmax(0,...) on every track, so the row can never force
   the card wider than it is — this only redistributes give within it. */
.coin-stats{display:grid;grid-template-columns:minmax(0,21fr) minmax(0,12fr) minmax(0,17fr);gap:8px;overflow-wrap:normal;}
.coin-stats>div{min-width:0;}
.coin-stats .k{font-size:11px;color:var(--ink-faint);margin-bottom:4px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.coin-stats .v{font-family:var(--font-mono);font-size:14px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

@media (max-width:1080px){.coin-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:640px){.coin-grid{grid-template-columns:1fr;}}

/* ================= MARKET CARD — HERO ART (foreman ticket: "coin cards
   bland compared to both his own mockups and the real competing
   launchpads" — the owner is right, we rendered the coin image at
   thumbnail size when pons.family, lemon.fun, and the owner's own
   approved explore-coin-grid-01.png mockup all make the artwork the
   dominant element of the card) =================
   New component classes for explore.html's "Live market" grid card only
   (marketCardHTML(), rendered into #marketGridView — see that page's own
   <style> for the id-scoped padding/gap wiring that lets .mk-art bleed to
   the card's edges). PONS is the model, named as the owner's favourite:
   a large square image at the top of the card, full card width, then
   name/$ticker/market-cap/supporting-numbers underneath — same "light
   glass" visual language as the rest of this file (--panel/--border/
   --r-panel), not a switch to pons/lemon's dark theme. Deliberately does
   NOT touch .coin-card/.coin-tile/.coin-top/.coin-name/.coin-ticker/
   .coin-stats' own existing rules above, or how leaderboard.html/
   portfolio.html/viral.html (all built by js/board.js, out of this
   ticket's write set) use those classes for their own .coin-card — those
   pages keep their existing small-thumbnail anatomy on purpose; only the
   Live market board's own card, built in explore.html's inline script,
   got the new anatomy. .coin-name/.coin-ticker are still reused
   as-is inside .mk-id (name/ticker stay first, per the ticket's priority
   order) — their global ellipsis/truncation rules above still apply. */
.mk-art{
  position:relative;width:100%;aspect-ratio:1/1;overflow:hidden;flex-shrink:0;
  display:flex;align-items:center;justify-content:center;
  background:var(--panel-2);
  font-family:var(--font-ui);font-weight:700;color:#fff;
  font-size:clamp(24px,6vw,44px);
}
.mk-art-top{
  position:absolute;top:10px;left:10px;right:10px;
  display:flex;align-items:flex-start;justify-content:space-between;
  gap:6px;z-index:3;min-width:0;
}
.mk-art-status{display:flex;flex-wrap:wrap;align-items:center;gap:6px;min-width:0;}
.mk-body{padding:16px;display:flex;flex-direction:column;gap:10px;min-width:0;}
.mk-id{display:flex;flex-direction:column;gap:2px;min-width:0;}
/* Market cap: the card's headline number now (MUST DO 3), monospace and
   prominent — same 21px/700 weight the old per-token price row used, so
   this reads as the SAME visual "most important number" slot, just
   holding a more useful figure. .empty is the honest "DexScreener never
   returned a market cap for this pair" case, matching every other
   formatter's null-safe contract on this page — never a fabricated $0. */
.mk-cap-row{display:flex;align-items:baseline;justify-content:space-between;gap:10px;min-width:0;flex-wrap:wrap;}
.mk-cap-main{display:flex;align-items:baseline;gap:8px;min-width:0;flex-wrap:wrap;}
.mk-cap-label{font-size:11px;color:var(--ink-faint);font-weight:500;flex-shrink:0;}
.mk-cap-value{font-family:var(--font-mono);font-size:21px;font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
.mk-cap-value.empty{color:var(--ink-faint);font-weight:600;font-size:15px;}
/* Supporting numbers row (24h volume, holders) — demoted below market
   cap per the ticket's priority order, smaller type, faint labels. */
.mk-sub-row{display:flex;align-items:center;gap:16px;min-width:0;flex-wrap:wrap;}
.mk-sub-item{display:flex;align-items:baseline;gap:5px;min-width:0;font-size:12px;}
.mk-sub-k{color:var(--ink-faint);flex-shrink:0;}
.mk-sub-v{font-family:var(--font-mono);color:var(--ink-muted);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}

/* ---- launch age (foreman ticket, 2026-08-09 — "no coin anywhere shows
   its age") ---- Real elapsed time since a coin's own on-chain launch tx,
   from window.HeedChainIndex's launchedAt/formatAge (js/chainindex.js,
   out of this ticket's write set). Quieter than .mk-sub-v on purpose — an
   age is a supporting fact, not a headline, closer to GMGN's own faint
   treatment — so this is placed AFTER .mk-sub-v above and wins the color/
   size tie on source order when both classes sit on the same element.
   Never fabricated: a coin with no known launch tx renders no age text at
   all (see explore.html/leaderboard.html's own callers), never a guessed
   value or "0s". Shared by both boards' card/row/table builders. */
.coin-age{font-family:var(--font-mono);font-size:10.5px;font-weight:600;color:var(--ink-faint);white-space:nowrap;flex-shrink:0;}

/* ================= VIRAL CARD ================= */
.viral-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;}
.viral-card{
  background:var(--panel);
  border-radius:var(--r-card);
  box-shadow:var(--glass-shadow);
  padding:20px;
  display:flex;flex-direction:column;gap:14px;
  transition:transform .25s cubic-bezier(.2,.8,.2,1),box-shadow .25s ease;
}
.viral-card:hover{transform:translateY(-4px);}
/* same border-less-card issue as .coin-card above, same fix, dark-scoped only. */
html[data-theme="dark"] .viral-card{border:1px solid var(--border);}
.viral-plat{
  display:flex;align-items:center;gap:8px;
  font-size:12px;font-weight:600;color:var(--ink-faint);
  text-transform:uppercase;letter-spacing:.04em;
}
.viral-plat svg{width:15px;height:15px;color:var(--ink-muted);}
.viral-trend{font-size:16px;font-weight:700;letter-spacing:-0.01em;line-height:1.3;}
.viral-meta{
  font-family:var(--font-mono);font-size:12px;color:var(--accent-ink);
  display:flex;align-items:center;gap:6px;
}
.viral-meta svg{width:10px;height:10px;}
.viral-spark{width:100%;height:28px;color:var(--accent-deep);}
.viral-launch{
  margin-top:auto;
  display:flex;align-items:center;justify-content:center;gap:7px;
  background:transparent;border:1px solid var(--border);color:var(--ink);
  font-size:13px;font-weight:600;padding:11px;border-radius:var(--r-pill);
  transition:background .18s ease,color .18s ease,border-color .18s ease;
}
.viral-launch svg{width:14px;height:14px;flex:0 0 14px;}
.viral-launch:hover,
.viral-launch:focus-visible{background:var(--accent);color:#0A3B2E;border-color:var(--accent);}
/* the #1 trend card keeps the accent fill at rest — it's the one place the
   accent is allowed to mark something permanently, not just on interaction */
.viral-top .viral-launch{background:var(--accent);color:#0A3B2E;border-color:var(--accent);}
/* defensive: an inline SVG with only a viewBox will stretch to fill its flex
   parent. Every icon slot sets its own size above; this stops any missed one
   from ballooning. */
button svg, a svg, .badge svg, .chip svg{max-width:22px;max-height:22px;}
/* SAME BUG, 2ND OCCURRENCE (foreman ticket, "TYPE icon in the trades table
   ballooned into three fat brick-red bars"): the guard above only covers
   button/a/.badge/.chip — a bare <svg viewBox> sitting loose in a <td> or an
   <li> isn't any of those, so it slipped through undefended, same root cause
   (.viral-launch's arrow) as before. Widened to table cells and list rows,
   the two loose-svg homes this bug keeps finding. A specific icon rule with
   its own explicit width/height (e.g. .copy-btn svg, .check-chip svg) still
   wins on specificity — this only catches ones nobody sized on purpose. */
td svg, li svg{max-width:22px;max-height:22px;}

@media (max-width:1080px){.viral-row{grid-template-columns:repeat(2,1fr);}}
@media (max-width:560px){.viral-row{grid-template-columns:1fr;}}

/* ================= TICKER ================= */
.ticker{
  position:relative;z-index:10;
  border-top:1px solid var(--border-soft);
  border-bottom:1px solid var(--border-soft);
  background:rgba(255,255,255,0.55);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  overflow:hidden;padding:13px 0;margin-top:6px;
}
html[data-theme="dark"] .ticker{background:rgba(22,25,28,0.5);}
.ticker-track{display:flex;width:max-content;animation:tickerScroll 38s linear infinite;}
@keyframes tickerScroll{from{transform:translateX(0);}to{transform:translateX(-50%);}}
.ticker-item{
  display:flex;align-items:center;gap:8px;
  padding:0 26px;font-family:var(--font-mono);font-size:13px;white-space:nowrap;
  border-right:1px solid var(--border-soft);
}
.ticker-item .sym{font-weight:600;color:var(--ink);}
.ticker-item .chg{display:flex;align-items:center;gap:3px;font-weight:600;}
.ticker-item .chg.up{color:var(--up);}
.ticker-item .chg.down{color:var(--down);}
.ticker-item .chg svg{width:9px;height:9px;}
.ticker-item .badge{
  font-size:9.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
  color:var(--accent-ink);background:var(--accent-soft);
  padding:2px 6px;border-radius:5px;font-family:var(--font-ui);border:none;
}

/* ================= TABLE ================= */
.table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;}
.table{width:100%;border-collapse:collapse;font-size:13.5px;}
.table th{
  text-align:left;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink-faint);padding:0 14px 10px;white-space:nowrap;
}
.table td{
  padding:14px;border-top:1px solid var(--border-soft);
  color:var(--ink);white-space:nowrap;
}
.table tbody tr{transition:background .15s ease;}
.table tbody tr:hover{background:var(--panel-2);}
.table .num{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
.table .up{color:var(--up);}
.table .down{color:var(--down);}

/* ================= FIELD / INPUT ================= */
.field{display:flex;flex-direction:column;gap:7px;}
.field label{font-size:12.5px;font-weight:600;color:var(--ink-muted);}
.field-hint{font-size:11.5px;color:var(--ink-faint);}
.input{
  width:100%;
  background:var(--panel);
  border:1px solid var(--border);
  border-radius:14px;
  padding:13px 15px;
  font-family:var(--font-ui);font-size:14px;color:var(--ink);
  transition:border-color .15s ease,box-shadow .15s ease;
}
.input::placeholder{color:var(--ink-faint);}
.input:focus{outline:none;border-color:var(--accent-deep);box-shadow:0 0 0 3px var(--accent-soft);}
textarea.input{resize:vertical;min-height:96px;}

/* ================= MODAL ================= */
.modal-overlay{
  position:fixed;inset:0;z-index:100;
  display:none;align-items:center;justify-content:center;
  padding:24px;
  background:rgba(12,14,16,0.42);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
}
.modal-overlay.open{display:flex;}
.modal{
  width:100%;max-width:420px;
  background:var(--panel);
  border-radius:var(--r-panel);
  box-shadow:var(--glass-shadow);
  padding:24px;
}
.modal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.modal-title{font-family:var(--font-serif);font-size:22px;}
.modal-close{
  width:32px;height:32px;border-radius:var(--r-pill);
  display:flex;align-items:center;justify-content:center;
  color:var(--ink-faint);background:var(--panel-2);
}
.modal-close:hover{color:var(--ink);}
.modal-close svg{width:15px;height:15px;}
.modal-body{display:flex;flex-direction:column;gap:12px;}
.modal-foot{margin-top:18px;display:flex;gap:10px;}

/* ================= HERO =================
   FOREMAN TICKET (nav+hero rebuild): .hero-inner is a named-area grid at
   every width, not plain block flow. Below 1240px this is what makes the
   two-column layout possible without touching the DOM — .hero-art keeps
   its exact source position (between .hero-cta and .hero-stats, see
   index.html) at every tier; only the grid-area assignment moves it.
   Base (mobile, <640px): single column, art banner slotted between the
   eyebrow and h1 (owner's rejected-twice defect: art must never render
   after the CTA or between the CTA and stats — this area order makes
   that impossible structurally, not just by convention).
   >=1240px keeps the original template (no "art" row) since .hero-art
   goes position:absolute there (own rule below, untouched) and is
   removed from grid flow entirely — giving it a track would leave a
   blank row. */
.hero{position:relative;padding:96px 0 76px;}
.hero-inner{
  max-width:860px;
  display:grid;
  grid-template-columns:1fr;
  grid-template-areas:"eyebrow" "art" "h1" "sub" "cta" "stats";
  row-gap:0;
}
.hero-inner .eyebrow{grid-area:eyebrow;}
.hero-inner h1{grid-area:h1;}
.hero-inner .hero-sub{grid-area:sub;}
.hero-inner .hero-cta{grid-area:cta;}
.hero-inner .hero-art{grid-area:art;}
.hero-inner .hero-stats{grid-area:stats;}

.eyebrow{
  display:inline-flex;align-items:center;gap:8px;
  font-family:var(--font-mono);font-size:12.5px;color:var(--accent-ink);
  background:var(--accent-soft);border:1px solid rgba(18,164,125,0.18);
  padding:6px 13px 6px 11px;border-radius:var(--r-pill);
  margin-bottom:28px;font-weight:500;
}
/* Bare h1 stays exactly what it was before this ticket — viral.html
   (.viral-hero-title) and token.html (#tokenName) both inherit from this
   same unscoped selector and only override font-size themselves, so
   margin-bottom/line-height/letter-spacing/font-weight here must stay
   fixed, not tiered. All of the ticket's responsive hero sizing below is
   scoped to ".hero-inner h1" instead (index.html's only h1, (0,1,1)
   specificity beats this bare (0,0,1) rule at every width) so those two
   other pages render byte-for-byte as they did before this ticket. */
h1{
  /* Instrument Sans only ships 400-700 (Inter's 800 doesn't exist in this
     family) — 800 was triggering a synthetic/faux bold in the browser.
     Dropped to the real 700. At 76px the lighter stroke reads a touch
     looser, so letter-spacing is nudged tighter (-0.03em -> -0.035em) to
     keep the same visual density; this is the only rule that gets it. */
  font-size:76px;line-height:0.98;letter-spacing:-0.035em;font-weight:700;
  margin-bottom:26px;
}
/* Mobile-first hero override: the single-column <640px banner tier (h1
   owns the full text column width there, so it can stay close to the
   old fixed 44px). Every wider tier below overrides font-size/margin
   again; >=1240px restores the bare rule's own 76px/26px explicitly
   (own rule further down) since it needs to win over this unconditional
   one on source order, not just fall through to the bare selector. */
.hero-inner h1{font-size:clamp(32px,9vw,44px);margin-bottom:20px;}
h1 em{
  font-family:var(--font-serif);font-style:italic;font-weight:400;
  color:var(--accent-ink);
}
h1 .outline{-webkit-text-stroke:1.5px var(--ink);color:transparent;}
.hero-sub{font-size:15.5px;line-height:1.55;color:var(--ink-muted);max-width:560px;margin-bottom:30px;}
.hero-cta{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:40px;}
.hero-stats{display:flex;gap:24px;padding-top:28px;border-top:1px solid var(--border);max-width:640px;flex-wrap:wrap;}
.hero-stats .stat{
  background:none;border:none;box-shadow:none;padding:0;gap:6px;
}
.hero-stats .num{
  font-family:var(--font-serif);font-size:22px;font-weight:400;letter-spacing:-0.01em;
  color:var(--ink);line-height:1;
}
.hero-stats .lbl{font-size:12px;color:var(--ink-faint);font-weight:500;}

/* ---- 640-1239px: true two-column hero (headline/copy left, art right).
   Ticket-authorized typography change: h1 shrinks per tier below so the
   text column always has room to breathe next to the art column — this
   is the fix for the "art stacked under the CTA" defect the owner
   rejected twice; the fixed 76px h1 was the reason side-by-side was
   never attempted before. >=1240px is untouched (own rule further
   below, still resolves against .hero exactly as before). ---- */
@media (min-width:640px) and (max-width:1239px){
  .hero-inner{
    max-width:none;
    grid-template-columns:1fr var(--hero-art-w, 300px);
    grid-template-areas:
      "eyebrow eyebrow"
      "h1      art"
      "sub     art"
      "cta     art"
      "stats   stats";
    column-gap:28px;
  }
  .hero-inner .hero-art{align-self:center;}
}
@media (min-width:640px) and (max-width:767px){
  .hero-inner{--hero-art-w:260px;column-gap:20px;}
  .hero-inner h1{font-size:32px;margin-bottom:16px;}
  .hero-sub{font-size:14.5px;margin-bottom:24px;}
  .hero-cta{margin-bottom:32px;}
}
@media (min-width:768px) and (max-width:899px){
  .hero{padding:64px 0 52px;}
  .hero-inner{--hero-art-w:300px;column-gap:28px;}
  .hero-inner h1{font-size:42px;margin-bottom:18px;}
  .hero-sub{font-size:16px;margin-bottom:26px;}
}
@media (min-width:900px) and (max-width:1023px){
  .hero{padding:76px 0 56px;}
  .hero-inner{--hero-art-w:360px;column-gap:32px;}
  .hero-inner h1{font-size:50px;margin-bottom:20px;}
  .hero-sub{font-size:17px;margin-bottom:30px;}
  .hero-stats .num{font-size:26px;}
}
@media (min-width:1024px) and (max-width:1151px){
  .hero{padding:84px 0 64px;}
  .hero-inner{--hero-art-w:400px;column-gap:36px;}
  .hero-inner h1{font-size:58px;margin-bottom:22px;}
  .hero-sub{font-size:18px;margin-bottom:34px;}
  .hero-stats{gap:36px;}
  .hero-stats .num{font-size:30px;}
}
@media (min-width:1152px) and (max-width:1239px){
  .hero{padding:90px 0 70px;}
  .hero-inner{--hero-art-w:460px;column-gap:40px;}
  .hero-inner h1{font-size:66px;margin-bottom:24px;}
  .hero-sub{font-size:18.5px;margin-bottom:36px;}
  .hero-stats{gap:40px;}
  .hero-stats .num{font-size:32px;}
}
/* ---- >=1240px: original desktop tier, byte-for-byte the same values
   that shipped before this ticket — .hero-art goes back to position:
   absolute (own rule below) so it needs no grid track here. ---- */
@media (min-width:1240px){
  .hero-inner{
    max-width:860px;
    grid-template-columns:1fr;
    grid-template-areas:"eyebrow" "h1" "sub" "cta" "stats";
  }
  .hero-inner h1{font-size:76px;margin-bottom:26px;}
  .hero-sub{font-size:19px;margin-bottom:38px;}
  .hero-cta{margin-bottom:56px;}
  .hero-stats{gap:44px;padding-top:36px;}
  .hero-stats .num{font-size:34px;}
  .hero-stats .lbl{font-size:12.5px;}
}

/* ================= CTA STRIP ================= */
.cta-strip{
  margin:16px 0 24px;
  background:linear-gradient(135deg,#101314 0%,#1B2320 100%);
  border-radius:32px;padding:64px 56px;
  display:flex;align-items:center;justify-content:space-between;gap:40px;
  position:relative;overflow:hidden;
  box-shadow:0 30px 70px rgba(11,18,16,0.28);
}
.cta-strip::before{
  content:'';position:absolute;top:-40%;right:-10%;
  width:520px;height:520px;
  background:radial-gradient(circle, rgba(56,225,176,0.22) 0%, rgba(56,225,176,0) 70%);
  pointer-events:none;
}
.cta-text h3{
  font-family:var(--font-serif);font-size:38px;font-weight:400;color:#fff;
  margin-bottom:12px;letter-spacing:-0.01em;
}
.cta-text p{color:rgba(255,255,255,0.6);font-size:15.5px;max-width:420px;}
.cta-strip .btn-primary{
  background:var(--accent);color:#0A3B2E;
  box-shadow:0 8px 24px rgba(56,225,176,0.3);flex-shrink:0;
}
.cta-strip .btn-primary:hover{background:#fff;color:#0A3B2E;}

@media (max-width:800px){
  .cta-strip{flex-direction:column;align-items:flex-start;padding:44px 32px;}
  .cta-text h3{font-size:28px;}
}

/* ================= STEPS (how it works) ================= */
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;}
.step{
  background:var(--panel);border:1px solid var(--border);
  border-radius:var(--r-card);padding:22px;
  display:flex;flex-direction:column;gap:8px;
}
.step-num{
  font-family:var(--font-mono);font-size:11px;font-weight:600;
  color:var(--ink-faint);text-transform:uppercase;letter-spacing:.08em;
  margin-bottom:6px;
}
.step-title{font-size:17px;font-weight:700;letter-spacing:-0.01em;}
.step-desc{font-size:13.5px;color:var(--ink-muted);line-height:1.5;}

@media (max-width:960px){.steps{grid-template-columns:repeat(2,1fr);}}
@media (max-width:560px){.steps{grid-template-columns:1fr;}}

/* ================= TRUST GRID ================= */
.trust-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;}
.trust-card{
  background:var(--panel);border:1px solid var(--border);
  border-radius:var(--r-card);padding:22px;
  display:flex;flex-direction:column;gap:12px;
}
.trust-icon{
  width:38px;height:38px;border-radius:11px;
  background:var(--accent-soft);color:var(--accent-ink);
  display:flex;align-items:center;justify-content:center;
}
.trust-icon svg{width:19px;height:19px;}
.trust-title{font-size:16px;font-weight:700;letter-spacing:-0.01em;}
.trust-desc{font-size:13.5px;color:var(--ink-muted);line-height:1.5;}
.trust-tag{
  font-family:var(--font-mono);font-size:10.5px;color:var(--ink-faint);
  text-transform:uppercase;letter-spacing:.06em;margin-top:auto;padding-top:8px;
}

@media (max-width:960px){.trust-grid{grid-template-columns:repeat(2,1fr);}}
@media (max-width:560px){.trust-grid{grid-template-columns:1fr;}}

/* ================= FAQ ================= */
.faq{background:var(--panel);border-radius:var(--r-panel);box-shadow:var(--glass-shadow);overflow:hidden;}
.faq-item{border-bottom:1px solid var(--border);}
.faq-item:last-child{border-bottom:none;}
.faq-q{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:20px 24px;font-size:15.5px;font-weight:600;color:var(--ink);
  list-style:none;
}
.faq-q::-webkit-details-marker{display:none;}
.faq-q .chev{width:16px;height:16px;color:var(--ink-faint);transition:transform .2s ease;flex-shrink:0;}
.faq-item[open] .faq-q .chev{transform:rotate(180deg);}
.faq-a{padding:0 24px 22px;font-size:14px;line-height:1.6;color:var(--ink-muted);max-width:680px;}

/* ================= FOOTER ================= */
.footer{border-top:1px solid var(--border);padding:64px 0 32px;}
.footer-top{display:flex;justify-content:space-between;align-items:flex-start;gap:40px;flex-wrap:wrap;}
.footer-brand{display:flex;align-items:center;gap:10px;font-weight:600;font-size:14px;color:var(--ink-muted);max-width:340px;}
.footer-brand img{height:20px;width:auto;flex-shrink:0;filter:drop-shadow(0 2px 6px rgba(18,164,125,.35)) drop-shadow(0 1px 2px rgba(20,20,20,.15));}
.footer-cols{display:grid;grid-template-columns:repeat(3,auto);gap:40px;}
.footer-col{display:flex;flex-direction:column;gap:10px;min-width:120px;}
.footer-col-h{
  font-family:var(--font-mono);font-size:10.5px;font-weight:600;
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);
}
.footer-col a{font-size:13.5px;color:var(--ink-muted);}
.footer-col a:hover{color:var(--ink);}
.footer-bottom{
  display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;
  margin-top:48px;padding-top:20px;border-top:1px solid var(--border);
}
.footer-copy{font-size:12px;color:var(--ink-faint);}
.footer-note{
  font-family:var(--font-mono);font-size:11px;color:var(--ink-faint);
  text-align:right;max-width:520px;
}
@media (max-width:640px){
  .footer{padding:48px 0 28px;}
  .footer-top{flex-direction:column;align-items:flex-start;gap:28px;}
  .footer-cols{grid-template-columns:1fr 1fr;gap:24px;width:100%;}
  .footer-bottom{flex-direction:column;align-items:flex-start;gap:8px;}
  .footer-note{text-align:left;max-width:none;}
}
@media (max-width:420px){
  .footer-cols{grid-template-columns:1fr;}
}
.data-note{
  display:flex;align-items:center;gap:6px;
  font-size:10.5px;color:var(--ink-faint);
}
.data-note svg{width:12px;height:12px;flex-shrink:0;}

/* ================= RESPONSIVE (global) ================= */
@media (max-width:1024px){
  .wrap{padding:0 24px;}
}

/* ================= MOTION ================= */
@media (prefers-reduced-motion:reduce){
  .ticker-track{animation:none;}
  .live-dot{animation:none;}
  *{scroll-behavior:auto !important;}
  /* MUST DO #3: the resolving-state shimmer above becomes a static, calm
     tint instead of a sweeping animation — still visibly distinct from the
     resolved gradient (so it doesn't read as a finished state), never
     moving. */
  .coin-tile.is-resolving::before{animation:none;background-position:50% 0;}
}

/* ================= FX ILLUSTRATIONS (3D clay set) =================
   Decorative, art-directed placement of the clay-illustration set: the
   launch-pad hero scene on index.html, small header accents everywhere
   else in the write set (explore/leaderboard/viral/portfolio/create).
   Rules: never over interactive controls or data, pointer-events:none,
   z-index kept below .nav's 50, one shared contact shadow (the cutouts
   don't carry their own), entrance is opacity/transform only. */
:root{
  --fx-shadow:
    drop-shadow(0 16px 26px rgba(20,20,20,0.16))
    drop-shadow(0 3px 8px rgba(20,20,20,0.08));
}
html[data-theme="dark"]{
  --fx-shadow:
    drop-shadow(0 18px 30px rgba(0,0,0,0.55))
    drop-shadow(0 3px 10px rgba(0,0,0,0.35));
}

@keyframes fxRise{
  from{opacity:0;transform:translateY(10px) scale(.97);}
  to{opacity:1;transform:translateY(0) scale(1);}
}
/* idle float, layered on top of fxRise's one-time entrance — lives on the
   IMAGE/model, one level inside the wrapper that carries fxRise, so the
   two transform animations never fight over the same property on the
   same element (CSS animations don't compose across elements, the later
   one just wins if both sat on one node). Symmetric ease-in/out (no
   bounce, no linear robotic feel) since this loops forever and true
   exponential-out would snap hard back to start every cycle. */
@keyframes fxFloat{
  0%,100%{transform:translateY(0);}
  50%{transform:translateY(-7px);}
}

/* ---- DEFECT 1 (owner ~1050px screenshot, foreman ticket): the hero art
   and the small header accents were plain painted content — a click-drag
   over them selected them like text and covered the whole illustration
   in the browser's blue selection highlight, and they could be dragged
   like any other image. None of this is real, selectable content.
   pointer-events:none already sits on the wrapper of both (.hero-art,
   .fx-accent) so neither ever received the drag/select gesture in the
   first place, but user-select/drag are locked down explicitly here too
   — on the wrappers AND their img/model-viewer children — rather than
   relying only on inherited hit-testing. model-viewer's own auto-rotate
   is a time-based internal render loop, not pointer-driven, so it
   keeps spinning fine with pointer-events:none on it (confirmed at
   768px+ in the rendered checks below). ---- */
.hero-art,.hero-art-static,.fx-accent,
.hero-art img,.hero-art-3d,.hero-art-static,.fx-accent img{
  -webkit-user-select:none;-moz-user-select:none;user-select:none;
  -webkit-user-drag:none;
  pointer-events:none;
}

/* ---- hero scene, index.html only.
   DEFECT 2 (owner ~1050px screenshot, foreman ticket): .hero-art used to
   be a sibling of .hero-inner, placed AFTER the whole hero copy block in
   the DOM — so at every width below the 1240px absolute-positioning
   breakpoint it rendered in normal flow after .hero-stats too: a large
   image floating between the stats row and "What's viral", disconnected
   from the hero copy it's meant to sit with. Fixed at the markup level,
   not just here: .hero-art now lives inside .hero-inner, between
   .hero-cta and .hero-stats (see index.html) — .hero-inner has no
   `position` of its own, so it still isn't the containing block for the
   >=1240px absolute rule below, which keeps resolving against .hero
   exactly as before. Under 1240px the art is now simply in flow right
   under the CTA row, before the stats, at every width — never after
   them. Sized down from 380px/260px to 320px/240px (max-height capped
   too) so it reads as "with the hero copy", not a big detached block,
   matching the mockup's art-lives-with-the-hero placement (mockups/
   Website mockup/home page.png). True side-by-side (art beside the
   copy) is reserved for >=1240px below, where there's confirmed room to
   clear the text without touching the (out-of-scope, untouched) 76px h1
   that runs from 769px up to that breakpoint.
   FOREMAN TICKET (nav+hero rebuild), superseding the note above: owner
   rejected the stacked-under-CTA placement twice ("whats the point its
   not aesthetic"). Two fixes now layer on top of DEFECT 2's grid-area
   move: (a) 640-1239px is a true two-column layout (.hero-inner grid,
   above) — width here comes from the --hero-art-w custom property each
   tier sets, so .hero-art fills its grid column edge-to-edge instead of
   carrying its own max-width; (b) below 640px .hero-art is a compact
   right-aligned banner ABOVE the headline (grid-area order puts it
   between eyebrow and h1, never after the CTA/stats), sized to the
   200-230px band the ticket calls for, bleeding off the wrap's own
   24px edge padding at this width (margin-right cancels it exactly —
   body/html carry overflow-x:clip sitewide, see that rule's own
   comment, so this bleed can never create horizontal scroll). ---- */
.hero-art{
  position:relative;
  width:210px;
  margin:0 -24px 22px auto;
  display:flex;
  justify-content:center;
  z-index:1;
  animation:fxRise .7s cubic-bezier(0.16,1,0.3,1) .08s both;
}
.hero-art img,.hero-art-3d,.hero-art-static{
  display:block;width:100%;height:auto;
  filter:var(--fx-shadow);
  animation:fxFloat 7s cubic-bezier(0.65,0,0.35,1) infinite;
}
/* the 3D <model-viewer> (PART 2) vs the static poster fallback: the GLB
   streams fine once there's tablet/desktop headroom, but a 3.5MB mesh
   plus its own render loop on top of the cursor-painted candle canvas
   this page already runs is exactly the kind of double render budget
   that janks first on the weakest phones — static webp only below
   768px, model-viewer at 768px and up. */
.hero-art-3d{
  display:none;
  aspect-ratio:1080/956;
  background:transparent;
  --poster-color:transparent;
}
.hero-art-static{display:block;}
@media (min-width:768px){
  .hero-art-3d{display:block;}
  .hero-art-static{display:none;}
}
/* 640-1239px two-column tier: art fills its grid column (--hero-art-w,
   set per breakpoint on .hero-inner above) instead of the <640 fixed
   210px banner width, and drops the banner's right-bleed/float since
   it's centered in its own column now, not overlapping the wrap edge. */
@media (min-width:640px) and (max-width:1239px){
  .hero-art{width:100%;margin:0;animation-delay:.15s;}
}
@media (min-width:1240px){
  .hero-art{
    position:absolute;top:54px;right:-6px;
    width:500px;margin:0;
    display:block;
    animation-delay:.15s;
  }
}
@media (min-width:1440px){ .hero-art{width:560px;top:44px;} }

/* ---- small header accents shared by explore/leaderboard/viral/
   portfolio/create.
   FIX (same ticket, same bug): this was display:none below 901px — same
   all-formats blank-art problem as .hero-art above. Visible at every
   width now; scaled down through a tablet tier below, then handled per
   page at phone width since the header shapes aren't all the same (a
   flex .section-head that can wrap vs. an absolute overlay on a
   narrower-than-desktop text block) — decided by actually rendering each
   page at 768/390, not a blanket rule. ---- */
.fx-accent{
  display:block;
  pointer-events:none;
  z-index:1;
  animation:fxRise .6s cubic-bezier(0.16,1,0.3,1) both;
}
.fx-accent img{
  display:block;width:100%;height:auto;
  filter:var(--fx-shadow);
  animation:fxFloat 6.5s cubic-bezier(0.65,0,0.35,1) infinite;
}

/* explore/leaderboard/portfolio host their accent inside the shared
   flex .section-head. Kept as a real (non-absolute) flex item, not
   position:absolute, so the row's flexbox math reserves the horizontal
   space for it — and, just as usefully, so a width squeeze naturally
   WRAPS the accent onto its own line under the heading instead of
   overlapping it (flex-wrap:wrap on .section-head already does this for
   free). The title+sub block also gets a capped max-width (via
   .sh-text) at the same breakpoint the accent sits beside it — without
   it, the wrap just drops the accent below instead of sitting beside
   the text, since the text block has no width limit of its own to
   yield room. section-sub length varies per page (leaderboard's is two
   lines), so this has to be automatic, not eyeballed per page. */
.section-head .fx-accent{flex:none;align-self:flex-start;}
.fx-accent.fx-explore{width:140px;}
.fx-accent.fx-leaderboard{width:130px;}
.fx-accent.fx-portfolio{width:150px;}
@media (min-width:901px){
  .section-head .sh-text{max-width:calc(100% - 180px);}
}
/* tablet: still beside/above the heading, sized down (~90-110px per the
   ticket) so it doesn't dominate the narrower row. */
@media (max-width:900px){
  .fx-accent.fx-explore,.fx-accent.fx-leaderboard,.fx-accent.fx-portfolio{width:104px;margin:0 0 10px auto;}
}
/* phone: rendered and checked at 390px on all three pages — the accent
   wraps onto its own line above the section-sub copy on each of them
   with clear space either side, so all three keep a small version
   rather than hiding it. */
@media (max-width:640px){
  .fx-accent.fx-explore,.fx-accent.fx-leaderboard,.fx-accent.fx-portfolio{width:80px;}
}

/* viral's header and create's header aren't flex rows — viral's copy is
   capped at max-width:760px and create's is a short centered line, both
   well short of the header's full width AT DESKTOP, so the overlaid
   absolute accent never reaches the text there. Checked at 768px: the
   accent still clears the text at a smaller size, so it stays absolute,
   just scaled down. Checked at 390px: below 640px the header copy IS
   the full width (no cap applies at that narrow a viewport), so the
   same absolute top-right overlay would sit on top of the title/
   paragraph — switched to static, small, and centered below the copy
   at that tier instead, since both pages render with clear space under
   the text for it. */
.fx-accent.fx-viral,.fx-accent.fx-create{position:absolute;}
.fx-accent.fx-viral{top:-2px;right:0;width:130px;}
.fx-accent.fx-create{top:-16px;right:0;width:150px;}
@media (max-width:900px){
  .fx-accent.fx-viral{width:100px;}
  .fx-accent.fx-create{width:110px;}
}
@media (max-width:640px){
  .fx-accent.fx-viral,.fx-accent.fx-create{
    position:static;
    width:80px;
    margin:14px auto 0;
  }
}

/* ================= MOTION: hero text/CTA entrance (index.html only —
   these classes only co-occur inside .hero-inner there) ================= */
@keyframes heroEnter{
  from{opacity:0;transform:translateY(14px);}
  to{opacity:1;transform:translateY(0);}
}
.hero-inner .eyebrow,.hero-inner h1,.hero-inner .hero-sub,.hero-inner .hero-cta{
  animation:heroEnter .6s cubic-bezier(0.16,1,0.3,1) both;
}
.hero-inner .eyebrow{animation-delay:.05s;}
.hero-inner h1{animation-delay:.15s;}
.hero-inner .hero-sub{animation-delay:.25s;}
.hero-inner .hero-cta{animation-delay:.35s;}

/* ================= MOTION: hover lift added only where a card/button
   didn't already have one (.coin-card/.card/.viral-card/.btn-primary/
   .btn-connect all had their own lift already — untouched) ================= */
.btn-ghost:hover{transform:translateY(-2px);box-shadow:0 8px 20px rgba(20,20,20,0.08);}
html[data-theme="dark"] .btn-ghost:hover{box-shadow:0 8px 20px rgba(0,0,0,0.3);}
.step{transition:transform .18s cubic-bezier(0.16,1,0.3,1),box-shadow .18s ease,border-color .18s ease;}
.step:hover{transform:translateY(-3px);box-shadow:0 10px 26px rgba(20,20,20,0.08);border-color:#D6D6D1;}
html[data-theme="dark"] .step:hover{box-shadow:0 10px 26px rgba(0,0,0,0.35);}
.trust-card{transition:transform .18s cubic-bezier(0.16,1,0.3,1),box-shadow .18s ease,border-color .18s ease;}
.trust-card:hover{transform:translateY(-3px);box-shadow:0 10px 26px rgba(20,20,20,0.08);border-color:#D6D6D1;}
html[data-theme="dark"] .trust-card:hover{box-shadow:0 10px 26px rgba(0,0,0,0.35);}

@media (prefers-reduced-motion:reduce){
  .hero-art,.hero-art img,.hero-art-3d,.hero-art-static,.fx-accent,.fx-accent img{animation:none;}
  .hero-inner .eyebrow,.hero-inner h1,.hero-inner .hero-sub,.hero-inner .hero-cta{animation:none;}
  .btn-ghost:hover,.step:hover,.trust-card:hover{transform:none;}
}
