/* ════════════════════════════════════════════════════════════════
   ELDARA GAMES — CORE
   ────────────────────────────────────────────────────────────────
   The shared design system for every page: tokens, fonts, reset,
   type, layout, header + nav sheet, footer, buttons, chips, the
   wordmark, rules, the newsletter form, dialogs, reveal states and
   utilities. Ambient layers live in ambience.css; each game's look
   lives in css/themes/<slug>.css.

   Two layers of colour
     --s-*  the studio frame: warm graphite, ivory, one mint signal.
     --t-*  the world an element sits in. :root maps every --t-* token
            back to the studio, and [data-theme="<slug>"] re-points
            them. Components read only --t-*, so an unthemed element
            always renders as studio.

   Cascade layers (order declared once, below)
     reset, tokens, themes, base, layout, components, ambience, utilities
     · "themes" sits early on purpose. A theme only re-points tokens,
       so an element that is both a component and a themed scope (a
       game card, a game chip) keeps the component's own type rules.
     · Page CSS (home.css, game.css, eldara.css, legal.css) is
       unlayered, so it always overrides everything in here.
     · !important is used only to enforce state: [hidden], pausing
       and the reduced-motion matrix. Layered !important outranks
       page CSS on purpose.

   JS hooks styled here (contract: build spec §6)
     html          .js .reduced .fine .save-data .intro .is-hidden .is-media
     .site-header  .is-solid (scrolled 24px) · .is-hidden (hide on scroll)
     .site-footer  .is-in (entered view once: the arch beam lights)
     [data-reveal].is-in · [data-reveal-stagger] > * { --i }
     [data-split] .w > span { --i }        word index, 40ms stagger
     [data-spotlight] { --mx --my }        px, from pointer.js
     [data-tilt] { --rx --ry }             deg, from pointer.js
     [data-magnetic] { --mag-x --mag-y }   px, from pointer.js
     video[data-video="hover"].is-playing
     .newsletter .is-done / .is-error
     dialog.dialog                         markup contract in DIALOGS
   ════════════════════════════════════════════════════════════════ */

@layer reset, tokens, themes, base, layout, components, ambience, utilities;


/* ── fonts ─────────────────────────────────────────────────────────
   Self-hosted variable fonts, split by unicode-range so a file only
   downloads when a glyph from its range is rendered. The studio sans
   and mono are system stacks, so the studio frame downloads nothing. */
@font-face{
  font-family:"Cinzel";
  src:url(/assets/fonts/cinzel-latin.woff2) format("woff2");
  font-weight:400 900; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Cinzel";
  src:url(/assets/fonts/cinzel-latin-ext.woff2) format("woff2");
  font-weight:400 900; font-style:normal; font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:"Nunito";
  src:url(/assets/fonts/nunito-latin.woff2) format("woff2");
  font-weight:200 1000; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Nunito";
  src:url(/assets/fonts/nunito-latin-ext.woff2) format("woff2");
  font-weight:200 1000; font-style:normal; font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}


/* ── registered properties ─────────────────────────────────────────
   --draw (0 → 1) drives the sigil stroke draw in the header and the
   intro. Its initial value is 1, "fully drawn", so any sigil that
   nothing animates renders complete.                                */
@property --draw{ syntax:"<number>"; inherits:false; initial-value:1; }


/* ── reset ─────────────────────────────────────────────────────── */
@layer reset{
  *,*::before,*::after{ box-sizing:border-box; }
  html{ -webkit-text-size-adjust:100%; text-size-adjust:100%; }
  body,h1,h2,h3,h4,h5,h6,p,figure,blockquote,dl,dd,ol,ul{ margin:0; }
  :where(ul,ol)[class], :where(nav) :where(ul,ol){ padding:0; list-style:none; }
  img,picture,video,canvas,svg{ display:block; max-width:100%; }
  img,video{ height:auto; }
  input,button,textarea,select{ font:inherit; color:inherit; letter-spacing:inherit; }
  button{ padding:0; border:0; background:none; cursor:pointer; }
  a{ color:inherit; text-decoration:none; }
  [hidden]:not([hidden="until-found"]){ display:none !important; }
  /* Smooth only after load (boot.js adds .is-loaded), so arriving on a
     /#hash jumps straight there instead of animating past sections
     that are still settling their size. */
  @media (prefers-reduced-motion:no-preference){ html.is-loaded{ scroll-behavior:smooth; } }
}


/* ── tokens ────────────────────────────────────────────────────── */
@layer tokens{
  :root{
    color-scheme:dark;

    /* Studio surfaces — warm graphite */
    --s-bg:#0a0a0b; --s-bg-2:#0e0e10; --s-surface:#141417; --s-surface-2:#1b1b1f; --s-surface-3:#232329;
    --s-line:rgba(242,239,233,.08); --s-line-2:rgba(242,239,233,.14); --s-line-3:rgba(242,239,233,.26);
    /* Ink — ivory (on --s-bg: 17.6 / 10.0 / 5.4 :1) */
    --s-ink:#f2efe9; --s-ink-2:#bdb8ae; --s-ink-3:#8c877e;
    /* Studio mark, from the shipped ident */
    --s-mark:#e9e1d1; --s-lumen:#fff3dc; --s-hair:#6e624a;
    /* Signal — the ONE studio accent, under 3% of pixels: focus ring,
       live dot, active nav and filter, link hover. Never inside a theme. */
    --s-signal:#36dede; --s-signal-ink:#04191a; --s-signal-dim:rgba(54,222,222,.14);
    --s-status-out:#36dede; --s-status-ea:#e8c26a; --s-status-dev:#b3a6ff; --s-status-ann:#f2efe9; --s-status-tba:#8c877e;
    --s-ok:#5fd39a; --s-danger:#ff6b5e; --s-focus:#36dede;

    --s-font-sans:"Segoe UI Variable Text","Segoe UI Variable","Segoe UI",system-ui,-apple-system,BlinkMacSystemFont,"SF Pro Text",Roboto,"Helvetica Neue",Arial,sans-serif;
    --s-font-display:"Segoe UI Variable Display","Segoe UI Variable","Segoe UI",system-ui,-apple-system,BlinkMacSystemFont,"SF Pro Display",Roboto,"Helvetica Neue",Arial,sans-serif;
    --s-font-mono:ui-monospace,"Cascadia Mono","Cascadia Code","SF Mono",Menlo,Consolas,monospace;
    --s-font-brand:"Cinzel",Georgia,"Times New Roman",serif;

    /* Theme fallbacks: an unthemed element renders as studio */
    --t-bg:var(--s-bg); --t-bg-2:var(--s-bg-2); --t-surface:var(--s-surface); --t-surface-2:var(--s-surface-2);
    --t-line:var(--s-line); --t-line-2:var(--s-line-2); --t-line-3:var(--s-line-3);
    --t-ink:var(--s-ink); --t-ink-2:var(--s-ink-2); --t-ink-3:var(--s-ink-3);
    --t-accent:var(--s-ink); --t-accent-hi:#fff; --t-accent-lo:var(--s-ink-3); --t-on-accent:var(--s-bg);
    --t-accent-2:var(--s-ink-2); --t-glow:rgba(255,243,220,.10); --t-focus:var(--s-focus);
    --t-particle:#efe8da; --t-particle-2:#fff3dc;
    --t-font-display:var(--s-font-display); --t-font-body:var(--s-font-sans);
    --t-radius:12px; --t-ease:var(--ease-out);
    --t-btn:var(--s-ink); --t-btn-border:var(--s-ink);

    /* Theme fallbacks — type roles (labels, display headings, buttons) */
    --t-label:var(--s-ink-3); --t-kicker:var(--s-ink-3); --t-subtitle:var(--s-ink-2);
    --t-font-label:var(--s-font-mono); --t-label-weight:500; --t-label-track:.14em;
    --t-display-weight:650; --t-display-lh:1.02; --t-display-track:-.035em;
    --t-btn-radius:var(--r-xs);

    /* Theme fallbacks — marks, signals and ornaments */
    --t-signal:var(--s-signal);              /* active nav, link hover */
    --t-mark:var(--s-mark); --t-lumen:var(--s-lumen);   /* sigil strokes, beam */
    --t-marker:var(--s-ink);                 /* themed diamond marker */
    --t-wordmark:linear-gradient(145deg,#a9a49b 4%,#f2efe9 31%,#cfc9bd 48%,#fffaf0 63%,#99948b 89%);
    --t-sweep:#fff;
    --t-rule-a:rgba(242,239,233,.16); --t-rule-b:rgba(242,239,233,.42); --t-gem:var(--s-ink-3); --t-glyph:var(--s-ink-3);
    --t-frame:none;

    /* Theme fallbacks — global ambience */
    --t-vignette:.45; --t-grain:.035;
    --t-cursor:rgba(255,243,220,.05); --t-cursor-blend:normal;
    --t-moon:none;

    /* Layout */
    --gutter:clamp(16px,4vw,56px);
    --wrap:min(1440px, 100% - 2*var(--gutter));
    --wrap-text:64ch;
    --section-y:clamp(96px,11vw,176px);
    --grid-gap:clamp(12px,1.6vw,24px);
    --cols:4;                                    /* 12 ≥1024 · 6 at 640–1023 · 4 below */
    --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px;
    --sp-7:48px; --sp-8:64px; --sp-9:96px; --sp-10:128px; --sp-11:192px;
    --r-xs:4px; --r-sm:8px; --r-md:12px; --r-lg:20px; --r-pill:999px;
    --e1:inset 0 1px 0 rgba(255,255,255,.04),0 1px 2px rgba(0,0,0,.5);
    --e2:inset 0 1px 0 rgba(255,255,255,.05),0 16px 40px -16px rgba(0,0,0,.7);
    --e3:0 40px 120px -40px rgba(0,0,0,.85);
    --e-glow:0 0 0 1px var(--t-line-2),0 30px 90px -30px var(--t-glow);
    --header-h:72px;                             /* 60px below 1024 */
    --z-bg:0; --z-fx:1; --z-content:2; --z-sticky:40; --z-header:50; --z-sheet:60; --z-grain:90; --z-intro:100;

    /* Type scale */
    --fs-mega:clamp(3.25rem,1.6rem + 7vw,9rem);
    --fs-hero:clamp(2.75rem,1.8rem + 4.6vw,6.5rem);
    --fs-h1:clamp(2.25rem,1.5rem + 3.2vw,4.5rem);
    --fs-h2:clamp(1.875rem,1.35rem + 2.2vw,3.25rem);
    --fs-h3:clamp(1.25rem,1.1rem + .6vw,1.625rem);
    --fs-lead:clamp(1.0625rem,1rem + .35vw,1.3125rem);
    --fs-body:1rem; --lh-body:1.62; --fs-sm:.875rem; --fs-label:.75rem; --fs-micro:.6875rem;

    /* Motion */
    --ease-out:cubic-bezier(.16,1,.3,1);         /* reveals, morphs */
    --ease-cine:cubic-bezier(.2,.8,.2,1);        /* camera, overlays, media */
    --ease-menu:cubic-bezier(.2,.75,.25,1);      /* the game's menu curve */
    --ease-io:cubic-bezier(.65,0,.35,1);         /* carousels, crossfades, drifts */
    --ease-soft:cubic-bezier(.4,0,.2,1);
    --ease-spring:cubic-bezier(.34,1.56,.64,1);  /* 8px or less, UI only */
    --d-1:120ms; --d-2:200ms; --d-3:320ms; --d-4:560ms; --d-5:900ms; --d-6:1400ms;
    --stagger:60ms;
  }

  /* Derived ambience colours are declared on every themed scope too, so
     they re-derive from that scope's particle colours (custom properties
     inherit computed values, not formulas). Theme files may override. */
  :root,[data-theme]{
    --t-ember:var(--t-particle-2);
    --t-ember-glow:color-mix(in srgb,var(--t-particle) 60%,transparent);
    --t-ember-halo:color-mix(in srgb,var(--t-particle) 38%,transparent);
    --t-fleck:radial-gradient(ellipse at center,color-mix(in srgb,var(--t-particle-2) 85%,transparent) 0%,color-mix(in srgb,var(--t-particle) 40%,transparent) 12%,transparent 70%);
    --t-corona:radial-gradient(circle,transparent 38%,color-mix(in srgb,var(--t-particle) 40%,transparent) 46%,color-mix(in srgb,var(--t-particle) 13%,transparent) 60%,transparent 72%);
    --t-ray:color-mix(in srgb,var(--t-particle) 9%,transparent);
  }

  @media (width >= 640px){ :root{ --cols:6; } }
  @media (width >= 1024px){ :root{ --cols:12; } }
  @media (width < 1024px){ :root{ --header-h:60px; } }
}


/* ── base ──────────────────────────────────────────────────────── */
@layer base{
  html{
    scroll-padding-top:calc(var(--header-h) + 16px);
    scrollbar-color:var(--s-surface-3) var(--s-bg);
  }
  body{
    min-height:100svh;
    background:var(--t-bg);
    color:var(--t-ink-2);
    font-family:var(--t-font-body);
    font-size:var(--fs-body);
    line-height:var(--lh-body);
    font-kerning:normal;
    -webkit-font-smoothing:antialiased;
    -moz-osx-font-smoothing:grayscale;
  }
  /* Content sits above the fixed ambient layers (vignette, canvas, cursor
     light). overflow-x:clip keeps decorative bleed from widening the page. */
  main{ display:block; position:relative; z-index:var(--z-content); overflow-x:clip; }
  main:focus{ outline:none; }               /* programmatic skip-link target, not interactive */

  h1,h2,h3,h4{
    font-family:var(--t-font-display);
    font-weight:var(--t-display-weight);
    line-height:var(--t-display-lh);
    letter-spacing:var(--t-display-track);
    color:var(--t-ink);
    text-wrap:balance;
  }
  h1{ font-size:var(--fs-h1); }
  h2{ font-size:var(--fs-h2); }
  h3{ font-size:var(--fs-h3); }
  p{ text-wrap:pretty; }
  strong,b{ font-weight:650; color:var(--t-ink); }
  hr{ height:1px; margin:0; border:0; background:var(--t-line); }

  /* Links inside running text (not navigation): underlined, signal on hover */
  :where(p,li,dd,figcaption):not(nav *) > a:not([class]){
    text-decoration:underline;
    text-decoration-thickness:1px;
    text-underline-offset:.22em;
    text-decoration-color:var(--t-line-3);
    transition:color var(--d-4) var(--ease-soft),text-decoration-color var(--d-4) var(--ease-soft);
  }
  @media (hover:hover){
    :where(p,li,dd,figcaption):not(nav *) > a:not([class]):hover{
      color:var(--t-ink); text-decoration-color:var(--t-signal); transition-duration:var(--d-3);
    }
  }

  ::selection{ background:color-mix(in srgb,var(--t-accent) 32%,transparent); color:var(--t-ink); }
  :focus-visible{ outline:2px solid var(--t-focus); outline-offset:3px; }
}


/* ── layout ────────────────────────────────────────────────────── */
@layer layout{
  .wrap{ width:var(--wrap); margin-inline:auto; }
  .grid{
    display:grid;
    grid-template-columns:repeat(var(--cols),minmax(0,1fr));
    column-gap:var(--grid-gap);
  }

  .section{ position:relative; padding-block:var(--section-y); }
  /* From the third section on, skip rendering until it nears the viewport. */
  /* No content-visibility on sections: it drops off-screen headings, landmarks and form
     fields from the accessibility tree. Ambient hosts pause offscreen instead ([data-amb]). */

  /* Section head: the editorial grid used on every page. A hairline,
     the index label, then the title (cols 1–7) with the aside in cols
     9–12, right-aligned. Everything stacks below 1024.             */
  .sec-head{
    display:grid;
    grid-template-columns:minmax(0,1fr);
    row-gap:clamp(12px,1.4vw,20px);
    align-items:end;
    margin-bottom:clamp(40px,5vw,80px);
    padding-top:clamp(14px,1.4vw,20px);
    border-top:1px solid var(--t-line);
  }
  .sec-head__index{
    margin-bottom:clamp(8px,2.2vw,36px);
    font:var(--t-label-weight) var(--fs-label)/1.3 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    color:var(--t-kicker);
  }
  .sec-head__lead{
    max-width:var(--wrap-text);
    font-size:var(--fs-lead);
    line-height:1.5;
    color:var(--t-ink-2);
  }
  @media (width >= 1024px){
    .sec-head{ grid-template-columns:repeat(12,minmax(0,1fr)); column-gap:var(--grid-gap); }
    .sec-head__index{ grid-column:1 / -1; }
    .sec-head__title,.sec-head__lead{ grid-column:1 / span 7; }
    .sec-head__aside{ grid-column:9 / -1; grid-row:2; justify-self:end; text-align:right; }
    .sec-head:has(.sec-head__lead) .sec-head__aside{ grid-row:2 / span 2; }
  }
}


/* ── components ────────────────────────────────────────────────── */
@layer components{

  /* ── sigil ─────────────────────────────────────────────────────
     The studio mark: concentric pointed arches, a beam and a horizon.
     Strokes are hairlines at every size (non-scaling-stroke in the
     markup). A sigil that draws itself (header, intro, or any sigil
     given .sigil--draw) switches to scaling strokes, because pathLength
     dash maths breaks under non-scaling-stroke once the SVG is scaled:
     give it a stroke-width in viewBox units (≈ 1.25 × 250 ÷ height px)
     and animate --draw 0 → 1 (keyframes sigil-draw).                */
  .sigil{
    display:block;
    overflow:visible;
    fill:none;
    stroke:currentColor;
    stroke-width:1;
    stroke-linecap:butt;
    color:var(--t-mark);
  }
  .sigil__beam{ stroke:var(--t-lumen); }
  .sigil__inner{ opacity:.62; }
  .sigil__horizon{ opacity:.7; }

  /* Draw maths, used wherever a sigil animates --draw from 0 to 1:
     the arches grow from both posts and meet at the apex, the beam
     rises from the horizon, the horizon opens from its centre.     */
  :is(.site-header .sigil,div.intro .sigil,.sigil--draw) path{ vector-effect:none; }
  :is(.site-header .sigil,div.intro .sigil,.sigil--draw) :is(.sigil__outer,.sigil__inner){
    stroke-dasharray:calc(var(--draw) * .5) calc(1 - var(--draw)) calc(var(--draw) * .5);
  }
  :is(.site-header .sigil,div.intro .sigil,.sigil--draw) .sigil__beam{ stroke-dasharray:1; stroke-dashoffset:calc(var(--draw) - 1); }
  :is(.site-header .sigil,div.intro .sigil,.sigil--draw) .sigil__horizon{ stroke-dasharray:0 calc(.5 - var(--draw) * .5) var(--draw) 1; }
  @keyframes sigil-draw{ from{ --draw:0; } }


  /* ── header ────────────────────────────────────────────────────
     Fixed, transparent over the first section. nav.js adds .is-solid
     after 24px and .is-hidden when hiding on scroll. The glass tint
     fades in on ::before (opacity only); the blur sits on the header. */
  .site-header{
    position:fixed; inset:0 0 auto; z-index:var(--z-header);
    height:var(--header-h);
    color:var(--t-ink);
    transition:translate var(--d-3) var(--ease-out);
  }
  .site-header::before{
    content:""; position:absolute; inset:0; pointer-events:none;
    background:color-mix(in srgb,var(--t-bg) 72%,transparent);
    border-bottom:1px solid var(--t-line);
    opacity:0;
    transition:opacity var(--d-3) var(--ease-soft);
  }
  .site-header.is-solid{
    -webkit-backdrop-filter:blur(14px) saturate(140%);
    backdrop-filter:blur(14px) saturate(140%);
  }
  .site-header.is-solid::before{ opacity:1; }
  @supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
    .site-header::before{ background:color-mix(in srgb,var(--t-bg) 94%,transparent); }
  }
  .site-header.is-hidden{ translate:0 -100%; }
  .site-header:focus-within{ translate:none; }

  .site-header__inner{
    display:flex; align-items:center; gap:clamp(10px,1.4vw,24px);
    width:var(--wrap); height:100%; margin-inline:auto;
  }
  /* Bar items stay above the mobile sheet, which is a sibling inside the header. */
  .site-header__inner > :not(.site-nav){ position:relative; z-index:1; }

  /* Brand: sigil + ELDARA (Cinzel, ivory) over GAMES (mono, between hairlines) */
  .brand{
    display:inline-grid;
    grid-template-columns:auto auto;
    align-items:center;
    column-gap:11px; row-gap:5px;
    flex:none;
    min-height:44px;
    color:var(--t-ink);
  }
  .brand > .sigil{ grid-row:1 / span 2; width:24px; height:30px; }
  .brand__word{
    align-self:end;
    margin-right:-.42em;                    /* cancel trailing tracking */
    font:400 15px/1 var(--s-font-brand);
    letter-spacing:.42em;
    text-transform:uppercase;
    color:var(--t-ink);
  }
  .brand__sub{
    display:flex; align-items:center; gap:6px;
    align-self:start; justify-self:center;
    font:500 9px/1 var(--s-font-mono);
    letter-spacing:.38em;
    text-transform:uppercase;
    color:var(--t-ink-3);
  }
  .brand__sub::before,.brand__sub::after{
    content:""; width:12px; height:1px;
    background:linear-gradient(90deg,transparent,var(--s-hair));
  }
  .brand__sub::after{ margin-left:-.38em; scale:-1 1; }   /* cancel trailing tracking */

  .brand--lg{ column-gap:18px; row-gap:9px; }
  .brand--lg > .sigil{ width:40px; height:50px; }
  .brand--lg .brand__word{ font-size:24px; }
  .brand--lg .brand__sub{ font-size:11px; gap:9px; }
  .brand--lg .brand__sub::before,.brand--lg .brand__sub::after{ width:22px; }

  @media (width < 1280px){
    .brand--compact{ grid-template-columns:auto; }
    .brand--compact :is(.brand__word,.brand__sub){ display:none; }
  }

  /* Header sigil: scaling strokes (≈1.1px at 30px tall) and a one-off
     draw-in on first paint. It waits while the intro cover is up. */
  .site-header .brand > .sigil path{ stroke-width:9; }
  @media (prefers-reduced-motion:no-preference){
    html:not(.intro) .site-header .brand > .sigil path{ animation:sigil-draw .7s var(--ease-out) both; }
    html:not(.intro) .site-header .brand > .sigil .sigil__horizon{ animation-delay:.15s; }
    html:not(.intro) .site-header .brand > .sigil .sigil__outer{ animation-duration:.85s; animation-delay:.3s; }
    html:not(.intro) .site-header .brand > .sigil .sigil__inner{ animation-duration:.8s; animation-delay:.4s; }
  }

  /* Game crumb: Eldara Games / Eldara: The Forgotten Realm */
  .crumb-sep{ flex:none; font:300 22px/1 var(--s-font-sans); color:var(--t-line-3); }
  .crumb-game{
    display:inline-flex; align-items:center;
    min-height:44px;
    font:500 15px/1 var(--t-font-display);
    letter-spacing:.06em;
    white-space:nowrap;
    color:var(--t-ink);
  }
  .crumb-game__sub{ color:var(--t-ink-3); }
  @media (width < 768px){ .crumb-game__sub{ display:none; } }

  .site-header__actions{ display:flex; align-items:center; gap:8px; margin-left:auto; flex:none; }

  /* The game bar carries seven sections, a crumb and a priced Buy button,
     so it tightens before anything can collide: the crumb drops its
     subtitle below 1600, and below 1280 the links set smaller and closer. */
  @media (width >= 1024px) and (width < 1600px){
    .site-header--game .crumb-game__sub{ display:none; }
  }
  @media (width >= 1024px) and (width < 1280px){
    .site-header--game .site-header__inner{ gap:12px; }
    .site-header--game .site-nav__list{ --nav-pad:6px; gap:0; }
    .site-header--game .site-nav__list a{ font-size:var(--fs-micro); letter-spacing:.1em; }
  }

  /* Primary nav (desktop) */
  .site-nav{ margin-inline:auto; }
  .site-nav__list{
    --nav-pad:clamp(8px,.9vw,14px);          /* link padding; the marker insets by it too */
    position:relative; display:flex; align-items:center; gap:clamp(0px,.4vw,8px);
  }
  .site-nav__list a{
    position:relative;
    display:flex; align-items:center;
    height:44px;
    padding-inline:var(--nav-pad);
    font:var(--t-label-weight) var(--fs-label)/1 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    white-space:nowrap;
    color:var(--t-ink-2);
    transition:color var(--d-4) var(--ease-soft);
  }
  .site-nav__list a[aria-current="true"]{ color:var(--t-ink); }
  @media (hover:hover){
    .site-nav__list a:hover{ color:var(--t-ink); transition-duration:var(--d-3); }
  }

  /* Scroll-spy marker: one element on the list slides to the current
     link. Studio: a 1px signal underline. Themed: the in-game gold
     diamond. Anchor positioning tracks the link by itself; elsewhere
     nav.js writes the link's offset and width as --spy-x / --spy-w. */
  @media (width >= 1024px){
    .site-nav__list::after{
      content:""; position:absolute; left:0; top:calc(50% + 11px);
      width:calc(var(--spy-w) - 2 * var(--nav-pad));
      height:1px;
      translate:calc(var(--spy-x) + var(--nav-pad)) 0;
      background:var(--t-signal);
      pointer-events:none;
      transition:translate var(--d-4) var(--ease-out),width var(--d-4) var(--ease-out),opacity var(--d-3) ease;
    }
    [data-theme] .site-nav__list::after{
      width:6px; height:6px;
      top:calc(50% + 13px);
      translate:calc(var(--spy-x) + var(--spy-w) / 2 - 3px) 0;
      rotate:45deg;
      background:var(--t-marker);
      box-shadow:0 0 12px rgba(217,174,66,.25);
    }
    @supports (anchor-name:--a){
      .site-nav__list{ anchor-scope:--spy; }
      .site-nav__list a[aria-current="true"]{ anchor-name:--spy; }
      .site-nav__list::after{
        position-anchor:--spy;
        left:calc(anchor(left) + var(--nav-pad));
        right:calc(anchor(right) + var(--nav-pad));
        top:calc(anchor(bottom) - 11px);
        width:auto;
        translate:none;
        transition:left var(--d-4) var(--ease-out),right var(--d-4) var(--ease-out),opacity var(--d-3) ease;
      }
      [data-theme] .site-nav__list::after{
        left:calc(anchor(center) - 3px); right:auto;
        top:calc(anchor(bottom) - 9px);
        width:6px;
        translate:none;
      }
    }
    .site-nav__list:not(:has(a[aria-current="true"]))::after{ opacity:0; }
  }

  /* Menu toggle (below 1024): two hairlines that cross into an X */
  .nav-toggle{
    display:none; place-items:center;
    width:44px; height:44px;
    margin-right:-10px;
    border-radius:var(--r-sm);
    color:var(--t-ink);
  }
  .nav-toggle__bars{ position:relative; width:22px; height:10px; }
  .nav-toggle__bars::before,.nav-toggle__bars::after{
    content:""; position:absolute; left:0; right:0;
    height:1.5px; border-radius:1px;
    background:currentColor;
    transition:translate var(--d-3) var(--ease-out),rotate var(--d-3) var(--ease-out);
  }
  .nav-toggle__bars::before{ top:0; }
  .nav-toggle__bars::after{ bottom:0; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars::before{ translate:0 4.25px; rotate:45deg; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars::after{ translate:0 -4.25px; rotate:-45deg; }

  /* Mobile sheet (below 1024). nav.js toggles .is-open on the header
     together with the toggle's aria-expanded. The sheet is absolutely
     positioned against the fixed header, so the header's blur or
     translate can never shrink it.                                 */
  @media (width < 1024px){
    .nav-toggle{ display:inline-grid; }

    .site-nav{
      position:absolute; inset:0 0 auto; z-index:0;
      height:100vh; height:100dvh;
      margin:0;
      padding:calc(var(--header-h) + clamp(20px,5vh,48px)) var(--gutter) calc(120px + env(safe-area-inset-bottom));
      overflow-y:auto; overscroll-behavior:contain;
      background:
        radial-gradient(120% 70% at 50% 0%,color-mix(in srgb,var(--t-surface) 70%,transparent),transparent 70%),
        var(--t-bg);
      visibility:hidden; opacity:0;
      transition:opacity var(--d-2) var(--ease-soft),visibility 0s linear var(--d-2);
    }
    .site-nav__list{ flex-direction:column; align-items:stretch; gap:0; }
    .site-nav__list li{ border-bottom:1px solid var(--t-line); }
    .site-nav__list li:nth-child(2){ --i:1; } .site-nav__list li:nth-child(3){ --i:2; }
    .site-nav__list li:nth-child(4){ --i:3; } .site-nav__list li:nth-child(5){ --i:4; }
    .site-nav__list li:nth-child(6){ --i:5; } .site-nav__list li:nth-child(7){ --i:6; }
    .site-nav__list li:nth-child(n+8){ --i:7; }
    .site-nav__list a{
      justify-content:space-between;
      height:auto; min-height:64px;
      padding:10px 0;
      font:var(--t-display-weight) 32px/1.1 var(--t-font-display);
      letter-spacing:var(--t-display-track);
      text-transform:none;
      color:var(--t-ink);
      opacity:0; translate:0 16px;
      transition:opacity var(--d-2) var(--ease-soft),translate var(--d-2) var(--ease-soft),color var(--d-4) var(--ease-soft);
    }
    .site-nav__list a::after{
      content:""; flex:none;
      width:7px; height:7px; border-radius:50%;
      background:var(--t-signal);
      opacity:0;
      transition:opacity var(--d-3) ease;
    }
    [data-theme] .site-nav__list a::after{ border-radius:0; rotate:45deg; background:var(--t-marker); }
    .site-nav__list a[aria-current="true"]::after{ opacity:1; }

    .site-header.is-open .site-nav{
      visibility:visible; opacity:1;
      transition:opacity var(--d-3) var(--ease-out),visibility 0s;
    }
    .site-header.is-open .site-nav__list a{
      opacity:1; translate:none;
      transition:
        opacity var(--d-4) var(--ease-out) calc(var(--d-1) + var(--i,0) * var(--stagger)),
        translate var(--d-4) var(--ease-out) calc(var(--d-1) + var(--i,0) * var(--stagger)),
        color var(--d-4) var(--ease-soft);
    }
    /* While open the header is solid ground for the sheet: no glass, no hiding. */
    .site-header.is-open{ translate:none; -webkit-backdrop-filter:none; backdrop-filter:none; }
    .site-header.is-open::before{ opacity:0; }
    html:has(.site-header.is-open){ overflow:hidden; }

    /* Studio header: Discord + Newsletter leave the bar and become the
       sheet's social row (the toggle takes their place at the right).
       The game header keeps its Buy button in the bar. */
    .site-header:not(.site-header--game) .site-header__actions{ display:none; }
    .site-header:not(.site-header--game) .nav-toggle{ margin-left:auto; }
    .site-header:not(.site-header--game).is-open .site-header__actions{
      display:flex;
      position:absolute;
      left:var(--gutter); right:var(--gutter);
      top:calc(100dvh - 96px - env(safe-area-inset-bottom));
      justify-content:space-between;
      margin:0; padding-top:20px;
      border-top:1px solid var(--t-line);
      animation:sheet-row-in var(--d-4) var(--ease-out) calc(var(--d-1) + 4 * var(--stagger)) backwards;
    }
  }
  @keyframes sheet-row-in{ from{ opacity:0; translate:0 12px; } }

  /* Without JavaScript the toggle cannot open the sheet, so below 1024
     the links stay in the bar as a compact row that scrolls sideways. */
  @media (width < 1024px){
    html:not(.js) .nav-toggle{ display:none; }
    html:not(.js) .site-nav{
      position:static; flex:1 1 auto; min-width:0; height:auto;
      margin-left:auto; padding:0;
      overflow-x:auto; scrollbar-width:none;
      background:none; visibility:visible; opacity:1;
      -webkit-mask-image:linear-gradient(90deg,#000 82%,transparent);
      mask-image:linear-gradient(90deg,#000 82%,transparent);
    }
    html:not(.js) .site-nav__list{ flex-direction:row; width:max-content; margin-left:auto; }   /* the auto margin collapses on overflow, so the first link stays reachable */
    html:not(.js) .site-nav__list li{ border:0; }
    html:not(.js) .site-nav__list a{
      min-height:44px; padding:0 8px;
      font:var(--t-label-weight) var(--fs-micro)/1 var(--t-font-label);
      letter-spacing:var(--t-label-track);
      text-transform:uppercase;
      color:var(--t-ink-2);
      opacity:1; translate:none;
    }
    html:not(.js) .site-nav__list a::after{ content:none; }
  }


  /* ── footer ────────────────────────────────────────────────────── */
  .site-footer{
    position:relative; z-index:var(--z-content);
    overflow:clip;
    padding-block:clamp(72px,9vw,128px) 28px;
    border-top:1px solid var(--t-line);
    font-size:var(--fs-sm);
    color:var(--t-ink-2);
  }
  .site-footer > :not(.site-footer__arch){ position:relative; z-index:1; width:var(--wrap); margin-inline:auto; }

  .site-footer__top{
    display:flex; flex-wrap:wrap; align-items:flex-end; justify-content:space-between;
    gap:24px 48px;
    padding-bottom:clamp(40px,5vw,64px);
  }
  .site-footer__tag{ max-width:34ch; font-size:var(--fs-lead); line-height:1.45; color:var(--t-ink-2); }

  .site-footer__cols{
    display:grid; grid-template-columns:repeat(4,minmax(0,1fr));
    gap:32px var(--grid-gap);
    padding-block:32px 48px;
    border-top:1px solid var(--t-line);
  }
  @media (width < 768px){ .site-footer__cols{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
  .site-footer__h{
    margin-bottom:14px;
    font:var(--t-label-weight) var(--fs-label)/1.3 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    color:var(--t-label);
  }
  .site-footer__cols ul{ display:grid; gap:2px; }
  .site-footer__cols a,.site-footer__totop{
    display:inline-flex; align-items:center;
    min-height:32px;
    color:var(--t-ink-2);
    text-decoration:underline 1px transparent;
    text-underline-offset:.3em;
    transition:color var(--d-4) var(--ease-soft),text-decoration-color var(--d-4) var(--ease-soft);
  }
  @media (pointer:coarse){ .site-footer__cols a,.site-footer__totop{ min-height:44px; } }
  @media (hover:hover){
    .site-footer__cols a:hover,.site-footer__totop:hover{
      color:var(--t-ink); text-decoration-color:var(--t-signal); transition-duration:var(--d-3);
    }
  }

  .site-footer__base{
    display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
    gap:8px 24px;
    padding-top:20px;
    border-top:1px solid var(--t-line);
    font:400 var(--fs-label)/1.5 var(--s-font-mono);
    letter-spacing:.06em;
    color:var(--t-ink-3);
  }
  .site-footer__base > p:first-child{ margin-right:auto; }
  .site-footer__totop{ gap:.5em; color:var(--t-ink-3); }

  /* The giant arch: hairline strokes, cut in half by the footer's bottom
     edge. Its beam lights once when the footer first enters view. */
  .site-footer__arch{
    position:absolute; left:50%; bottom:0; z-index:0;
    width:auto; max-width:none; height:clamp(560px,64vw,1040px);
    translate:-50% 50%;
    pointer-events:none;
  }
  .site-footer__arch :is(.sigil__outer,.sigil__inner,.sigil__horizon){ opacity:.06; }
  .site-footer__arch .sigil__beam{ opacity:.14; }
  @media (prefers-reduced-motion:no-preference){
    .site-footer.is-in .site-footer__arch .sigil__beam{ animation:arch-beam 2.6s var(--ease-io) both; }
  }
  @keyframes arch-beam{ 0%{ opacity:.08; } 38%{ opacity:.32; } 100%{ opacity:.14; } }


  /* ── buttons ───────────────────────────────────────────────────
     .btn--primary · --ghost · --text, sizes --sm · --lg, --block-sm.
     Studio primary is ivory; inside a theme it takes the game's
     button (tokens) plus the eld-shimmer sweep.                    */
  .btn{
    --btn-h:44px;
    position:relative; isolation:isolate;
    display:inline-flex; align-items:center; justify-content:center; gap:.55em;
    min-height:var(--btn-h);
    padding:0 1.35em;
    border:1px solid transparent;
    border-radius:var(--t-btn-radius);
    font-family:inherit; font-size:var(--fs-sm); font-weight:650; line-height:1.1;
    letter-spacing:.01em;
    text-align:center; white-space:nowrap;
    color:var(--t-ink);
    background:transparent;
    cursor:pointer;
    transition:
      color var(--d-4) var(--ease-soft),
      background-color var(--d-4) var(--ease-soft),
      border-color var(--d-4) var(--ease-soft),
      box-shadow var(--d-4) var(--ease-soft),
      filter var(--d-4) var(--ease-soft),
      translate var(--d-4) var(--ease-spring);
  }
  .btn--sm{ --btn-h:36px; padding-inline:1em; font-size:.8125rem; }
  .btn--lg{ --btn-h:52px; padding-inline:1.7em; font-size:1rem; }
  @media (pointer:coarse){
    .btn{ --btn-h:48px; }
    .btn--sm{ --btn-h:44px; }
    .btn--lg{ --btn-h:56px; }
  }
  @media (width < 640px){ .btn--block-sm{ display:flex; width:100%; } }
  .btn:disabled,.btn[aria-disabled="true"]{ opacity:.55; cursor:not-allowed; }
  .btn .icon{ font-size:1.15em; }

  .btn--primary{
    background:var(--t-btn);
    border-color:var(--t-btn-border);
    color:var(--t-on-accent);
  }
  .btn--ghost{ border-color:var(--t-line-2); color:var(--t-ink); }
  .btn--text{ min-height:44px; padding-inline:0; border:0; gap:.45em; color:var(--t-ink); }
  .btn--text > :is(.icon,.btn__arrow){ transition:translate var(--d-4) var(--ease-out); }

  @media (hover:hover){
    .btn:hover{ transition-duration:var(--d-3); }
    .btn--primary:hover{ background:var(--t-accent-hi); border-color:var(--t-accent-hi); }
    .btn--ghost:hover{ border-color:var(--t-line-3); background-color:color-mix(in srgb,var(--t-ink) 6%,transparent); }
    .btn--text:hover{ color:var(--t-accent-hi); }
    .btn--text:hover > :is(.icon,.btn__arrow){ translate:4px 0; }
    .btn--text:hover > .icon:has(use[href$="#i-arrow-ne"]){ translate:3px -3px; }
  }
  .btn--primary:active{ translate:0 1px; }

  /* Themed primary: the game's gold button, clipped so the shimmer band
     never leaves it. The sweep is transform-only and fills the first 24%
     of a 7s cycle (the game's eldShimmerLoop). */
  [data-theme] .btn--primary{
    overflow:hidden;
    box-shadow:inset 0 1px 1px rgba(255,240,197,.5),0 5px 24px rgba(0,0,0,.13);
  }
  [data-theme] .btn--primary::after{
    content:""; position:absolute; inset:0; z-index:-1;
    border-radius:inherit;
    pointer-events:none;
    background:linear-gradient(115deg,
      transparent 0% 16%,
      rgba(255,235,180,.04) 30%,
      rgba(255,225,140,.18) 45%,
      rgba(255,250,210,.42) 50%,
      rgba(255,225,140,.18) 55%,
      rgba(255,235,180,.04) 70%,
      transparent 84% 100%);
    mix-blend-mode:screen;
    opacity:0;
    transform:translateX(-100%);
    animation:eld-shimmer 7s cubic-bezier(.4,0,.2,1) infinite;
  }
  @keyframes eld-shimmer{
    0%  { transform:translateX(-100%); opacity:0; }
    6%  { opacity:.95; }
    20% { transform:translateX(100%); opacity:.95; }
    24% { opacity:0; }
    100%{ transform:translateX(100%); opacity:0; }
  }
  @media (hover:hover){
    [data-theme] .btn--primary:hover{
      background:var(--t-btn);
      border-color:var(--t-btn-border);
      filter:brightness(1.08);
      box-shadow:inset 0 1px 1px rgba(255,240,197,.5),0 0 24px color-mix(in srgb,var(--t-accent) 22%,transparent);
    }
  }

  /* Icon button: 44px round target */
  .icon-btn{
    display:inline-grid; place-items:center;
    width:44px; height:44px;
    border-radius:50%;
    color:var(--t-ink-2);
    font-size:20px;
    transition:color var(--d-4) var(--ease-soft),background-color var(--d-4) var(--ease-soft);
  }
  @media (hover:hover){
    .icon-btn:hover{ color:var(--t-ink); background-color:color-mix(in srgb,var(--t-ink) 7%,transparent); transition-duration:var(--d-3); }
  }


  /* ── chips ─────────────────────────────────────────────────────
     Always studio-styled, even inside a theme, so status reads the
     same for every game. The dot colour follows data-status.       */
  .chip{
    display:inline-flex; align-items:center; gap:.65em;
    min-height:26px;
    padding:0 .85em;
    border:1px solid var(--s-line-2);
    border-radius:var(--r-pill);
    background:rgb(10 10 11 / .55);
    font:500 var(--fs-label)/1 var(--s-font-mono);
    letter-spacing:.14em;
    text-transform:uppercase;
    white-space:nowrap;
    color:var(--s-ink);
  }
  .chip__dot{
    position:relative;
    flex:none;
    width:6px; height:6px;
    border-radius:50%;
    background:var(--chip-dot,var(--s-status-tba));
  }
  .chip[data-status="released"]{ --chip-dot:var(--s-status-out); }
  .chip[data-status="early-access"]{ --chip-dot:var(--s-status-ea); }
  .chip[data-status="in-development"]{ --chip-dot:var(--s-status-dev); }
  .chip[data-status="announced"]{ --chip-dot:var(--s-status-ann); }
  .chip[data-status="tba"]{ --chip-dot:var(--s-status-tba); }
  .chip[data-status="released"] .chip__dot::after{
    content:""; position:absolute; inset:0;
    border-radius:50%;
    background:inherit;
    animation:dot-pulse 2.4s var(--ease-out) infinite;
  }
  @keyframes dot-pulse{ 0%{ opacity:.7; scale:1; } 80%,100%{ opacity:0; scale:3; } }

  /* Game chip (news): studio type, the game's line and accent diamond */
  .chip--game{ border-color:var(--t-line-2); color:var(--s-ink-2); }
  .chip--game::before{
    content:""; flex:none;
    width:6px; height:6px;
    rotate:45deg;
    background:var(--t-accent);
  }

  /* Tags: mono pills */
  .tag-list{ display:flex; flex-wrap:wrap; gap:6px; }
  .tag-list > li{
    padding:.45em .8em;
    border:1px solid var(--t-line);
    border-radius:var(--r-pill);
    font:500 var(--fs-micro)/1 var(--s-font-mono);
    letter-spacing:.1em;
    text-transform:uppercase;
    color:var(--t-ink-2);
  }


  /* ── wordmark ──────────────────────────────────────────────────
     Gradient Cinzel with the game's logo light sweep: ::after repeats
     the word in a pale gold and crosses it on a 19s cycle (7s delay).
     The sweep's alt text is empty so the name is not read twice.   */
  .wordmark{
    position:relative;
    display:inline-block;
    vertical-align:top;
    margin:0;
    padding-left:.066em;
    font:600 var(--wm-size,var(--fs-mega))/1.06 var(--s-font-brand);
    letter-spacing:.066em;
    text-transform:uppercase;
    color:var(--t-accent);
    background:var(--t-wordmark);
    -webkit-background-clip:text;
    background-clip:text;
    -webkit-text-fill-color:transparent;
  }
  .wordmark[data-wordmark]::after{
    content:attr(data-wordmark);
    content:attr(data-wordmark) / "";
    position:absolute; inset:0;
    padding-left:.066em;
    color:var(--t-sweep);
    -webkit-text-fill-color:var(--t-sweep);
    clip-path:polygon(-35% 0,-20% 0,-38% 100%,-53% 100%);
    opacity:0;
    pointer-events:none;
    animation:logo-sweep 19s ease-in-out 7s infinite;
  }
  @keyframes logo-sweep{
    0%,83%,100%{ opacity:0; clip-path:polygon(-35% 0,-20% 0,-38% 100%,-53% 100%); }
    85%{ opacity:.48; }
    93%{ opacity:.38; clip-path:polygon(135% 0,150% 0,132% 100%,117% 100%); }
    94%{ opacity:0; clip-path:polygon(135% 0,150% 0,132% 100%,117% 100%); }
  }
  .wordmark__sub{
    margin:.85em 0 0;
    padding-left:.31em;
    font:500 var(--wm-sub-size,clamp(.6875rem,.55rem + .5vw,1.0625rem))/1.4 var(--s-font-brand);
    letter-spacing:.31em;
    text-transform:uppercase;
    color:var(--t-subtitle);
  }
  body[data-theme] .wordmark__sub{ font-family:var(--t-font-body); }
  /* Forced colours drop background images: keep the word, lose the sweep. */
  @media (forced-colors:active){
    .wordmark{ background:none; -webkit-text-fill-color:currentColor; }
    .wordmark::after{ content:none; }
  }


  /* ── kicker ────────────────────────────────────────────────────── */
  .kicker{
    font:var(--t-label-weight) var(--fs-label)/1.4 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    color:var(--t-kicker);
  }


  /* ── rules ─────────────────────────────────────────────────────
     .rule--diamond  1px line at 62% width, a 7px gem at its centre
     .rule--glyph    two hairlines around a glyph (put it in the text)
     .rule--fade     one faded hairline                              */
  .rule{ display:block; width:100%; margin:0; border:0; }
  .rule--diamond{
    position:relative;
    width:62%; height:1px;
    margin-inline:auto;
    background:linear-gradient(90deg,transparent,var(--t-rule-a) 23%,var(--t-rule-b) 48%,var(--t-rule-a) 77%,transparent);
  }
  .rule--diamond::after{
    content:""; position:absolute; left:calc(50% - 3.5px); top:-3px;
    width:7px; height:7px;
    border:1px solid var(--t-gem);
    background:var(--t-bg);
    box-shadow:0 0 0 4px var(--t-bg);
    rotate:45deg;
  }
  .rule--glyph{
    display:flex; align-items:center; justify-content:center; gap:18px;
    width:100%;
    font-size:11px; line-height:1;
    color:var(--t-glyph);
    text-shadow:0 0 7px color-mix(in srgb,var(--t-glyph) 50%,transparent);
  }
  .rule--glyph::before,.rule--glyph::after{
    content:""; flex:0 1 var(--rule-arm,96px);
    height:1px;
    background:linear-gradient(90deg,transparent,var(--t-glyph));
  }
  .rule--glyph::after{ scale:-1 1; }
  .rule--fade{
    width:100%; height:1px;
    background:linear-gradient(90deg,transparent,var(--t-line-2),transparent);
  }


  /* ── ornate frame (themed only) ────────────────────────────────
     The game's corner frame as a 9-slice border-image, drawn on an
     overlay so it costs no layout. Studio has no frame (--t-frame:none). */
  .frame-ornate{ position:relative; }
  .frame-ornate::after{
    content:""; position:absolute; inset:-10px; z-index:1;
    pointer-events:none;
    border:28px solid transparent;
    border-image:var(--t-frame) 36 / 28px stretch;
  }


  /* ── icons ─────────────────────────────────────────────────────── */
  .icon{
    display:inline-block;
    flex:none;
    width:1em; height:1em;
    vertical-align:-.14em;
    overflow:visible;
  }


  /* ── newsletter ────────────────────────────────────────────────
     Formspree contract (spec §6.8). newsletter.js adds .is-done on
     success (the beam flashes once beside the message), .is-error
     on failure, and disables the button while sending.             */
  .newsletter{ display:grid; gap:12px; max-width:540px; }
  .newsletter .hp{ display:none; }
  .newsletter__row{ display:flex; flex-wrap:wrap; gap:8px; }
  .newsletter__input{
    flex:1 1 220px; min-width:0;
    min-height:48px;
    padding:0 16px;
    border:1px solid var(--t-line-2);
    border-radius:var(--t-btn-radius);
    background:color-mix(in srgb,var(--t-surface) 72%,transparent);
    font-size:1rem;                         /* 16px: no zoom on focus in iOS */
    color:var(--t-ink);
    transition:border-color var(--d-4) var(--ease-soft),background-color var(--d-4) var(--ease-soft);
  }
  .newsletter__input::placeholder{ color:var(--t-ink-3); opacity:1; }
  @media (hover:hover){ .newsletter__input:hover{ border-color:var(--t-line-3); transition-duration:var(--d-3); } }
  .newsletter__input{ border-color:color-mix(in srgb,var(--t-ink) 45%,transparent); }
  .newsletter__input:focus-visible{ outline-offset:2px; border-color:transparent; background-color:var(--t-surface); }
  .newsletter__row .btn{ --btn-h:48px; }
  .newsletter button:disabled{ opacity:.6; cursor:progress; }
  .newsletter__status{
    display:flex; align-items:center; gap:10px;
    min-height:1.5em;
    font-size:var(--fs-sm);
    color:var(--t-ink-2);
  }
  .newsletter.is-done .newsletter__status{ color:var(--t-ink); }
  .newsletter.is-done .newsletter__status::before{
    content:""; flex:none;
    width:1px; height:1.25em;
    background:var(--t-lumen);
    box-shadow:0 0 8px 1px color-mix(in srgb,var(--t-lumen) 60%,transparent);
    animation:beam-flash 1.6s var(--ease-io) both;
  }
  .newsletter.is-error .newsletter__status{ color:var(--s-danger); }
  @keyframes beam-flash{ 0%{ opacity:.2; } 30%{ opacity:1; } 100%{ opacity:.45; } }
  .newsletter__fine{ font-size:var(--fs-label); line-height:1.5; color:var(--t-ink-3); }


  /* ── dialogs (lightbox and video theatre) ──────────────────────
     Built by lightbox.js / video.js and opened with showModal():

     <dialog class="dialog dialog--lightbox|dialog--theatre" aria-label="…">
       <div class="dialog__bar">
         <p class="dialog__counter">3 / 7</p>  or  <p class="dialog__title">…</p>
         <button class="dialog__close" type="button" aria-label="Close">[i-close]</button>
       </div>
       <figure class="dialog__frame">
         <img class="dialog__media" data-loading …>  or  <video class="dialog__media" controls playsinline>
         <figcaption class="dialog__caption">…</figcaption>
       </figure>
       <button class="dialog__nav dialog__nav--prev" aria-label="Previous image">[i-chevron-l]</button>
       <button class="dialog__nav dialog__nav--next" aria-label="Next image">[i-chevron-r]</button>
     </dialog>

     data-loading on the image = blur-up: show the thumbnail, swap in
     the full file, then remove the attribute.                      */
  .dialog{
    --dialog-chrome:176px;                  /* bar + caption + padding */
    position:fixed; inset:0;
    width:100%; height:100%;
    max-width:none; max-height:none;
    margin:0;
    padding:clamp(12px,2.6vw,36px) clamp(12px,2.6vw,36px) max(clamp(12px,2.6vw,36px),env(safe-area-inset-bottom));
    border:0;
    background:transparent;
    color:var(--t-ink);
    overflow:hidden;
    display:grid;
    grid-template-rows:auto minmax(0,1fr);
    justify-items:center; align-items:center;
    gap:clamp(8px,1.4vw,16px);
    opacity:0;
    transition:opacity var(--d-2) var(--ease-soft),overlay var(--d-2) allow-discrete,display var(--d-2) allow-discrete;
  }
  .dialog:not([open]){ display:none; }
  .dialog[open]{ opacity:1; transition-duration:var(--d-3); }
  @starting-style{ .dialog[open]{ opacity:0; } }
  .dialog::backdrop{
    background:rgb(3 3 5 / .92);
    -webkit-backdrop-filter:blur(10px);
    backdrop-filter:blur(10px);
    opacity:0;
    transition:opacity var(--d-2) var(--ease-soft),overlay var(--d-2) allow-discrete,display var(--d-2) allow-discrete;
  }
  .dialog[open]::backdrop{ opacity:1; transition-duration:var(--d-3); }
  @starting-style{ .dialog[open]::backdrop{ opacity:0; } }

  .dialog__bar{
    justify-self:stretch;
    display:flex; align-items:center; justify-content:space-between; gap:16px;
    min-height:44px;
  }
  .dialog__title,.dialog__counter{
    font:var(--t-label-weight) var(--fs-label)/1.3 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    color:var(--t-ink-2);
  }
  .dialog__counter{ font-variant-numeric:tabular-nums; }
  .dialog__close,.dialog__nav{
    display:grid; place-items:center;
    width:44px; height:44px;
    border:1px solid var(--t-line-2);
    border-radius:50%;
    background:rgb(10 10 11 / .5);
    color:var(--t-ink);
    font-size:20px;
    transition:border-color var(--d-4) var(--ease-soft),background-color var(--d-4) var(--ease-soft);
  }
  .dialog__close{ margin-left:auto; }
  @media (hover:hover){
    :is(.dialog__close,.dialog__nav):hover{ border-color:var(--t-line-3); background-color:rgb(30 30 34 / .75); transition-duration:var(--d-3); }
  }

  .dialog__frame{
    position:relative;
    display:flex; flex-direction:column; align-items:center; gap:14px;
    max-width:100%; min-height:0;
    margin:0;
  }
  .dialog__media{
    display:block;
    max-width:min(100%,1600px);
    max-height:calc(100svh - var(--dialog-chrome));
    width:auto; height:auto;
    border-radius:var(--r-sm);
    box-shadow:var(--e3);
    background:#000;
    transition:filter var(--d-4) var(--ease-out),scale var(--d-4) var(--ease-out);
  }
  .dialog__media[data-loading]{ filter:blur(14px); scale:1.02; }
  .dialog__caption{
    font:var(--t-label-weight) var(--fs-label)/1.4 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    text-align:center;
    color:var(--t-ink-2);
  }
  .dialog__nav{
    position:absolute; top:50%;
    width:52px; height:52px;
    translate:0 -50%;
  }
  .dialog__nav--prev{ left:clamp(8px,2vw,28px); }
  .dialog__nav--next{ right:clamp(8px,2vw,28px); }
  @media (width < 640px){
    .dialog__nav{ top:auto; bottom:max(16px,env(safe-area-inset-bottom)); translate:none; }
  }

  .dialog--lightbox .dialog__media{
    width:min(100%,1600px,calc((100svh - var(--dialog-chrome)) * var(--ar)));
    aspect-ratio:var(--ar);
    object-fit:contain;
  }
  .dialog--theatre .dialog__media{
    width:min(100%,1600px,calc((100svh - var(--dialog-chrome)) * 16 / 9));
    max-height:none;
    aspect-ratio:16 / 9;
  }


  /* ── scroll cue ────────────────────────────────────────────────
     Studio: a label over a falling hairline. Themed: the game's gold
     diamond, bobbing 6px.                                          */
  .scroll-cue{
    position:absolute; left:50%; bottom:clamp(14px,3vh,32px); z-index:2;
    display:flex; flex-direction:column; align-items:center; gap:10px;
    min-width:44px; min-height:44px;
    translate:-50% 0;
    font:var(--t-label-weight) var(--fs-micro)/1 var(--t-font-label);
    letter-spacing:var(--t-label-track);
    text-transform:uppercase;
    color:var(--t-ink-3);
    transition:color var(--d-4) var(--ease-soft);
  }
  .scroll-cue::after{
    content:"";
    width:1px; height:36px;
    background:linear-gradient(var(--t-ink-3),transparent);
    transform-origin:50% 0;
    animation:cue-line 2.6s var(--ease-io) infinite;
  }
  [data-theme] .scroll-cue::after{
    width:8px; height:8px;
    margin-top:2px;
    background:var(--t-marker);
    box-shadow:0 0 12px rgba(217,174,66,.25);
    transform-origin:50% 50%;
    animation:cue-bob 2.4s ease-in-out infinite;
  }
  @media (hover:hover){ .scroll-cue:hover{ color:var(--t-ink); transition-duration:var(--d-3); } }
  @keyframes cue-line{ 0%{ transform:scaleY(0); opacity:0; } 35%{ opacity:1; } 100%{ transform:scaleY(1); opacity:0; } }
  @keyframes cue-bob{ 0%,100%{ transform:translateY(0) rotate(45deg); } 50%{ transform:translateY(6px) rotate(45deg); } }


  /* ── reveal states ─────────────────────────────────────────────
     Hidden only with JS and motion allowed; reveal.js adds .is-in
     (IntersectionObserver, rootMargin 0 0 -12% 0). Entrances are
     keyframes with backwards fill on the individual transform
     properties, so they never fight a component's own transform or
     leave a stagger delay on its hover transitions. Stagger:
     reveal.js gives each child --i; the delay caps at 8 steps.     */
  [data-reveal],[data-reveal-stagger] > *{ --reveal-delay:calc(min(var(--i,0),8) * var(--stagger)); }
  @media (prefers-reduced-motion:no-preference){
    html.js:not(.reduced) [data-reveal]:not([data-reveal-stagger]):not(.is-in),
    html.js:not(.reduced) [data-reveal][data-reveal-stagger]:not(.is-in) > *{ opacity:0; }

    html.js:not(.reduced) :is([data-reveal=""],[data-reveal="rise"]):not([data-reveal-stagger]):not(.is-in),
    html.js:not(.reduced) :is([data-reveal=""],[data-reveal="rise"])[data-reveal-stagger]:not(.is-in) > *{ translate:0 24px; }
    html.js:not(.reduced) [data-reveal="scale"]:not([data-reveal-stagger]):not(.is-in),
    html.js:not(.reduced) [data-reveal="scale"][data-reveal-stagger]:not(.is-in) > *{ scale:.96; }
    html.js:not(.reduced) [data-reveal="mask"]:not([data-reveal-stagger]):not(.is-in),
    html.js:not(.reduced) [data-reveal="mask"][data-reveal-stagger]:not(.is-in) > *{ clip-path:inset(100% 0 0 0); }

    :is([data-reveal=""],[data-reveal="rise"]).is-in:not([data-reveal-stagger]),
    :is([data-reveal=""],[data-reveal="rise"])[data-reveal-stagger].is-in > *{ animation:reveal-rise var(--d-5) var(--ease-out) var(--reveal-delay) backwards; }
    [data-reveal="fade"].is-in:not([data-reveal-stagger]),
    [data-reveal="fade"][data-reveal-stagger].is-in > *{ animation:reveal-fade var(--d-5) var(--ease-out) var(--reveal-delay) backwards; }
    [data-reveal="scale"].is-in:not([data-reveal-stagger]),
    [data-reveal="scale"][data-reveal-stagger].is-in > *{ animation:reveal-scale var(--d-5) var(--ease-out) var(--reveal-delay) backwards; }
    [data-reveal="mask"].is-in:not([data-reveal-stagger]),
    [data-reveal="mask"][data-reveal-stagger].is-in > *{ animation:reveal-mask var(--d-5) var(--ease-cine) var(--reveal-delay) backwards; }
  }
  @keyframes reveal-rise{ from{ opacity:0; translate:0 24px; } }
  @keyframes reveal-fade{ from{ opacity:0; } }
  @keyframes reveal-scale{ from{ opacity:0; scale:.96; } }
  @keyframes reveal-mask{ from{ clip-path:inset(100% 0 0 0); translate:0 16px; } to{ clip-path:inset(0); } }

  /* Split words: reveal.js wraps each word in .w > span (outer clips,
     inner rises from 110%, 40ms apart) and marks the [data-split]
     element .is-in when it is seen. Only JS creates .w, so words are
     never stranded hidden without it. */
  .w{
    display:inline-block;
    overflow:clip;
    vertical-align:top;
    padding-block:.1em .14em;
    margin-block:-.1em -.14em;
  }
  .w > span{ display:inline-block; }
  @media (prefers-reduced-motion:no-preference){
    html.js:not(.reduced) [data-split]:not(.is-in) .w > span{ translate:0 110%; }
    [data-split].is-in .w > span{
      animation:word-rise var(--d-5) var(--ease-out) backwards;
      animation-delay:calc(var(--i,0) * 40ms);
    }
  }
  @media (prefers-reduced-motion:no-preference) and (width >= 1024px){
    html.js:not(.reduced) [data-split-blur]:not(.is-in) .w > span{ filter:blur(8px); }
    [data-split-blur].is-in .w > span{ animation-name:word-rise-blur; }
  }
  @keyframes word-rise{ from{ translate:0 110%; } }
  @keyframes word-rise-blur{ from{ translate:0 110%; filter:blur(8px); } }

  /* Count-up: digits keep their width while countTo() runs. */
  [data-count-to]{ font-variant-numeric:tabular-nums; }


  /* ── media and live data hooks ─────────────────────────────────── */
  /* Hover previews fade in over 400ms once video.js marks them playing. */
  video[data-video="hover"]{ opacity:0; transition:opacity var(--d-2) var(--ease-soft); }
  video[data-video="hover"].is-playing{ opacity:1; transition:opacity 400ms var(--ease-out); }
  a[data-lightbox]{ cursor:zoom-in; }

  /* Steam: discount markup <span class="off">-NN%</span> <s>was</s> now */
  [data-steam="price"] .off{
    display:inline-block;
    margin-right:.25em;
    padding:.12em .42em;
    border-radius:var(--r-xs);
    background:var(--t-accent);
    color:var(--t-on-accent);
    font-weight:700; font-size:.86em; line-height:1.1;
  }
  [data-steam="price"] s{ opacity:.6; text-decoration-thickness:1px; }
  .btn--primary [data-steam="price"] .off{ background:var(--t-on-accent); color:var(--t-accent-hi); }
  [data-steam="review-desc"][data-tone="positive"]{ color:var(--t-accent-hi); }
  [data-steam="review-desc"][data-tone="mixed"]{ color:var(--s-status-ea); }
  [data-steam="review-desc"][data-tone="negative"]{ color:var(--s-danger); }

  /* Pointer hooks: pointer.js writes the variables, CSS composes them,
     so a component's own transform and hover lift still apply. */
  [data-spotlight]{ --mx:50%; --my:50%; }
  @media (prefers-reduced-motion:no-preference){
    html.fine [data-tilt]{ transform:perspective(1200px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)); }
    html.fine [data-magnetic]{ translate:var(--mag-x,0px) var(--mag-y,0px); }
    html.fine [data-magnetic]:not(.btn){ transition:translate var(--d-4) var(--ease-spring); }
  }


  /* ── theme islands ─────────────────────────────────────────────
     Themed islands on a studio page (cards, slides, news art, game
     chips) show the game's colour and its Cinzel wordmark, but their
     text stays in studio type, so the homepage never downloads Nunito.                               */
  body:not([data-theme]) [data-theme]{
    --t-font-body:var(--s-font-sans);
    --t-font-label:var(--s-font-mono);
    --t-label-weight:500;
    --t-label-track:.14em;
    font-family:var(--s-font-sans);
  }
}


/* ── utilities ─────────────────────────────────────────────────── */
@layer utilities{
  .sr-only,
  .visually-hidden-until-focus:not(:focus):not(:focus-within){
    position:absolute;
    width:1px; height:1px;
    margin:-1px; padding:0;
    overflow:hidden;
    clip-path:inset(50%);
    white-space:nowrap;
    border:0;
  }

  /* Skip link: off-canvas until focused, always the mint ring. */
  .skip-link{
    position:fixed; left:var(--gutter); top:12px; z-index:calc(var(--z-intro) + 1);
    padding:13px 18px;
    border-radius:var(--r-sm);
    background:var(--s-ink);
    color:var(--s-bg);
    font:650 var(--fs-sm)/1 var(--s-font-sans);
    translate:0 calc(-100% - 24px);
    transition:translate var(--d-3) var(--ease-out);
  }
  .skip-link:focus,.skip-link:focus-visible{
    translate:none;
    outline:2px solid var(--s-focus);
    outline-offset:3px;
  }
}
