@charset "UTF-8";
/* ===========================================================================
   TheGameExtras — Design System
   "Broadcast Noir": late-night gaming broadcast graphics + magazine typography.

   Rules this file enforces, deliberately:
   · No gradient-filled backgrounds or buttons. Flat surfaces, hairline borders.
   · Accent colour appears ONLY on things you can click or that carry a warning.
   · Radii are hierarchical (2/6/12/20px), never one uniform "rounded card" look.
   · Motion has weight — spring easing and stagger, not a global fade-in.
   · Body copy is capped at 68ch and set at 17px+. People come here to read.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. FONTS — self-hosted. No CDN request blocks first paint.
   --------------------------------------------------------------------------- */
@font-face {
  font-family: 'Clash';
  src: url('../fonts/ClashGrotesk-500.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Clash';
  src: url('../fonts/ClashGrotesk-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Clash';
  src: url('../fonts/ClashGrotesk-700.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Instrument';
  src: url('../fonts/InstrumentSans-latin.woff2') format('woff2');
  font-weight: 400 700; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}
@font-face {
  font-family: 'Instrument';
  src: url('../fonts/InstrumentSans-latin-ext.woff2') format('woff2');
  font-weight: 400 700; font-style: normal; font-display: swap;
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF;
}
@font-face {
  font-family: 'JetBrains';
  src: url('../fonts/JetBrainsMono-latin.woff2') format('woff2');
  font-weight: 400 700; font-style: normal; font-display: swap;
}

/* ---------------------------------------------------------------------------
   2. TOKENS
   --------------------------------------------------------------------------- */
:root {
  /* — Type families — */
  --f-display: 'Clash', 'Arial Narrow', system-ui, sans-serif;
  --f-body:    'Instrument', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --f-mono:    'JetBrains', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* — Fluid type scale — */
  --fs-mega:  clamp(3rem, 9vw, 7.5rem);
  --fs-hero:  clamp(2.5rem, 6.4vw, 5rem);
  --fs-h1:    clamp(2rem, 4.2vw, 3.125rem);
  --fs-h2:    clamp(1.5rem, 2.8vw, 2.125rem);
  --fs-h3:    clamp(1.25rem, 2vw, 1.5rem);
  --fs-h4:    1.125rem;
  --fs-body:  clamp(1.0625rem, 0.45vw + 0.98rem, 1.1875rem);
  --fs-sm:    0.9375rem;
  --fs-xs:    0.8125rem;
  --fs-2xs:   0.6875rem;

  /* — Spacing (4px base) — */
  --s-1: 4px;   --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 24px;  --s-6: 32px;  --s-7: 48px;  --s-8: 64px;
  --s-9: 96px;  --s-10: 128px;

  /* — Radii: hierarchical on purpose — */
  --r-xs: 2px; --r-sm: 6px; --r-md: 12px; --r-lg: 20px; --r-full: 999px;

  /* — Layout — */
  --shell: 1320px;
  --measure: 68ch;
  --header-h: 68px;

  /* — Motion — */
  --e-out:    cubic-bezier(0.22, 1, 0.36, 1);      /* decelerate, confident */
  --e-spring: cubic-bezier(0.34, 1.46, 0.64, 1);   /* slight overshoot */
  --e-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --t-fast: 140ms; --t-base: 240ms; --t-slow: 480ms;

  /* — Z — */
  --z-header: 100; --z-drawer: 200; --z-modal: 300; --z-toast: 400;

  color-scheme: dark light;
}

/* — DARK (default) —
   Cool-shifted near-black. No pure #000: it crushes cover art and reads cheap. */
:root {
  --bg:          #0A0710;
  --bg-deep:     #06040A;
  --surface-1:   #120D1C;
  --surface-2:   #1B1428;
  --surface-3:   #241B34;
  --border:      #2A2039;
  --border-soft: #1F1830;
  --border-loud: #3D3050;

  --text:        #F4F1F8;
  --text-2:      #A79FB8;
  --text-3:      #6F6683;

  --accent:      #FF2E88;   /* primary — CTAs, active state, links on hover */
  --accent-ink:  #0A0710;   /* text ON accent */
  --accent-dim:  #B31F5F;
  --accent-wash: rgba(255, 46, 136, 0.12);

  --code:        #00E5D0;   /* cheat codes, copy affordances, verified */
  --code-wash:   rgba(0, 229, 208, 0.10);

  --warn:        #FFAF3F;   /* unconfirmed, rumours, caveats */
  --warn-wash:   rgba(255, 175, 63, 0.12);

  --ok:          #3DDC84;
  --bad:         #FF4D5E;

  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
  --shadow-2: 0 8px 24px -8px rgba(0,0,0,.7);
  --shadow-3: 0 24px 64px -16px rgba(0,0,0,.85);
  --grain-opacity: .035;
}

/* — LIGHT — real, not an afterthought. Accents darkened for AA on white. — */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg:          #FBF9FD;
    --bg-deep:     #F2EEF7;
    --surface-1:   #FFFFFF;
    --surface-2:   #F6F2FA;
    --surface-3:   #EDE7F3;
    --border:      #E0D8EA;
    --border-soft: #EDE7F3;
    --border-loud: #C9BED8;

    --text:        #150F1F;
    --text-2:      #574B68;
    --text-3:      #837792;

    --accent:      #D9006A;
    --accent-ink:  #FFFFFF;
    --accent-dim:  #A30050;
    --accent-wash: rgba(217, 0, 106, 0.08);

    --code:        #00756B;
    --code-wash:   rgba(0, 117, 107, 0.08);

    --warn:        #A36200;
    --warn-wash:   rgba(163, 98, 0, 0.10);

    --ok:          #12833F;
    --bad:         #C9202F;

    --shadow-1: 0 1px 2px rgba(21,15,31,.06);
    --shadow-2: 0 8px 24px -10px rgba(21,15,31,.14);
    --shadow-3: 0 24px 56px -20px rgba(21,15,31,.20);
    --grain-opacity: .02;
  }
}
:root[data-theme="light"] {
  --bg:#FBF9FD; --bg-deep:#F2EEF7; --surface-1:#FFFFFF; --surface-2:#F6F2FA; --surface-3:#EDE7F3;
  --border:#E0D8EA; --border-soft:#EDE7F3; --border-loud:#C9BED8;
  --text:#150F1F; --text-2:#574B68; --text-3:#837792;
  --accent:#D9006A; --accent-ink:#FFFFFF; --accent-dim:#A30050; --accent-wash:rgba(217,0,106,.08);
  --code:#00756B; --code-wash:rgba(0,117,107,.08);
  --warn:#A36200; --warn-wash:rgba(163,98,0,.10); --ok:#12833F; --bad:#C9202F;
  --shadow-1:0 1px 2px rgba(21,15,31,.06); --shadow-2:0 8px 24px -10px rgba(21,15,31,.14);
  --shadow-3:0 24px 56px -20px rgba(21,15,31,.20); --grain-opacity:.02;
}

/* ---------------------------------------------------------------------------
   3. RESET
   --------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--f-body);
  font-size: var(--fs-body);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  min-height: 100vh;
}

/* Film grain. One fixed layer, GPU-cheap, sells "broadcast" without any
   decorative blobs. Sits under everything interactive. */
body::before {
  content: '';
  position: fixed; inset: 0;
  pointer-events: none; z-index: 1;
  opacity: var(--grain-opacity);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

img, svg, video, canvas { display: block; max-width: 100%; }
img { height: auto; background: var(--surface-2); }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
ul, ol { padding: 0; list-style: none; }
table { border-collapse: collapse; width: 100%; }

a { color: inherit; text-decoration: none; }

/* Underline links inside running text only.
   `a:not([class])` looks like the right selector and is not: card headlines,
   table cells and image wrappers all use classless anchors, so the global rule
   underlined every card title on the site. Scope it to the containers that
   actually hold prose. */
.prose a,
.answer a,
.faq__a a,
.notice a,
.footer__legal a,
.facts__v a,
.field__hint a,
.spec-table td a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  transition: color var(--t-fast) var(--e-out);
}
.prose a:hover,
.answer a:hover,
.faq__a a:hover,
.notice a:hover,
.footer__legal a:hover,
.facts__v a:hover,
.field__hint a:hover,
.spec-table td a:hover { color: var(--accent); }

/* A link wrapping an image or a card is a target, not running text. */
.prose a:has(> img),
.prose a:has(> figure) { text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: var(--accent); color: var(--accent-ink); }

/* Scrollbar — thin, themed, not a novelty */
* { scrollbar-width: thin; scrollbar-color: var(--border-loud) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border-loud); border-radius: var(--r-full); border: 3px solid var(--bg); }
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); }

/* ---------------------------------------------------------------------------
   4. TYPOGRAPHY
   --------------------------------------------------------------------------- */
h1, h2, h3, h4, h5 {
  font-family: var(--f-display);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); letter-spacing: -0.018em; }
h3 { font-size: var(--fs-h3); line-height: 1.2; letter-spacing: -0.01em; }
h4 { font-size: var(--fs-h4); line-height: 1.3; letter-spacing: -0.005em; }

p { text-wrap: pretty; }

/* Eyebrow — the small uppercase label above section titles.
   Uses a leading rule instead of a coloured pill: quieter, more editorial. */
.eyebrow {
  display: flex; align-items: center; gap: var(--s-3);
  font-family: var(--f-body);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: var(--s-3);
}
.eyebrow::before {
  content: ''; width: 22px; height: 2px;
  background: var(--accent); flex: none;
}

.lede { font-size: 1.1875rem; line-height: 1.6; color: var(--text-2); max-width: 62ch; }

.mono { font-family: var(--f-mono); font-variant-ligatures: none; }
.tnum { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------------
   5. LAYOUT
   --------------------------------------------------------------------------- */
.shell {
  width: min(100% - 2 * var(--s-4), var(--shell));
  margin-inline: auto;
  position: relative; z-index: 2;
}
@media (min-width: 768px) { .shell { width: min(100% - 2 * var(--s-6), var(--shell)); } }

.section { padding-block: clamp(var(--s-7), 7vw, var(--s-9)); }
.section--tight { padding-block: clamp(var(--s-6), 5vw, var(--s-7)); }
.section--flush { padding-block-start: 0; }

.section-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s-5); margin-bottom: var(--s-5);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--border-soft);
}
.section-head__title { font-size: var(--fs-h2); }
.section-head__link {
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-2);
  display: inline-flex; align-items: center; gap: var(--s-2);
  white-space: nowrap; transition: color var(--t-fast) var(--e-out);
}
.section-head__link:hover { color: var(--accent); }
.section-head__link svg { transition: transform var(--t-base) var(--e-spring); }
.section-head__link:hover svg { transform: translateX(4px); }

.grid { display: grid; gap: var(--s-5); }
.grid--games   { grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: var(--s-4); }
.grid--cards   { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.grid--wide    { grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
.grid--2       { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }

@media (min-width: 900px) {
  .grid--games { grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); }
}

/* Article body: a fixed measure inside a wider shell */
.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1.35em; }
.prose h2 { margin-top: 2em; font-size: var(--fs-h2); }
.prose h3 { margin-top: 1.7em; font-size: var(--fs-h3); }
.prose h2 + *, .prose h3 + * { margin-top: 0.7em; }
.prose ul, .prose ol { padding-left: 1.35em; }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose li + li { margin-top: 0.45em; }
.prose li::marker { color: var(--accent); }
.prose img { border-radius: var(--r-md); margin-block: 2em; }
.prose blockquote {
  border-left: 2px solid var(--accent);
  padding: var(--s-1) 0 var(--s-1) var(--s-5);
  font-size: 1.1875rem; line-height: 1.5;
  font-family: var(--f-display); font-weight: 500;
  color: var(--text);
}
.prose code {
  font-family: var(--f-mono); font-size: 0.875em;
  background: var(--code-wash); color: var(--code);
  padding: 0.15em 0.4em; border-radius: var(--r-xs);
}
.prose pre {
  background: var(--bg-deep); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--s-4);
  overflow-x: auto; font-family: var(--f-mono); font-size: var(--fs-sm);
}
.prose pre code { background: none; color: var(--text); padding: 0; }
.prose hr { border: 0; border-top: 1px solid var(--border); margin-block: 2.5em; }
.prose table { font-size: var(--fs-sm); margin-block: 2em; }

/* ---------------------------------------------------------------------------
   6. HEADER
   --------------------------------------------------------------------------- */
.header {
  position: sticky; top: 0; z-index: var(--z-header);
  height: var(--header-h);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--border-soft);
  transition: border-color var(--t-base) var(--e-out), background var(--t-base);
}
.header.is-scrolled { border-bottom-color: var(--border); box-shadow: var(--shadow-2); }

.header__inner {
  height: 100%; display: flex; align-items: center; gap: var(--s-5);
}

.logo {
  display: inline-flex; align-items: baseline; gap: 1px;
  font-family: var(--f-display); font-weight: 700;
  font-size: 1.3125rem; letter-spacing: -0.035em;
  color: var(--text); flex: none;
}
.logo__mark { color: var(--accent); }
.logo:hover .logo__mark { color: var(--text); }
.logo__mark, .logo span { transition: color var(--t-base) var(--e-out); }

.nav { display: none; align-items: center; gap: var(--s-1); margin-right: auto; }
@media (min-width: 1024px) { .nav { display: flex; } }

.nav__link {
  position: relative;
  padding: var(--s-2) var(--s-3);
  font-size: var(--fs-sm); font-weight: 500;
  color: var(--text-2); border-radius: var(--r-sm);
  transition: color var(--t-fast) var(--e-out), background var(--t-fast);
}
.nav__link:hover { color: var(--text); background: var(--surface-1); }
.nav__link[aria-current] { color: var(--text); }
/* Active state is a 2px underline, not a filled pill — quieter, more editorial. */
.nav__link[aria-current]::after {
  content: ''; position: absolute; left: var(--s-3); right: var(--s-3); bottom: 2px;
  height: 2px; background: var(--accent); border-radius: var(--r-full);
}

.header__actions { display: flex; align-items: center; gap: var(--s-2); margin-left: auto; }

/* 40px on a mouse, 44px under (pointer: coarse) — see section 32.
   The size lives here, at the base rule, and nowhere else. A second
   `.icon-btn { width: 40px }` used to sit *below* that media query with equal
   specificity, so it won on touch devices too and quietly reinstated the
   under-44px tap target the media query existed to fix. */
.icon-btn {
  width: 40px; height: 40px; display: grid; place-items: center;
  border-radius: var(--r-sm); color: var(--text-2);
  border: 1px solid transparent;
  transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast);
}
.icon-btn:hover { color: var(--text); background: var(--surface-1); border-color: var(--border); }

/* ---------------------------------------------------------------------------
   7. SEARCH
   --------------------------------------------------------------------------- */
.search { position: relative; flex: 1; max-width: 420px; display: none; }
@media (min-width: 768px) { .search { display: block; } }

.search__field {
  display: flex; align-items: center; gap: var(--s-2);
  height: 38px; padding-inline: var(--s-3);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  transition: border-color var(--t-fast), background var(--t-fast);
}
.search__field:focus-within { border-color: var(--accent); background: var(--surface-2); }
.search__field svg { color: var(--text-3); flex: none; }
/* `align-self: stretch` is the touch target. The field is 38–52px tall but
   `align-items: center` above left the input at its own line-height — 27px —
   and a tap on the 12px of field above or below it landed on the form, which
   focuses nothing. Stretching the input to the field's height makes the whole
   bar tappable. Do not give this rule a `height`: stretch only applies while
   the cross size is auto. */
.search__input {
  flex: 1; min-width: 0; align-self: stretch;
  background: none; border: 0; outline: none;
  font-size: var(--fs-sm); color: var(--text);
}
.search__input::placeholder { color: var(--text-3); }
.search__kbd {
  font-family: var(--f-mono); font-size: var(--fs-2xs);
  color: var(--text-3); border: 1px solid var(--border);
  border-radius: var(--r-xs); padding: 1px 5px; flex: none;
}

.suggest {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-3);
  overflow: hidden; max-height: 70vh; overflow-y: auto;
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity var(--t-fast) var(--e-out), transform var(--t-fast) var(--e-out), visibility var(--t-fast);
}
.suggest.is-open { opacity: 1; visibility: visible; transform: none; }
.suggest__item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--border-soft);
  transition: background var(--t-fast);
}
.suggest__item:last-child { border-bottom: 0; }
.suggest__item:hover, .suggest__item.is-active { background: var(--surface-2); }
.suggest__img { width: 34px; height: 45px; border-radius: var(--r-xs); object-fit: cover; flex: none; }
.suggest__name { font-size: var(--fs-sm); font-weight: 600; line-height: 1.3; }
.suggest__meta { font-size: var(--fs-2xs); color: var(--text-3); }
.suggest__empty { padding: var(--s-5); text-align: center; color: var(--text-3); font-size: var(--fs-sm); }

/* ---------------------------------------------------------------------------
   8. BUTTONS
   --------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  height: 44px; padding-inline: var(--s-5);
  font-family: var(--f-body); font-size: var(--fs-sm); font-weight: 600;
  letter-spacing: -0.005em;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  white-space: nowrap; cursor: pointer;

  /* A button label can contain a game name, and game names are not short.
     "See all 17 cheats for Grand Theft Auto: San Andreas" measures 404px —
     wider than the whole column on a phone — and `nowrap` meant it could not
     shrink, so it dragged the entire page into horizontal scroll. The cap
     below stops one long label breaking the layout; the media query further
     down lets it wrap rather than clip. */
  max-width: 100%;
  transition: transform var(--t-fast) var(--e-out), background var(--t-fast),
              border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.btn:active { transform: translateY(1px); }

/* Solid, flat. No gradient — that is the single loudest AI-template tell. */
.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { background: var(--accent-dim); }

.btn--ghost { border-color: var(--border); color: var(--text); background: var(--surface-1); }
.btn--ghost:hover { border-color: var(--border-loud); background: var(--surface-2); }

.btn--quiet { color: var(--text-2); padding-inline: var(--s-3); }
.btn--quiet:hover { color: var(--text); background: var(--surface-1); }

.btn--code { background: var(--code-wash); color: var(--code); border-color: color-mix(in srgb, var(--code) 30%, transparent); }
.btn--code:hover { background: color-mix(in srgb, var(--code) 20%, transparent); }

.btn--sm { height: 34px; padding-inline: var(--s-3); font-size: var(--fs-xs); }
.btn--lg { height: 52px; padding-inline: var(--s-6); font-size: 1rem; }
.btn--block { width: 100%; }

/* ---------------------------------------------------------------------------
   9. BADGES / PILLS / CHIPS
   --------------------------------------------------------------------------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  height: 22px; padding-inline: var(--s-2);
  font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  border-radius: var(--r-xs);
  background: var(--surface-2); color: var(--text-2);
  border: 1px solid var(--border);
}
.badge--accent  { background: var(--accent-wash); color: var(--accent); border-color: color-mix(in srgb, var(--accent) 28%, transparent); }
.badge--code    { background: var(--code-wash);   color: var(--code);   border-color: color-mix(in srgb, var(--code) 28%, transparent); }
.badge--warn    { background: var(--warn-wash);   color: var(--warn);   border-color: color-mix(in srgb, var(--warn) 28%, transparent); }
.badge--ok      { background: color-mix(in srgb, var(--ok) 12%, transparent); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 28%, transparent); }

.chip {
  display: inline-flex; align-items: center; gap: var(--s-2);
  height: 32px; padding-inline: var(--s-3);
  font-size: var(--fs-xs); font-weight: 500;
  border-radius: var(--r-full);
  background: var(--surface-1); border: 1px solid var(--border);
  color: var(--text-2);
  transition: all var(--t-fast) var(--e-out);
}
a.chip:hover { color: var(--text); border-color: var(--border-loud); background: var(--surface-2); }
.chip.is-active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.chip__count { font-size: var(--fs-2xs); color: var(--text-3); font-variant-numeric: tabular-nums; }
.chip.is-active .chip__count { color: color-mix(in srgb, var(--accent-ink) 70%, transparent); }

.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }

/* Metascore — colour-coded, tabular, never a coloured blob */
.score {
  display: inline-grid; place-items: center;
  width: 38px; height: 38px;
  font-family: var(--f-display); font-weight: 700; font-size: 1rem;
  border-radius: var(--r-sm); border: 1.5px solid;
  font-variant-numeric: tabular-nums; flex: none;
}
.score.is-great { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 45%, transparent);   background: color-mix(in srgb, var(--ok) 10%, transparent); }
.score.is-good  { color: #A3E635;     border-color: rgba(163,230,53,.45);  background: rgba(163,230,53,.10); }
.score.is-mixed { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: var(--warn-wash); }
.score.is-poor  { color: var(--bad);  border-color: color-mix(in srgb, var(--bad) 45%, transparent);  background: color-mix(in srgb, var(--bad) 10%, transparent); }
.score.is-none  { color: var(--text-3); border-color: var(--border); }
.score--sm { width: 30px; height: 30px; font-size: var(--fs-xs); }

/* ---------------------------------------------------------------------------
   10. GAME CARD
   Cover-led. Metadata sits BELOW the image, not overlaid — overlays on box art
   are unreadable at phone size and hide the thing people scan for.
   --------------------------------------------------------------------------- */
.gcard { display: block; position: relative; }

.gcard__art {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  transition: transform var(--t-base) var(--e-spring),
              border-color var(--t-base) var(--e-out),
              box-shadow var(--t-base) var(--e-out);
  will-change: transform;
}
.gcard__art img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--t-slow) var(--e-out), filter var(--t-base);
}
.gcard:hover .gcard__art {
  transform: translateY(-5px);
  border-color: var(--border-loud);
  box-shadow: var(--shadow-3);
}
.gcard:hover .gcard__art img { transform: scale(1.045); }

/* A single hairline of accent slides in on hover. Cheap, precise, not a glow. */
.gcard__art::after {
  content: ''; position: absolute; inset-inline: 0; bottom: 0; height: 2px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-base) var(--e-out);
}
.gcard:hover .gcard__art::after { transform: scaleX(1); }

.gcard__flag {
  position: absolute; top: var(--s-2); left: var(--s-2);
  z-index: 2;
  backdrop-filter: blur(8px);
  background: rgba(10, 7, 16, .72);
  border-color: rgba(255,255,255,.14);
  color: #fff;
}
.gcard__score { position: absolute; top: var(--s-2); right: var(--s-2); z-index: 2; backdrop-filter: blur(8px); }

/* "Will it run?" badge, added by rig-badge.js only once a PC has been saved.
   Sits along the bottom edge so it never collides with the flag or the score,
   and is absolutely positioned inside art that already has a reserved aspect
   ratio — so adding it after load cannot shift anything. CLS stays at zero. */
.rigbadge {
  position: absolute; left: var(--s-2); right: var(--s-2); bottom: var(--s-2);
  z-index: 2;
  display: block;
  padding: 4px var(--s-2);
  border-radius: var(--r-sm);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: .01em;
  text-align: center;
  backdrop-filter: blur(8px);
  border: 1px solid transparent;
}
.rigbadge--ok   { background: color-mix(in srgb, var(--ok) 22%, rgba(10,7,16,.72));   color: #fff; border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.rigbadge--warn { background: color-mix(in srgb, var(--warn) 22%, rgba(10,7,16,.72)); color: #fff; border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.rigbadge--bad  { background: color-mix(in srgb, var(--bad) 22%, rgba(10,7,16,.72));  color: #fff; border-color: color-mix(in srgb, var(--bad) 40%, transparent); }

.gcard__body { padding-top: var(--s-3); }
.gcard__name {
  font-family: var(--f-display); font-weight: 600;
  font-size: 0.9375rem; line-height: 1.25; letter-spacing: -0.012em;
  color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color var(--t-fast) var(--e-out);
}
.gcard:hover .gcard__name { color: var(--accent); }
.gcard__meta {
  margin-top: 3px; font-size: var(--fs-2xs); color: var(--text-3);
  display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
}
.gcard__meta span + span::before { content: '·'; margin-right: var(--s-2); color: var(--border-loud); }
.gcard__price { color: var(--code); font-weight: 600; }
.gcard__was { color: var(--text-3); text-decoration: line-through; }

/* ---------------------------------------------------------------------------
   11. HERO  — asymmetric editorial split, not a centred stack
   --------------------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(var(--s-7), 8vw, var(--s-9));
  border-bottom: 1px solid var(--border-soft);
  overflow: hidden;
}
/* Backdrop is the featured game's own art, heavily dimmed. Not a synthetic blob. */
.hero__bg {
  position: absolute; inset: 0; z-index: 0;
  background-size: cover; background-position: center 22%;
  opacity: .30;
  filter: saturate(115%);
  mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,.55) 45%, transparent 92%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,.55) 45%, transparent 92%);
  transform: scale(1.06);
  will-change: transform;
}
.hero__scrim {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(100deg, var(--bg) 18%, color-mix(in srgb, var(--bg) 55%, transparent) 62%, transparent 100%);
}
.hero__inner { position: relative; z-index: 2; }

.hero__grid { display: grid; gap: var(--s-6); align-items: center; }
@media (min-width: 900px) { .hero__grid { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: var(--s-8); } }

.hero__title {
  font-size: var(--fs-hero);
  font-weight: 700; line-height: 0.94; letter-spacing: -0.035em;
  margin-block: var(--s-3);
}
.hero__sub { font-size: 1.125rem; color: var(--text-2); max-width: 54ch; }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-6); }

.hero__stats {
  display: flex; flex-wrap: wrap; gap: var(--s-6);
  margin-top: var(--s-7); padding-top: var(--s-5);
  border-top: 1px solid var(--border);
}
.hero__stat b {
  display: block;
  font-family: var(--f-display); font-weight: 700;
  font-size: 1.75rem; line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.hero__stat span {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--text-3); margin-top: 5px; display: block;
}

/* Featured cover with a real 3D tilt on pointer */
.hero__art {
  position: relative; max-width: 300px; margin-inline: auto;
  perspective: 1100px;
}
.hero__art img {
  width: 100%; border-radius: var(--r-lg);
  border: 1px solid var(--border-loud);
  box-shadow: var(--shadow-3);
  transform: rotateY(var(--ry, -7deg)) rotateX(var(--rx, 3deg));
  transition: transform var(--t-slow) var(--e-out);
  will-change: transform;
}

/* ---------------------------------------------------------------------------
   12. COUNTDOWN — for GTA 6 and any upcoming release
   --------------------------------------------------------------------------- */
.countdown { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.countdown__unit {
  min-width: 74px; padding: var(--s-3) var(--s-2);
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-sm); text-align: center;
}
.countdown__num {
  display: block;
  font-family: var(--f-display); font-weight: 700;
  font-size: 2rem; line-height: 1; letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.countdown__label {
  display: block; margin-top: var(--s-2);
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--text-3);
}
.countdown--lg .countdown__unit { min-width: 96px; padding: var(--s-4) var(--s-3); }
.countdown--lg .countdown__num { font-size: clamp(2.25rem, 5vw, 3.25rem); }

/* ---------------------------------------------------------------------------
   13. CHEAT CODE BLOCK — the highest-stakes component on the site
   --------------------------------------------------------------------------- */
.cheat {
  display: grid; gap: var(--s-3);
  grid-template-columns: 1fr;
  padding: var(--s-4);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color var(--t-fast);
}
.cheat:hover { border-color: var(--border-loud); }
@media (min-width: 720px) {
  .cheat { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
}

.cheat__title { font-family: var(--f-display); font-weight: 600; font-size: 1.0625rem; line-height: 1.25; }
.cheat__effect { font-size: var(--fs-sm); color: var(--text-2); margin-top: 3px; }
.cheat__how { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--s-2); }

.cheat__code {
  display: inline-flex; align-items: center; gap: var(--s-3);
  font-family: var(--f-mono); font-size: var(--fs-sm); font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--code);
  background: var(--code-wash);
  border: 1px solid color-mix(in srgb, var(--code) 26%, transparent);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3);
  cursor: pointer; user-select: all;
  transition: background var(--t-fast), border-color var(--t-fast);
  white-space: nowrap; overflow-x: auto;
}
.cheat__code:hover { background: color-mix(in srgb, var(--code) 18%, transparent); }
.cheat__copy { color: var(--text-3); flex: none; user-select: none; transition: color var(--t-fast); }
.cheat__code:hover .cheat__copy { color: var(--code); }
.cheat__code.is-copied { border-color: var(--ok); color: var(--ok); }

.cheat__warn {
  display: flex; gap: var(--s-2); align-items: flex-start;
  font-size: var(--fs-xs); color: var(--warn);
  background: var(--warn-wash); border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3); margin-top: var(--s-2);
  grid-column: 1 / -1;
}

/* Reader confirmations. Quiet on purpose — this sits below the tested badge
   and must never compete with it for attention, because it is the weaker
   claim of the two. */
.cheat__report {
  display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center;
  font-size: var(--fs-xs); color: var(--text-3);
  margin-top: var(--s-2); padding-top: var(--s-3);
  border-top: 1px dashed var(--border);
  grid-column: 1 / -1;
}
.cheat__report-q { margin-right: var(--s-1); }
.cheat__vote {
  font: inherit; color: var(--text-2);
  background: transparent; border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 2px var(--s-3);
  cursor: pointer;
  transition: border-color var(--t-fast), color var(--t-fast);
}
.cheat__vote:hover { border-color: var(--border-loud); color: var(--text); }
.cheat__vote[aria-pressed="true"] { border-color: var(--ok); color: var(--ok); }
.cheat__report.is-busy .cheat__vote { opacity: 0.5; pointer-events: none; }
.cheat__report-n { margin-left: auto; }

.cheat-group { margin-bottom: var(--s-7); }
.cheat-group__head { display: flex; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-4); }
.cheat-list { display: grid; gap: var(--s-3); }

/* ---------------------------------------------------------------------------
   14. FACT TABLE (GEO) + SPEC TABLE
   --------------------------------------------------------------------------- */
.facts {
  border: 1px solid var(--border); border-radius: var(--r-md);
  overflow: hidden; background: var(--surface-1);
}
.facts__row {
  display: grid; grid-template-columns: minmax(120px, 34%) 1fr;
  gap: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--border-soft);
  font-size: var(--fs-sm);
}
.facts__row:last-child { border-bottom: 0; }
.facts__row:nth-child(odd) { background: color-mix(in srgb, var(--surface-2) 45%, transparent); }
.facts__k { color: var(--text-3); font-weight: 500; }
.facts__v { color: var(--text); font-variant-numeric: tabular-nums; }
.facts__v a { color: var(--text); text-decoration: underline; text-decoration-color: var(--border-loud); text-underline-offset: 2px; }
.facts__v a:hover { color: var(--accent); text-decoration-color: var(--accent); }

.spec-table th, .spec-table td {
  padding: var(--s-3) var(--s-4); text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border-soft); font-size: var(--fs-sm);
}
.spec-table th {
  font-family: var(--f-body); font-weight: 600;
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--text-3); background: var(--surface-2);
}
.spec-table td:first-child { color: var(--text-3); width: 30%; }
.table-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-md); }

/* ---------------------------------------------------------------------------
   15. ANSWER BLOCK (AEO) — the quotable summary at the top of every page
   --------------------------------------------------------------------------- */
.answer {
  position: relative;
  padding: var(--s-5);
  padding-left: var(--s-6);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: 1.0625rem; line-height: 1.6;
  color: var(--text);
  max-width: 70ch;
}
.answer::before {
  content: ''; position: absolute; left: 0; top: var(--s-4); bottom: var(--s-4);
  width: 3px; background: var(--accent); border-radius: var(--r-full);
}
.answer__meta {
  margin-top: var(--s-3); padding-top: var(--s-3);
  border-top: 1px solid var(--border-soft);
  font-size: var(--fs-2xs); color: var(--text-3);
  display: flex; flex-wrap: wrap; gap: var(--s-4);
}

/* ---------------------------------------------------------------------------
   16. FAQ ACCORDION
   --------------------------------------------------------------------------- */
.faq { border-top: 1px solid var(--border); max-width: var(--measure); }
.faq__item { border-bottom: 1px solid var(--border); }
.faq__q {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4) 0; text-align: left;
  font-family: var(--f-display); font-weight: 600; font-size: 1.0625rem; line-height: 1.35;
  color: var(--text); transition: color var(--t-fast);
}
.faq__q:hover { color: var(--accent); }
.faq__icon { flex: none; color: var(--text-3); transition: transform var(--t-base) var(--e-spring), color var(--t-fast); }
.faq__item[open] .faq__icon { transform: rotate(45deg); color: var(--accent); }
.faq__a { padding-bottom: var(--s-5); color: var(--text-2); font-size: var(--fs-sm); line-height: 1.65; max-width: 66ch; }
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary { list-style: none; cursor: pointer; }

/* ---------------------------------------------------------------------------
   17. TABS  — used on game pages (Overview / Cheats / Requirements / Price)
   --------------------------------------------------------------------------- */
.tabs {
  display: flex; gap: var(--s-1);
  border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
  margin-bottom: var(--s-6);

  /* overflow-x alone does NOT let this shrink.
     A grid or flex item defaults to min-width:auto, which resolves to its
     min-content size — and because every .tabs__link is white-space:nowrap,
     that is the full width of all tabs laid end to end. On a game page with
     five tabs that came to ~676px, which forced the grid track wider than the
     viewport and pushed the entire page into horizontal scroll. The scroll
     container could never engage, because nothing was ever allowed to be
     narrower than its contents. */
  min-width: 0;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs__link {
  position: relative; flex: none;
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-3); white-space: nowrap;
  transition: color var(--t-fast);
}
.tabs__link:hover { color: var(--text); }
.tabs__link[aria-current] { color: var(--text); }
.tabs__link[aria-current]::after {
  content: ''; position: absolute; inset-inline: var(--s-3); bottom: -1px; height: 2px;
  background: var(--accent);
}
.tabs__count {
  display: inline-block; margin-left: 6px;
  font-size: var(--fs-2xs); color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------------
   18. GAME PAGE LAYOUT
   --------------------------------------------------------------------------- */
/* `minmax(0, 1fr)` rather than a bare implicit track.
   An implicit grid track is `auto`, whose minimum is min-content — so one wide
   child (a nowrap tab bar, a table, a long URL) silently widens the track past
   the container and takes the whole page with it. A zero minimum makes the
   track obey the container, and any over-wide child scrolls inside its own box
   instead. The same reasoning applies to .gmast below. */
.gpage { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-7); }
@media (min-width: 1000px) {
  .gpage { grid-template-columns: minmax(0, 1fr) 340px; gap: var(--s-8); align-items: start; }
  .gpage__aside { position: sticky; top: calc(var(--header-h) + var(--s-5)); }
}

.gmast { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }
@media (min-width: 640px) { .gmast { grid-template-columns: 200px minmax(0, 1fr); gap: var(--s-6); } }
@media (min-width: 1000px) { .gmast { grid-template-columns: 232px minmax(0, 1fr); } }

.gmast__cover {
  border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--border); box-shadow: var(--shadow-2);
  aspect-ratio: 3 / 4; background: var(--surface-2);
}
.gmast__cover img { width: 100%; height: 100%; object-fit: cover; }
.gmast__title { font-size: var(--fs-h1); font-weight: 700; letter-spacing: -0.03em; line-height: 1; }
.gmast__tagline { color: var(--text-2); margin-top: var(--s-3); max-width: 60ch; }
.gmast__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-top: var(--s-4); }

.aside-card {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--s-4);
}
.aside-card + .aside-card { margin-top: var(--s-4); }
.aside-card__title {
  font-family: var(--f-body); font-size: var(--fs-2xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-3);
  margin-bottom: var(--s-3);
}

/* Price rows */
.price-row {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) 0; border-bottom: 1px solid var(--border-soft);
}
.price-row:last-child { border-bottom: 0; }
.price-row__store { flex: 1; min-width: 0; font-size: var(--fs-sm); font-weight: 500; }
.price-row__now { font-family: var(--f-display); font-weight: 700; font-size: 1.0625rem; font-variant-numeric: tabular-nums; }
.price-row__was { font-size: var(--fs-xs); color: var(--text-3); text-decoration: line-through; }
.price-row__off { color: var(--ok); font-size: var(--fs-2xs); font-weight: 700; }
.price-row--best { border-left: 2px solid var(--code); padding-left: var(--s-3); margin-left: calc(var(--s-3) * -1); }

/* ---------------------------------------------------------------------------
   19. MEDIA GALLERY
   --------------------------------------------------------------------------- */
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--s-3); }
.gallery__item {
  aspect-ratio: 16 / 9; border-radius: var(--r-sm); overflow: hidden;
  border: 1px solid var(--border-soft); background: var(--surface-2);
  position: relative;
}
.gallery__item img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow) var(--e-out); }
.gallery__item:hover img { transform: scale(1.05); }

.video-embed { position: relative; aspect-ratio: 16 / 9; border-radius: var(--r-md); overflow: hidden; background: var(--bg-deep); border: 1px solid var(--border); }
.video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Facade: we render a thumbnail and only load the YouTube iframe on click,
   because an autoloaded embed costs ~700KB and wrecks LCP on mobile. */
.video-facade { cursor: pointer; }
.video-facade img { width: 100%; height: 100%; object-fit: cover; }
.video-facade__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgba(6,4,10,.35); transition: background var(--t-base);
}
.video-facade:hover .video-facade__play { background: rgba(6,4,10,.15); }
.video-facade__btn {
  width: 66px; height: 46px; border-radius: var(--r-md);
  background: var(--accent); color: var(--accent-ink);
  display: grid; place-items: center;
  transition: transform var(--t-base) var(--e-spring);
}
.video-facade:hover .video-facade__btn { transform: scale(1.09); }

/* ---------------------------------------------------------------------------
   20. CHARACTER CARD
   --------------------------------------------------------------------------- */
.ccard { display: block; }
.ccard__art {
  aspect-ratio: 3 / 4; border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--border-soft); background: var(--surface-2);
  position: relative;
  transition: border-color var(--t-base), transform var(--t-base) var(--e-spring);
}
.ccard:hover .ccard__art { border-color: var(--accent); transform: translateY(-4px); }
.ccard__art img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.ccard__name { font-family: var(--f-display); font-weight: 600; font-size: var(--fs-sm); margin-top: var(--s-3); }
.ccard__role { font-size: var(--fs-2xs); color: var(--text-3); text-transform: uppercase; letter-spacing: 0.08em; }

/* ---------------------------------------------------------------------------
   21. ARTICLE CARD
   --------------------------------------------------------------------------- */
/* `position: relative` is the anchor for the stretched title link in section 32.
   It is here, on the base rule, rather than inside the media query, because a
   `position` that only exists at one breakpoint is the kind of thing that gets
   deleted as dead code later. */
.acard { display: grid; gap: var(--s-3); position: relative; }
.acard__art {
  aspect-ratio: 16 / 10; border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--border-soft); background: var(--surface-2);
}
.acard__art img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow) var(--e-out); }
.acard:hover .acard__art img { transform: scale(1.04); }
.acard__cat { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); }
.acard__title {
  font-family: var(--f-display); font-weight: 600; font-size: 1.125rem; line-height: 1.25;
  letter-spacing: -0.015em; margin-top: var(--s-1);
  transition: color var(--t-fast);
}
.acard:hover .acard__title { color: var(--accent); }
.acard__meta { font-size: var(--fs-2xs); color: var(--text-3); margin-top: var(--s-2); }

/* Featured article — bigger type, no bigger box */
.acard--lead .acard__art { aspect-ratio: 16 / 9; }
.acard--lead .acard__title { font-size: clamp(1.375rem, 2.4vw, 1.875rem); }

/* ---------------------------------------------------------------------------
   22. FILTER BAR
   --------------------------------------------------------------------------- */
.filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  padding: var(--s-4) 0; margin-bottom: var(--s-5);
  border-block: 1px solid var(--border-soft);
}
.filters__group { display: flex; align-items: center; gap: var(--s-2); }
.filters__label {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--text-3); font-weight: 600;
}
.select {
  height: 36px; padding: 0 var(--s-6) 0 var(--s-3);
  background: var(--surface-1) no-repeat right var(--s-2) center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%236F6683' stroke-width='2'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
  border: 1px solid var(--border); border-radius: var(--r-sm);
  font-size: var(--fs-sm); color: var(--text);
  appearance: none; cursor: pointer;
  transition: border-color var(--t-fast);
}
.select:hover { border-color: var(--border-loud); }
.results-count { font-size: var(--fs-sm); color: var(--text-3); margin-left: auto; font-variant-numeric: tabular-nums; }

/* A-Z index strip */
.az { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.az__link {
  min-width: 36px; height: 36px; display: grid; place-items: center;
  font-family: var(--f-display); font-weight: 600; font-size: var(--fs-sm);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text-2); transition: all var(--t-fast) var(--e-out);
}
.az__link:hover { border-color: var(--accent); color: var(--accent); }
.az__link.is-active { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* ---------------------------------------------------------------------------
   23. PAGINATION
   --------------------------------------------------------------------------- */
.pager { display: flex; align-items: center; justify-content: center; gap: var(--s-2); margin-top: var(--s-7); flex-wrap: wrap; }
.pager__link {
  min-width: 40px; height: 40px; padding-inline: var(--s-3);
  display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-2);
  font-variant-numeric: tabular-nums;
  transition: all var(--t-fast) var(--e-out);
}
.pager__link:hover { border-color: var(--border-loud); color: var(--text); }
.pager__link[aria-current] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.pager__gap { color: var(--text-3); padding-inline: var(--s-1); }

/* ---------------------------------------------------------------------------
   24. BREADCRUMBS
   --------------------------------------------------------------------------- */
.crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
  font-size: var(--fs-xs); color: var(--text-3);
  padding-block: var(--s-4);
}
.crumbs a { color: var(--text-3); transition: color var(--t-fast); }
.crumbs a:hover { color: var(--accent); }
.crumbs li:not(:last-child)::after { content: '/'; margin-left: var(--s-2); color: var(--border-loud); }
.crumbs li { display: inline-flex; align-items: center; gap: var(--s-2); }
.crumbs [aria-current] { color: var(--text-2); }

/* ---------------------------------------------------------------------------
   25. MARQUEE TICKER — live deals / new releases strip
   --------------------------------------------------------------------------- */
.ticker {
  border-block: 1px solid var(--border-soft);
  background: var(--surface-1);
  overflow: hidden; padding-block: var(--s-2);
  display: flex; align-items: center; gap: var(--s-4);
}
.ticker__tag {
  flex: none; margin-left: var(--s-4);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent); display: flex; align-items: center; gap: var(--s-2);
}
.ticker__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pulse 2s var(--e-in-out) infinite; }
.ticker__track { display: flex; gap: var(--s-6); white-space: nowrap; animation: marquee 48s linear infinite; }
.ticker:hover .ticker__track { animation-play-state: paused; }
.ticker__item { font-size: var(--fs-xs); color: var(--text-2); display: inline-flex; gap: var(--s-2); align-items: center; }
.ticker__item b { color: var(--text); font-weight: 600; }
.ticker__item em { color: var(--code); font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums; }

@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes pulse   { 0%,100% { opacity: 1; } 50% { opacity: .3; } }

/* ---------------------------------------------------------------------------
   26. FOOTER
   --------------------------------------------------------------------------- */
.footer {
  border-top: 1px solid var(--border);
  background: var(--bg-deep);
  padding-block: var(--s-8) var(--s-5);
  margin-top: var(--s-9);
}
.footer__grid { display: grid; gap: var(--s-6); }
@media (min-width: 720px) { .footer__grid { grid-template-columns: 1.6fr repeat(3, 1fr); gap: var(--s-7); } }
.footer__title {
  font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em;
  color: var(--text-3); margin-bottom: var(--s-3);
}
.footer__list li + li { margin-top: var(--s-2); }
.footer__list a { font-size: var(--fs-sm); color: var(--text-2); transition: color var(--t-fast); }
.footer__list a:hover { color: var(--accent); }
.footer__bottom {
  margin-top: var(--s-7); padding-top: var(--s-5);
  border-top: 1px solid var(--border-soft);
  display: flex; flex-wrap: wrap; gap: var(--s-4); justify-content: space-between;
  font-size: var(--fs-xs); color: var(--text-3);
}
.footer__legal { max-width: 70ch; line-height: 1.6; }

/* ---------------------------------------------------------------------------
   27. MOBILE DRAWER
   --------------------------------------------------------------------------- */
.drawer {
  position: fixed; inset: 0; z-index: var(--z-drawer);
  background: var(--bg);
  transform: translateX(100%);
  transition: transform var(--t-slow) var(--e-out);
  overflow-y: auto; padding: var(--s-4);
  visibility: hidden;
}
.drawer.is-open { transform: none; visibility: visible; }
.drawer__head { display: flex; align-items: center; justify-content: space-between; height: var(--header-h); }
.drawer__nav { margin-top: var(--s-5); }
.drawer__link {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s-4) 0; font-family: var(--f-display); font-weight: 600; font-size: 1.375rem;
  border-bottom: 1px solid var(--border-soft); color: var(--text);
  letter-spacing: -0.02em;
}
.drawer__link:hover { color: var(--accent); }
body.is-locked { overflow: hidden; }

/* ---------------------------------------------------------------------------
   28. NOTICES
   --------------------------------------------------------------------------- */
.notice {
  display: flex; gap: var(--s-3); align-items: flex-start;
  padding: var(--s-4); border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface-1);
  font-size: var(--fs-sm); color: var(--text-2);
}
.notice svg { flex: none; margin-top: 2px; }
.notice--warn { border-color: color-mix(in srgb, var(--warn) 30%, transparent); background: var(--warn-wash); color: var(--warn); }
.notice--ok   { border-color: color-mix(in srgb, var(--ok) 30%, transparent);   background: color-mix(in srgb, var(--ok) 10%, transparent); color: var(--ok); }
.notice--bad  { border-color: color-mix(in srgb, var(--bad) 30%, transparent);  background: color-mix(in srgb, var(--bad) 10%, transparent); color: var(--bad); }

.empty { text-align: center; padding: var(--s-9) var(--s-4); color: var(--text-3); }
.empty h3 { color: var(--text); margin-bottom: var(--s-2); }

/* Toast */
.toast {
  position: fixed; bottom: var(--s-5); left: 50%;
  transform: translate(-50%, 120%);
  z-index: var(--z-toast);
  background: var(--surface-3); border: 1px solid var(--border-loud);
  border-radius: var(--r-sm); padding: var(--s-3) var(--s-5);
  font-size: var(--fs-sm); font-weight: 500;
  box-shadow: var(--shadow-3);
  transition: transform var(--t-base) var(--e-spring);
}
.toast.is-visible { transform: translate(-50%, 0); }

/* ---------------------------------------------------------------------------
   29. FORMS
   --------------------------------------------------------------------------- */
.field { margin-bottom: var(--s-4); }
.field__label { display: block; font-size: var(--fs-sm); font-weight: 600; margin-bottom: var(--s-2); }
.field__hint { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--s-2); }
.input, .textarea {
  width: 100%; padding: var(--s-3);
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-sm); font-size: var(--fs-sm); color: var(--text);
  transition: border-color var(--t-fast), background var(--t-fast);
}
.input:focus, .textarea:focus { outline: none; border-color: var(--accent); background: var(--surface-2); }
.textarea { min-height: 140px; resize: vertical; line-height: 1.6; }

/* ---------------------------------------------------------------------------
   30. MOTION
   Reveal is opt-in per element via [data-reveal], and staggered by index.
   Nothing animates unless it earns it.
   --------------------------------------------------------------------------- */
/* Scoped to .js, which the inline head script sets. Without that guard a
   blocked or broken script leaves every revealed element permanently at
   opacity 0 — invisible content, on a site whose entire job is content. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--t-slow) var(--e-out), transform var(--t-slow) var(--e-out);
  transition-delay: calc(var(--i, 0) * 45ms);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

@keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.anim-in { animation: fadeUp var(--t-slow) var(--e-out) both; }

/* Skeleton shimmer for async panels */
.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.6s ease-in-out infinite;
  border-radius: var(--r-sm);
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
  .hero__bg, .hero__art img { transform: none !important; }
}

/* ===========================================================================
   31. ADVANCED MOTION
   All native browser features, all behind @supports, all optional. Nothing
   here is required for the page to work — a browser that lacks any of it gets
   the plain version, not a broken one.
   =========================================================================== */

/* --- Cross-page view transitions ------------------------------------------
   Turns a full page load into a morph. Chrome/Edge only for now; everywhere
   else this declaration is simply ignored and navigation stays instant. */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation: vt-out 180ms var(--e-in-out) both; }
::view-transition-new(root) { animation: vt-in  260ms var(--e-out) both; }

@keyframes vt-out { to   { opacity: 0; transform: translateY(-6px); } }
@keyframes vt-in  { from { opacity: 0; transform: translateY(10px); } }

/* The cover art keeps its identity across the jump from a grid to a game page,
   which is the single most expensive-feeling thing on the site and costs no JS. */
.gcard__art img { view-transition-name: none; }
.gmast__cover img { view-transition-name: game-cover; }

/* --- Scroll-driven reveal -------------------------------------------------
   Where the browser supports scroll-linked animation, the reveal runs on the
   compositor instead of through an IntersectionObserver callback. Smoother,
   and it takes the work off the main thread on the mid-range Android phones
   most of this audience is using. app.js detects support and skips its
   observer entirely when this is active. */
@supports (animation-timeline: view()) {
  .js.has-scroll-timeline [data-reveal] {
    opacity: 1;
    transform: none;
    animation: reveal-rise linear both;
    animation-timeline: view();
    animation-range: entry 2% cover 18%;
  }
}

@keyframes reveal-rise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

/* --- Card light sweep -----------------------------------------------------
   A single hard-edged highlight crossing the art on hover. Deliberately a
   sweep rather than a glow: a glow around a static box is decoration, a sweep
   reads as a surface catching light and disappears the moment you stop. */
.gcard__art::before {
  content: '';
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 38%,
              rgba(255, 255, 255, .13) 46%, rgba(255, 255, 255, .04) 52%, transparent 60%);
  transform: translateX(-120%);
  transition: transform 620ms var(--e-out);
}
@media (hover: hover) and (pointer: fine) {
  .gcard:hover .gcard__art::before { transform: translateX(120%); }
}

/* --- Pointer tilt ---------------------------------------------------------
   Driven by two custom properties JS writes on pointermove. Kept to a couple
   of degrees; anything more turns a catalogue into a toy. */
@media (hover: hover) and (pointer: fine) {
  .gcard--tilt .gcard__art {
    transform: perspective(760px)
               rotateX(var(--tilt-x, 0deg))
               rotateY(var(--tilt-y, 0deg))
               translateY(-5px);
    transition: transform 90ms linear, border-color var(--t-base), box-shadow var(--t-base);
  }
  .gcard--tilt:not(:hover) .gcard__art {
    transition: transform var(--t-base) var(--e-spring), border-color var(--t-base), box-shadow var(--t-base);
  }
}

/* --- Reading progress ----------------------------------------------------- */
.progress {
  position: fixed; top: 0; left: 0; right: 0; z-index: calc(var(--z-header) + 1);
  height: 2px; background: transparent; pointer-events: none;
}
.progress__bar {
  height: 100%; width: 100%; transform-origin: 0 50%; transform: scaleX(0);
  background: var(--accent);
}
@supports (animation-timeline: scroll()) {
  .progress__bar {
    animation: progress-grow linear both;
    animation-timeline: scroll(root block);
  }
}
@keyframes progress-grow { to { transform: scaleX(1); } }

/* --- Sticky section rail on long pages ------------------------------------ */
.tabs--sticky {
  position: sticky; top: var(--header-h); z-index: 20;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(150%) blur(12px);
  -webkit-backdrop-filter: saturate(150%) blur(12px);
  margin-inline: calc(var(--s-4) * -1);
  padding-inline: var(--s-4);
}

/* --- Counter ------------------------------------------------------------- */
.tnum-roll { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  @supports (animation-timeline: view()) {
    .js.has-scroll-timeline [data-reveal] { animation: none; opacity: 1; transform: none; }
  }
  .gcard__art::before { display: none; }
  .progress__bar { animation: none; transform: scaleX(0); }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ===========================================================================
   32. TOUCH TARGETS
   Enforced only where the pointer is coarse. A mouse hits a 34px control
   accurately; a thumb does not. Bumping everything to 44px unconditionally
   would make the desktop UI chunky for no benefit, so the minimum applies to
   touch devices, which is also where most of this audience is.
   =========================================================================== */
@media (pointer: coarse) {
  .icon-btn { width: 44px; height: 44px; }
  .btn--sm  { height: 44px; padding-inline: var(--s-4); }
  .az__link { min-width: 44px; height: 44px; }
  .pager__link { min-width: 44px; height: 44px; }
  /* Let a long button label wrap on a phone instead of being clipped by the
     max-width above. Two tidy lines beat a truncated sentence. */
  .btn { white-space: normal; text-align: center; height: auto; min-height: 44px;
         padding-block: var(--s-3); }

  /* The drawer search is the only search field a phone ever sees, and it
     inherited the 38px desktop height. 46px, not 44: the stretched input fills
     the *content* box, so a 44px field with 1px borders leaves a 42px input and
     the audit still reads it as undersized. The /search/ and 404 fields carry
     an inline height:52px, which correctly wins over this. */
  .search__field { height: 46px; }

  .chip { height: 44px; padding-inline: var(--s-4); }
  .tabs__link { padding-block: var(--s-4); }
  .nav__link  { padding-block: var(--s-3); }

  /* Footer links were the largest group of undersized targets — 35 of them on
     a single page, at roughly 21px tall. 6px of padding was not enough to
     reach a usable height; 12px brings a 21px line to 45px. */
  .footer__list a { display: inline-block; padding-block: 12px; }

  /* Breadcrumbs sit at the top of every page and were the same height as body
     text, which makes the "up one level" link genuinely hard to hit.
     12px and not 11: 11px measured 43.4px on the live site, which is under the
     target by half a pixel and reappears in every audit. 12px gives 45.4px. */
  .crumbs a { display: inline-block; padding-block: 12px; }

  /* The RSS · Sitemap · llms.txt row in the footer is three standalone utility
     links in a bare <p>, not prose, and they were 16px tall — the smallest
     targets left on the site once the search input was fixed.

     `:not(.footer__legal)` is doing real work here. The sibling paragraph is
     the copyright and data-attribution sentence, whose links (IGDB, RAWG,
     Steam, Wikipedia, CheapShark, IsThereAnyDeal) ARE inline prose. Padding
     those would break the line spacing of a paragraph, and they are licence
     attribution links people read rather than tap. They stay at 16px. */
  .footer__bottom p:not(.footer__legal) a { display: inline-block; padding-block: 12px; }

  /* ---- Article cards -------------------------------------------------------
     The card looks like one big tappable block and was not one. `.acard` is an
     <article>, and the only real target was the title link at 43.5px; the rest
     of the card — the artwork aside — did nothing.

     The title link is stretched over the whole card instead. That fixes the
     undersized title target and makes the card behave the way it already
     looks. The image link above it is `aria-hidden` with `tabindex="-1"` and
     points at the same URL, so covering it costs nothing.

     `.acard__cat` then has to be lifted back above the stretched layer, or it
     stops being separately clickable and the category link silently becomes
     part of the article link. It also gets a real height of its own: it was
     14px, the smallest target on the site. This costs about 25px of card
     height on touch devices only. */
  .acard__title a::after {
    content: ''; position: absolute; inset: 0; z-index: 1;
  }

  .acard__cat {
    position: relative; z-index: 2;
    display: inline-flex; align-items: center; min-height: 44px;
  }

  /* The logo is the most-tapped link on any site. */
  .logo { display: inline-flex; align-items: center; min-height: 44px; }

  /* Inline links in prose stay text-sized, but get a larger hit area. */
  .prose a { padding-block: 2px; }
}

/* (The stray `.icon-btn { width: 40px }` that used to sit here has moved up to
   the base rule in section 12. It was below the coarse-pointer media query, so
   it overrode it and every header button stayed 40px on touch — the exact
   regression that media query was added to prevent.) */

/* ---------------------------------------------------------------------------
   33. UTILITIES
   --------------------------------------------------------------------------- */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; top: -60px; left: var(--s-4); z-index: 999;
  background: var(--accent); color: var(--accent-ink);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-sm); font-weight: 600;
  transition: top var(--t-base) var(--e-out);
}
.skip-link:focus { top: var(--s-3); }

.stack   { display: flex; flex-direction: column; }
.row     { display: flex; align-items: center; }
.wrap    { flex-wrap: wrap; }
.between { justify-content: space-between; }
.gap-1 { gap: var(--s-1); } .gap-2 { gap: var(--s-2); } .gap-3 { gap: var(--s-3); }
.gap-4 { gap: var(--s-4); } .gap-5 { gap: var(--s-5); } .gap-6 { gap: var(--s-6); }
.mt-3 { margin-top: var(--s-3); } .mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); } .mt-6 { margin-top: var(--s-6); } .mt-7 { margin-top: var(--s-7); }
.mb-3 { margin-bottom: var(--s-3); } .mb-4 { margin-bottom: var(--s-4); } .mb-5 { margin-bottom: var(--s-5); }
.t-2 { color: var(--text-2); } .t-3 { color: var(--text-3); }
.t-sm { font-size: var(--fs-sm); } .t-xs { font-size: var(--fs-xs); }
.t-center { text-align: center; }
.flex-1 { flex: 1; min-width: 0; }
.hide-sm { display: none; }
@media (min-width: 640px) { .hide-sm { display: revert; } }
@media (max-width: 639px) { .only-sm { display: revert; } }
.only-sm { display: none; }

/* ---------------------------------------------------------------------------
   Cheat tools — troubleshooter, finder, command builder, gamepad tester.
   Existing tokens only. The option tiles are real radio inputs inside labels,
   so the wizard is a plain form that works without JavaScript.
   --------------------------------------------------------------------------- */
.mt-1 { margin-top: var(--s-1); } .mt-2 { margin-top: var(--s-2); } .mb-2 { margin-bottom: var(--s-2); }
/* A class that sets display beats the UA's [hidden] rule, which is how the
   builder's server form stayed visible under the Jedison one. */
[hidden] { display: none !important; }

.wizard { padding: var(--s-5); scroll-margin-top: calc(var(--header-h) + var(--s-4)); }
.wizard__set { border: 0; padding: 0; margin: 0; min-width: 0; }
.wizard__q { font-family: var(--f-display); font-size: var(--fs-h3); font-weight: 600; padding: 0; margin-bottom: var(--s-4); }
.wizard__options { display: grid; gap: var(--s-2); }
.wizard__options--grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); }
.wizard__option {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: 48px; padding: var(--s-3) var(--s-4);
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--r-sm);
  cursor: pointer; transition: border-color var(--t-fast), background var(--t-fast);
}
.wizard__option:hover { border-color: var(--border-loud); }
.wizard__option input { flex: none; width: 18px; height: 18px; accent-color: var(--accent); margin: 0; }
.wizard__option:has(input:checked) { border-color: var(--accent); background: var(--accent-wash); }
.wizard__option:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.wizard__trail {
  list-style: none; padding: 0; margin: 0 0 var(--s-5);
  display: flex; flex-wrap: wrap; gap: var(--s-2);
  font-size: var(--fs-xs);
}
.wizard__trail li { padding: 2px var(--s-3); border: 1px solid var(--border-soft); border-radius: var(--r-full); background: var(--surface-2); }
.wizard__steps { padding-left: 1.25rem; display: grid; gap: var(--s-2); }
.wizard__steps li { padding-left: var(--s-1); }
.wizard__games {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: var(--s-2) var(--s-5);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
}

/* Command builder */
.cb-form { display: grid; gap: var(--s-4); }
.cb-form [data-control] { display: grid; gap: var(--s-1); }
.cb-form label, .cb-form legend { font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.cb-form input, .cb-form select {
  width: 100%; height: 44px; padding: 0 var(--s-3);
  font: inherit; color: var(--text);
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.cb-form input:focus-visible, .cb-form select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cb-form fieldset { border: 0; padding: 0; margin: 0; min-width: 0; display: grid; gap: var(--s-4); }
.cb-form fieldset > legend { display: none; }
.cb-form small, .cb-form .jedi-description { font-size: var(--fs-xs); color: var(--text-3); }
.cb-form .jedi-messages-slot { display: none; }
.cb-form .cb-err { font-size: var(--fs-xs); color: var(--bad); }
.cb-form [aria-invalid="true"] { border-color: var(--bad); }
.cb-output {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  padding: var(--s-4); border: 1px dashed var(--border-loud); border-radius: var(--r-md);
  background: var(--surface-2);
}
.cb-output .cheat__code { max-width: 100%; }

/* Gamepad tester */
.gp-pads { display: grid; gap: var(--s-5); }
.gp-pad { padding: var(--s-5); }
.gp-buttons { display: grid; gap: var(--s-2); grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
.gp-btn {
  position: relative; overflow: hidden;
  padding: var(--s-2) var(--s-2) var(--s-3); min-height: 56px;
  font-size: var(--fs-xs); text-align: center; line-height: 1.2;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: border-color var(--t-fast);
}
.gp-btn b { display: block; font-family: var(--f-mono); font-size: var(--fs-sm); color: var(--text); }
.gp-btn span { color: var(--text-3); }
.gp-btn i { position: absolute; left: 0; bottom: 0; height: 4px; width: 0; background: var(--accent); }
.gp-btn.is-down { border-color: var(--accent); background: var(--accent-wash); }
.gp-sticks { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.gp-stick { text-align: center; font-size: var(--fs-xs); color: var(--text-3); }
.gp-stick__pad {
  position: relative; width: 120px; height: 120px; margin: 0 auto var(--s-2);
  border: 1px solid var(--border-loud); border-radius: 50%; background: var(--surface-1);
}
.gp-stick__pad::before, .gp-stick__pad::after { content: ""; position: absolute; background: var(--border-soft); }
.gp-stick__pad::before { left: 50%; top: 0; bottom: 0; width: 1px; }
.gp-stick__pad::after { top: 50%; left: 0; right: 0; height: 1px; }
.gp-stick__dot {
  position: absolute; left: 50%; top: 50%; width: 18px; height: 18px; margin: -9px 0 0 -9px;
  border-radius: 50%; background: var(--accent); z-index: 1;
}
.gp-stick.is-drifting .gp-stick__pad { border-color: var(--warn); }
.gp-axes { font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-2); font-variant-numeric: tabular-nums; }
.gp-seq { display: flex; flex-wrap: wrap; gap: var(--s-1); min-height: 32px; }
.gp-seq span {
  font-family: var(--f-mono); font-size: var(--fs-xs); padding: 2px var(--s-2);
  border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface-1);
}
.gp-seq span.is-ok { border-color: var(--ok); color: var(--ok); }
.gp-seq span.is-bad { border-color: var(--bad); color: var(--bad); }
.gp-seq span.is-slow { border-color: var(--warn); color: var(--warn); }
/* ---------------------------------------------------------------------------
   Newsletter form, comments, charts, article search (batch 2)
   --------------------------------------------------------------------------- */
.nl-form__title { font-family: var(--f-display); font-weight: 600; font-size: 1.125rem; margin-bottom: var(--s-3); }
.nl-form__row { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-2); }
.nl-form__row .input { flex: 1 1 200px; min-width: 0; height: 44px; }
.nl-form__note { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--s-2); }
.nl-form__note a { color: var(--text-2); text-decoration: underline; text-underline-offset: 2px; }
.nl-form__msg { font-size: var(--fs-sm); margin-top: var(--s-2); min-height: 1.2em; }
.nl-form__msg:empty { min-height: 0; margin: 0; }
.nl-form__msg.is-ok { color: var(--ok); }
.nl-form__msg.is-bad { color: var(--bad); }
.nl-form__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.nl-form--compact .footer__title { display: block; margin-bottom: 0; }

.giscus, .giscus-frame { width: 100%; }

.chart { position: relative; }
.chart__canvas { width: 100%; height: 260px; }
.chart__canvas[hidden] + [data-chart-fallback] { display: block; }
.chart-table summary { cursor: pointer; font-size: var(--fs-sm); color: var(--text-2); margin-top: var(--s-3); }
.chart-table summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chart-table[open] summary { margin-bottom: var(--s-3); }

.article-search { display: flex; flex-wrap: wrap; gap: var(--s-2); max-width: 560px; }
.article-search .input { flex: 1 1 220px; min-width: 0; height: 44px; }
#pagefind-search { --pagefind-ui-scale: 0.9; --pagefind-ui-primary: var(--accent); --pagefind-ui-text: var(--text);
  --pagefind-ui-background: var(--surface-1); --pagefind-ui-border: var(--border); --pagefind-ui-tag: var(--surface-2);
  --pagefind-ui-border-radius: 6px; --pagefind-ui-font: var(--f-body); }
#pagefind-search mark { background: var(--accent-wash); color: var(--text); }
/* ---------------------------------------------------------------------------
   Retro codes, Sequence Rush, pixel art editor, emulator (batch 3)
   --------------------------------------------------------------------------- */
.retro__set { border: 1px solid var(--border); border-radius: var(--r-md); background: var(--surface-1); padding: var(--s-3) var(--s-4); }
.retro__set summary { cursor: pointer; font-size: var(--fs-sm); padding-block: var(--s-1); }
.retro__set summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.retro__set[open] summary { margin-bottom: var(--s-2); }

.game__stage, .emu__player, .editor {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: #0a0710; border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden;
}
.game__stage:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.game__stage canvas { display: block; }
.game__start, .emu__cover, .editor__cover {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: var(--s-5);
}
.game__title { font-family: var(--f-display); font-size: clamp(1.75rem, 5vw, 2.75rem); font-weight: 600; color: #f4f1f8; }
.editor { aspect-ratio: auto; height: min(78vh, 760px); }
.editor__frame { width: 100%; height: 100%; border: 0; display: block; }
.emu__player { aspect-ratio: 10 / 9; max-width: 640px; margin-inline: auto; }