  :root {
    --void: #040912;
    --bg: #07101f;
    --panel: #0b1830;
    --line: #16263f;
    --line-2: #21354f;
    --ink: #eaf6ff;
    --muted: #7f93ad;
    --dim: #56688a;
    --aqua: #35e0d0;
    --amber: #ffc24d;
    --green: #36e07f;
    --red: #ff5d6c;
  }
  * { margin:0; padding:0; box-sizing: border-box; }
  ::selection { background: var(--aqua); color: #042; }
  html, body { height: 100%; }
  body {
    background: var(--bg);
    color: var(--ink);
    font-family: 'Chakra Petch', sans-serif;
    font-size: 16px; line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
  }
  .mono { font-family:'Red Hat Mono', monospace; }
  .up { color: var(--green); } .dn { color: var(--red); } .src { color: #6fb89a; }

  .page { min-height: 100dvh; display:flex; flex-direction: column; max-width: 1280px; margin: 0 auto; padding: 0 34px; }

  /* ===== top bar ===== */
  .bar { display:flex; align-items:center; justify-content: space-between; gap: 16px; padding: 18px 0; position: relative; z-index: 5; }
  .brand { display:flex; align-items:center; gap: 10px; font-weight: 700; font-size: 19px; letter-spacing: 0.01em; text-decoration:none; color: inherit; }
  .brand .o { width: 12px; height: 12px; border-radius: 50%; background: var(--aqua); box-shadow: 0 0 14px var(--aqua); }
  .stat { display:flex; align-items:center; gap: 12px; font-size: 12px; letter-spacing: 0.04em; }
  .stat .sep { color: var(--line-2); }
  .stat .live { display:inline-flex; align-items:center; gap: 7px; color: var(--green); }
  .stat .live i { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 9px var(--green); animation: pulse 1.6s infinite; }
  .stat .clock { color: var(--muted); }
  @keyframes pulse { 50% { opacity: .25; } }

  /* ===== hero: copy left, globe + callouts filling the right ===== */
  .hero { position: relative; flex: 1; display: flex; align-items: center; min-height: 620px; }
  .stage { position: absolute; inset: -78px -34px 0; overflow: hidden; pointer-events: none; }
  /* top/bottom shift the layer down in the stage (height stays 138%) — the globe sits
     lower so the northern-hemisphere camera doesn't push the populated latitudes into
     the top edge, and the horizon reads under the headline rather than through it. */
  #globeViz { position: absolute; left: 28%; right: -14%; top: -2%; bottom: -36%;
    /* stacking context: globe.gl's CSS2DRenderer depth-sorts the .beam-anchor markers by stamping inline
       z-index 1..N (N = pin count) every frame — uncontained, those out-rank .callouts(4) and the spikes
       paint over the flag. z-index:1 here keeps that sort internal to the globe layer. */
    z-index: 1;
    /* No backdrop of its own: the layer is transparent and the page --bg shows through.
       (A 4096x2048 starfield PNG used to sit here — 882K, the single heaviest asset on
       the page, for a field of dots behind a globe. Dropped.) */
    /* fade the layer's own left edge so the globe canvas dissolves into the copy
       rather than ending on a hard vertical edge under the half-faded scrim */
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 26%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 26%); }
  #globeViz canvas { display:block; }
  .scrim { position:absolute; inset: 0; z-index: 2;
    background: linear-gradient(100deg, var(--bg) 8%, rgba(7,16,31,0.86) 30%, rgba(7,16,31,0.12) 52%, transparent 68%); }
  .stage::after { content:""; position:absolute; inset:0; z-index: 2;
    background: linear-gradient(0deg, var(--bg) 1%, transparent 14%), linear-gradient(180deg, var(--bg) 0.5%, transparent 10%); }

  .copy { position: relative; z-index: 3; max-width: 560px; padding: 24px 0; }
  h1 { font-weight: 700; font-size: clamp(38px, 4.8vw, 62px); line-height: 1.02; letter-spacing: -0.01em; }
  h1 .a { color: var(--aqua); }
  h1 .am { color: var(--amber); text-shadow: 0 0 28px rgba(255,194,77,0.4); }
  .lede { font-size: 17px; color: var(--muted); max-width: 44ch; margin-top: 20px; }
  /* Apple's official badge artwork (static/appstore-badge.svg) — per the
     marketing guidelines it ships as-is: no recolor, no hover restyle. */
  .appstore-badge { display:inline-block; margin-top: 30px; transition: transform .15s; }
  .appstore-badge:hover { transform: translateY(-2px); }
  .appstore-badge:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
  .appstore-badge img { display:block; height: 54px; width: auto; }

  /* one small light spike at every front-facing pin — globe.gl positions the 0×0
     .beam-anchor on the pin (it owns the anchor's transform); the spike rises from it.
     No drop-shadow/filter on per-frame-moved elements (blur outruns damage rects → ghost trails). */
  .beam-anchor { position:absolute; width:0; height:0; pointer-events:none; }
  /* Beam colour = the ticker's day move, so the globe reads at a glance: green up, red
     down, amber when there's no applicable move (macro stories, entities with no price).
     --fx is the body colour, --fxl the hot core at the base. The JS stamps .up/.dn/.flat
     from the same `dir` the callout's pct already uses — one source of truth for the sign. */
  .minibeam { --fx: 54,224,127; --fxl: 185,255,216;                    /* default = up/green */
    position:absolute; left:0; top:0; width:3px; height:32px; pointer-events:none; will-change:transform;
    transform: translate(-50%,-100%); transform-origin:50% 100%;
    background: linear-gradient(to top, rgba(var(--fxl),0.8), rgba(var(--fx),0.42) 46%, rgba(var(--fx),0) 100%);
    border-radius:2px 2px 0 0; }
  .minibeam.dn   { --fx: 255,93,108;  --fxl: 255,198,204; }            /* --red   #ff5d6c */
  .minibeam.flat { --fx: 255,194,77;  --fxl: 255,232,183; }            /* --amber #ffc24d */
  .minibeam::after { content:""; position:absolute; left:50%; bottom:0; transform:translate(-50%,40%);
    width:8px; height:8px; border-radius:50%;
    background: radial-gradient(circle, rgba(var(--fxl),0.92), rgba(var(--fx),0.45) 48%, transparent 72%); }

  /* ===== callout overlay (single, follows the front-facing story) ===== */
  .callouts { position:absolute; inset: 0; z-index: 4; pointer-events:none; }
  /* no drop-shadow on anything repositioned per frame (spikes/reticle) — the blur outruns
     the browser's damage rect while the globe rotates and smears ghost trails across the hero */
  .callout {
    position:absolute; width: 226px; will-change: transform, opacity;
    z-index: 6;                                             /* frontmost — opaque flag covers the reticle (z5); spikes live inside #globeViz(1), under everything here */
    background: #06140d;                                    /* opaque HUD panel */
    border: 1px solid rgba(64,236,142,0.28); border-left: 3px solid rgba(64,236,142,0.95); border-radius: 3px; padding: 11px 13px 12px;
    box-shadow: 0 12px 30px -20px rgba(0,0,0,0.9);
  }
  .callout .co-head { display:flex; align-items:center; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; row-gap: 5px; }
  .callout .co-tk { font-weight: 700; font-size: 16px; letter-spacing: 0.01em; white-space: nowrap; color:#5fe8a4; }
  .callout .co-tag { font-family:'Red Hat Mono',monospace; font-size: 9px; letter-spacing: 0.07em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px; border: 1px solid currentColor; margin-left: auto; white-space: nowrap; }
  .co-tag.brk, .co-tag.sig, .co-tag.pre, .co-tag.mac { color: #46e08a; } .co-tag.mac { background: rgba(64,236,142,0.12); }
  .callout .co-line { font-size: 13.5px; line-height: 1.35; color: #d3efdf; }
  .callout .co-foot { display:flex; align-items:center; justify-content: space-between; margin-top: 9px; }
  .callout .co-pct { font-family:'Red Hat Mono',monospace; font-size: 13px; font-weight: 600; }
  .callout .co-time { font-family:'Red Hat Mono',monospace; font-size: 10px; color: #3a9c6c; letter-spacing: 0.03em; }
  /* live pins link through to their alert pages */
  a.callout { display:block; pointer-events:auto; text-decoration:none; cursor:pointer; }

  /* ===== sci-fi lock-on: corner-bracket snap (bright-green HUD reticle) ===== */
  .lockfx { position:absolute; width:0; height:0; pointer-events:none; z-index:5; --fx:64,236,142; }
  .lockfx.macro { --fx:64,236,142; }
  .lockfx .br { position:absolute; width:11px; height:11px; box-sizing:border-box; opacity:0; }
  .lockfx .br.nw { left:-20px; top:-20px; border-top:3px solid rgba(var(--fx),1); border-left:3px solid rgba(var(--fx),1); }
  .lockfx .br.ne { left:  9px; top:-20px; border-top:3px solid rgba(var(--fx),1); border-right:3px solid rgba(var(--fx),1); }
  .lockfx .br.sw { left:-20px; top:  9px; border-bottom:3px solid rgba(var(--fx),1); border-left:3px solid rgba(var(--fx),1); }
  .lockfx .br.se { left:  9px; top:  9px; border-bottom:3px solid rgba(var(--fx),1); border-right:3px solid rgba(var(--fx),1); }
  /* corners clamp in place from a few px out — no cross-screen streak (long glowing travel
     smeared trails over the globe) */
  /* The reticle is a lock-ON effect, not a state: corners snap in, hold long enough to
     read as a lock, then fade. Animating the container (not the .br corners) leaves the
     snap keyframes untouched — and an animation outranks the per-frame inline opacity
     the tracking loop writes, so the fade sticks without touching the JS. */
  .lockfx.run { animation: lf-fade 1.6s linear both; }
  @keyframes lf-fade { 0%, 56% { opacity:1; } 100% { opacity:0; } }
  .lockfx.run .br.nw { animation: br-nw .3s cubic-bezier(.78,0,.86,.2) 0.2s both; }
  .lockfx.run .br.ne { animation: br-ne .3s cubic-bezier(.78,0,.86,.2) 0.2s both; }
  .lockfx.run .br.sw { animation: br-sw .3s cubic-bezier(.78,0,.86,.2) 0.2s both; }
  .lockfx.run .br.se { animation: br-se .3s cubic-bezier(.78,0,.86,.2) 0.2s both; }
  @keyframes br-nw { 0%{ transform:translate(-26px,-26px); opacity:0; } 12%{ opacity:1; } 78%{ transform:translate(4px,4px); opacity:1; } 100%{ transform:translate(0,0); opacity:1; } }
  @keyframes br-ne { 0%{ transform:translate(26px,-26px); opacity:0; } 12%{ opacity:1; } 78%{ transform:translate(-4px,4px); opacity:1; } 100%{ transform:translate(0,0); opacity:1; } }
  @keyframes br-sw { 0%{ transform:translate(-26px,26px); opacity:0; } 12%{ opacity:1; } 78%{ transform:translate(4px,-4px); opacity:1; } 100%{ transform:translate(0,0); opacity:1; } }
  @keyframes br-se { 0%{ transform:translate(26px,26px); opacity:0; } 12%{ opacity:1; } 78%{ transform:translate(-4px,-4px); opacity:1; } 100%{ transform:translate(0,0); opacity:1; } }
  /* scan tick that flashes through the frame as it clamps */
  .lockfx .tick { position:absolute; left:-1.5px; top:-9px; width:3px; height:18px; transform-origin:50% 50%;
    background:linear-gradient(rgba(var(--fx),0), rgba(var(--fx),1), rgba(var(--fx),0)); opacity:0; }
  .lockfx.run .tick { animation: lf-tick .6s ease-out 0.4s; }
  @keyframes lf-tick { 0%{ transform:scaleY(0); opacity:0; } 55%{ transform:scaleY(1); opacity:.95; } 100%{ transform:scaleY(1); opacity:0; } }

  /* lock-on reveal fires at 0.5s — the moment the reticle corners clamp (0.2s delay + 0.3s snap),
     NOT when they launch. The flag unrolls rightward from its left border. */
  .callout.flick { animation: unroll .5s cubic-bezier(.2,.75,.3,1) 0.5s backwards; }
  /* left border stays the fixed spine; the panel reveals left→right (content undistorted) */
  @keyframes unroll { from{ clip-path: inset(0 100% 0 0); } to{ clip-path: inset(0 0 0 0); } }

  /* ===== live alert stack — recent flags settle here, head recycles ===== */
  .alertfeed { position:absolute; z-index: 4; right: 4%; width: min(360px, 42%); bottom: 16px; pointer-events:none; }
  .af-list { display:flex; flex-direction: column; gap: 6px; }
  .af-row { display:flex; align-items:center; gap: 9px; padding: 7px 11px; overflow:hidden;
    background: rgba(6,18,12,0.9); border: 1px solid rgba(64,236,142,0.28); border-left: 3px solid rgba(64,236,142,0.95); border-radius: 3px; }
  .af-row .af-tk { font-weight: 700; font-size: 12.5px; color: #5fe8a4; white-space: nowrap; }
  .af-row .af-line { flex:1; min-width:0; overflow:hidden; text-overflow: ellipsis; white-space: nowrap; font-family:'Red Hat Mono',monospace; font-size: 11px; color: #9fc7b1; }
  .af-row .af-pct { font-family:'Red Hat Mono',monospace; font-size: 11.5px; font-weight: 600; white-space: nowrap; }
  /* rows are links, animated via the JS FLIP morph from the flag position */
  a.af-row { pointer-events:auto; text-decoration:none; cursor:pointer; transition: border-color .15s; }
  a.af-row:hover { border-color: rgba(64,236,142,0.7); }

  @media (prefers-reduced-motion: reduce) {
    .stat .live i { animation: none; }
    .appstore-badge { transition: none; }
  }

  @media (max-width: 920px) {
    .page { padding: 0 22px; }
    .hero { flex-direction: column; align-items: stretch; min-height: 0; }
    /* copy (and the download button) first, globe below — the stage precedes it in the
       DOM only because desktop takes it out of flow */
    /* Copy paints ABOVE the globe (z-index over the stage's stacking context) so the
       stage's negative top margin can slide the globe up behind the lede + badge. .copy
       has no background — only the glyphs and the badge occlude, so the globe stays
       visible between them rather than being boxed out. */
    .copy { max-width: 100%; padding: 12px 0 8px; order: -1; position: relative; z-index: 1; }
    /* -150px, not -76px: the stage box is bigger than the sphere inside it, so a margin that
       merely overlaps the stage with the copy leaves the globe itself sitting below the badge.
       This lifts the sphere's crown up behind the badge. Moving the stage (not #globeViz's
       insets) keeps the canvas size — and therefore the globe's rendered radius — unchanged. */
    .stage { position: relative; inset: auto; margin: -150px -22px 0; height: 68vh; min-height: 500px;
      z-index: 0; }                    /* own stacking context: keeps globe/scrim/callouts sorting internal */
    #globeViz { left: -18%; right: -18%; top: -2%; bottom: -34%; -webkit-mask-image: none; mask-image: none; }
    /* Same job as the wide layout's scrim, rotated: wide fades the globe out to the LEFT
       behind the copy column, narrow fades it out UPWARD behind the stacked copy. Opaque
       at the very top, clear by mid-stage, so the overlap reads as the globe emerging from
       under the text instead of colliding with it. */
    .scrim { display: block;
      background: linear-gradient(180deg, var(--bg) 0%, rgba(7,16,31,0.90) 17%, rgba(7,16,31,0.46) 28%, rgba(7,16,31,0.14) 40%, transparent 52%); }
    /* bottom fade only — the scrim owns the top edge now (a second top gradient here
       would double-darken the overlap band and swallow the globe behind the copy) */
    .stage::after { background: linear-gradient(0deg, var(--bg) 1%, transparent 16%); }
    .alertfeed {
      right: 16px;
      left: 16px;
      bottom: max(18px, env(safe-area-inset-bottom));
      width: auto;
      max-width: 420px;
      margin-left: auto;
      margin-right: auto;
    }
    .af-row { gap: 8px; padding: 7px 10px; }
    .af-row .af-tk { font-size: 12px; }
    .af-row .af-line { font-size: 10.5px; }
    .af-row .af-pct { font-size: 11px; }
  }
