/* Tenshin Arts — marketing pages (home + games). Extracted from index.html so the
   home page and the games page share one stylesheet. Auth/app pages use app.css. */
  /* ── SLATE (2026-07-25) ────────────────────────────────────────────────────
     The warm ivory/coral system was replaced with a cool neutral + indigo one at
     Dr. Ray's pick, chosen from a five-way preview rather than argued about.

     The token NAMES changed with it, on purpose: they used to be `--coral` and
     `--teal`, and a token called coral that renders indigo is a trap for whoever
     reads this next. They are `--accent` / `--support` now, so the name says the
     JOB and the value says the hue.

     Measured on this paper, not assumed (WCAG AA needs 4.5 for body text):
       heading 16.2  ·  muted text 5.6  ·  links 6.9  ·  button label 6.9
     The bright pair (--accent / --support) stays display-only — large headings,
     fills, lamps, focus rings. Anything small or text-on-colour uses -deep. */
  :root{
    --paper:#F6F7F9;
    --paper-2:#ECEFF3;
    --paper-3:#F1F4F8;
    --ink:#171A21;
    --ink-soft:#5D6472;
    --accent:#5567E0;          /* display only */
    --support:#2A93A0;         /* display only */
    --warn:#B4741A;
    /* AA-contrast siblings: text-on-colour + small text on paper */
    --accent-deep:#3A49B8;     /* white 7.4:1 · paper 6.93 */
    --accent-lift:#93A0F5;     /* the ON-DARK sibling — ink-900 7.53:1 */
    --support-deep:#166B75;    /* white 6.5:1 · paper 6.1 */
    --line:rgba(23,26,33,.14);
    /* The two surfaces that were HARD-CODED #fff in a dozen rules until the dark
       switch landed (2026-08-02). A card that is literally white and a label that
       is literally white cannot follow a theme, and every one of them had to be
       found by eye. They are tokens now so the dark block below is the only place
       that ever restates them.
         --surface   a raised sheet on --paper (cards, plates, the wall)
         --on-accent text/icons sitting ON --accent-deep */
    --surface:#fff;
    --on-accent:#fff;
    /* THE INVERTED SURFACE — the footer slab and the dark bands. ⚠ These were
       `background:var(--ink)` + `color:var(--paper)`, which is correct exactly once:
       in light. Flip the theme and --ink IS the light value, so the footer became a
       white slab at the bottom of a dark page, and every text rule spelled that way on a
       band that stayed dark became dark-on-dark at 1.05:1 — invisible, not merely
       low. An inversion written as "use the opposite token" inverts WITH the theme;
       these surfaces are dark in both, so they need their own pair. */
    /* --warn is display-only per the note above, and it had no -deep sibling — so a
       status line that used it measured 3.85:1 on a white card. white 6.2:1. */
    --warn-deep:#8A5510;
    /* The DANGER family, which was eleven raw hexes across app.css and no token at
       all. --ember is the FILL and stays red in both themes (a delete button that
       goes pastel in dark stops reading as danger); --ember-deep is the text-on-paper
       sibling; --on-ember is the label ON the fill and is NOT --on-accent, which
       flips to near-black in dark and would have put dark text on a dark red button. */
    --ember-deep:#96301A;
    --on-ember:#fff;
    --invert:#171A21;
    --on-invert:#F6F7F9;
    --accent-hover:#2E3A94;   /* was typed raw into .cta:hover and .btn:hover */
    /* --- deepened tones for the living worlds --- */
    --ink-900:#12141A;
    --display:"Fraunces",Georgia,serif;
    --body:"Figtree",system-ui,sans-serif;
    --stage:1760px;   /* wide live stages use the whole monitor */
    --prose:1180px;   /* quieter narrative sections */
    --nav-h:72px;     /* sticky header — keep scroll offsets in sync */
    --sticky-bar-h:4.5rem; /* games filter bar (allow chip wrap) */
    --scroll-gap:1.25rem;  /* breathing room below stuck chrome */
    --scroll-offset:calc(var(--nav-h) + var(--sticky-bar-h) + var(--scroll-gap));
  }
  /* ── DARK (2026-08-02) ───────────────────────────────────────────────────
     The switch is TOKEN-LEVEL, not page-level. `data-theme="dark"` goes on
     <html> and every surface that already reads a token follows; there is no
     dark COPY of any rule below and there must never be one. A page that needs
     its own dark rules is a page with a hard-coded colour in it — fix the
     hard-code, do not add a second rule.

     ⚠ THIS BLOCK MUST STAY BYTE-IDENTICAL IN site.css AND app.css, exactly like
     the :root block above, and for a sharper reason: :root drifting means two
     pages look different, this drifting means one of them is UNREADABLE. That
     is how --accent-lift went missing from app.css and resolved to inherit.

     Not one new hue. Slate keeps its indigo; every value here is the ON-DARK
     sibling of a value measured above. --accent-deep becomes the --accent-lift
     that was already measured at 7.53:1 on --ink-900 — that swap IS the theme.

     Measured on --paper #171A21 (WCAG AA needs 4.5 for body text):
       heading 14.9  ·  muted text 6.2  ·  links 7.4  ·  button label 8.7

     ★ --paper SITS ABOVE --ink-900, never equal to it. This site already has
     dark bands (.realms-band, .wall, .hero, .cab-screen) built as translucent
     white on --ink-900. Set --paper to --ink-900 and every one of those edges
     disappears — the page goes flat black and the bands stop being bands. The
     floor is --ink-900, --paper is the first step up, --surface is the second. */
  :root[data-theme="dark"]{
    --paper:#171A21;
    --paper-2:#1C1F28;
    --paper-3:#20242E;
    --ink:#EEF0F5;
    --ink-soft:#9AA2B4;
    --accent-deep:#93A0F5;     /* the --accent-lift above, doing its actual job */
    --support-deep:#5FC6D2;
    --warn:#E0A33C;
    --line:rgba(255,255,255,.12);
    --surface:#1D2029;
    --warn-deep:#E0A33C;       /* on dark the display value already passes */
    --ember-deep:#F08A6B;      /* 7.5:1 on --paper; the fill and its label do not move */
    --on-ember:#fff;
    --invert:#12141A;          /* the floor, so the footer sits UNDER --paper */
    --on-invert:#EEF0F5;
    --on-accent:#12141A;       /* --accent-deep is now LIGHT: white on it fails */
    --accent-hover:#B6C0FA;    /* hover LIFTS on dark, it does not deepen */
  }
  *{box-sizing:border-box}
  html{scroll-behavior:smooth;scroll-padding-top:var(--scroll-offset)}
  @media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
  body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--body);
    font-size:1.0625rem;line-height:1.65;-webkit-font-smoothing:antialiased;overflow-x:clip;
    /* THE UNIT SILHOUETTE — rounded top, near-square base: the shape reads as a
       thing STANDING on the floor, not a pill floating on it. Lifted straight
       off `.cab` so the hall and the page around it share one silhouette.
       ⚠ Deliberately NOT in :root — that block must stay byte-identical to
       app.css, and the auth/app pages do not carry this vocabulary. */
    --r-unit:14px 14px 4px 4px}
  a{color:var(--accent-deep)}
  ::selection{background:var(--accent);color:#fff}
  img{max-width:100%}

  /* full-bleed band + inner shells */
  .band{width:100%;position:relative}
  .shell{width:100%;max-width:var(--stage);margin:0 auto;
    padding-left:clamp(1.25rem,4vw,3.5rem);padding-right:clamp(1.25rem,4vw,3.5rem)}
  .shell.prose{max-width:var(--prose)}

  /* Section labels wear the cabinet's COIN SLOT (`.cab-coin .slot`) — the same
     mark, so a section header and a machine's coin line read as one system.
     `currentColor` on purpose: the dark realms band recolours .eyebrow to
     --accent-lift and the slot follows without a second rule. */
  .eyebrow{font-family:var(--body);font-weight:600;font-size:.76rem;letter-spacing:.18em;
    text-transform:uppercase;color:var(--support-deep);margin:0 0 1rem;
    display:flex;align-items:center;gap:.7rem}
  /* Solid, not the topper's .55 — `.cab-coin .slot` is a solid bar, and .55 of a
     mid-teal on paper disappears. The light BAR is lit (translucent); the coin
     SLOT is milled metal (solid). Two devices, two treatments, on purpose. */
  .eyebrow::before{content:"";width:22px;height:3px;border-radius:2px;
    background:currentColor;flex:none}
  :focus-visible{outline:3px solid var(--support);outline-offset:2px}

  /* ── THE UNIT (site-wide, 2026-07-26) ──────────────────────────────────────
     The hall shipped a machine vocabulary and then stopped at the card grid:
     every other surface on the page was pillowy 16–18px paper, so the page ran
     two design languages at once. These are not new devices — they are the
     cabinet's own `.cab` silhouette and `.cab-top::after` light bar, promoted
     to site level and applied to the surfaces the hall pass didn't reach.

     ★ The rule that makes it structural, not decorative: a UNIT is a surface
     you can act on or read a standing from. Prose does not get a light bar.

     Decorative only — `::before` carries no content and every unit's text
     contrast is unchanged from what was measured on 2026-07-25. */
  .unit,.pillar,.name-card,.way,.realm{border-radius:var(--r-unit);
    position:relative;overflow:hidden}
  .unit::before,.pillar::before,.name-card::before,.way::before,.realm::before{
    content:"";position:absolute;left:1.1rem;right:1.1rem;top:0;height:3px;
    border-radius:0 0 2px 2px;background:var(--acc,var(--accent));opacity:.55;
    pointer-events:none}
  /* on the dark realms band the bright --accent goes muddy against --ink-900 */
  .realm::before{background:var(--accent-lift)}

  /* --- the broadcast/data voice: tracked small-caps figtree, tabular --- */
  .mono{font-family:var(--body);font-weight:700;letter-spacing:.14em;text-transform:uppercase;
    font-variant-numeric:tabular-nums;font-size:.72rem}

  .btn{display:inline-block;font-family:var(--body);font-weight:600;font-size:1rem;text-decoration:none;
    padding:.85rem 1.7rem;border-radius:12px;transition:transform .14s,background .2s,color .2s,border-color .2s}
  .btn:active{transform:translateY(1px)}
  .btn-accent{background:var(--accent-deep);color:var(--on-accent)}
  .btn-accent:hover,.btn-accent:focus-visible{background:var(--accent-hover)}
  .btn-line{border:1.5px solid var(--line);color:var(--ink)}
  .btn-line:hover,.btn-line:focus-visible{border-color:var(--ink)}

  /* header */
  header{position:sticky;top:0;z-index:60;background:var(--paper);
    border-bottom:1px solid var(--line)}
  .nav{display:flex;align-items:center;justify-content:space-between;height:var(--nav-h);
    max-width:var(--stage);margin:0 auto;padding-left:clamp(1.25rem,4vw,3.5rem);padding-right:clamp(1.25rem,4vw,3.5rem)}
  .brand{font-family:var(--display);font-weight:600;font-size:1.35rem;color:var(--ink);text-decoration:none;
    display:inline-flex;align-items:center;gap:.6rem}
  .brand .beacon{width:28px;height:28px;border-radius:4px;background:url('../static/images/beacon.png') center/contain no-repeat;flex:none;
    box-shadow:0 0 0 0 rgba(85,103,224,.45);animation:pulse 1.9s ease-out infinite;image-rendering:pixelated}
  /* ⚠ TAP TARGETS — WCAG 2.5.8 wants 24x24 (2026-07-26). An inline <a> with no padding has a hit
     box the size of its GLYPHS, so these read at ~18px however roomy the bar looks. Type size is
     UNCHANGED; only the hit box grows, and the .3rem it costs horizontally is taken straight back
     out of the flex `gap` (2 -> 1.7rem) so the spacing is identical to the pixel. The bar is a
     fixed 72px centred row, so the vertical growth is absorbed by the bar's own padding.
     `a.cta` keeps its own larger padding — the rule below it is more specific and still wins. */
  .nav .links{display:flex;align-items:center;gap:1.7rem}
  .nav .links a{display:inline-flex;align-items:center;min-height:24px;padding:.3rem .15rem;
    font-family:var(--body);font-weight:500;font-size:.95rem;text-decoration:none;color:var(--ink-soft);transition:color .2s}
  .nav .links a:hover{color:var(--ink)}
  /* `a.cta` and not `.cta` — the rule above is `.nav .links a` (0,2,1), so a bare
     `.nav .cta` (0,2,0) loses and the label falls back to muted ink ON the filled
     button, which is unreadable. Match the element too, or lose to the sibling. */
  .nav .links a.cta{background:var(--accent-deep);color:var(--on-accent);padding:.55rem 1.25rem;
    border-radius:12px;font-weight:600}
  .nav .links a.cta:hover{background:var(--accent-hover);color:var(--on-accent)}
  @media (max-width:860px){.nav .links a.hide-sm{display:none}}

  @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(85,103,224,.5)}70%{box-shadow:0 0 0 8px rgba(85,103,224,0)}
    100%{box-shadow:0 0 0 0 rgba(85,103,224,0)}}

  /* ============================================================= HERO */
  .hero{position:relative;overflow:hidden;padding-top:clamp(3rem,6vw,5rem);padding-bottom:clamp(2.5rem,5vw,4rem)}
  .hero>*{position:relative;z-index:1}
  .hero-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(1.5rem,4vw,3.5rem);align-items:end}
  .hero h1{font-family:var(--display);font-weight:600;line-height:.98;letter-spacing:-.02em;
    font-size:clamp(2.8rem,7vw,5.6rem);margin:.6rem 0 0;font-optical-sizing:auto}
  .hero h1 em{font-style:normal;font-weight:700;color:var(--accent)}
  .hero .sub{font-size:clamp(1.08rem,1.7vw,1.32rem);color:var(--ink-soft);max-width:46ch;margin:1.5rem 0 0}
  .hero .actions{display:flex;flex-wrap:wrap;gap:.9rem;margin-top:2rem}
  .hero-note{font-size:.92rem;color:var(--ink-soft);margin:1.15rem 0 0;max-width:52ch}

  /* hero control wall — four live channels previewing the stages below */
  .wall{border:1px solid var(--line);border-radius:var(--r-unit);background:var(--surface);overflow:hidden;
    box-shadow:0 20px 50px rgba(24,20,27,.10)}
  .wall-top{display:flex;align-items:center;justify-content:space-between;gap:.6rem;
    padding:.7rem 1rem;background:var(--ink-900);color:var(--on-invert)}
  .wall-top .mono{color:rgba(246,247,249,.72)}
  .wall-top .rec{display:inline-flex;align-items:center;gap:.5rem;color:#fff}
  .wall-top .rec i{width:8px;height:8px;border-radius:50%;background:var(--accent);
    box-shadow:0 0 0 0 rgba(85,103,224,.6);animation:pulse 1.6s ease-out infinite}
  /* three across: the wall carries 18 worlds, and two columns made it nine rows tall. */
  .channels{display:grid;grid-template-columns:repeat(3,1fr)}
  .channel{padding:1rem 1.1rem;border-top:1px solid var(--line);position:relative;
    text-decoration:none;color:var(--ink);display:block;transition:background .2s}
  .channel:not(:nth-child(3n)){border-right:1px solid var(--line)}
  .channel:hover{background:var(--paper-3)}
  .channel .cn{font-family:var(--display);font-weight:600;font-size:1.02rem;line-height:1.15;letter-spacing:-.01em}
  .channel .cs{display:flex;align-items:center;gap:.45rem;margin-top:.5rem}
  .channel .cs .dot{width:7px;height:7px;border-radius:50%;background:var(--acc,var(--accent));flex:none}
  .channel .cs .mono{color:var(--ink-soft)}
  /* tiny signal bars = liveness, decorative */
  /* static equaliser glyph — a liveness mark, not fake real-time telemetry */
  .bars{display:inline-flex;align-items:flex-end;gap:2px;height:12px;margin-left:auto}
  .bars i{width:3px;background:var(--acc,var(--accent));border-radius:1px;height:55%}
  .bars i:nth-child(1){height:35%}
  .bars i:nth-child(2){height:80%}
  .bars i:nth-child(3){height:55%}
  .bars i:nth-child(4){height:100%}
  .channel{position:relative;overflow:hidden}
  .channel>*{position:relative;z-index:1}
  .channel::before{content:"";position:absolute;inset:0;background:var(--thumb,none) center/cover no-repeat;opacity:.1;z-index:0;transition:opacity .2s}
  .channel:hover::before{opacity:.16}
  .channel.w-freight{--acc:#D9922B;--thumb:url('static/images/cards/freightmogul.png')}
  .channel.w-ashen{--acc:#E2663F;--thumb:url('static/images/cards/ashendragon.png')}
  .channel.w-union{--acc:#2E7D74}  .channel.w-fading{--acc:#3fb98f;--thumb:url('static/images/cards/fadingwilds.png')}
  .channel.w-vested{--acc:#2e6d4e} .channel.w-nullroute{--acc:#3457c9}
  .channel.w-progeny{--acc:#7a4bb8} .channel.w-epoch{--acc:#4A5A70;--thumb:url('static/images/cards/epochentropy.png')}
  /* the arcade titles */
  .channel.w-emberdown{--acc:#8E2749}
  /* These were on the wall with no accent — they fell back to --accent, so different
     worlds shared one indigo dot. Same hues as their cabinets below. */
  .channel.w-continuousyield{--acc:#1F7A3D}
  .channel.w-feedstock{--acc:#1C7F91}
  .channel.w-pyrogen{--acc:#96307D} .channel.w-sanctuary{--acc:#5F6E12;--thumb:url('static/images/cards/sanctuary.png')}
  .channel.w-brainstem{--acc:#7A4A2A;--thumb:url('static/images/cards/brainstem.png')}
  .channel.w-uptime{--acc:#26596B} .channel.w-sfoxnuance{--acc:#5A4A8C;--thumb:url('static/images/cards/sfoxnuance.png')}
  .channel.w-doctrineofsignatures{--acc:#35479B}
  .channel.w-silicon{--acc:#9A4E2A;--thumb:url('static/images/cards/siliconexodus.png')}
  .channel.w-insert{--acc:#2E7D32;--thumb:url('static/images/cards/insertcredits.png')}
  .channel.w-bramble{--acc:#7A8B3A;--thumb:url('static/images/cards/bramblewick.png')}
  .channel.w-hollow{--acc:#3A6A7A;--thumb:url('static/images/cards/hollowdive.png')}
  .channel.w-drift{--acc:#B35A2A;--thumb:url('static/images/cards/thedrift.png')}
  /* A world with no agent on it yet. The wall header claims "Live · 24/7", so a row that
     is not live has to LOOK not-live — muted bars, hollow dot. Drop .soon when it ships. */
  .channel.soon .cs .dot{background:transparent;box-shadow:inset 0 0 0 1.5px var(--acc,var(--accent))}
  .channel.soon .bars i{opacity:.34}
  .channel.soon .cs .mono{font-style:italic}

  @media (max-width:920px){.hero-grid{grid-template-columns:1fr;gap:2.2rem}}
  @media (max-width:700px){.channels{grid-template-columns:1fr 1fr}
    .channel:not(:nth-child(3n)){border-right:0}.channel:nth-child(odd){border-right:1px solid var(--line)}}
  @media (max-width:460px){.channels{grid-template-columns:1fr}.channel:nth-child(odd){border-right:0}}

  /* ============================================================= LIVING WORLDS */
  .worlds-intro{padding-top:clamp(2.5rem,5vw,4rem);padding-bottom:.5rem}
  .worlds-intro .h2{font-family:var(--display);font-weight:600;line-height:1.04;letter-spacing:-.015em;
    font-size:clamp(1.9rem,4vw,2.9rem);margin:.3rem 0 .6rem;max-width:20ch}
  .worlds-intro p{color:var(--ink-soft);font-size:1.1rem;max-width:60ch;margin:0}

  /* one live stage per world */
  /* THREE games per row (2026-07-24). The catalogue is eight games and growing; at two per
     row the page was a long scroll of huge live feeds and nobody reached the bottom of it.
     Three narrows each stage enough to read the whole shelf at a glance, and the step-downs
     below keep a card from ever being too narrow to show its feed:
       >=1280px  3 up   — the full shelf
       >=820px   2 up   — laptops; three would squeeze the 16:10 embed
       <820px    1 up   — phones/tablets, stacked as before */
  .stages{display:grid;grid-template-columns:repeat(3,1fr)}
  @media(max-width:1280px){.stages{grid-template-columns:repeat(2,1fr)}}
  @media(max-width:820px){.stages{grid-template-columns:1fr}}
  /* At three up a stage is ~a third as wide, so the heading block must stop trying to sit
     beside its buttons — it wrapped to a cramped two-line title next to a stray CTA. */
  @media(min-width:1281px){
    .stages .stage-head{display:block}
    .stages .stage-cta{margin-top:1rem}
  }
  .stage{padding-top:clamp(2.6rem,5vw,4.5rem);padding-bottom:clamp(2.6rem,5vw,4.5rem);
    position:relative;overflow:hidden;color:var(--fg)}
  /* ⚠ INK, not fill. --acc-mark is the AA-safe sibling where the bright accent
     fails as text (Fading Wilds' mint measures 1.97:1 on paper); every other
     cabinet falls back to --acc, which already passes. The token existed for
     exactly this and was wired to nothing for weeks. */
  .stage .eyebrow{color:var(--acc-mark,var(--acc))}
  .stage-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;
    gap:1rem 2rem;margin-bottom:1.6rem}
  .stage-head .lead-wrap{max-width:60ch}
  .stage-title{font-family:var(--display);font-weight:600;line-height:1;letter-spacing:-.02em;
    font-size:clamp(2.1rem,4.6vw,3.6rem);margin:.25rem 0 .7rem;color:var(--fg)}
  .stage-title .tm-mark{font-size:.42em;vertical-align:super;font-weight:600;letter-spacing:0;opacity:.7}
  .stage-lead{font-size:1.08rem;color:var(--fg-soft);margin:0;max-width:60ch}
  .stage-cta{display:flex;gap:.7rem;flex-wrap:wrap;flex:none}
  .stage .btn-play{background:var(--acc);color:var(--acc-ink,#fff)}
  .stage .btn-play:hover,.stage .btn-play:focus-visible{filter:brightness(1.08)}
  .stage .btn-watch{border:1.5px solid var(--fg-line);color:var(--fg)}
  .stage .btn-watch:hover,.stage .btn-watch:focus-visible{border-color:var(--fg);background:var(--fg-wash)}

  /* the monitor: the live iframe, given the whole screen */
  .mon{position:relative;border-radius:16px;overflow:hidden;background:#04050a;
    border:1px solid var(--bezel);box-shadow:0 30px 70px rgba(0,0,0,.28),inset 0 0 0 1px rgba(255,255,255,.04)}
  /* gate-47: no re-drawn monitor chrome. The live iframe stands as a framed figure;
     liveness lives in the per-stage eyebrow + caption, not a fake bezel bar. */
  /* The live embeds are 16:10 cards. A full-bleed 64vh box left them filling the width
     with a tall dead strip below — so give the frame the embed's own 16:10 shape and cap
     its width. It scales down a bit and the game fills the screen, no empty space. */
  .mon-screen{position:relative;width:100%;max-width:1120px;margin-inline:auto;
    aspect-ratio:16/10;max-height:72vh;background:#04050a}
  /* a stage for a world that is built but not yet serving — same frame, honest content */
  .mon-soon{display:grid;place-items:center;text-align:center;padding:1.5rem}
  .mon-soon .soon-inner{max-width:44ch}
  .mon-soon .mono{color:var(--accent);display:block;margin-bottom:.6rem}
  .mon-soon p{color:rgba(246,247,249,.72);margin:0}
  .mon-screen iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
  /* corner registration ticks + faint vignette — instrument feel, never blocks the game */
  .mon-screen::before,.mon-screen::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:2}
  .mon-screen::before{
    background:
      linear-gradient(var(--acc),var(--acc)) left 12px top 12px/16px 2px no-repeat,
      linear-gradient(var(--acc),var(--acc)) left 12px top 12px/2px 16px no-repeat,
      linear-gradient(var(--acc),var(--acc)) right 12px top 12px/16px 2px no-repeat,
      linear-gradient(var(--acc),var(--acc)) right 12px top 12px/2px 16px no-repeat,
      linear-gradient(var(--acc),var(--acc)) left 12px bottom 12px/16px 2px no-repeat,
      linear-gradient(var(--acc),var(--acc)) left 12px bottom 12px/2px 16px no-repeat,
      linear-gradient(var(--acc),var(--acc)) right 12px bottom 12px/16px 2px no-repeat,
      linear-gradient(var(--acc),var(--acc)) right 12px bottom 12px/2px 16px no-repeat;
    opacity:.55}
  .mon-screen::after{box-shadow:inset 0 0 120px rgba(0,0,0,.45);border-radius:0}
  .mon-foot{display:flex;flex-wrap:wrap;align-items:center;gap:.9rem 1.4rem;
    padding:1rem 1.1rem;background:var(--chrome);border-top:1px solid var(--bezel)}
  .mon-foot p{margin:0;color:var(--chrome-mut);font-size:.96rem;line-height:1.5;max-width:74ch}
  .mon-foot p b{color:var(--chrome-fg);font-weight:700}
  .mon-foot p i{font-style:normal;font-weight:700;color:var(--acc-mark,var(--acc))}
  .mon-foot .spacer{flex:1}

  /* per-world atmospheres --------------------------------------- */
  /* Freight Mogul — road-night dispatch */
  .w1{background:radial-gradient(140% 120% at 85% 0%,#22303b 0%,#141b22 55%,#0e141a 100%);
    --fg:#f3efe6;--fg-soft:#c3ccce;--fg-line:rgba(243,239,230,.28);--fg-wash:rgba(243,239,230,.06);
    --acc:#D9922B;--acc-ink:#20160a;--acc-glow:rgba(217,146,43,.55);
    --bezel:rgba(243,239,230,.14);--chrome:#0d1319;--chrome-fg:#f3efe6;--chrome-mut:#9fb0b3}
  /* dashed teal highway line drifting across the top */
  .w1::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;pointer-events:none;
    background:repeating-linear-gradient(90deg,var(--support) 0 22px,transparent 22px 46px);
    opacity:.5;animation:road 2.4s linear infinite}
  @keyframes road{to{background-position:46px 0}}

  /* Legend of the Ashen Dragon — ash-dark ember fantasy (the dramatic one) */
  .w2{background:radial-gradient(120% 130% at 50% 120%,#3a1410 0%,#1a0d12 45%,#0b070c 100%);
    --fg:#f4e9df;--fg-soft:#cbb6ac;--fg-line:rgba(244,233,223,.24);--fg-wash:rgba(244,233,223,.06);
    --acc:#C9491F;--acc-ink:#fff;--acc-glow:rgba(226,102,63,.6);   /* white 4.71:1 */
    --bezel:rgba(244,233,223,.13);--chrome:#140a0d;--chrome-fg:#f4e9df;--chrome-mut:#b79a90}
  /* rising embers */
  .embers{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:0}
  .embers i{position:absolute;bottom:-12px;width:3px;height:3px;border-radius:50%;
    background:var(--warn);box-shadow:0 0 8px 2px rgba(244,183,64,.55);opacity:0;
    animation:ember 7s linear infinite}
  @keyframes ember{0%{transform:translateY(0) translateX(0);opacity:0}
    12%{opacity:.9}80%{opacity:.7}100%{transform:translateY(-70vh) translateX(24px);opacity:0}}

  /* A More Perfect Union — civic parchment (the light, restrained one) */
  .w3{background:linear-gradient(180deg,#f3e7d1 0%,#efe1c8 100%);
    --fg:#2b3640;--fg-soft:#5a6a74;--fg-line:rgba(43,54,64,.22);--fg-wash:rgba(43,54,64,.05);
    --acc:#2E7D74;--acc-ink:#fff;--acc-glow:rgba(46,125,116,.5);
    --bezel:rgba(43,54,64,.22);--chrome:#e7d7ba;--chrome-fg:#2b3640;--chrome-mut:#6b7680}
  /* engraved civic hairlines top + bottom */
  .w3::before,.w3::after{content:"";position:absolute;left:0;right:0;height:0;pointer-events:none;
    border-top:1px solid rgba(43,54,64,.18);box-shadow:0 3px 0 -1px rgba(43,54,64,.10)}
  .w3::before{top:14px}.w3::after{bottom:14px}

  /* Fading Wilds — forest-dusk procgen wilds */
  .w4{background:radial-gradient(130% 120% at 20% 0%,#123a34 0%,#0d211d 55%,#08130f 100%);
    --fg:#e9f2ec;--fg-soft:#aec7bb;--fg-line:rgba(233,242,236,.24);--fg-wash:rgba(233,242,236,.06);
    --acc:#41c896;--acc-ink:#06231a;--acc-glow:rgba(65,200,150,.5);
    --bezel:rgba(233,242,236,.13);--chrome:#0b1a16;--chrome-fg:#e9f2ec;--chrome-mut:#8fb3a3}
  /* drifting fireflies */
  .flies{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:0}
  .flies i{position:absolute;width:3px;height:3px;border-radius:50%;background:var(--warn);
    box-shadow:0 0 7px 2px rgba(244,183,64,.5);opacity:0;animation:fly 9s ease-in-out infinite}
  @keyframes fly{0%{transform:translate(0,0);opacity:0}20%{opacity:.85}
    50%{transform:translate(30px,-26px);opacity:.5}80%{opacity:.7}
    100%{transform:translate(-10px,-48px);opacity:0}}

  /* Vested — statement paper / ledger green (the passbook) */
  .w5{background:linear-gradient(180deg,#ece7da 0%,#e6e0d0 100%);
    --fg:#1b2a23;--fg-soft:#5c685c;--fg-line:rgba(27,42,35,.20);--fg-wash:rgba(27,42,35,.05);
    --acc:#2e6d4e;--acc-ink:#f6f1e6;--acc-glow:rgba(46,109,78,.45);
    --bezel:rgba(27,42,35,.20);--chrome:#e2dccb;--chrome-fg:#1b2a23;--chrome-mut:#6b7568}
  /* dashed ledger ruling along the top edge */
  .w5::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;pointer-events:none;
    background:repeating-linear-gradient(90deg,var(--acc) 0 14px,transparent 14px 22px);opacity:.5}

  /* Nullroute — dark net / terminal cyber */
  .w6{background:radial-gradient(130% 120% at 80% 0%,#141a2e 0%,#0d1120 55%,#080a14 100%);
    --fg:#e7ecf5;--fg-soft:#aab6cf;--fg-line:rgba(231,236,245,.24);--fg-wash:rgba(231,236,245,.06);
    --acc:#6b8cff;--acc-ink:#0a0d18;--acc-glow:rgba(107,140,255,.5);
    --bezel:rgba(231,236,245,.13);--chrome:#0e1322;--chrome-fg:#e7ecf5;--chrome-mut:#8898b8}
  /* scanning packet line along the top edge */
  .w6::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;pointer-events:none;
    background:repeating-linear-gradient(90deg,var(--acc) 0 10px,transparent 10px 20px);opacity:.5}

  /* Progeny — the fluorescence lab. Added 2026-07-25: the stage markup shipped as
     `.w7` when Progeny went live, but no `.w7` block existed, so the band inherited
     nothing — dark screen, dark text on it, and an invisible "Play free". A stage
     without its palette does not fall back to something readable; it falls back to
     nothing. Adding a game to /games means adding its band here too. */
  .w7{background:radial-gradient(130% 120% at 25% 0%,#1b1030 0%,#120a20 55%,#090512 100%);
    --fg:#efe9fb;--fg-soft:#bcb0d6;--fg-line:rgba(239,233,251,.24);--fg-wash:rgba(239,233,251,.06);
    --acc:#a06bff;--acc-ink:#12081f;--acc-glow:rgba(160,107,255,.5);
    --bezel:rgba(239,233,251,.13);--chrome:#150c26;--chrome-fg:#efe9fb;--chrome-mut:#a494c4}
  /* a slow stain creeping across the top edge — the specimen under the lamp */
  .w7::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;pointer-events:none;
    background:linear-gradient(90deg,transparent,var(--acc),transparent);opacity:.55}

  /* Epoch & Entropy — wet asphalt under a sodium lamp. Amber is the only warm
     accent on /games, which is the point: every other band is cool or cream, so
     the noir card reads as its own place. Added with its markup, per the .w7
     lesson above — a stage without its palette falls back to nothing. */
  .w8{background:radial-gradient(135% 125% at 78% 0%,#1c2430 0%,#121822 55%,#0d1117 100%);
    --fg:#e8ecf2;--fg-soft:#a2acbd;--fg-line:rgba(232,236,242,.24);--fg-wash:rgba(232,236,242,.06);
    --acc:#e8a33d;--acc-ink:#1a1206;--acc-glow:rgba(232,163,61,.45);
    --bezel:rgba(232,236,242,.13);--chrome:#111721;--chrome-fg:#e8ecf2;--chrome-mut:#93a0b4}
  .w8::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;pointer-events:none;
    background:linear-gradient(90deg,transparent,var(--acc),transparent);opacity:.5}


  /* The three arcade titles. Each palette lands WITH its markup — the .w7 lesson
     above: a stage without its palette falls back to nothing, not to something
     readable. Accents match their world card so a game is one colour everywhere. */
  .w9{background:radial-gradient(135% 125% at 78% 0%,#132630 0%,#0f1c22 55%,#0b1519 100%);
    --fg:#e6eef2;--fg-soft:#9db2bd;--fg-line:rgba(255,255,255,.22);--fg-wash:rgba(255,255,255,.06);
    --acc:#1F6F8B;--acc-ink:#ffffff;--acc-glow:rgba(31,111,139,.45);
    --bezel:rgba(255,255,255,.13);--chrome:#0b1519;--chrome-fg:#e6eef2;--chrome-mut:#9db2bd}
  .w9::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;
    pointer-events:none;background:linear-gradient(90deg,transparent,var(--acc),transparent);opacity:.5}
  .w10{background:radial-gradient(135% 125% at 78% 0%,#2a161f 0%,#1d1016 55%,#150b10 100%);
    --fg:#f2e6ea;--fg-soft:#c2a3ad;--fg-line:rgba(255,255,255,.22);--fg-wash:rgba(255,255,255,.06);
    --acc:#8E2749;--acc-ink:#ffffff;--acc-glow:rgba(142,39,73,.45);
    --bezel:rgba(255,255,255,.13);--chrome:#150b10;--chrome-fg:#f2e6ea;--chrome-mut:#c2a3ad}
  .w10::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;
    pointer-events:none;background:linear-gradient(90deg,transparent,var(--acc),transparent);opacity:.5}
  .w11{background:radial-gradient(135% 125% at 78% 0%,#28162c 0%,#1c1020 55%,#140b17 100%);
    --fg:#f3e8f2;--fg-soft:#c0a4bd;--fg-line:rgba(255,255,255,.22);--fg-wash:rgba(255,255,255,.06);
    --acc:#96307D;--acc-ink:#ffffff;--acc-glow:rgba(150,48,125,.45);
    --bezel:rgba(255,255,255,.13);--chrome:#140b17;--chrome-fg:#f3e8f2;--chrome-mut:#c0a4bd}
  .w11::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;
    pointer-events:none;background:linear-gradient(90deg,transparent,var(--acc),transparent);opacity:.5}


  /* ═══ THE HALL — games as cabinets, three columns ═══════════════════════════
     Modern industrial, NOT retro pastiche: no scanlines, no woodgrain, no
     imitation of anyone's cabinet art. Flat planes, precise radii, one top-light,
     one accent per unit. Pure CSS — no images, so nothing is fetched and the whole
     hall re-skins from tokens.
     The FRAME TELLS YOU WHAT KIND OF GAME IT IS before you read a word: the control
     panel differs by type (arcade / workstation / console) while the silhouette
     stays constant, so the hall reads as a hall instead of fragmenting. */
  /* The last row of cabinets sat flush on the footer — the pedestal needs air above it. */
  .hall{display:grid;gap:clamp(1rem,2.2vw,1.9rem);margin-bottom:clamp(2.5rem,5vw,4.5rem);
    grid-template-columns:repeat(4,minmax(0,1fr))}
  @media(max-width:1440px){.hall{grid-template-columns:repeat(3,minmax(0,1fr))}}
  @media(max-width:1080px){.hall{grid-template-columns:repeat(2,minmax(0,1fr))}}
  @media(max-width:680px){.hall{grid-template-columns:minmax(0,1fr)}}

  .cab{--cab:#1B1F27;--cab-2:#141821;--cab-3:#0F131A;
    display:flex;flex-direction:column;min-width:0;
    background:linear-gradient(180deg,#232833 0%,var(--cab) 18%,var(--cab-2) 100%);
    border-radius:16px 16px 6px 6px;overflow:hidden;
    box-shadow:0 18px 40px rgba(15,19,26,.28),0 2px 0 rgba(255,255,255,.06) inset}

  /* topper — the light box above the marquee. Height belongs to the CABINET BODY,
     not to a stretched screen: a monitor stays 4:3, so a taller silhouette comes
     from the topper and the pedestal, the way the real object does. */
  .cab-top{height:34px;background:linear-gradient(180deg,#2C323E 0%,#222732 100%);
    border-bottom:1px solid rgba(0,0,0,.35);position:relative}
  .cab-top::after{content:"";position:absolute;left:1rem;right:1rem;bottom:6px;height:3px;
    border-radius:2px;background:var(--acc,var(--accent));opacity:.55}

  /* pedestal — kick plate under the coin line, the rest of the height */
  .cab-base{height:52px;background:linear-gradient(180deg,#12161D 0%,#0B0E13 100%);
    border-top:1px solid rgba(255,255,255,.05);position:relative}
  .cab-base::after{content:"";position:absolute;left:14%;right:14%;top:14px;height:2px;
    border-radius:2px;background:rgba(255,255,255,.06)}

  /* marquee — backlit panel. --acc/--acc-ink are the SAME pair the play button
     uses, so its contrast is already verified per game. */
  .cab-marquee{background:var(--acc,var(--accent));color:var(--acc-ink,#fff);
    padding:.85rem 1rem;display:flex;align-items:baseline;gap:.5rem;min-width:0}
  .cab-marquee .mk{font-family:var(--display);font-weight:600;font-size:.82rem;
    letter-spacing:.14em;opacity:.72;flex:none}
  .cab-marquee h3{font-family:var(--display);font-weight:600;margin:0;
    font-size:1.02rem;line-height:1.15;letter-spacing:-.005em;
    overflow-wrap:anywhere;min-width:0}
  .cab-marquee .tm-mark{opacity:.6}

  /* screen — inset glass */
  /* A cabinet is TALL. The screen carries a real 4:3 monitor aspect and the body
     grows from there, so the silhouette reads as a standing machine rather than a
     wide card with a coloured hat. */
  .cab-screen{background:var(--cab-3);color:#C9D1DC;padding:1.15rem 1.1rem 1.3rem;
    flex:1;min-height:15rem;display:flex;flex-direction:column;justify-content:center;
    box-shadow:0 2px 12px rgba(0,0,0,.5) inset;border-top:1px solid rgba(255,255,255,.07)}
  /* live variant — the monitor IS the feed, so the glass gets the aspect and no padding */
  .cab-screen.is-live{padding:0;min-height:0;aspect-ratio:4/3;position:relative;overflow:hidden}
  .cab-screen.is-live iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
  /* spec plate — the copy strip under the glass, like the info card on a real cabinet */
  .cab-plate{background:#171C25;color:#AAB4C2;padding:.85rem 1.1rem;font-size:.88rem;
    line-height:1.5;border-top:1px solid rgba(255,255,255,.06)}
  .cab-plate p{margin:0}
  .cab-screen p{margin:0;font-size:.92rem;line-height:1.55}
  .cab-state{display:inline-block;font-family:var(--body);font-weight:700;
    font-size:.62rem;letter-spacing:.16em;text-transform:uppercase;
    color:var(--acc-lift,#fff);border:1px solid currentColor;border-radius:3px;
    padding:.15rem .4rem;margin:0 0 .55rem}

  /* control panel — the type tell */
  .cab-panel{background:linear-gradient(180deg,#252B36 0%,#1D222B 100%);
    padding:.7rem 1rem;display:flex;align-items:center;gap:.75rem;flex-wrap:nowrap;
    border-top:1px solid rgba(255,255,255,.06)}
  .ctl{display:flex;align-items:center;gap:.34rem;flex:none;min-width:5.25rem}
  .ctl i{display:block;border-radius:50%;background:#39404E;
    box-shadow:0 1px 0 rgba(255,255,255,.10) inset}
  .ctl i.b{width:9px;height:9px}
  .ctl i.lit{background:var(--acc,var(--accent))}
  .ctl .stick{width:7px;height:16px;border-radius:4px;background:#39404E;
    box-shadow:0 1px 0 rgba(255,255,255,.10) inset}
  .ctl .pad{width:16px;height:16px;border-radius:3px;background:#39404E;
    box-shadow:0 1px 0 rgba(255,255,255,.10) inset}
  .ctl .keys{display:flex;gap:2px}
  .ctl .keys i{width:5px;height:9px;border-radius:1.5px}
  .cab-panel .go{margin-left:0;font-family:var(--body);font-weight:600;
    font-size:.82rem;text-decoration:none;border-radius:8px;padding:.38rem .85rem;
    background:var(--acc,var(--accent));color:var(--acc-ink,#fff);
    transition:filter .18s}
  .cab-panel .go:hover,.cab-panel .go:focus-visible{filter:brightness(1.1)}
  .cab-panel .go:active{transform:translateY(1px)}
  /* 2.5.8: `Watch` sat at ~16px next to a 28px `Play free`. It is a chrome control on every lit
     cabinet, so it gets a 24px box. Free vertically — `.go` already sets the panel's height. */
  .cab-panel .about,.cab-panel .watch{display:inline-flex;align-items:center;min-height:24px;padding:.25rem 0;
    font-family:var(--body);font-weight:600;font-size:.82rem;
    text-decoration:none;color:#AEB8C6;border-bottom:1px solid rgba(174,184,198,.4)}
  .cab-panel .about:hover,.cab-panel .about:focus-visible,.cab-panel .watch:hover,.cab-panel .watch:focus-visible{color:#fff;border-color:#fff}
  .cab-panel .cab-actions{margin-left:auto;display:inline-flex;align-items:center;gap:.85rem;flex-wrap:wrap}
  .cab-panel .soon{font-family:var(--body);font-weight:700;font-size:.66rem;
    letter-spacing:.14em;text-transform:uppercase;color:#8E99A9;margin-left:auto}
  .cab :focus-visible{outline:3px solid #fff;outline-offset:2px}

  /* coin line — FREE PLAY is the truthful arcade word for a nonprofit's games */
  .cab-coin{background:var(--cab-3);padding:.5rem 1rem;display:flex;
    align-items:center;justify-content:space-between;gap:.6rem;
    font-family:var(--body);font-weight:700;font-size:.6rem;letter-spacing:.2em;
    text-transform:uppercase;color:#77839A;border-top:1px solid rgba(255,255,255,.05)}
  .cab-coin .slot{width:22px;height:3px;border-radius:2px;background:#2C333F;flex:none}

  /* Status chip for a game that is not playable yet. Deliberately NOT an <a> and
     NOT .btn — a button that goes nowhere is worse than no button. (It goes somewhere now:
     the WIP placeholder. Still not .btn — the destination is a notice, not a game.) */
  .stage-soon{display:inline-flex;align-items:center;gap:.5rem;
    font-family:var(--body);font-weight:600;font-size:.8rem;letter-spacing:.1em;
    text-transform:uppercase;color:var(--fg-soft);
    border:1.5px dashed var(--fg-line);border-radius:999px;padding:.62rem 1.1rem}
  .stage-soon::before{content:"";width:.5rem;height:.5rem;border-radius:50%;
    background:var(--acc);box-shadow:0 0 0 3px var(--acc-glow);flex:none}
  a.stage-soon{text-decoration:none}
  a.stage-soon:hover,a.stage-soon:focus-visible{color:var(--fg);border-color:var(--fg-soft)}

  .stage>.shell{position:relative;z-index:1}

  /* ============================================================= REALMS (kept script) */
  .realms-band{background:var(--ink-900);color:var(--on-invert);
    padding-top:clamp(3.2rem,6vw,5rem);padding-bottom:clamp(3.2rem,6vw,5rem)}
  .realms-band .eyebrow{color:var(--accent-lift)}
  .realms-band .h2{font-family:var(--display);font-weight:600;line-height:1.04;letter-spacing:-.01em;
    font-size:clamp(2rem,4vw,3rem);margin:.2rem 0 .7rem;color:var(--on-invert)}
  .realms-band .lead{color:rgba(246,247,249,.72);font-size:1.08rem;max-width:64ch;margin:0}
  .sec-head{margin-bottom:2.4rem;max-width:60ch}
  /* FOUR ACROSS, MAX. `auto-fit` had no ceiling, so a wide screen packed five and
     left one orphan card alone on row two. It also squeezed "Legend of the Ashen
     Dragon" onto two lines. Explicit steps instead of a minmax floor — the cap is
     the point, and auto-fit cannot express one. */
  .realms{display:grid;gap:1.3rem;margin:2.4rem 0 0;grid-template-columns:repeat(2,1fr)}
  @media (max-width:700px) {.realms{grid-template-columns:1fr}}
  @media (min-width:1020px){.realms{grid-template-columns:repeat(3,1fr)}}
  @media (min-width:1320px){.realms{grid-template-columns:repeat(4,1fr)}}
  .realm{background:rgba(246,247,249,.04);border:1px solid rgba(246,247,249,.14);
    padding:1.3rem 1.4rem}
  .realm-top{display:flex;align-items:baseline;justify-content:space-between;gap:.6rem;
    margin-bottom:.9rem;padding-bottom:.7rem;border-bottom:1px solid rgba(246,247,249,.14)}
  .realm-name{font-family:var(--display);font-weight:600;font-size:1.12rem;color:var(--on-invert);
    text-decoration:none;line-height:1.15}
  .realm-name:hover{color:var(--accent-lift)}
  .realm-season{font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
    color:var(--support);white-space:nowrap;flex:none}
  .realm ol{list-style:none;margin:0;padding:0;display:grid;gap:.5rem}
  .realm li{display:grid;grid-template-columns:1.5rem 1fr auto;gap:.6rem;align-items:baseline;
    font-size:.96rem}
  .realm .rk{color:rgba(246,247,249,.6);font-weight:700;font-variant-numeric:tabular-nums;text-align:right}
  .realm li:first-child .rk{color:var(--accent-lift)}
  .realm .who{color:var(--on-invert);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .realm .stat{color:rgba(246,247,249,.62);font-size:.84rem;font-variant-numeric:tabular-nums;
    white-space:nowrap;text-align:right}
  .realms-empty{color:rgba(246,247,249,.6);font-size:.98rem;margin:1.6rem 0 0}

  /* ============================================================= NARRATIVE (2nd act) */
  section.narr{padding-top:clamp(3.4rem,7vw,6rem);padding-bottom:clamp(3.4rem,7vw,6rem)}
  .band-blush{background:var(--paper-2)}
  .h2{font-family:var(--display);font-weight:600;line-height:1.04;letter-spacing:-.01em;
    font-size:clamp(2rem,4.5vw,3.1rem);margin:0}

  /* ethos */
  /* .eyebrow is flex now (it carries the slot mark), so text-align no longer
     centres it — the flex box does. */
  .ethos{text-align:center}
  .ethos .eyebrow{justify-content:center}
  .ethos-inner{max-width:760px;margin:0 auto}
  .ethos-head{font-family:var(--display);font-weight:600;letter-spacing:-.015em;line-height:1.05;
    font-size:clamp(2.3rem,6vw,4rem);margin:.3rem 0 1.3rem;color:var(--ink)}
  .ethos-head em{font-style:normal;font-weight:700;color:var(--accent)}
  .ethos-lead{font-size:clamp(1.1rem,1.8vw,1.3rem);color:var(--ink-soft);max-width:48ch;margin:0 auto 2.8rem}
  .ethos-pair{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;text-align:left}
  .pillar{background:var(--paper-3);padding:clamp(1.5rem,3vw,2rem)}
  .pillar h3{font-family:var(--display);font-weight:600;font-size:1.5rem;margin:0 0 .5rem;color:var(--ink)}
  .pillar p{margin:0;color:var(--ink-soft);font-size:1rem}
  @media (max-width:640px){.ethos-pair{grid-template-columns:1fr;gap:1.1rem}}

  .sec-head2{margin-bottom:2.4rem;max-width:34ch}
  .sec-head2 .h2{margin-top:.2rem}

  /* the name */
  .name-intro{font-size:1.12rem;color:var(--ink-soft);max-width:58ch;margin:-1.4rem 0 2.4rem}
  .name-grid{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}
  .name-card{background:var(--paper-2);padding:clamp(1.6rem,3vw,2.4rem)}
  .name-card .chars{font-family:"Noto Serif JP",var(--display),serif;font-weight:600;
    font-size:clamp(3rem,8vw,4.6rem);line-height:1;color:var(--accent);margin:0}
  .name-card .reading{font-weight:600;font-size:.76rem;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-soft);margin:.9rem 0 .5rem}
  .name-card h3{font-family:var(--display);font-weight:600;font-size:1.5rem;color:var(--ink);margin:0 0 .6rem}
  .name-card>p:last-child{margin:0;color:var(--ink-soft)}
  @media (max-width:720px){.name-grid{grid-template-columns:1fr}}

  /* support / steps */
  .steps{display:grid;grid-template-columns:repeat(3,1fr);gap:2rem}
  /* the training columns were already a light bar in all but name — same 3px
     and the same .55, so they stop being a third treatment */
  .step{border-top:3px solid color-mix(in srgb,var(--accent) 55%,var(--paper));
    padding-top:1.1rem}
  .step h3{font-family:var(--display);font-weight:600;font-size:1.25rem;margin:.5rem 0 .5rem}
  .step p{margin:0;color:var(--ink-soft);font-size:.99rem}
  @media (max-width:720px){.steps{grid-template-columns:1fr;gap:1.7rem}}

  /* give — support, recut off the 3-up card grid into hairline-ruled rows so the
     page doesn't stamp the same 3-column template three times (M4). */
  .give{margin:0;display:grid}
  .give>div{display:grid;grid-template-columns:minmax(0,17ch) 1fr;gap:.4rem 2.4rem;
    padding:1.5rem 0;border-top:1px solid var(--line);align-items:baseline}
  .give>div:last-child{border-bottom:1px solid var(--line)}
  .give dt{font-family:var(--display);font-weight:600;font-size:1.25rem;color:var(--ink);margin:0}
  .give dd{margin:0;color:var(--ink-soft);font-size:1rem}
  @media (max-width:640px){.give>div{grid-template-columns:1fr;gap:.35rem;padding:1.2rem 0}}

  /* involved */
  .ways{display:grid;grid-template-columns:repeat(3,1fr);gap:1.5rem}
  .way{background:var(--paper);border:1px solid var(--line);padding:clamp(1.5rem,3vw,2rem);display:flex;flex-direction:column}
  .way h3{font-family:var(--display);font-weight:600;font-size:1.3rem;margin:0 0 .55rem}
  .way p{margin:0 0 1.5rem;color:var(--ink-soft);font-size:.98rem;flex:1}
  .way a{align-self:flex-start;font-weight:600;color:var(--accent-deep);text-decoration:none;border-bottom:2px solid transparent;padding-bottom:2px;transition:border-color .2s}
  .way a:hover{border-color:var(--accent)}
  .soon-note{font-weight:600;font-size:.82rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-soft)}
  @media (max-width:720px){.ways{grid-template-columns:1fr}}

  /* ── footer ────────────────────────────────────────────────────────────────
     Was a two-column flex: brand left, a right-aligned `.meta` stack right, and
     an empty middle on anything wider than a laptop — the trademark sentence
     squeezed into a 52ch gutter while half the band sat unused. It is a grid
     now: identity · worlds · site, across the full --stage width, with the legal
     line running the whole width underneath instead of hiding in a column.

     Measured on --ink (#171A21), not assumed (AA needs 4.5 for body text):
       brand/headings  --paper  16.24  ·  links  --accent-lift  7.12
       muted + the trademark line  #AAACB0 (the mix below)  7.66
     --foot-muted is scoped to the footer on purpose — it is a derived shade of
     --paper, not a new palette entry, so the shared :root block stays identical
     to app.css. If color-mix ever fails the colour is invalid and inherits
     --paper (16.24), so the degraded state is still AA. */
  /* The footer is the page's PEDESTAL — `.cab-base`, at site scale. The page
     ends the way every machine on it ends: a dark kick plate with one rule
     across it, so the whole document stands on the floor instead of stopping. */
  footer{background:var(--invert);color:var(--on-invert);padding:clamp(2.6rem,5vw,3.8rem) 0 1.9rem;
    position:relative;
    /* ⚠ Mixed off the INVERT pair, not paper/ink (2026-08-02). The light values are
       byte-identical to what this was — --on-invert IS --paper's value and --invert IS
       --ink's — but in dark the old mix ran 66% of a DARK paper toward a LIGHT ink and
       landed at 3.1:1 on the footer slab. Derived colour inherits the bug of whatever it
       derives from; these mix off the pair that stays dark, so they stay readable. */
    --foot-muted:color-mix(in srgb,var(--on-invert) 66%,var(--invert));
    --foot-rule:color-mix(in srgb,var(--on-invert) 18%,var(--invert))}
  /* footer has no horizontal padding (the .shell inside carries it), so this
     spans the band on its own — no -100vw bleed hack needed. The negative
     bottom margin eats footer's own 1.9rem so the plate sits on the edge. */
  footer::after{content:"";display:block;height:52px;margin:1.9rem 0 -1.9rem;
    background:var(--ink-900);border-top:1px solid var(--foot-rule);
    box-shadow:0 14px 0 -11px rgba(255,255,255,.06) inset}
  .foot{display:grid;grid-template-columns:minmax(15rem,1fr) minmax(0,2.1fr) minmax(9rem,.7fr);
    gap:clamp(1.8rem,3.5vw,3.5rem);align-items:start}
  .foot .brand{color:var(--on-invert);font-size:1.45rem}
  .foot-tag{font-family:var(--display);font-style:italic;font-size:1.05rem;color:var(--accent-lift);margin:.4rem 0 0}
  .foot-where{margin:1rem 0 0;font-size:.9rem;color:var(--foot-muted)}
  .foot h2{font-family:var(--body);font-weight:700;font-size:.72rem;letter-spacing:.16em;
    text-transform:uppercase;color:var(--on-invert);margin:0 0 1rem}
  .foot ul{list-style:none;margin:0;padding:0}
  /* WCAG 2.5.8 (24x24): these are nav links in a list, not links in a sentence, so the inline
     exception does not cover them. The row is 24px now and the 3px it gained comes back out of
     the margin (.5 -> .3rem), so the column pitch is within half a pixel of what it was.
     ⚠ app.css carries the same two rules — keep them in step. */
  .foot li{margin:0 0 .3rem;font-size:.92rem;line-height:1.45;min-height:24px;display:flex;align-items:center}
  .foot li a{display:flex;align-items:center;min-height:24px}
  .foot-games{columns:3;column-gap:clamp(1rem,2.2vw,2.4rem)}
  .foot-games li{break-inside:avoid}
  .foot a:not(.brand){color:var(--accent-lift);text-decoration:none}
  .foot a:not(.brand):hover{text-decoration:underline}
  .foot .soon{color:var(--foot-muted)}
  /* the three unreleased marks: named, never dressed as a link you can play */
  .foot .soon i{font-style:normal;font-size:.64rem;letter-spacing:.1em;text-transform:uppercase;
    white-space:nowrap;margin-left:.4rem}
  .foot-base{margin-top:clamp(2rem,4vw,3rem);padding-top:1.4rem;border-top:1px solid var(--foot-rule)}
  .foot-base p{margin:0;font-size:.85rem;color:var(--foot-muted)}
  .foot-base a{color:var(--accent-lift);text-decoration:none}
  .foot-base a:hover{text-decoration:underline}
  .foot .tm{display:block;margin-top:.7rem;font-size:.78rem}
  footer :focus-visible{outline:3px solid var(--accent-lift);outline-offset:3px;border-radius:2px}
  /* 1080: the brand takes the full first row, worlds + site share the second.
     560: one column — long world names in two columns wrap to three lines each. */
  @media (max-width:1080px){
    .foot{grid-template-columns:1fr minmax(9rem,.55fr)}
    .foot-brandcol{grid-column:1/-1}
    .foot-games{columns:2}
  }
  @media (max-width:560px){
    .foot{grid-template-columns:1fr;gap:1.9rem}
    .foot-games{columns:1}
  }
  .tm-mark{font-size:.5em;vertical-align:super;font-weight:600;letter-spacing:0}

  .reveal{opacity:0;transform:translateY(16px);transition:opacity .7s ease,transform .7s ease}
  .reveal.in{opacity:1;transform:none}

  /* ---- reduced motion: still, but fully legible ---- */
  @media (prefers-reduced-motion:reduce){
    .reveal{opacity:1;transform:none;transition:none}
    .w1::before,.brand .beacon,.wall-top .rec i,.embers i,.flies i{animation:none!important}
    .embers,.flies{display:none}
  }

  /* ============================================================= WORLD CARDS (home) */
  /* Compact "mention + describe" of the four games; the full live stages live on /games. */
  .worlds-cards{padding-top:0;padding-bottom:clamp(1rem,3vw,2rem)}
  /* The readiness group heading (both cabinet pages) — "Ready to play" vs "Work in
     progress". Deliberately quieter than the section h2 above it: it labels a shelf,
     it does not open a section, and four of these shouting over one another is worse
     than the ungrouped wall it replaced. Element selectors inside, so the loop stays
     free of per-tier classes. */
  .tier-head{max-width:64ch;margin:0 0 1.4rem}
  .shelf-tier{margin-bottom:clamp(2rem,4vw,3rem)}
  .shelf-tier:last-of-type{margin-bottom:0}
  .tier-head h2{font-family:var(--display);font-weight:600;font-size:clamp(1.2rem,1.9vw,1.45rem);
    letter-spacing:-.01em;margin:0}
  .tier-head p{margin:.45rem 0 0;color:var(--ink-soft);font-size:.96rem}
  .worlds-more{margin:1.9rem 0 0}
  /* ⚠ `.wcards` / `.wcard` / `.wsoon` / `.wlinks` / `.wplay` / `.wwatch` were
     deleted 2026-07-26 — the cabinets replaced them in `c8d02c0` and the old
     rules stayed behind with zero HTML using them. The per-world accents below
     survived because `.cab.w-*` shares the selector; only the dead half went. */
  /* per-world accents (reused from the hero channels) */
  /* ⚠ These were `.cab.w-*` until 2026-08-02. The prefix came off because /games
     now draws a game as a `.gcard` and the featured one as a `.feat`, and a
     cabinet-only selector meant seventeen shelf cards rendered with NO --acc at
     all — the exact defect the accent-rule check in `app.py check` exists for,
     recreated by a component rename rather than by a delete. The hue belongs to
     the GAME, not to the frame it happens to be drawn in.
     ⚠ `.channel.w-*` above still wins on specificity (0,2,0 over 0,1,0) and keeps
     its brighter strip values. Do not re-add a prefix here to "match" it. */
  .w-freight{--acc:#9E6210;--acc-ink:#fff}   /* white 4.99:1 */
  .w-ashen{--acc:#B3441F}
  .w-union{--acc:#2E7D74}
  /* ⚠ the mint is a FILL colour — as ink on paper it measured 1.97:1, far under AA.
     --acc-mark carries the readable sibling; --acc keeps the bright button. */
  .w-fading{--acc:#41c896;--acc-ink:#06231a;--acc-mark:#1E7A57}
  .w-vested{--acc:#2e6d4e;--acc-ink:#f6f1e6}
  .w-nullroute{--acc:#3457c9;--acc-ink:#fff}
  .w-progeny{--acc:#7a4bb8;--acc-ink:#fff}
  /* Epoch & Entropy — slate, deliberately the quietest accent on the row. The
     amber slot is taken (w-freight), and a card with nothing to click should not
     shout louder than the seven you can actually play. */
  .w-epoch{--acc:#4A5A70;--acc-ink:#fff}
  .w-emberdown{--acc:#8E2749;--acc-ink:#fff}
  /* ⚠ `w-corpus` (315°) and `w-titer` (290°) were deleted 2026-08-01 when both games
     retired into Pyrogen. Their rules are gone; PYROGEN TOOK CORPUS'S HUE rather than
     keeping the olive it wore as `febrile`, for the same reason it took Corpus's port —
     the child inherits the parent's slot, and a measured accent already on this paper
     beats a fresh one nobody has checked. 6.96:1 on white ink, unchanged from Corpus.
     ⚠ The GAP RULE still picks the next hue: pick the emptiest arc on the wheel, then
     measure it on this paper. But RECOMPUTE the wheel — 290° and 315° just reopened
     alongside 92° and 195° from the 2026-07-27 kills, and every hole size written into
     a comment here has been wrong within the week. */
  .w-pyrogen{--acc:#96307D;--acc-ink:#fff}
  /* Sanctuary takes the olive `w-febrile` wore before Pyrogen moved to Corpus's hue —
     a measured accent already on this paper (5.64:1 on white ink) beats a fresh one
     nobody has checked, and 92° was the emptiest arc left after the 2026-07-27 kills. */
  .w-sanctuary{--acc:#5F6E12;--acc-ink:#fff}
    /* Continuous Yield, added 2026-07-26 — same gap rule. It was placed at 120° against
     asymptote's 92°, which is now vacant; the choice stands, the reasoning is historical.
     white 5.38:1 · paper 5.02:1. */
  .w-continuousyield{--acc:#1F7A3D;--acc-ink:#fff}
  .w-feedstock{--acc:#1C7F91;--acc-ink:#fff}
  /* the four registered 2026-07-30 — all AA on white ink (7.4, 7.7, 7.5:1); the fourth
     was `w-febrile` at 5.64:1 and is now `w-pyrogen`, one line up on Corpus's hue. */
  .w-brainstem{--acc:#7A4A2A;--acc-ink:#fff}
  .w-uptime{--acc:#26596B;--acc-ink:#fff}
  .w-sfoxnuance{--acc:#5A4A8C;--acc-ink:#fff}
  /* registered 2026-07-30 — 8.31:1 on white ink, and the furthest hue left from the
     five cool accents already here */
  .w-doctrineofsignatures{--acc:#35479B;--acc-ink:#fff}
  .w-silicon{--acc:#9A4E2A;--acc-ink:#fff}
  .w-insert{--acc:#2E7D32;--acc-ink:#fff}
  .w-bramble{--acc:#7A8B3A;--acc-ink:#fff}
  .w-hollow{--acc:#3A6A7A;--acc-ink:#fff}
  .w-drift{--acc:#B35A2A;--acc-ink:#fff}

/* ═══ /games — THE FRONT DOOR AND THE SHELF (2026-08-02) ═════════════════════
   The cabinet hall above still runs the home page. This is the games INDEX, and
   an index has a different job: seventeen equal cabinets is a wall, not a
   recommendation. One game gets the feed and the top of the page, the rest get a
   card, and a chip bar lets a stranger cut the roster by what they came for.
   ⚠ Everything here is token-only — the theme switch works on this page because
   no rule below names a colour outside the dark front band. If you find yourself
   typing a hex into the shelf, you are about to give dark a bug it cannot see. */
  .front{background:var(--ink-900);color:#EEF0F5;padding:clamp(2.4rem,5vw,4rem) 0;position:relative;overflow:hidden}
  .front .shell{position:relative;z-index:1}
  .front::before,.front::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:0}
  .front::before{background:radial-gradient(80% 60% at 70% 20%,rgba(85,103,224,.18),transparent 60%),radial-gradient(60% 50% at 20% 90%,rgba(42,147,160,.12),transparent 55%);animation:front-breathe 10s ease-in-out infinite}
  .front::after{background-image:url('../static/images/grain.png');background-repeat:repeat;opacity:.35;image-rendering:pixelated;animation:grain .55s steps(9) infinite}
  @keyframes front-breathe{0%,100%{opacity:.7;transform:scale(1)}50%{opacity:1;transform:scale(1.04)}}
  @keyframes grain{0%,100%{transform:translate(0,0)}11%{transform:translate(-4%,-3%)}22%{transform:translate(3%,-5%)}33%{transform:translate(-5%,4%)}44%{transform:translate(4%,2%)}55%{transform:translate(-2%,5%)}66%{transform:translate(5%,-3%)}77%{transform:translate(-3%,-4%)}88%{transform:translate(2%,3%)}}
  .fx-stars{position:absolute;inset:0;pointer-events:none;z-index:0!important;opacity:.35}
  .hero .fx-stars{opacity:.22}
  .fx-spotlight{position:absolute;inset:0;pointer-events:none;z-index:0!important;background:radial-gradient(520px circle at var(--x,50%) var(--y,50%),rgba(85,103,224,.16),transparent 45%);opacity:0;transition:opacity .35s ease}
  .hero .fx-spotlight{background:radial-gradient(520px circle at var(--x,50%) var(--y,50%),rgba(85,103,224,.08),transparent 45%)}
  .fx-spotlight.is-on{opacity:1}
  .scroll-progress{position:fixed;top:0;left:0;height:3px;background:linear-gradient(90deg,var(--accent),var(--support));width:0%;z-index:9999;pointer-events:none}
  /* .eyebrow defaults to --ink-soft, which is measured for PAPER — on this dark band
     it lands at 2.69:1. The realms band solves the same problem the same way. */
  .front .eyebrow{color:var(--accent-lift)}
  .front-h{font-family:var(--display);font-weight:600;letter-spacing:-.015em;
    font-size:clamp(2rem,4.6vw,3.3rem);line-height:1.05;margin:0 0 .55rem;color:#fff;
    overflow-wrap:anywhere}
  .front-sub{color:rgba(246,247,249,.72);max-width:60ch;margin:0 0 clamp(1.6rem,3vw,2.4rem);
    font-size:1.05rem}
  .front-sub b{color:#fff;font-weight:600}
  .pick{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);
    gap:clamp(1.1rem,2.4vw,2.2rem);align-items:start}
  @media(max-width:900px){.pick{grid-template-columns:minmax(0,1fr)}}

  /* The featured unit wears the cabinet silhouette — it IS one, opened out.
     `.cab-screen` (from _cab.html) already styles the feed; nothing here restates it. */
  .feat{background:#1B1E27;border:1px solid rgba(255,255,255,.09);
    border-radius:var(--r-unit);overflow:hidden;min-width:0}
  .feat-body{padding:1.3rem 1.45rem 1.5rem}
  .feat-body h2{font-family:var(--display);font-weight:600;font-size:clamp(1.4rem,2.4vw,1.85rem);
    line-height:1.15;margin:.2rem 0 .55rem;color:#fff;overflow-wrap:anywhere}
  .feat-body p{color:rgba(246,247,249,.72);margin:0 0 1.2rem;font-size:.98rem}
  /* The alternates rail: worded as what the READER wants, not what the game is. */
  .alts{display:flex;flex-direction:column;gap:.6rem;min-width:0}
  .alts-lbl{font-family:var(--body);font-weight:600;font-size:.72rem;letter-spacing:.14em;
    text-transform:uppercase;color:rgba(246,247,249,.6);margin-bottom:.15rem}
  .alt{display:flex;gap:.8rem;align-items:flex-start;text-decoration:none;min-width:0;
    padding:.8rem .9rem;border:1px solid rgba(255,255,255,.09);border-radius:var(--r-unit);
    background:rgba(246,247,249,.04);transition:border-color .18s,background .18s}
  .alt:hover,.alt:focus-visible{border-color:var(--accent-lift);background:rgba(246,247,249,.07)}
  .alt-mk{font-family:var(--body);font-weight:700;font-size:.68rem;letter-spacing:.02em;
    color:var(--ink-900);background:var(--accent-lift);border-radius:4px;padding:.25rem .38rem;
    flex:none;margin-top:.12rem}
  .alt-t{min-width:0}
  .alt-n{display:block;color:#fff;font-weight:600;font-size:.96rem;line-height:1.3}
  .alt-w{display:block;color:rgba(246,247,249,.6);font-size:.85rem;margin-top:.1rem}

  /* ── the chip bar ─────────────────────────────────────────────────────────
     Sticky, because the shelf is long and the way back to "All" should not be a
     scroll. The count in each label is rendered by `shelves()`; nothing here. */
  .bar{position:sticky;top:var(--nav-h);z-index:50;padding:.8rem 0;
    background:color-mix(in srgb,var(--paper) 92%,transparent);
    backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
  .bar-in{display:flex;gap:.9rem;align-items:center;flex-wrap:wrap}
  .chips{display:flex;gap:.4rem;flex-wrap:wrap;min-width:0}
  .chip{font-family:var(--body);font-size:.87rem;color:var(--ink-soft);cursor:pointer;
    background:var(--surface);border:1px solid var(--line);border-radius:999px;
    padding:.4rem .85rem;white-space:nowrap;transition:background .18s,color .18s,border-color .18s}
  .chip i{font-style:normal;opacity:.6;font-variant-numeric:tabular-nums}
  .chip:hover{color:var(--ink);border-color:var(--ink-soft)}
  .chip[aria-pressed="true"]{background:var(--accent-deep);color:var(--on-accent);
    border-color:var(--accent-deep)}
  .chip[aria-pressed="true"] i{opacity:.75}
  .chip:focus-visible{outline:3px solid var(--accent-deep);outline-offset:2px}
  .bar-count{margin-left:auto;color:var(--ink-soft);font-size:.87rem;
    font-variant-numeric:tabular-nums;white-space:nowrap}

  /* ── the shelf ────────────────────────────────────────────────────────────
     NOT `.hall` — that grid is the cabinet wall and the home page still uses it.
     Two names because they are two different things. */
  .shelf{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,330px),1fr));
    gap:1rem;padding:clamp(1.6rem,3vw,2.4rem) 0 clamp(2.5rem,5vw,4rem)}
  /* Padding lives on `.gc-body`, not here: the card ends in the cabinet's own
     `.cab-panel`, which is full-bleed to the card edge exactly as it is on a `.cab`.
     Same two-part shape — a plate you read, a panel you act on. */
  .gcard{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-unit);
    overflow:hidden;display:flex;flex-direction:column;min-width:0;
    transition:border-color .18s,transform .18s,box-shadow .18s}
  .gcard:hover{border-color:var(--acc,var(--accent-deep));transform:translateY(-3px);box-shadow:0 14px 34px rgba(0,0,0,.14),0 0 0 1px rgba(85,103,224,.08)}
  .gcard[id],.feat[id]{scroll-margin-top:var(--scroll-offset)}
  .gc-body{padding:1.1rem 1.2rem 1rem;display:flex;flex-direction:column;gap:.6rem;flex:1}
  /* ⚠ `.gcard` is display:flex, which OUTRANKS the UA's `[hidden]{display:none}`.
     Without this line every filter chip updates the count and hides nothing — the
     bug reads as "the filter is broken" and is actually one missing rule. */
  .gcard[hidden]{display:none}
  .gc-thumb{aspect-ratio:16/9;background-size:cover;background-position:50% 40%;border-bottom:1px solid var(--line);position:relative;overflow:hidden;transition:transform .35s ease}
  .gc-thumb::before{content:"";position:absolute;inset:0;background:var(--action,none) center/cover no-repeat;opacity:0;transition:opacity .35s ease;z-index:0}
  .gcard:hover .gc-thumb::before{opacity:1}
  .gc-thumb::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,transparent 55%,rgba(0,0,0,.18) 100%);pointer-events:none;z-index:1}
  .gcard:hover .gc-thumb{transform:scale(1.05)}
  .gcard{position:relative}
  .gcard > *{position:relative;z-index:1}
  .gcard::before{content:"";position:absolute;inset:0;background:linear-gradient(105deg,transparent 40%,rgba(255,255,255,.08) 50%,transparent 60%);transform:translateX(-120%);transition:none;pointer-events:none;z-index:0;border-radius:inherit}
  .gcard:hover::before{transform:translateX(120%);transition:transform .7s ease-in-out}
  .gc-head{display:flex;gap:.7rem;align-items:flex-start;min-width:0}
  .gc-mk{font-family:var(--body);font-weight:700;font-size:.68rem;letter-spacing:.02em;
    color:var(--acc-ink,#fff);background:var(--acc,var(--ink));border-radius:5px;
    padding:.28rem .4rem;flex:none;margin-top:.15rem}
  .gc-head h2{font-family:var(--display);font-weight:600;font-size:1.1rem;line-height:1.25;
    margin:0;min-width:0;overflow-wrap:anywhere}
  .gc-hook{color:var(--ink-soft);font-size:.93rem;margin:0;flex:1}
  .gc-meta{display:flex;gap:.35rem;flex-wrap:wrap}
  .tag{font-family:var(--body);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
    font-weight:600;color:var(--ink-soft);border:1px solid var(--line);border-radius:4px;
    padding:.18rem .42rem}
  .tag.subj{color:var(--support-deep);border-color:var(--support-deep)}
  .gc-state{margin-top:auto;padding-top:.55rem;display:inline-flex;align-items:center;gap:.4rem;font-size:.8rem;color:var(--ink-soft)}
  .gc-state .dot{width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
  .gc-state.t-soon{color:var(--ink-soft)}
  .gc-state.t-ready{color:var(--support-deep)}
  .gc-state.t-active{color:var(--accent-deep)}
  .gc-state.t-beta{color:var(--support-deep)}
  .gc-state.t-design{color:var(--warn-deep)}
  /* ⚠ Explicit, though it is the same colour `.gc-state` already sets. Left to fall
     through it is a class in the DOM with no rule anywhere, which `assert_classes_ruled`
     refuses — and rightly: "inherits on purpose" and "the rule got deleted" look
     identical from the markup, and the second one is how a state goes invisible. */
  .gc-state.t-wip{color:var(--ink-soft)}
  .shelf-empty{color:var(--ink-soft);text-align:center;padding:0 0 3rem}
  @media(prefers-reduced-motion:reduce){.gcard:hover{transform:none}}

/* ── the theme switch (2026-08-02) ───────────────────────────────────────────
   Lives in BOTH stylesheets because the button sits in both navs, and like the
   :root and dark blocks the two copies must match. currentColor throughout, so
   the knob needs no per-theme rule of its own — it is already whatever the nav
   link beside it is. Markup: templates/_theme.html. */
.theme-tgl{display:inline-grid;place-items:center;width:2rem;height:2rem;flex:none;
  padding:0;background:none;border:1px solid var(--line);border-radius:999px;
  color:var(--ink-soft);cursor:pointer;transition:color .18s,border-color .18s}
.theme-tgl:hover{color:var(--ink);border-color:var(--ink-soft)}
.theme-tgl:focus-visible{outline:3px solid var(--accent-deep);outline-offset:2px}
.tt-i{width:13px;height:13px;border-radius:50%;border:1.5px solid currentColor;
  background:linear-gradient(90deg,currentColor 50%,transparent 50%)}
[data-theme="dark"] .tt-i{background:linear-gradient(90deg,transparent 50%,currentColor 50%)}

/* ── auth (login / signup / recover) ───────────────────────────────────────── */
body.auth-page{
  min-height:100dvh;
  display:flex;
  flex-direction:column
}
body.auth-page footer{
  flex:none;
  margin-top:auto;
  padding:clamp(.85rem,2vw,1.15rem) 0 clamp(.65rem,1.5vw,.85rem)
}
body.auth-page footer::after{
  height:36px;
  margin:clamp(.65rem,1.2vw,.85rem) 0 calc(-1 * clamp(.65rem,1.5vw,.85rem))
}
body.auth-page .foot-base{
  margin-top:0;
  padding-top:0;
  border-top:0
}
body.auth-page .foot-base p{font-size:clamp(.82rem,.95vw,.9rem)}
.auth-band{
  flex:1 1 auto;
  min-height:0;
  padding:clamp(.5rem,1.2vw,.85rem) 0;
  display:flex;
  flex-direction:column
}
.auth-shell{
  max-width:none;
  flex:1;
  min-height:0;
  display:flex;
  flex-direction:column;
  padding-left:clamp(.75rem,1.8vw,1.5rem);
  padding-right:clamp(.75rem,1.8vw,1.5rem)
}
.auth-layout{
  flex:1;
  min-height:0;
  display:grid;
  grid-template-columns:1fr;
  gap:clamp(.65rem,1.2vw,1rem);
  align-items:stretch
}
@media (min-width:860px){
  .auth-layout{grid-template-columns:1fr 1fr;gap:clamp(.75rem,1.4vw,1.15rem);align-items:stretch}
}
.auth-privacy{
  padding:clamp(1.1rem,2.2vw,1.85rem);
  border:1px solid var(--line);
  border-radius:var(--r-unit);
  background:var(--paper-3);
  display:flex;
  flex-direction:column
}
.auth-privacy .eyebrow,
.auth-card > .eyebrow,
#signup-form-wrap > .eyebrow,
#recover-form-wrap > .eyebrow{margin-bottom:.4rem}
.privacy-h{font-family:var(--display);font-weight:600;font-size:clamp(1.45rem,2.4vw,2.15rem);
  line-height:1.15;margin:0 0 .65rem;color:var(--ink)}
.privacy-lead{color:var(--ink-soft);font-size:clamp(.95rem,1.25vw,1.1rem);
  line-height:1.5;margin:0 0 1rem;max-width:none}
.privacy-list{
  list-style:none;padding:0;margin:0;display:grid;gap:clamp(.55rem,1vw,.75rem);
  flex:1;align-content:stretch
}
@media (min-width:640px){
  .privacy-list{grid-template-columns:repeat(2,1fr)}
}
.privacy-item{padding:clamp(.75rem,1.2vw,1rem) clamp(.85rem,1.4vw,1.1rem);
  border:1px solid var(--line);border-radius:12px;
  background:var(--surface);font-size:clamp(.88rem,1.05vw,1rem);
  line-height:1.45;color:var(--ink-soft)}
.privacy-item strong{display:block;font-size:clamp(.95rem,1.12vw,1.06rem);
  color:var(--ink);margin:0 0 .25rem}
.privacy-foot{margin:.85rem 0 0;padding-top:.75rem;border-top:1px solid var(--line);
  font-size:clamp(.88rem,1vw,.95rem);line-height:1.45;color:var(--ink-soft)}
.auth-card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-unit);
  padding:clamp(1.1rem,2.2vw,1.85rem);
  display:flex;flex-direction:column
}
.auth-notice{
  padding:clamp(.75rem,1.2vw,1rem) clamp(.85rem,1.4vw,1.1rem);
  border:1px solid var(--line);border-radius:12px;
  background:var(--paper-3);font-size:clamp(.88rem,1.05vw,1rem);
  line-height:1.45;color:var(--ink-soft)
}
.auth-notice strong{display:block;font-size:clamp(.95rem,1.12vw,1.06rem);
  color:var(--ink);margin:0 0 .25rem}
.auth-h{font-family:var(--display);font-weight:600;font-size:clamp(1.35rem,2.2vw,1.85rem);
  line-height:1.15;margin:.2rem 0 .55rem}
.auth-lead{color:var(--ink-soft);margin:0 0 1rem;font-size:clamp(.92rem,1.1vw,1.02rem);
  line-height:1.45;max-width:none}
.auth-form{display:grid;gap:clamp(.55rem,.9vw,.7rem);max-width:36rem;width:100%}
.auth-field{display:grid;gap:.25rem;font-size:clamp(.86rem,.95vw,.92rem)}
.auth-field span{font-weight:600;color:var(--ink-soft)}
.auth-field input{font:inherit;padding:clamp(.45rem,.7vw,.55rem) .65rem;
  border:1px solid var(--line);border-radius:10px;background:var(--paper);color:var(--ink)}
.auth-field input:focus-visible{outline:3px solid var(--accent-deep);outline-offset:1px}
.auth-submit{width:100%;text-align:center;margin-top:.1rem;border:none;cursor:pointer;
  padding:clamp(.55rem,.9vw,.65rem) 1rem;font-size:clamp(.88rem,1vw,.94rem)}
.auth-err{color:var(--warn-deep);font-size:.82rem;margin:0}
.auth-alt{color:var(--ink-soft);font-size:clamp(.78rem,.9vw,.84rem);margin:.55rem 0 0}
.recovery-warn{color:var(--warn-deep);font-size:clamp(.95rem,1.15vw,1.08rem);margin:0 0 .75rem;
  line-height:1.45;padding:clamp(.75rem,1.2vw,1rem) clamp(.85rem,1.4vw,1.1rem);
  border:1px solid color-mix(in srgb,var(--warn-deep) 35%,var(--line));
  border-radius:12px;background:color-mix(in srgb,var(--warn-deep) 8%,var(--paper))}
.recovery-warn strong{font-size:clamp(1.02rem,1.22vw,1.12rem)}
.recovery-note{color:var(--ink-soft);font-size:clamp(.88rem,1.05vw,.98rem);line-height:1.45;
  margin:0 0 .75rem}
.recovery-box{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;
  font-size:clamp(.95rem,1.2vw,1.12rem);line-height:1.45;word-break:break-all;
  padding:clamp(.75rem,1.2vw,1rem) clamp(.85rem,1.4vw,1.1rem);border:1px solid var(--line);
  border-radius:12px;background:var(--paper-2);color:var(--ink);margin:0 0 .75rem;
  max-height:7rem;overflow:auto}
.recovery-actions{display:flex;flex-wrap:wrap;gap:.55rem}
.recovery-actions .btn{cursor:pointer;padding:clamp(.55rem,.9vw,.65rem) 1rem;
  font-size:clamp(.92rem,1.05vw,1rem)}
.nav-user{font-weight:600;color:var(--ink-soft);margin-right:.5rem}
.nav-out{display:inline;margin:0;padding:0;border:none;background:none}
.nav-link-btn{font:inherit;font-weight:600;color:var(--accent-deep);background:none;border:none;
  cursor:pointer;padding:0;text-decoration:underline;text-underline-offset:.15em}
.nav-link-btn:hover{color:var(--accent-hover)}
.foot-games li.live a{color:inherit;text-decoration:none}
.foot-games li.live a:hover{color:var(--accent-deep)}
