/* =============================================================================
 * NCG GAMES — games.css  ·  the games surface design language
 * Canonical source: sites/games/css/games.css   ·   Docs: css/games.md
 * =============================================================================
 *
 * WHAT THIS IS
 * ------------
 * The approved games hub (`sites/games/index.html`) is the REFERENCE for this
 * surface. Its design language was read out of that file and written down here
 * so the three other games can wear it without anyone re-deriving it by eye:
 *
 *   · cream paper ground with a fine rule, generous whitespace, calm rhythm
 *   · hairline-bordered cards in a grid, ONE burgundy accent cell
 *   · mono small-caps letter-spaced micro-labels as section eyebrows
 *   · serif display (Fraunces) over sans body (Inter); mono (JetBrains Mono)
 *     for data, keys, counts and code
 *   · numbered curriculum steps with a thin rule and muted numerals
 *   · explanatory cards in plain language
 *   · muted crimson used sparingly as the SINGLE accent; status colours
 *     reserved for status
 *
 * WHAT IT CONSUMES, AND WHAT IT MAY NOT DECLARE
 * ---------------------------------------------
 * `css/tokens.css` is the canonical palette, type scale, spacing, radii, motion
 * and focus contract. It is namespaced `--ncg-*`. THIS FILE DECLARES NO COLOUR
 * OF ITS OWN. Every `--games-*` property below is a POINTER at an `--ncg-*`
 * token, with the token's own literal as the `var()` fallback so the sheet still
 * renders if `tokens.css` fails to load — the same fallback convention
 * `components.css` uses. If you need a value this system does not have, add an
 * `--ncg-*` token to `sites/shared/tokens.css` and re-copy it; do not invent one
 * here.
 *
 *   LOAD ORDER (both files must be served from this origin — see components.md
 *   §1; a cross-origin <link> is blocked by our own CSP):
 *     <link rel="stylesheet" href="/css/tokens.css">   <!-- FIRST  -->
 *     <link rel="stylesheet" href="/css/games.css">    <!-- SECOND -->
 *
 * TWO PLACES WHERE THE HUB'S LITERAL AND THE TOKEN DIFFER — recorded, not hidden
 * -----------------------------------------------------------------------------
 *   1. The hub's card surface is `--hub-surface:#FFFDF8`; the shared role is
 *      `--ncg-surface-card:#FFFFFF`. Delta is 7/255 on blue — below a perceptual
 *      threshold on a cream ground. The ROLE is used, so the games surface
 *      consumes the shared contract unmodified rather than forcing a token edit
 *      that every other property would have to re-sync for an invisible change.
 *   2. The hub's hairline is `--hub-line:#DED8CC`; the default role `--ncg-line`
 *      is `#ECE9E0`, which is too faint to carry the hub's grid — on the hub the
 *      grid IS the design. `--ncg-warm-line` (#E7E0D5) is the nearest shipped
 *      token and is used for the hairline; `--ncg-warm-line-2` (#D8CFC0) for an
 *      edge that has to be seen. Both are existing tokens.
 *
 * GROUNDS — burgundy is a FIRST-CLASS ground, not an exception
 * ------------------------------------------------------------
 * A page declares its ground once, on <html>:
 *
 *     <html data-games-ground="paper">      <!-- PCT, Career, Interceptor -->
 *     <html data-games-ground="burgundy">   <!-- Ciber Traversal          -->
 *
 * Both grounds re-point the SAME aliases, so every class in this file and every
 * bridged engine surface renders correctly on either without a single forked
 * rule. Ciber Traversal's deep burgundy is deliberate — on the hub it is the one
 * accent cell among light cards, and that contrast is what marks it as the
 * destination. It is supported here on purpose so nobody later "harmonises" it
 * away as a bug.
 *
 * THE SEAM BETWEEN WORLD AND CHROME
 * ---------------------------------
 * Career and Interceptor render a 3D world. The world keeps its own scene
 * palette — sky, terrain, water, vehicle are not chrome. Everything the player
 * READS is chrome, and chrome is PAPER LAID OVER THE WORLD: a near-solid sheet
 * with a hairline border and a soft shadow, so the seam reads as deliberate.
 *
 *   CONTRAST GUARANTEE (this is the reason for the number, see §7):
 *   the sheet backing is 96% opaque, so the scene beneath can move the
 *   composited surface by at most 4%. Measured worst cases on the paper ground —
 *   #FAF9F6 over pure black composites to #F0EFEC, and body ink #1C1917 on that
 *   is 15.2:1; over a white sky it is 16.6:1. On the burgundy ground, #3D1219
 *   over a white sky composites to #451C22 and its cream ink is 13.3:1. A
 *   low-alpha wash would have inverted over pale terrain. This cannot.
 *
 * THE ENGINE BRIDGE (§8)
 * ----------------------
 * `js/engine3d/hud.js`, `js/engine3d/a11y.js` and `js/games/career/overlay.js`
 * each inject their own <style> at runtime and each declares its own local
 * custom properties. Those files are NOT edited here. This sheet re-points their
 * local properties from the ground attribute — an ancestor selector, so it wins
 * on specificity regardless of injection order — which is the same "re-point the
 * roles, never edit a component rule" mechanism components.md §3 documents.
 * A page that does not carry `data-games-ground` keeps the engine's own look;
 * adoption is one attribute and cannot half-apply.
 *
 * THE BRIDGE IS SCOPED TO `[data-scale="normal"]` ON PURPOSE.
 * At `[data-scale="large"]` the engine switches to Ciber Traversal's MEASURED
 * Senior treatment (20px base, #FFF6E4 on #241016, 58px targets, gold #FFD24A —
 * tokens.css §20, CAREER-CAR.md §5). That treatment is accessibility work that
 * was measured, not styled, and this layer does not touch it.
 *
 * CLASS NAMING
 * ------------
 * `.games-*` are the canonical names for new markup. Where an engine module
 * already ships a name for the same thing, both appear in ONE selector list on
 * ONE declaration block, so they cannot drift apart — the convention
 * components.md §1 already sets for `.terminal-card` / `.term-card`.
 * ========================================================================== */


/* ═══════════════════════════════════════════════════════════════════════════
   §1 · ALIASES — pointers, never literals.
   Declared on :root so the .games-* classes work even on a page that forgot the
   ground attribute; §2 re-points them per ground.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* surfaces */
  --games-paper:var(--ncg-surface,#FAF9F6);            /* the page ground        */
  --games-surface:var(--ncg-surface-card,#FFFFFF);     /* cards — LIGHTER than
                                                          the ground, as on the
                                                          hub                    */
  --games-surface-2:var(--ncg-surface-raised,#F3F1EA); /* hover / sunken         */
  /* --games-surface may be a GRADIENT on the burgundy ground. Anything that
     needs a flat fill — a keycap, a chip, a color-mix() argument — uses this. */
  --games-surface-flat:var(--ncg-surface-card,#FFFFFF);

  /* ink. --ncg-text-3 is 4.56:1 on the base surface and 4.24:1 on a raised one,
     so it is never used on a card in this file (tokens.css §1 contrast trap). */
  --games-ink:var(--ncg-text,#1C1917);
  --games-ink-2:var(--ncg-text-2,#44403C);
  --games-ink-3:var(--ncg-text-3,#78716C);
  --games-on-accent:var(--ncg-text-on-accent,#FAF9F6);

  /* rules. See the header: the hub's grid needs a hairline that reads. */
  --games-rule:var(--ncg-warm-line,#E7E0D5);
  --games-rule-strong:var(--ncg-warm-line-2,#D8CFC0);

  /* the single accent */
  --games-accent:var(--ncg-accent,#914955);
  --games-accent-strong:var(--ncg-accent-strong,#7A3B46);
  --games-accent-soft:var(--ncg-accent-soft,rgba(145,73,85,.08));
  --games-accent-line:var(--ncg-accent-line,rgba(145,73,85,.22));

  /* status — reserved for status, never decoration */
  --games-ok:var(--ncg-ok-text,#1F7A3A);
  --games-ok-tint:var(--ncg-ok-bg,#E7F3EB);
  --games-ok-line:var(--ncg-ok-line,rgba(31,122,58,.32));
  --games-danger:var(--ncg-danger-text,#B81D30);
  --games-danger-tint:var(--ncg-danger-tint,#FCE8EB);
  --games-danger-line:var(--ncg-danger-line,rgba(184,29,48,.30));

  /* focus. Navy, not the accent: a focus ring must never be mistaken for an
     accent state, and #1E5285 measures 7.67:1 on paper. §2 swaps it on the
     burgundy ground, where navy would be 2.0:1. */
  --games-focus:var(--ncg-focus-alt-color,#1E5285);
  --games-focus-w:var(--ncg-focus-alt-w,3px);
  --games-focus-off:var(--ncg-focus-alt-off,2px);

  /* type */
  --games-serif:var(--ncg-serif,'Fraunces',Georgia,serif);
  --games-sans:var(--ncg-sans,'Inter',system-ui,-apple-system,sans-serif);
  --games-mono:var(--ncg-mono,'JetBrains Mono',ui-monospace,Menlo,monospace);
  --games-lh:var(--ncg-lh-editorial,1.65);

  /* geometry */
  --games-r:var(--ncg-r-card,14px);
  --games-r-sm:var(--ncg-r-sm,10px);
  --games-r-pill:var(--ncg-r-pill,999px);
  --games-hair:var(--ncg-hairline,1px);
  --games-tap:var(--ncg-target-min,44px);
  --games-shadow:var(--ncg-elev-1,0 1px 2px rgba(28,25,23,.04),0 8px 24px rgba(28,25,23,.05));
  --games-shadow-lift:var(--ncg-shadow,0 4px 12px rgba(26,21,18,.06),0 12px 32px rgba(26,21,18,.07));

  /* rhythm and measure */
  --games-rhythm:clamp(var(--ncg-sp-7-5,34px),5vw,var(--ncg-sp-9,48px));
  --games-measure:66ch;        /* body copy — the hub's 60–66ch                  */
  --games-measure-wide:74ch;   /* long-form briefing pages                       */
  --games-wrap:var(--ncg-maxw,1080px);

  /* motion */
  --games-dur:var(--ncg-dur-3,.2s);
  --games-ease:var(--ncg-ease,cubic-bezier(.22,.61,.36,1));

  /* PAPER-OVER-WORLD backing. Fully opaque by default; §8 raises it to 96%
     where color-mix() is supported. Declared OUTSIDE @supports as well, because
     a custom property whose value resolves to an unsupported function is
     invalid at computed-value time — the declaration is not "skipped", the
     property computes to `transparent`, and the sheet disappears. */
  --games-veil:var(--games-surface-flat);
}


/* ═══════════════════════════════════════════════════════════════════════════
   §2 · GROUNDS.
   Each ground declares the SAME complete set, so either can be nested inside
   the other as an island and nothing leaks across the boundary. Equal
   specificity, so the two blocks are order-dependent only against each other;
   keep them adjacent and keep them complete.
   ═══════════════════════════════════════════════════════════════════════════ */

[data-games-ground="paper"]{
  --games-paper:var(--ncg-surface,#FAF9F6);
  --games-surface:var(--ncg-surface-card,#FFFFFF);
  --games-surface-2:var(--ncg-surface-raised,#F3F1EA);
  --games-surface-flat:var(--ncg-surface-card,#FFFFFF);
  --games-ink:var(--ncg-text,#1C1917);
  --games-ink-2:var(--ncg-text-2,#44403C);
  --games-ink-3:var(--ncg-text-3,#78716C);
  --games-on-accent:var(--ncg-text-on-accent,#FAF9F6);
  --games-rule:var(--ncg-warm-line,#E7E0D5);
  --games-rule-strong:var(--ncg-warm-line-2,#D8CFC0);
  --games-accent:var(--ncg-accent,#914955);
  --games-accent-strong:var(--ncg-accent-strong,#7A3B46);
  --games-accent-soft:var(--ncg-accent-soft,rgba(145,73,85,.08));
  --games-accent-line:var(--ncg-accent-line,rgba(145,73,85,.22));
  --games-ok:var(--ncg-ok-text,#1F7A3A);
  --games-ok-tint:var(--ncg-ok-bg,#E7F3EB);
  --games-ok-line:var(--ncg-ok-line,rgba(31,122,58,.32));
  --games-danger:var(--ncg-danger-text,#B81D30);
  --games-danger-tint:var(--ncg-danger-tint,#FCE8EB);
  --games-danger-line:var(--ncg-danger-line,rgba(184,29,48,.30));
  --games-focus:var(--ncg-focus-alt-color,#1E5285);
  --games-shadow:var(--ncg-elev-1,0 1px 2px rgba(28,25,23,.04),0 8px 24px rgba(28,25,23,.05));
  --games-shadow-lift:var(--ncg-shadow,0 4px 12px rgba(26,21,18,.06),0 12px 32px rgba(26,21,18,.07));
  --games-dot:rgba(28,25,23,.05);
}

/* THE FLAGSHIP'S GROUND. Ciber Traversal's dark terminal identity, expressed in
   tokens that already ship: --ncg-capstone-* for the surfaces and
   --ncg-dark-crimson-ink for the accent. This is NOT a second accent colour —
   it is the same crimson lifted for a dark ground, exactly as tokens.css §18
   lifts it for dark mode. Measured on --ncg-capstone-deep #3D1219:
     capstone-cream #FBF3E4 ink ........ 14.66:1  AAA
     dark-crimson-ink #E0A0AC accent ....  7.54:1  AAA
     capstone-cream focus ring ......... 14.66:1  (navy would be 2.00:1)     */
[data-games-ground="burgundy"]{
  --games-paper:var(--ncg-capstone-deep,#3d1219);
  --games-surface:var(--ncg-capstone-grad,linear-gradient(155deg,#7A2C39,#5e2029));
  --games-surface-2:var(--ncg-capstone-deep,#3d1219);
  --games-surface-flat:var(--ncg-capstone-deep,#3d1219);
  --games-ink:var(--ncg-capstone-cream,#FBF3E4);
  --games-ink-2:var(--ncg-capstone-cream,#FBF3E4);
  --games-ink-3:var(--ncg-capstone-cream,#FBF3E4);
  --games-on-accent:var(--ncg-capstone-deep,#3d1219);
  --games-rule:var(--ncg-dark-line-2,#3A2D25);
  --games-rule-strong:var(--ncg-dark-ink-4,#7A6F64);
  --games-accent:var(--ncg-dark-crimson-ink,#E0A0AC);
  --games-accent-strong:var(--ncg-dark-crimson-2,#D98A98);
  --games-accent-soft:rgba(199,107,123,.12);
  --games-accent-line:rgba(199,107,123,.32);
  --games-ok:var(--ncg-dark-green-t,#84B583);
  --games-ok-tint:var(--ncg-capstone-deep,#3d1219);
  --games-ok-line:rgba(132,181,131,.34);
  --games-danger:var(--ncg-dark-crimson-2,#D98A98);
  --games-danger-tint:var(--ncg-capstone-deep,#3d1219);
  --games-danger-line:rgba(217,138,152,.34);
  --games-focus:var(--ncg-capstone-cream,#FBF3E4);
  /* The dark shadow literals, quoted from tokens.css §18. They are NOT read
     through var(--ncg-shadow) because that token only becomes the dark pair
     inside [data-theme="dark"] — and this ground is a dark ISLAND that must
     also work on a light page, which is exactly the case var() would miss. */
  --games-shadow:0 1px 2px rgba(0,0,0,.3),0 2px 8px rgba(0,0,0,.3);
  --games-shadow-lift:0 4px 12px rgba(0,0,0,.35),0 12px 32px rgba(0,0,0,.4);
  --games-dot:rgba(251,243,228,.05);   /* capstone cream at the hub's 5% */
}

/* Re-resolve the veil ON the ground element, so a dark island declared on a
   SUBTREE (rather than on <html>) still composites from its own surface. */
[data-games-ground]{ --games-veil:var(--games-surface-flat); }


/* ═══════════════════════════════════════════════════════════════════════════
   §3 · PAGE SHELL — paper ground, fine rule, measure, vertical rhythm.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The dotted paper ground, at the hub's 22px pitch (--ncg-sp-5-5). The dot
   colour is the ink at 5% — quoted verbatim from `neural.css` body, which is
   where the hub gets it; the dark-ground counterpart is the same 5% of the dark
   ground's ink, quoted from the hub's own dark block.
   `background-attachment:fixed` is deliberately NOT used: it forces a repaint of
   the whole ground on every scroll frame. */
.games-page{
  margin:0;
  background-color:var(--games-paper);
  background-image:radial-gradient(var(--games-dot,rgba(28,25,23,.05)) 1px,transparent 1px);
  background-size:var(--ncg-sp-5-5,22px) var(--ncg-sp-5-5,22px);
  color:var(--games-ink);
  font-family:var(--games-sans);
  font-size:var(--ncg-fs-body,16px);
  line-height:var(--games-lh);
  -webkit-font-smoothing:antialiased;
}

.games-shell{ max-width:var(--games-wrap); margin-inline:auto; padding-inline:var(--ncg-sp-5,20px); }
.games-measure{ max-width:var(--games-measure); }
.games-measure--wide{ max-width:var(--games-measure-wide); }

/* Vertical rhythm. One rule, applied to the section container, rather than
   per-element margins that drift out of step. */
.games-section{ margin-block-start:var(--games-rhythm); scroll-margin-top:var(--ncg-sp-5,20px); }
.games-section > * + *{ margin-block-start:var(--ncg-sp-3,12px); }

/* The fine rule. Decorative, so it is hidden from assistive technology by the
   markup (see games.md), not by a role here. */
.games-rule{
  border:0; height:var(--games-hair); background:var(--games-rule);
  margin-block:var(--games-rhythm);
}

.games-title{
  font-family:var(--games-serif); font-weight:600;
  letter-spacing:var(--ncg-ls-tight,-.02em); line-height:var(--ncg-lh-tight,1.1);
  color:var(--games-ink); margin:0;
  font-size:clamp(1.9rem,1.35rem + 2.4vw,2.9rem);
}
.games-title em{ font-style:italic; color:var(--games-accent); }

.games-h2{
  font-family:var(--games-serif); font-weight:600; color:var(--games-ink);
  letter-spacing:var(--ncg-ls-snug,-.01em);
  font-size:var(--ncg-fs-h2,clamp(22px,3vw,30px)); margin:0;
}
.games-h3{
  font-family:var(--games-serif); font-weight:600; color:var(--games-ink);
  font-size:1.06rem; margin:0;
}
.games-lede{ font-size:var(--ncg-fs-lead,clamp(16px,2vw,18.5px)); color:var(--games-ink-2); margin:0; }
.games-body{ color:var(--games-ink-2); margin:0; }
.games-body b,.games-body strong,.games-lede b,.games-lede strong{ color:var(--games-ink); font-weight:600; }


/* ═══════════════════════════════════════════════════════════════════════════
   §4 · .games-eyebrow — the mono small-caps letter-spaced section label.
   The hub sets these at 11px / .18em ("NEURALCIBERGUARD · GAMES",
   "PROGRESSION", "BEFORE YOU START", "OPTIONAL"). The shipped tokens nearest
   those are --ncg-fs-eyebrow (11.5px) and --ncg-ls-widest (.2em), and they are
   used rather than re-typing the hub's literals. 11px is the FLOOR wherever a
   size is set by hand below: under it, letter-spaced small caps stop reading as
   small caps and start reading as noise.
   ═══════════════════════════════════════════════════════════════════════════ */

.games-eyebrow{
  display:block; margin:0 0 var(--ncg-sp-1-5,6px);
  font-family:var(--games-mono); font-size:var(--ncg-fs-eyebrow,11.5px);
  font-weight:500; letter-spacing:var(--ncg-ls-widest,.2em); text-transform:uppercase;
  color:var(--games-ink-3);
}
/* --ncg-text-3 is only safe on the BASE surface. On a card the eyebrow uses
   --ncg-text-2, which measures 9.09:1 on every light surface in the system. */
.games-card .games-eyebrow,
.games-panel .games-eyebrow{ color:var(--games-ink-2); }
/* The accented variant — the hub's crimson kicker, measured 6.07:1 on cream. */
.games-eyebrow--accent{ color:var(--games-accent); }
.games-eyebrow--accent::before{
  content:""; display:inline-block; width:var(--ncg-sp-6,24px); height:1.5px;
  margin-inline-end:var(--ncg-sp-2-5,10px); vertical-align:middle;
  background:currentColor;
}


/* ═══════════════════════════════════════════════════════════════════════════
   §5 · .games-card-grid / .games-card — the hairline grid.
   The hairlines are the CONTAINER background showing through a 1px gap, so
   there is exactly one line between any two cells at any breakpoint. Explicit
   column counts, never auto-fit: auto-fit orphans the accent cell onto a row of
   its own at some widths, which is the one thing this grid must not do.
   ═══════════════════════════════════════════════════════════════════════════ */

.games-card-grid{
  display:grid; grid-template-columns:repeat(var(--games-cols,3),1fr);
  gap:var(--games-hair);
  background:var(--games-rule);
  border:var(--games-hair) solid var(--games-rule);
  border-radius:var(--games-r); overflow:hidden;
  margin:var(--ncg-sp-2-5,10px) 0 var(--ncg-sp-1-5,6px);
  padding:0; list-style:none;
  box-shadow:var(--games-shadow);
}
.games-card-grid--2{ --games-cols:2; }
.games-card-grid--4{ --games-cols:4; }
@media (max-width:1080px){ .games-card-grid{ --games-cols:2; } }
@media (max-width:620px){ .games-card-grid{ --games-cols:1; } }

.games-card-grid > li{ background:var(--games-surface); display:flex; }
.games-card{
  display:flex; flex-direction:column; gap:var(--ncg-sp-2,8px);
  width:100%; padding:var(--ncg-sp-6-5,26px) var(--ncg-sp-6,24px) var(--ncg-sp-5-5,22px);
  background:var(--games-surface); color:var(--games-ink); text-decoration:none;
  position:relative;
}
a.games-card{ transition:background var(--games-dur) var(--games-ease); }
a.games-card:hover{ background:var(--games-surface-2); }
.games-card .games-h3,.games-card h2,.games-card h3{
  font-family:var(--games-serif); font-weight:600; letter-spacing:var(--ncg-ls-snug,-.01em);
  font-size:1.3rem; margin:0; color:inherit;
}
.games-card p{ font-size:var(--ncg-fs-sm,13.5px); line-height:var(--ncg-lh-normal,1.5); color:var(--games-ink-2); margin:0; flex:1; }

/* THE ONE ACCENT CELL. On the hub this is Ciber Traversal, and it is the whole
   reason the grid reads as "one of these is the destination". Exactly one per
   grid — two accents is no accent.
   Goes on the grid CELL (`<li class="games-card--accent">`) or, outside a grid,
   on the card itself. Measured: --ncg-text-on-accent on --ncg-accent is 6.07:1
   — the same figure as accent-on-cream in §14.1 of games.md, because the pair is
   the same two colours and contrast is symmetric — and the .86-opacity secondary
   copy the hub uses is 4.96:1. Both AA. (This comment read 6.15:1 until it was
   re-measured; the value was never used by a declaration, only quoted.) */
/* `.games-card-grid > li` is (0,1,1); the cell selector below must out-specify
   it or the accent silently loses to the default cell fill. */
.games-card-grid > li.games-card--accent,
.games-card--accent,
.games-card--accent .games-card{ background:var(--games-accent); color:var(--games-on-accent); }
.games-card--accent .games-eyebrow,
.games-card--accent p{ color:var(--games-on-accent); opacity:.86; }
.games-card--accent .games-h3,
.games-card--accent h2,.games-card--accent h3{ color:var(--games-on-accent); }
.games-card--accent a.games-card:hover,
a.games-card.games-card--accent:hover{ background:var(--games-accent-strong); }

/* The card's "go" affordance — mono, uppercase, accent, with a chevron that
   moves on hover and stands still under reduced motion. */
.games-card-go{
  display:inline-flex; align-items:center; gap:var(--ncg-sp-1-5,6px);
  margin-block-start:var(--ncg-sp-1-5,6px); min-height:var(--ncg-sp-6,24px);
  font-family:var(--games-mono); font-size:11.5px;
  letter-spacing:var(--ncg-ls-wider,.1em); text-transform:uppercase;
  color:var(--games-accent);
}
.games-card--accent .games-card-go{ color:var(--games-on-accent); opacity:1; }
.games-card-go .games-chev{ display:inline-block; transition:transform var(--ncg-dur-4,.25s) var(--games-ease); }
a.games-card:hover .games-card-go{ color:var(--games-accent-strong); }
a.games-card:hover .games-card-go .games-chev{ transform:translateX(4px); }
.games-card--accent a.games-card:hover .games-card-go{ color:var(--games-on-accent); }


/* ═══════════════════════════════════════════════════════════════════════════
   §6 · .games-step — the numbered curriculum row (01 / 02 / 03).
   Same hairline construction as the card grid, so the two read as one system.
   The numeral is decorative repetition of the heading order and is hidden from
   assistive technology in the markup, not here.
   ═══════════════════════════════════════════════════════════════════════════ */

.games-steps{
  display:grid; grid-template-columns:repeat(var(--games-step-cols,3),1fr);
  gap:var(--games-hair);
  background:var(--games-rule); border:var(--games-hair) solid var(--games-rule);
  border-radius:var(--games-r); overflow:hidden;
  margin:var(--ncg-sp-6-5,26px) 0 var(--ncg-sp-2,8px); padding:0; list-style:none;
}
/* Three across is the hub's arrangement for a three-step curriculum. A route
   with more stops than that reads better stacked — the numerals then form a
   single column the eye can run down. */
.games-steps--stack{ --games-step-cols:1; }
@media (max-width:760px){ .games-steps{ --games-step-cols:1; } }

.games-step{
  list-style:none; background:var(--games-surface);
  padding:var(--ncg-sp-4-5,18px) var(--ncg-sp-5,20px);
  display:flex; gap:var(--ncg-sp-3-5,14px); align-items:flex-start;
}
.games-step-n{
  font-family:var(--games-mono); font-size:1.35rem; font-weight:600; line-height:1;
  color:var(--games-accent); min-width:2.2ch; font-variant-numeric:tabular-nums;
}
.games-step h2,.games-step h3,.games-step .games-h3{
  font-family:var(--games-serif); font-size:1.06rem; font-weight:600;
  letter-spacing:var(--ncg-ls-snug,-.01em); margin:0 0 3px; color:var(--games-ink);
}
.games-step p{ font-size:var(--ncg-fs-sm,13.5px); line-height:var(--ncg-lh-normal,1.5); color:var(--games-ink-2); margin:0; }


/* ═══════════════════════════════════════════════════════════════════════════
   §7 · .games-panel — the explanatory card ("What you need", "Controls",
   "XP and rank", "Where your saves live", "Accessibility", "Accounts,
   honestly"). Plain language, honest, no marketing voice — that is a copy rule
   as much as a style rule, and it is repeated in games.md because the class
   cannot enforce it.
   ═══════════════════════════════════════════════════════════════════════════ */

.games-panel-grid{
  /* `min(19rem,100%)` rather than a bare `19rem`: a grid track has an implicit
     minimum of its content size, so `minmax(19rem,1fr)` cannot shrink below
     304px and overflows any container narrower than that — measured at 322px
     viewport, where a 304px floor sits in a 282px box and pushes the page into
     horizontal scroll. Clamping the floor to the container width lets the track
     collapse instead. This is a shared component, so the bug reached every page
     that used it. */
  display:grid; grid-template-columns:repeat(auto-fit,minmax(min(19rem,100%),1fr));
  gap:var(--games-hair);
  background:var(--games-rule); border:var(--games-hair) solid var(--games-rule);
  border-radius:var(--games-r); overflow:hidden;
  margin:var(--ncg-sp-4-5,18px) 0 0; padding:0; list-style:none;
}
/* Standalone it carries its own hairline and radius; inside the grid the
   container's 1px gaps are already drawing the lines, so it drops both. */
.games-panel{
  background:var(--games-surface); padding:var(--ncg-sp-5-5,22px);
  color:var(--games-ink-2);
  border:var(--games-hair) solid var(--games-rule); border-radius:var(--games-r);
}
.games-panel-grid > .games-panel,
.games-panel-grid > li > .games-panel{ border:0; border-radius:0; }
.games-panel > h2,.games-panel > h3,.games-panel > .games-h3{
  font-family:var(--games-serif); font-size:1.03rem; font-weight:600;
  margin:0 0 var(--ncg-sp-2,8px); color:var(--games-ink);
}
.games-panel p,.games-panel li{ font-size:var(--ncg-fs-sm,13.5px); line-height:var(--ncg-lh-body,1.6); color:var(--games-ink-2); }
.games-panel p + p{ margin-block-start:var(--ncg-sp-2-5,10px); }
.games-panel ul{ margin:0; padding-inline-start:var(--ncg-sp-4-5,18px); }
.games-panel ul li{ margin:5px 0; }

/* A note that is a note: quieter, with the accent doing the pointing. */
.games-note{
  margin:var(--ncg-sp-6-5,26px) 0 0;
  padding:var(--ncg-sp-4,16px) var(--ncg-sp-4-5,18px);
  background:var(--games-surface); color:var(--games-ink-2);
  border:var(--games-hair) solid var(--games-rule);
  border-inline-start:3px solid var(--games-accent);
  border-radius:var(--games-r);
  font-size:.95rem;
}
.games-note b,.games-note strong{ color:var(--games-ink); }

/* Data, keys, counts and code are mono. */
.games-data{ font-family:var(--games-mono); font-variant-numeric:tabular-nums; }
.games-key,
kbd.games-key{
  display:inline-block; font-family:var(--games-mono); font-size:.82em; font-weight:600;
  padding:4px 9px; border-radius:var(--ncg-r-sm,10px);
  background:var(--games-ink); color:var(--games-paper); border:0;
}

/* Status chip. Colour is NEVER the only channel — the class requires a word in
   the element, and games.md makes that a contract, not a suggestion. */
.games-chip{
  display:inline-flex; align-items:center; gap:var(--ncg-sp-1-5,6px);
  font-family:var(--games-mono); font-size:10.5px;
  letter-spacing:var(--ncg-ls-wider,.1em); text-transform:uppercase;
  border:var(--games-hair) solid currentColor; border-radius:var(--games-r-pill);
  padding:4px 10px; color:var(--games-ink-2);
}
.games-chip--ok{ color:var(--games-ok); }
.games-chip--accent{ color:var(--games-accent); }

/* Buttons. Same construction as the hub's: an accent plate for the primary, a
   currentColor outline for the ghost, both at the 44px target floor. */
.games-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--ncg-sp-2,8px);
  min-height:var(--games-tap); padding:13px 26px;
  border-radius:var(--ncg-r-md,12px); cursor:pointer;
  font-family:var(--games-sans); font-size:1rem; font-weight:600; text-decoration:none;
  background:var(--games-accent); color:var(--games-on-accent);
  border:2px solid var(--games-accent);
  transition:background var(--ncg-dur-2,.18s) var(--games-ease),color var(--ncg-dur-2,.18s) var(--games-ease);
}
.games-btn:hover{ background:var(--games-accent-strong); border-color:var(--games-accent-strong); color:var(--games-on-accent); }
.games-btn--ghost{ background:transparent; color:var(--games-ink); border-color:currentColor; }
.games-btn--ghost:hover{ background:transparent; color:var(--games-accent); }
.games-btn[disabled]{ opacity:.55; cursor:progress; }


/* ═══════════════════════════════════════════════════════════════════════════
   §8 · .games-hud-* — THE IN-GAME CHROME. PAPER LAID OVER THE WORLD.
   ---------------------------------------------------------------------------
   Every surface here is a near-solid sheet with a hairline border and a soft
   shadow. See the file header for the measured contrast guarantee; the number
   that makes it true is the 96% in the @supports block immediately below.

   backdrop-filter is deliberately NOT used on these sheets. Behind a 96%-opaque
   surface a blur is invisible, and a backdrop blur over a live WebGL canvas is a
   full-screen read every frame — cost with no visible return.
   ═══════════════════════════════════════════════════════════════════════════ */

@supports (background:color-mix(in srgb,red 96%,transparent)){
  :root,
  [data-games-ground]{ --games-veil:color-mix(in srgb,var(--games-surface-flat) 96%,transparent); }
}

/* The readout sheet: telemetry, objective card, progress, the time triptych. */
.games-hud-panel{
  background:var(--games-veil);
  color:var(--games-ink);
  border:var(--games-hair) solid var(--games-rule-strong);
  border-radius:var(--games-r);
  box-shadow:var(--games-shadow-lift);
  backdrop-filter:none;
}
.games-hud-label{
  font-family:var(--games-mono); font-size:11px; font-weight:500;
  letter-spacing:var(--ncg-ls-widest,.2em); text-transform:uppercase;
  color:var(--games-ink-2);
}
.games-hud-value{
  font-family:var(--games-mono); font-weight:500; color:var(--games-ink);
  font-variant-numeric:tabular-nums; letter-spacing:var(--ncg-ls-tight,-.02em);
}
.games-hud-objective{ font-family:var(--games-serif); font-weight:500; color:var(--games-ink); }
.games-hud-meter{ background:var(--games-accent-soft); border:var(--games-hair) solid var(--games-accent-line); }
.games-hud-meter-fill{ background:var(--games-accent); }
.games-hud-pip{ border-color:var(--games-rule-strong); opacity:1; }
.games-hud-pip[data-on]{ background:var(--games-accent); border-color:var(--games-accent); }

/* The modal sheet: mission brief, debrief, pause, settings, result.
   The scrim is the WORLD being dimmed, not chrome, so it keeps hud.js' own
   value verbatim rather than being re-coloured per ground. */
.games-hud-scrim{ background:rgba(28,25,23,.72); }
.games-hud-sheet{
  background:var(--games-surface); color:var(--games-ink);
  border:var(--games-hair) solid var(--games-rule-strong);
  border-radius:var(--games-r);
  box-shadow:var(--games-shadow-lift);
}
.games-hud-eyebrow{
  font-family:var(--games-mono); font-size:11px; font-weight:500;
  letter-spacing:var(--ncg-ls-widest,.2em); text-transform:uppercase;
  color:var(--games-accent);
}
.games-hud-title{ font-family:var(--games-serif); font-weight:600; color:var(--games-ink); letter-spacing:var(--ncg-ls-snug,-.01em); }
.games-hud-toast{ background:var(--games-surface); color:var(--games-ink); border:var(--games-hair) solid var(--games-rule-strong); }
.games-hud-notice{ background:var(--games-accent); color:var(--games-on-accent); }
.games-hud-prompt{ background:var(--games-surface); color:var(--games-ink); border:var(--games-hair) solid var(--games-rule-strong); }
.games-hud-btn{
  background:var(--games-accent); color:var(--games-on-accent); border:2px solid var(--games-accent);
  min-height:var(--games-tap);
}
.games-hud-btn--ghost{ background:transparent; color:var(--games-ink); border-color:currentColor; }


/* ═══════════════════════════════════════════════════════════════════════════
   §9 · THE ENGINE BRIDGE.
   The engine modules already emit exactly these surfaces. Their names are
   mapped onto the canonical ones above in ONE declaration block each, so the
   two spellings cannot drift apart (components.md §1 convention).

   Gated on `[data-games-ground] … [data-scale="normal"]`:
     · the ancestor attribute lifts specificity above the runtime-injected
       rules, which would otherwise win on source order;
     · `normal` leaves the Senior/large treatment (tokens.css §20) untouched.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 9a · hud.js ─────────────────────────────────────────────────────────
   Re-pointing the module's own custom properties carries most of the sheet for
   free: the modal, the toast, the buttons, the notice and the accent all follow
   without a rule each. --hud-gold is pointed at the accent because gold on
   paper measures 1.5:1 — on the burgundy ground it is the lifted crimson. */
[data-games-ground] .ncg-hud[data-scale="normal"]{
  --hud-panel:var(--games-veil);
  --hud-panel-ink:var(--games-ink);
  --hud-cream:var(--games-surface);
  --hud-cream-2:var(--games-surface-2);
  --hud-ink:var(--games-ink);
  --hud-ink-2:var(--games-ink-2);
  --hud-crimson:var(--games-accent);
  --hud-crimson-d:var(--games-accent-strong);
  --hud-gold:var(--games-accent);
  --hud-r:var(--games-r);
  --hud-tap:var(--games-tap);
  --hud-serif:var(--games-serif);
  --hud-sans:var(--games-sans);
  --hud-mono:var(--games-mono);
}

/* the three readout sheets */
.games-hud-panel,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__tele,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__obj,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__prog,
[data-games-ground] .cro[data-scale="normal"] .cro__time{
  background:var(--games-veil);
  color:var(--games-ink);
  border:var(--games-hair) solid var(--games-rule-strong);
  border-radius:var(--games-r);
  box-shadow:var(--games-shadow-lift);
  backdrop-filter:none;
}

/* micro-labels — 11px floor and the hub's tracking. The engine sets these as
   low as 9.3px, which is below the point where letter-spaced small caps read. */
.games-hud-label,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__objkick,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__unit,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__learnh,
[data-games-ground] .cro[data-scale="normal"] .cro__k{
  font-family:var(--games-mono); font-size:11px; font-weight:500;
  letter-spacing:var(--ncg-ls-widest,.2em); text-transform:uppercase;
  color:var(--games-ink-2); opacity:1;
}
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__objkick{ color:var(--games-accent); }

.games-hud-value,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__num,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__alt,
[data-games-ground] .cro[data-scale="normal"] .cro__v{
  font-family:var(--games-mono); color:var(--games-ink);
  font-variant-numeric:tabular-nums; opacity:1;
}

.games-hud-objective,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__objtext{
  font-family:var(--games-serif); color:var(--games-ink);
}

/* the objective meter. The engine's track is a white wash that vanishes on
   paper; this gives it a real edge, which SC 1.4.11 needs anyway. */
.games-hud-meter,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__bar{
  background:var(--games-accent-soft);
  border:var(--games-hair) solid var(--games-accent-line);
}
.games-hud-meter-fill,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__barfill{ background:var(--games-accent); }
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__ringbg{ stroke:var(--games-accent-line); }
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__ringfg{ stroke:var(--games-accent); }

/* the modal sheet — brief, debrief, pause, settings, result, confirm */
.games-hud-sheet,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__modal{
  background:var(--games-surface); color:var(--games-ink);
  border:var(--games-hair) solid var(--games-rule-strong);
  border-radius:var(--games-r);
  box-shadow:var(--games-shadow-lift);
}
.games-hud-eyebrow,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__kick{
  font-family:var(--games-mono); font-size:11px; font-weight:500;
  letter-spacing:var(--ncg-ls-widest,.2em); text-transform:uppercase;
  color:var(--games-accent);
}
.games-hud-title,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__h{
  font-family:var(--games-serif); font-weight:600; color:var(--games-ink);
  letter-spacing:var(--ncg-ls-snug,-.01em);
}
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__p{ color:var(--games-ink-2); }
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__learn{ border-inline-start-color:var(--games-accent); }
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__hint{ color:inherit; opacity:.8; }

.games-hud-scrim,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__scrim{ background:rgba(28,25,23,.72); }

.games-hud-toast,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__toast{
  background:var(--games-surface); color:var(--games-ink);
  border:var(--games-hair) solid var(--games-rule-strong);
  box-shadow:var(--games-shadow-lift);
}
/* hud.js hard-codes #FAF9F6 as the ink on both of these, which is correct on the
   paper ground's crimson and wrong on the burgundy ground's lifted crimson. The
   role token makes it right on both. */
.games-hud-notice,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__notice{
  background:var(--games-accent); color:var(--games-on-accent);
}
.games-hud-btn,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__btn{
  background:var(--games-accent); color:var(--games-on-accent);
  border-color:var(--games-accent); min-height:var(--games-tap);
}
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__btn:hover{
  background:var(--games-accent-strong); border-color:var(--games-accent-strong);
}
/* MUST stay after the rule above: the engine declares the ghost modifier at one
   class of specificity, so without an equally-scoped rule here every ghost
   button in the pause, settings and result screens would fill solid. */
.games-hud-btn--ghost,
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__btn--ghost{
  background:transparent; color:var(--games-ink); border-color:currentColor;
}
[data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__btn--ghost:hover{
  background:var(--games-surface-2); color:var(--games-accent);
}

/* ── 9b · overlay.js — the Record / Current / Difference triptych ─────────
   The delta is the one place a status colour earns its keep. It is never the
   only channel: overlay.js already writes a sign (− / +), an arrow (▼ / ▲) and
   a title, so the colour is the third signal, not the first. The engine's own
   pair fails SC 1.4.3 on every light surface in the system — measured on
   raised #F3F1EA / paper #FAF9F6 / card #FFFFFF:
     #009E73 ... 3.03:1 · 3.25:1 · 3.42:1
     #D55E00 ... 3.42:1 · 3.67:1 · 3.87:1
   so both are re-pointed at the system's status roles (5.11:1 and 6.12:1 on
   paper; 4.93:1 and 5.90:1 at the veil's darkest composite, which is where the
   triptych actually renders). This comment used to quote 3.03:1 as the PAPER
   figure and 3.73:1 for #D55E00; 3.03:1 is the raised-surface figure and 3.73:1
   was never one of the three. Every value still fails the 4.5:1 floor, which is
   the point the comment makes, so the re-pointing below stands unchanged. */
[data-games-ground] .cro[data-scale="normal"]{
  --cro-panel:var(--games-veil);
  --cro-fg:var(--games-ink);
  --cro-cream:var(--games-surface);
  --cro-ink:var(--games-ink);
  --cro-crimson:var(--games-accent);
  --cro-gold:var(--games-accent);
  --cro-green:var(--games-ok);
  --cro-vermill:var(--games-danger);
  --cro-r:var(--games-r);
  --cro-sans:var(--games-sans);
  --cro-serif:var(--games-serif);
  --cro-mono:var(--games-mono);
}
[data-games-ground] .cro[data-scale="normal"] .cro__cell,
[data-games-ground] .cro[data-scale="normal"] .cro__cp{ border-inline-start-color:var(--games-rule); }
[data-games-ground] .cro[data-scale="normal"] .cro__cell:first-child{ border-inline-start:0; }
/* The "No record" empty state is deliberately sans italic, not mono — a blank
   or mono-numeric cell reads as a bug on a first run. The value rule above sets
   mono for every .cro__v, so the empty state has to say so again here. */
[data-games-ground] .cro[data-scale="normal"] .cro__v--empty{
  font-family:var(--games-sans); font-style:italic;
  color:var(--games-ink-2); opacity:1;
}
[data-games-ground] .cro[data-scale="normal"] .cro__v--fast{ color:var(--games-ok); }
[data-games-ground] .cro[data-scale="normal"] .cro__v--slow{ color:var(--games-danger); }

/* checkpoint pips. The engine draws them at .45 opacity, which is below the 3:1
   SC 1.4.11 floor for a component that reports state. Full opacity, real edge —
   and the "3/5" text beside them stays the primary channel. */
.games-hud-pip,
[data-games-ground] .cro[data-scale="normal"] .cro__pip{
  border-color:var(--games-rule-strong); opacity:1;
}
.games-hud-pip[data-on],
[data-games-ground] .cro[data-scale="normal"] .cro__pip[data-on]{
  background:var(--games-accent); border-color:var(--games-accent);
}

/* the contextual road prompt: a paper pill, with the single accent for a locked
   stop and the OK status for a finished one. */
.games-hud-prompt,
[data-games-ground] .cro[data-scale="normal"] .cro__prompt{
  background:var(--games-surface); color:var(--games-ink);
  border:var(--games-hair) solid var(--games-rule-strong);
  box-shadow:var(--games-shadow-lift);
}
[data-games-ground] .cro[data-scale="normal"] .cro__prompt[data-tone="locked"]{
  background:var(--games-accent); color:var(--games-on-accent); border-color:var(--games-accent);
}
[data-games-ground] .cro[data-scale="normal"] .cro__prompt[data-tone="done"]{
  background:var(--games-ok-tint); color:var(--games-ok); border-color:var(--games-ok-line);
}
.games-hud-key,
[data-games-ground] .cro[data-scale="normal"] .cro__key{
  background:var(--games-ink); color:var(--games-paper);
}
[data-games-ground] .cro[data-scale="normal"] .cro__prompt[data-tone="locked"] .cro__key{
  background:var(--games-on-accent); color:var(--games-accent);
}
[data-games-ground] .cro[data-scale="normal"] .cro__prompt[data-tone="done"] .cro__key{
  background:var(--games-ok); color:var(--games-ok-tint);
}

/* ── 9c · a11y.js — the 2D fallback map and the remap dialog ──────────────
   This surface is already an editorial cream page in document flow, so the
   bridge is mostly a re-point: cards become LIGHTER than the ground (the hub's
   relationship, not the engine's darker-card default) and the hairline becomes
   the one the hub's grid needs. */
[data-games-ground] .ncg-a11y[data-scale="normal"]{
  --a-cream:var(--games-paper);
  --a-cream-2:var(--games-surface);
  --a-ink:var(--games-ink);
  --a-ink-2:var(--games-ink-2);
  --a-crimson:var(--games-accent);
  --a-line:var(--games-rule);
  --a-r:var(--games-r);
  --a-tap:var(--games-tap);
  --a-serif:var(--games-serif);
  --a-sans:var(--games-sans);
  --a-mono:var(--games-mono);
}
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-a11y__kick{
  font-size:11px; letter-spacing:var(--ncg-ls-widest,.2em); color:var(--games-accent);
}
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-a11y__wrap{ max-width:var(--games-wrap); }
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-a11y__m,
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-a11y__note,
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-a11y__mapwrap{
  border-color:var(--games-rule); box-shadow:var(--games-shadow);
}
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-a11y__badge{ color:var(--games-ink-2); }
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-remap th{ color:var(--games-ink-2); }
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-remap__key{ color:var(--games-ink); }
[data-games-ground] .ncg-a11y[data-scale="normal"] .ncg-remap__key[data-capturing]{ background:var(--games-accent-soft); }


/* ═══════════════════════════════════════════════════════════════════════════
   §10 · FOCUS, MOTION, CONTRAST, PRINT.
   ═══════════════════════════════════════════════════════════════════════════ */

/* One visible focus ring for the whole surface. `:where()` keeps it at zero
   specificity so any component can raise it without !important. */
[data-games-ground] :where(a,button,input,select,textarea,summary,details,[tabindex]):focus-visible{
  outline:var(--games-focus-w) solid var(--games-focus);
  outline-offset:var(--games-focus-off);
  border-radius:var(--ncg-focus-r,4px);
}

.games-skip{
  position:absolute; left:-9999px; top:0; z-index:var(--ncg-z-skip,1100);
  background:var(--games-ink); color:var(--games-paper);
  padding:12px 18px; border-radius:0 0 12px 0; font-weight:600; text-decoration:none;
}
.games-skip:focus{ left:0; }

.games-sr{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}

/* Reduced motion. Scoped to this file's own selectors: a blanket
   `*{transition:none!important}` from a shared sheet would also reach the
   reduced-motion blocks the pages and engine modules already ship deliberately. */
@media (prefers-reduced-motion:reduce){
  .games-card-go .games-chev,
  a.games-card,
  .games-btn{ transition:none; }
  a.games-card:hover .games-card-go .games-chev{ transform:none; }
}

/* More contrast. The engine's own more-contrast rule flips these sheets to a
   dark plate; this layer wins on specificity, so it must supply its own —
   a fully opaque sheet with an ink edge, which is the paper answer. */
@media (prefers-contrast:more){
  .games-hud-panel,
  .games-hud-sheet,
  [data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__tele,
  [data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__obj,
  [data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__prog,
  [data-games-ground] .ncg-hud[data-scale="normal"] .ncg-hud__modal,
  [data-games-ground] .cro[data-scale="normal"] .cro__time,
  [data-games-ground] .cro[data-scale="normal"] .cro__prompt{
    background:var(--games-surface-flat);
    border-color:var(--games-ink);
  }
  .games-card-grid,.games-steps,.games-panel-grid,.games-note{ border-color:var(--games-ink); }
}

/* Forced colours: nothing to do but stay out of the way. `forced-color-adjust`
   is deliberately never set — overriding a user's chosen high-contrast palette
   is the opposite of the point (components.md §10). The one thing that does
   need help is a border drawn with a colour the OS will not repaint. */
@media (forced-colors:active){
  .games-hud-panel,.games-hud-sheet,.games-card-grid,.games-steps,.games-panel-grid{
    border-color:CanvasText;
  }
}

/* Print. The 3D stage cannot be printed, so the chrome over it is dropped
   entirely; the 2D map view IS in document flow and prints, which is why
   .ncg-a11y is deliberately not in this list. */
@media print{
  .games-page{ background:#fff; }
  .games-hud-panel,.games-hud-scrim,.games-hud-prompt,.games-hud-toast,
  .games-hud-notice,.ncg-hud,.cro{ display:none; }
}
