  :root {
    --void: #040912;
    --bg: #07101f;
    --panel: #0b1830;
    --line: #16263f;
    --line-2: #21354f;
    --ink: #eaf6ff;
    /* Secondary/fine-print tiers. Contrast against --panel (the worst of the
       two surfaces) is the sizing constraint: the ladder runs ink 16.1 :1 ->
       muted 7.2 -> dim 5.9, so even the 12px fine print clears WCAG AA with
       room. The pre-2026-08-18 values (#7f93ad / #56688a) put --dim at 3.2 :1,
       below AA — legible on a desktop panel, not on a phone in daylight. */
    --muted: #97a7bc;
    --dim: #8595b3;
    --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(--dim); }
  .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; }
  /* Full-bleed to the viewport: the globe fills to the right screen edge rather than
     stopping at the 1280px .page container (dead margin on wide monitors). .hero is
     centered in the viewport (centered .page + symmetric padding), so left:50% lands on
     the viewport centre and -50vw pulls the box out to span the whole width. The .copy
     stays inside the container; only this absolute stage breaks out. body has
     overflow-x:hidden so 100vw can't spawn a horizontal scrollbar. */
  .stage { position: absolute; top: -78px; bottom: 0; left: 50%; width: 100vw; margin-left: -50vw;
    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; }
  /* ===== waitlist ===== */
  /* Reads as another instrument on the same panel as the .stat strip: hairline
     border on --panel, mono type in the field, aqua focus glow borrowed from the
     pin spikes. No new colors — every value below is an existing token. */
  .waitlist { margin-top: 30px; max-width: 440px; }
  .wl-label { position:absolute; width:1px; height:1px; padding:0; overflow:hidden;
    clip-path: inset(50%); white-space: nowrap; }
  .wl-row { display:flex; gap: 8px; align-items: stretch; }
  /* Aqua outline at rest rather than --line-2: on --panel the structural line
     is nearly invisible on a phone in daylight, so the one thing the page is
     actually asking for read as decoration. Focus keeps its own brighter
     treatment (ring + glow) so the two states stay distinguishable. */
  .wl-input {
    flex: 1; min-width: 0; height: 58px; padding: 0 16px;
    background: var(--panel); color: var(--ink);
    border: 1px solid var(--aqua); border-radius: 12px;
    font-size: 15px; letter-spacing: 0.01em;
    transition: border-color .15s, box-shadow .15s;
  }
  /* The optional name field stands alone above the row, so it needs its own
     width — .wl-input's flex:1 only does anything inside .wl-row. */
  .wl-input--name { display: block; width: 100%; margin-bottom: 8px; }
  /* Email row sits between name and the framed ask rows (the .wl-row wrapper
     also scopes the error state to the address alone). */
  .wl-row { margin-bottom: 8px; }
  /* --ink is the page's white. The default placeholder grey didn't read against
     --panel, and these two strings are the form's only instructions. `opacity:1`
     is load-bearing, not redundant: Firefox dims ::placeholder by default, which
     would put it back to roughly the grey this replaces. */
  .wl-input::placeholder { color: var(--ink); opacity: 1; }
  /* Beta yes/no + iPhone/Android rows: each is a framed row in the INPUT's own
     frame (58px, --panel, aqua hairline, 12px radius) — label left in the
     field's 15px ink, segmented pills right. Same prominence as name/email by
     design (2026-08-18): the ask used to be 13px muted text sharing the
     submit's row and read as fine print. Radios stay in the tree for
     keyboard/AT but paint as pills via their labels. */
  .wl-ask { display: flex; align-items: center; justify-content: space-between;
    gap: 12px; height: 58px; padding: 0 8px 0 16px; margin-bottom: 8px;
    background: var(--panel); border: 1px solid var(--aqua); border-radius: 12px; }
  .wl-ask[hidden] { display: none; }
  .wl-ask-label { font-size: 15px; letter-spacing: 0.01em; color: var(--ink); }
  /* Pill choices: quiet at rest, the checked one gets an aqua-tinted fill,
     aqua hairline and bold ink. Deliberately NOT solid aqua — the submit stays
     the only solid-aqua object so the eye still lands there last. Padding
     keeps a finger-sized hit area (the invisible input fills the label, so
     the whole pill IS the radio). */
  .wl-seg { display: flex; gap: 6px; }
  .wl-seg label { position: relative; display: inline-flex; align-items: center;
    height: 40px; padding: 0 16px; border-radius: 9px; font-size: 14px;
    color: var(--muted); border: 1px solid transparent; cursor: pointer;
    transition: color .15s, background-color .15s, border-color .15s; }
  .wl-seg label:hover { color: var(--ink); }
  .wl-seg input { position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; margin: 0; cursor: pointer; }
  .wl-seg span { display: inline-block; text-align: center; }
  /* Hidden bold ghost of the same word (data-t) reserves the bold width so
     toggling the selection never shifts the row. */
  .wl-seg span::after { content: attr(data-t); display: block; height: 0;
    overflow: hidden; font-weight: 700; visibility: hidden; }
  .wl-seg label:has(input:checked) { color: var(--ink);
    background: rgba(53,224,208,0.14); border-color: var(--aqua); }
  .wl-seg label:has(input:checked) span { font-weight: 700; }
  .wl-seg label:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }
  .wl-input:focus { outline: none; border-color: var(--aqua); box-shadow: 0 0 0 1px var(--aqua), 0 0 18px rgba(53,224,208,0.22); }
  /* Submit sits on its own full-width row under the asks. */
  .wl-btn {
    display: block; width: 100%; height: 58px; padding: 0 22px;
    background: var(--aqua); color: #042; border: 1px solid var(--aqua); border-radius: 12px;
    font-family: inherit; font-size: 15px; font-weight: 600; letter-spacing: 0.01em;
    cursor: pointer; transition: filter .15s, transform .15s;
  }
  /* Install link wears the submit's shape so the two lanes read as one
     control stack, but it is an <a>: line-height, not flex centring, keeps
     the label on the 58px baseline without changing .wl-btn's box. */
  .wl-install { line-height: 58px; text-align: center; text-decoration: none; }
  .wl-install-note { margin: 10px 0 0; font-size: 13px; color: var(--dim); }
  /* "or leave your email": a hairline through the column with the label
     knocked out of it — the two lanes are alternatives, not a sequence. */
  .wl-or { display: flex; align-items: center; gap: 12px; margin: 18px 0 14px;
    font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); }
  .wl-or::before, .wl-or::after { content: ""; flex: 1; height: 1px; background: var(--line-2); }
  .wl-btn:hover { filter: brightness(1.08); }
  .wl-btn:active { transform: translateY(1px); }
  .wl-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
  .wl-btn[disabled] { opacity: .55; cursor: default; filter: none; transform: none; }
  /* Secondary submit: the install link owns solid aqua, so the form's button
     keeps the same frame the inputs wear. Declared after .wl-btn:hover so the
     brightness lift (which does nothing to a transparent fill) is replaced
     rather than layered. */
  .wl-btn--ghost { background: transparent; color: var(--ink); border-color: var(--aqua); }
  .wl-btn--ghost:hover { background: rgba(53,224,208,0.14); filter: none; }
  .wl-note { margin-top: 10px; font-size: 13px; color: var(--dim); }
  /* error state reuses the field's own border rather than adding an icon —
     scoped to the row so a bad address never reddens the optional name box,
     which cannot itself be invalid */
  .waitlist.is-error .wl-row .wl-input { border-color: var(--red); box-shadow: 0 0 0 1px var(--red); }
  .waitlist.is-error .wl-note { color: var(--red); }
  /* Success speaks the top bar's language: same dot geometry as .stat .live,
     but steady — the pulse means "streaming", and this is a settled fact. */
  .wl-done { display:flex; align-items: flex-start; gap: 10px; min-height: 48px; }
  .wl-done i { flex:none; width: 7px; height: 7px; margin-top: 8px; border-radius: 50%;
    background: var(--aqua); box-shadow: 0 0 9px var(--aqua); }
  .wl-done b { display:block; font-weight: 600; font-size: 17px; color: var(--ink); }
  /* The address line is the actionable half of the confirmation ("check X
     for your invite") — it carries the greeting's brightness, not the
     footnote register: --dim at 13px on the dark hero didn't read. */
  .wl-done span { display:block; margin-top: 3px; font-size: 15px; color: var(--ink);
    overflow-wrap: anywhere; }

  /* Legal links under the waitlist — quiet, terminal-footnote register. */
  /* ===== footer: Privacy · Terms, pinned to the page bottom ===== */
  .legal { position: relative; z-index: 5; flex: none; padding: 10px 0 max(14px, env(safe-area-inset-bottom));
    font-size: 12px; color: var(--dim); }
  .legal a { color: var(--muted); text-decoration: none; }
  .legal a:hover { text-decoration: underline; }
  .legal .sep { margin: 0 8px; color: var(--dim); }

  /* 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; }
  /* The down beam's old pale core (255,198,204) washed the whole spike toward
     white-orange against the night map — it read as amber, not red, which made
     the down/no-move tiers hard to tell apart (TestFlight ANNF). Deeper body,
     hot core kept in the red family. */
  .minibeam.dn   { --fx: 255,48,64;   --fxl: 255,140,150; }            /* --red   #ff3040 */
  .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; }
  /* elbow leader line drawn from the pin out to the flag (desktop only — the JS gates the
     draw on the wide layout; on narrow the globe tucks behind the copy and a tail would
     cross the text). */
  .leader { position:absolute; inset:0; width:100%; height:100%; overflow:visible; pointer-events:none; }
  /* no drop-shadow on anything repositioned per frame (leader/spikes/reticle) — the blur outruns
     the browser's damage rect while the globe rotates and smears ghost trails across the hero */
  .leader .lpath, .leader.macro .lpath { fill:none; stroke:rgba(64,236,142,0.95); stroke-width:1.5; stroke-linejoin:round; stroke-linecap:round; }
  .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; }
  /* no nowrap: macro pins put the (≤30-char) story slug here, wider than the 226px flag —
     it must wrap inside the card, not run off the border. Tickers/entity keys never wrap anyway. */
  .callout .co-tk { font-weight: 700; font-size: 16px; letter-spacing: 0.01em; line-height: 1.2; 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 tail switches on cleanly at that instant (desktop; the
     fluorescent-tube flicker it used to do was cut 2026-08-18, owner call — don't
     re-add it); the flag unrolls rightward from its left border. */
  .leader.reveal { animation: tail-on .5s step-end backwards; }
  .callout.flick { animation: unroll .5s cubic-bezier(.2,.75,.3,1) 0.5s backwards; }
  /* step-end + backwards: held invisible for the 0.5s, then fully on — one edge, no ramp */
  @keyframes tail-on { from{ opacity:0; } to{ opacity:1; } }
  /* 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; }
  /* macro slugs land here too — cap + ellipsize so a long slug can't squeeze the headline out of the row */
  .af-row .af-tk { font-weight: 700; font-size: 12.5px; color: #5fe8a4; white-space: nowrap; max-width: 50%; overflow: hidden; text-overflow: ellipsis; }
  .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; }
    .wl-input, .wl-btn { transition: none; }
    .wl-btn:active { transform: none; }
  }

  @media (max-width: 920px) {
    /* Cap the page at one viewport so the stacked layout never scrolls: the hero
       fills the leftover space and the stage (below) flexes into it instead of
       claiming a fixed 68vh. overflow:hidden absorbs any sub-pixel rounding. */
    .page { padding: 0 22px; height: 100dvh; overflow: hidden; }
    .hero { flex-direction: column; align-items: stretch; min-height: 0; }
    /* copy (and the waitlist form) 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 + form. .copy
       has no background — only the glyphs and the form 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; }
    /* the lede is desktop flavor text — on the stacked mobile layout it just
       crowds the h1 and pushes the globe down, so drop it. */
    .lede { display: none; }
    .waitlist { max-width: 100%; margin-top: 22px; }
    /* -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; width: auto; margin: -150px -22px 0; flex: 1 1 auto; min-height: 0;
      z-index: 0; }                    /* own stacking context: keeps globe/scrim/callouts sorting internal */
    /* width:auto + inset:auto undo the desktop full-bleed (100vw / left:50% / -50vw margin) so the
       stacked layout keeps its container-width globe; the margin shorthand already resets margin-left. */
    #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%); }
    /* Mobile shows a single lock-on flag, pinned to the bottom of the screen —
       the accumulating list is dropped here (one flag reads cleaner on a phone;
       the list stays the desktop treatment). The JS still updates the flag's
       content per lock-on and its opacity (fades in only while a story is in
       range); we override just its position — hence !important over the inline
       left/top it writes every frame — to park it bottom-centre instead of
       floating beside the pin. */
    .alertfeed { display: none; }
    .callout {
      left: 50% !important;
      right: auto !important;
      top: auto !important;
      bottom: max(18px, env(safe-area-inset-bottom)) !important;
      transform: translateX(-50%);
    }
    /* Smaller header on mobile: the brand + LIVE/clock share one narrow line, so
       scale the type (and the dot/gaps/padding with it) so it reads as a compact
       status strip rather than crowding the top edge. */
    .bar { padding: 14px 0; gap: 12px; }
    .brand { font-size: 16px; gap: 8px; }
    .brand .o { width: 10px; height: 10px; }
    .stat { font-size: 10.5px; gap: 9px; }
    .stat .live i { width: 6px; height: 6px; }
    /* Centred under the bottom-pinned callout flag; tight padding so it costs
       the globe as little height as possible on a one-viewport page. */
    .legal { text-align: center; font-size: 11px; padding: 6px 0 max(10px, env(safe-area-inset-bottom)); }
  }

  @media (max-width: 560px) {
    /* Phone-width globe flags: two content lines — the headline and a single
       ticker chip (the pct row restyled as a pill). Drop the ticker/tag head
       row and the timestamp; the DOM order (co-line → co-foot) already reads
       headline then chip once the head is gone. The flag spans nearly the full
       phone width (90vw, bottom-pinned + centred) so the headline has room to
       breathe instead of wrapping to four lines in a narrow 172px column. */
    .callout { width: 90vw; padding: 9px 11px 10px; }
    .callout .co-head, .callout .co-time { display: none; }
    .callout .co-line { font-size: 12.5px; }
    .callout .co-foot { margin-top: 7px; }
    .callout .co-pct {
      font-size: 11px; padding: 2px 8px; border-radius: 999px;
      border: 1px solid currentColor;   /* up=green / dn=red / src=muted, matching the pct colour */
    }
  }
