/* ============================================================
   Game Rig — stylesheet.

   Look: an arcade cabinet, not a modern app. Near-black ground
   with a green cast, phosphor text, monospace throughout, visible
   scanlines, square corners and hard 2px rules. Nothing is
   rounded, nothing is blurred, nothing floats on a soft shadow.
   If a panel needs to sit above the page it gets a hard offset
   edge, the way a printed cabinet decal would.

   Order: tokens -> reset -> CRT -> chrome -> type -> buttons ->
   landing -> game page -> prose -> footer -> responsive.
   ============================================================ */

/* ---------- tokens ----------
   --black is not a colour, it is the ground the whole screen sits
   on: the page itself, and the bars and menus that belong to it.
   In the light theme that ground is green-tinted paper, so the
   token keeps its job and changes its value. Nothing outside this
   block should ever assume it means "dark".

   `.frame` is in the selector on purpose. The board a game is drawn
   on is the screen, and the screen does not turn into paper. The
   light blocks below are written against `:root` only, so a `.frame`
   subtree keeps these values while the room around it goes light.
   That is not decoration: `.panel` is `position:absolute; inset:8px`
   over the canvas, so without this a game page in light mode opens
   as a pale plate the size of the board and then snaps to a dark
   canvas on Play. It is also shipped at (0,1,0) against the light
   blocks' (0,2,0), which is fine precisely because those blocks
   never match a `.frame`. */
:root, .frame {
  /* Dark is the theme you get for free, so it declares its own
     colour-scheme here. The light theme sets it inside its own
     blocks below. */
  color-scheme: dark;

  /* grounds, furthest from the page first */
  --black:   #050706;
  --screen:  #080d0b;
  --panel:   #0c1512;
  --panel-2: #101d18;
  --panel-3: #16281f;

  /* hard rules. These are borders you can see, on purpose. */
  --line:    #1c3428;
  --line-2:  #2b5440;
  --edge:    #030504;

  /* Text sitting ON a filled button, bar or pill. Separate from
     --edge, which is the offset shadow, because the two stop being
     the same colour as soon as the lights come on: a green dark
     enough to read on paper is far too dark to carry dark ink, and
     a shadow light enough to carry white ink is not a shadow. */
  --on-fill: #030504;

  /* phosphors */
  --green:   #3dff8a;
  --green-2: #1fa85c;
  --amber:   #ffb020;
  --cyan:    #3ad9e8;
  --magenta: #ff4fa3;
  --red:     #ff5252;

  /* type */
  --text:    #cdf7de;
  --muted:   #6f9a83;
  /* The third tier, for the footer, the breadcrumb and the counts.
     It shipped at #45604f, which is 2.92:1 on this ground — under
     even the 3:1 floor for large text, let alone the 4.5:1 that
     12px copy needs. Found by measuring the rendered page rather
     than the token, which is the only way this kind of thing shows
     up. #638670 is the smallest step up that clears 4.5:1 on the
     tighter of its two grounds and still reads as the quiet tier:
     4.98:1, against --muted's 6.37:1. */
  --dim:     #638670;

  /* The washes. These were literal rgba() values scattered down the
     file until there was a second theme to put them in, at which
     point every one of them turned out to be a decision rather than
     a detail: the phosphor bloom over the top of the page, four
     separate scanline strengths for four different surfaces, the
     near-opaque plate the HUD wears, and the veil over a card. */
  --wash:       rgba(61, 255, 138, 0.05);
  --scan-page:  rgba(0, 0, 0, 0.20);
  --scan-bezel: rgba(0, 0, 0, 0.26);
  --scan-card:  rgba(0, 0, 0, 0.24);
  --scan-frame: rgba(0, 0, 0, 0.16);
  --plate:      rgba(3, 5, 4, 0.93);
  --veil:       rgba(5, 7, 6, 0.72);
  --dot:        0 0 8px var(--green);

  /* shape. There is one radius on this site and it is zero. */
  --bw: 2px;
  --sh: 4px 4px 0;
  --glow: 0 0 10px;

  /* layout */
  --max: 1160px;
  --pad: clamp(16px, 4vw, 30px);
  --gap: 18px;

  --display: "VT323", "IBM Plex Mono", ui-monospace, monospace;
  --body:    "IBM Plex Mono", ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
}

/* ---------- the lights on ----------
   The same cabinet with the room lit: green-tinted paper instead of
   phosphor on black. Dark is what you get unless you ask for
   something else — there is no state in which this theme forces
   itself on a visitor, which is why the attribute only ever appears
   because a script or a click put it there.

   The palette is written out twice on purpose. One copy answers the
   device (no attribute: a media query is not a selector and cannot
   be folded into a selector list), the other answers a click that
   disagrees with the device. They are the same numbers and they have
   to be edited together.

   Every pair below was measured, not eyeballed; the ratios are in
   NOTES.md. Held to the dark theme's own standard rather than a
   stricter invented one, and ahead of it in the two tiers that
   matter most for reading: --muted 6.43:1 here against 6.37:1 on
   black, --dim 4.90:1 against 4.98:1 there — and both of those
   clear 4.5:1, which is the number that actually matters and which
   neither tier managed before this pass. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;

    /* the page is paper; the SCREEN is still glass. --screen is the
       only ground that does not invert: it sits behind live canvases
       and behind card art whose neons were mixed against near-black,
       and a white board would flash on every game page before the
       first frame. Light page, dark screens, and it reads as
       deliberate rather than as one token somebody forgot. */
    --black:   #e6efe3;
    --screen:  #0d1a14;
    --panel:   #f0f7ed;
    --panel-2: #f8fcf6;
    --panel-3: #fbfdfa;

    /* Rules are hairlines you are meant to see, so on paper they get
       MORE contrast than they have on black, not less: 1.95:1 and
       3.85:1 here against 1.51:1 and 2.35:1 there. */
    --line:    #95b393;
    --line-2:  #548060;

    /* The offset shadow stays a dark green-black. On paper it is
       stronger than it ever was on black (15.59:1), and since
       --on-fill now carries the writing on fills, --edge is free to
       be nothing but ink and shadow. */
    --edge:    #0a1710;
    --on-fill: #f2fbf3;

    /* Phosphors re-mixed for paper. #3dff8a is 1.12:1 on this ground:
       a colour that glows on black is not a colour you can read on
       stock, so each one is chosen to clear 4.5:1 as TEXT and to stay
       recognisably itself. */
    --green:   #0d783a;
    --green-2: #37905c;
    --amber:   #8a5200;
    --cyan:    #0c6675;
    --magenta: #a81f63;
    --red:     #b21f1f;

    --text:    #101d15;
    --muted:   #345c48;
    --dim:     #4d6d55;

    /* One line retires all four text glows, because all four are
       written as `var(--glow) <colour>`. A halo behind dark green
       type on paper is a smudge, and this design has no blurred
       things in it anyway. */
    --glow: 0 0 0;

    /* The wash at the top of the page was a phosphor bloom; on paper
       the same light source is a white sheen, which is the one place
       light mode gets to be brighter rather than merely inverted. */
    --wash: rgba(255, 255, 255, 0.62);

    /* The full-page scanline is gone, not faded, and it is the only
       wash here that had to be measured rather than chosen. It is a
       fixed overlay at z-index 900 and a 1px-on/2px-off stripe, so
       worst case a glyph lands on a darkened row. At the dark
       theme's 0.20 every green link on the site drops to 2.97:1
       against a white-ish ground; 0.02 is the first alpha that
       clears 4.5:1 and at that point nothing is visible anyway. The
       three in-bezel scanlines stay, because they sit on art and on
       game boards rather than on prose. */
    --scan-page:  rgba(0, 0, 0, 0);
    --scan-bezel: rgba(0, 0, 0, 0.10);
    --scan-card:  rgba(0, 0, 0, 0.10);
    --scan-frame: rgba(0, 0, 0, 0.05);

    /* The HUD plate has to invert even though it sits on the dark
       board: the writing on it is --text, and dark ink on a dark
       plate is not a theme choice, it is an unreadable one. */
    --plate: rgba(247, 252, 246, 0.94);
    --veil:  rgba(244, 249, 242, 0.92);
    --dot:   none;
  }
}

/* The same palette again, for a click that overrules the device. */
:root[data-theme="light"] {
  color-scheme: light;

  --black:   #e6efe3;
  --screen:  #0d1a14;
  --panel:   #f0f7ed;
  --panel-2: #f8fcf6;
  --panel-3: #fbfdfa;

  --line:    #95b393;
  --line-2:  #548060;

  --edge:    #0a1710;
  --on-fill: #f2fbf3;

  --green:   #0d783a;
  --green-2: #37905c;
  --amber:   #8a5200;
  --cyan:    #0c6675;
  --magenta: #a81f63;
  --red:     #b21f1f;

  --text:    #101d15;
  --muted:   #345c48;
  --dim:     #4d6d55;

  --glow: 0 0 0;

  --wash:       rgba(255, 255, 255, 0.62);
  --scan-page:  rgba(0, 0, 0, 0);
  --scan-bezel: rgba(0, 0, 0, 0.10);
  --scan-card:  rgba(0, 0, 0, 0.10);
  --scan-frame: rgba(0, 0, 0, 0.05);
  --plate:      rgba(247, 252, 246, 0.94);
  --veil:       rgba(244, 249, 242, 0.92);
  --dot:        none;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 92px;
  background: var(--black);
}

body {
  margin: 0;
  min-height: 100%;
  background: var(--black);
  color: var(--text);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}

img, svg, canvas { max-width: 100%; }
img, svg { display: block; }

a { color: inherit; }
button { font: inherit; color: inherit; }

:focus-visible {
  outline: var(--bw) solid var(--green);
  outline-offset: 2px;
}

/* ---------- the screen ----------
   Two layers: scanlines, and a wash of phosphor along the top so
   the page has a light source. Kept faint enough that body text
   is unaffected — a CRT effect you have to squint through is a
   worse site, not a more atmospheric one. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 900;
  pointer-events: none;
  background:
    radial-gradient(120% 70% at 50% -10%, var(--wash), rgba(0, 0, 0, 0) 62%),
    repeating-linear-gradient(180deg,
      var(--scan-page) 0px,
      var(--scan-page) 1px,
      rgba(0, 0, 0, 0) 1px,
      rgba(0, 0, 0, 0) 3px);
}

.blink {
  display: inline-block;
  width: 0.62em;
  height: 1em;
  margin-left: 0.18em;
  background: var(--green);
  vertical-align: -0.12em;
  animation: blink 1.05s steps(1, end) infinite;
}
@keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .blink { animation: none; opacity: 1; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  background: var(--green);
  color: var(--on-fill);
  padding: 10px 16px;
  font-weight: 700;
}
.skip:focus { left: 0; }

.wrap {
  width: 100%;
  max-width: var(--max);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* ---------- top bar ---------- */
.top {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--black);
  border-bottom: var(--bw) solid var(--line);
}

.top__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 62px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  font-family: var(--display);
  font-size: 1.7rem;
  letter-spacing: 0.08em;
  color: var(--green);
  text-shadow: var(--glow) rgba(61, 255, 138, 0.45);
  line-height: 1;
}
/* Everything after the brand belongs on the right — the menu, and the
   theme switch past it at the far end of the row. An auto margin does
   that with the items in source order and without a wrapper, so the
   menu can still take a full-width row of its own when it wraps on a
   phone. */
.top .brand { margin-right: auto; }
/* The switch is emitted last, after the menu, so on a wide screen it
   lands on the end of the row where the eye expects chrome to be —
   it used to sit between the logo and the menu, which left it
   floating in the gap on its own. Source order alone would put it on
   a row below the menu once the header wraps, so the phone layout
   pulls it back up beside the burger with `order` instead. */
.top__in > .theme__btn { order: 2; }
.brand__mark { display: inline-flex; }

/* The mark is a green plate with the ground showing through it, so
   it has to move with the theme. These two rules are why it does:
   CSS beats an SVG presentation attribute, so the colours live here
   and the generator that writes the mark into every header and
   footer writes no colour at all. #3dff8a on paper is 1.12:1 — an
   invisible logo in the header of all fifty-one pages is what
   leaving it in the markup would have cost. */
.brand__mark rect { fill: var(--green); }
.brand__mark path { stroke: var(--on-fill); }

.nav { display: flex; flex-wrap: wrap; gap: 4px; }
.nav a {
  text-decoration: none;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 8px 12px;
  border: var(--bw) solid transparent;
  transition: color 0.1s linear, border-color 0.1s linear;
}
.nav a:hover { color: var(--green); border-color: var(--line); }
.nav a[aria-current="page"] {
  color: var(--on-fill);
  background: var(--green);
  border-color: var(--green);
}

/* ---------- the menu ----------
   Eight top-level items now, four of which open a list of their games.
   On a wide screen the lists open on hover and on keyboard focus, both
   of which are pure CSS, so the menu keeps working with JavaScript
   switched off. The button beside each category is there for touch
   only and is hidden until the layout goes narrow. */
.nav__item { position: relative; display: flex; align-items: center; }
.nav__menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 40;
  display: none;
  flex-direction: column;
  min-width: 200px;
  padding: 6px;
  background: var(--black);
  border: var(--bw) solid var(--line);
  box-shadow: var(--sh) var(--edge);
}
.nav__item:hover .nav__menu,
.nav__item:focus-within .nav__menu { display: flex; }
.nav__menu a {
  padding: 9px 12px;
  border-color: transparent;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: none;
  white-space: nowrap;
}
.nav__menu a:hover { border-color: var(--line); }

/* The arrow that opens a category on a touch screen. Hidden wherever
   there is a mouse, because there the whole item already opens. */
.nav__caret { display: none; }

.nav__burger {
  display: none;
  align-items: center;
  gap: 8px;
  padding: 9px 13px;
  border: var(--bw) solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}
.nav__burger:hover { border-color: var(--green); color: var(--green); }
.nav__burger i {
  display: block;
  width: 14px;
  height: 8px;
  border-top: var(--bw) solid currentColor;
  border-bottom: var(--bw) solid currentColor;
}

@media (max-width: 900px) {
  .top__in { flex-wrap: wrap; padding-block: 10px; }
  .nav__burger { display: inline-flex; }
  /* The switch is last in the markup, so without this it would land on
     a line of its own under the open menu. Order puts the first row
     back to brand, burger, switch, and the menu keeps the second row
     to itself through its 100% flex basis. */
  .top__in > .theme__btn { order: 2; }
  .nav {
    order: 3;
    display: none;
    flex-basis: 100%;
    flex-direction: column;
    gap: 2px;
    padding: 8px 0 4px;
    border-top: var(--bw) solid var(--line);
  }
  .nav.is-open { display: flex; }
  .nav > a,
  .nav__item { width: 100%; }
  .nav__item { flex-wrap: wrap; }
  .nav__item > .nav__link { flex: 1 1 auto; }
  .nav > a,
  .nav__item > .nav__link { padding: 11px 4px; }
  .nav__caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    padding: 11px 0;
    border: var(--bw) solid transparent;
    background: none;
    color: var(--muted);
    font: inherit;
    font-size: 0.8rem;
    cursor: pointer;
  }
  .nav__caret:hover { color: var(--green); }
  .nav__caret span {
    width: 9px;
    height: 9px;
    border-right: var(--bw) solid currentColor;
    border-bottom: var(--bw) solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform 0.12s linear;
  }
  .nav__item--open > .nav__caret span { transform: rotate(225deg) translate(-2px, -2px); }
  .nav__menu {
    position: static;
    display: none;
    width: 100%;
    min-width: 0;
    padding: 0 0 6px 12px;
    border: 0;
    box-shadow: none;
  }
  .nav__item--open .nav__menu { display: flex; }
  /* On touch there is no hover to lose, so an opened list stays open. */
  .nav__item:hover .nav__menu { display: none; }
  .nav__item--open:hover .nav__menu { display: flex; }
}

/* ---------- type ---------- */
h1, h2, h3 {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: 0.02em;
  line-height: 1.05;
  margin: 0 0 0.4em;
  color: var(--text);
  text-transform: uppercase;
}

h1 { font-size: clamp(2.6rem, 6vw, 4.4rem); color: var(--green); text-shadow: var(--glow) rgba(61, 255, 138, 0.35); }
h2 { font-size: clamp(1.8rem, 3.6vw, 2.6rem); }
h3 { font-size: 1.5rem; }

p { margin: 0 0 1em; }

.lede { font-size: 0.98rem; color: var(--muted); max-width: 54ch; }

.rule {
  height: var(--bw);
  background: var(--line);
  border: 0;
  margin: 0;
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 20px;
  border: var(--bw) solid var(--line-2);
  background: var(--panel-2);
  color: var(--text);
  font-family: var(--body);
  font-size: 0.84rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.08s linear, background-color 0.1s linear, border-color 0.1s linear;
}
.btn:hover { background: var(--panel-3); border-color: var(--green); color: var(--green); }
.btn:active { transform: translate(2px, 2px); }

.btn--primary {
  background: var(--green);
  border-color: var(--green);
  color: var(--on-fill);
  box-shadow: var(--sh) var(--edge);
}
.btn--primary:hover {
  background: var(--green);
  color: var(--on-fill);
  box-shadow: 2px 2px 0 var(--edge);
  transform: translate(2px, 2px);
}

.btn--ghost { background: transparent; }

/* ============================================================
   Landing page
   ============================================================ */

.hero {
  padding: clamp(34px, 6vw, 68px) 0 clamp(30px, 5vw, 54px);
  border-bottom: var(--bw) solid var(--line);
}

.hero__in {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 48px);
  align-items: center;
}

.kicker {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 16px;
  padding: 5px 11px;
  border: var(--bw) solid var(--line);
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.kicker__dot {
  width: 8px;
  height: 8px;
  background: var(--green);
  box-shadow: var(--dot);
}

.hero h1 { margin-bottom: 14px; }

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 24px 0 0;
}

/* the cabinet screen on the right */
.cabinet {
  border: var(--bw) solid var(--line-2);
  background: var(--panel);
  box-shadow: var(--sh) var(--edge);
  padding: 10px;
}
.cabinet__bezel {
  position: relative;
  border: var(--bw) solid var(--line);
  background: var(--screen);
  overflow: hidden;
}
.cabinet__bezel::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(180deg,
    var(--scan-bezel) 0px, var(--scan-bezel) 1px,
    rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 3px);
}
.cabinet canvas { display: block; width: 100%; height: auto; }

.cabinet__label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
  padding-top: 9px;
  border-top: var(--bw) solid var(--line);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.cabinet__label b { color: var(--amber); }

/* ---------- the cabinet cards ---------- */
.games { padding: clamp(30px, 5vw, 56px) 0; }

.section__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 22px;
  padding-bottom: 14px;
  border-bottom: var(--bw) solid var(--line);
}
.section__head p {
  margin: 0;
  color: var(--muted);
  font-size: 0.88rem;
  max-width: 40ch;
}

.grid-games {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
}

.gamecard {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  border: var(--bw) solid var(--line);
  background: var(--panel);
  color: inherit;
  transition: transform 0.08s linear, border-color 0.1s linear, box-shadow 0.1s linear;
}
.gamecard:hover {
  border-color: var(--green);
  box-shadow: var(--sh) var(--edge);
  transform: translate(-2px, -2px);
}
.gamecard__art {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--screen);
  border-bottom: var(--bw) solid var(--line);
  overflow: hidden;
}
.gamecard__art svg { display: block; width: 100%; height: 100%; }
.gamecard__art::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(180deg,
    var(--scan-card) 0px, var(--scan-card) 1px,
    rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 3px);
}
.gamecard__play {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  background: var(--veil);
  transition: opacity 0.1s linear;
}
.gamecard:hover .gamecard__play { opacity: 1; }
.gamecard__play span {
  padding: 10px 20px;
  border: var(--bw) solid var(--green);
  background: var(--green);
  color: var(--on-fill);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.gamecard__body { padding: 14px 16px 18px; }
.gamecard__tag {
  display: inline-block;
  margin-bottom: 7px;
  padding: 2px 7px;
  border: 1px solid var(--line-2);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--amber);
}
.gamecard h3 { margin-bottom: 5px; color: var(--text); }
.gamecard p { margin: 0; color: var(--muted); font-size: 0.85rem; line-height: 1.6; }
.gamecard__spec {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
}
.gamecard__spec b { color: var(--muted); font-weight: 700; margin-left: 5px; }

/* ---------- the scoreboard strip ---------- */
.points {
  padding: clamp(28px, 4vw, 48px) 0;
  border-top: var(--bw) solid var(--line);
}
.points__in {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}
.point {
  padding: 16px;
  border: var(--bw) solid var(--line);
  background: var(--panel);
}
.point b {
  display: block;
  margin-bottom: 5px;
  font-family: var(--display);
  font-size: 1.45rem;
  letter-spacing: 0.04em;
  color: var(--green);
  text-transform: uppercase;
}
.point p { margin: 0; color: var(--muted); font-size: 0.83rem; line-height: 1.65; }
.point__ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-bottom: 12px;
  border: var(--bw) solid var(--line-2);
  color: var(--green);
}

/* ============================================================
   Game page
   ============================================================ */

.stage { padding: clamp(22px, 4vw, 40px) 0 clamp(18px, 3vw, 28px); }

.stage__kicker {
  margin: 0 0 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--amber);
}
.stage__title { margin-bottom: 6px; }
.stage__lede {
  margin: 0 0 20px;
  color: var(--muted);
  max-width: 70ch;
  font-size: 0.92rem;
}

.frame {
  position: relative;
  border: var(--bw) solid var(--line-2);
  background: var(--panel);
  box-shadow: var(--sh) var(--edge);
  padding: 8px;
}
/* The scanlines sit above everything inside the bezel, panels included,
   so the whole screen reads as one piece of glass. Toggling a panel used
   to swap the CRT effect off, which made the overlays look pasted on. */
.frame::after {
  content: "";
  position: absolute;
  inset: 8px;
  z-index: 8;
  pointer-events: none;
  background: repeating-linear-gradient(180deg,
    var(--scan-frame) 0px, var(--scan-frame) 1px,
    rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 3px);
}

.frame canvas {
  display: block;
  width: 100%;
  height: auto;
  background: var(--screen);
  touch-action: none;
}

.hud {
  position: absolute;
  top: 20px;
  left: 20px;
  right: 20px;
  z-index: 6;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  pointer-events: none;
}
.hud__group { display: flex; gap: 6px; pointer-events: auto; }

/* The chips sit directly on the running board, which is the busiest
   surface on the site, so they get a heavier ground and a brighter
   rule than a panel would need. At 0.82 the labels dissolved into
   whatever the game happened to be drawing underneath. */
.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 5px 10px;
  border: var(--bw) solid var(--line-2);
  background: var(--plate);
  font-variant-numeric: tabular-nums;
}
.chip__k {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}
.chip b { font-size: 1.05rem; font-weight: 700; color: var(--green); }

.icon-btn {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--bw) solid var(--line-2);
  background: var(--plate);
  color: var(--text);
  cursor: pointer;
  transition: color 0.1s linear, border-color 0.1s linear;
}
.icon-btn:hover { color: var(--green); border-color: var(--green); }
.icon-btn[aria-pressed="true"] { color: var(--amber); border-color: var(--amber); }

/* ---------- the theme switch ----------
   It lives in the header beside the burger, so it is on all fifty-one
   pages, and it is hidden until the script that drives it has run: a
   button that cannot do anything is worse than no button, and the
   stylesheet answers the device on its own without any script at all.

   The glyph is whichever theme you would get by pressing it — a sun
   while the room is dark — and the script swaps the two, because
   working out the current theme in CSS means writing the light
   selectors a third time and the script has to know anyway. */
.theme__btn[hidden] { display: none; }
.theme__moon { display: none; }
.theme__btn.is-light .theme__sun { display: none; }
.theme__btn.is-light .theme__moon { display: block; }

/* ---------- overlays ---------- */
/* Overlays are solid, not translucent. A 90% panel let the board bleed
   through as an unreadable smudge behind the title, which read as a
   rendering fault rather than as atmosphere — and a soft wash under the
   text is the one blurred thing this design is meant not to have. The
   board waits underneath; the panel is a screen door, not a veil. */
.panel {
  position: absolute;
  inset: 8px;
  z-index: 7;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  text-align: center;
  padding: 22px;
  background: var(--black);
  border: var(--bw) solid var(--line-2);
}
.panel[hidden] { display: none; }
.panel h2 { margin-bottom: 4px; color: var(--green); text-shadow: var(--glow) rgba(61, 255, 138, 0.3); }
.panel p { margin: 0 0 10px; color: var(--muted); max-width: 50ch; font-size: 0.87rem; }
.panel .btn { margin: 3px 3px 0; }

.panel__keys {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  list-style: none;
  margin: 2px 0 16px;
  padding: 0;
}
.panel__keys li {
  padding: 5px 10px;
  border: var(--bw) solid var(--line);
  background: var(--panel-2);
  font-size: 0.76rem;
  color: var(--muted);
}
.panel__keys b { color: var(--green); font-weight: 700; }

.panel__score {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 4px 0 16px;
}
.panel__score > b {
  font-family: var(--display);
  font-size: 3.2rem;
  color: var(--amber);
  line-height: 1;
  text-shadow: var(--glow) rgba(255, 176, 32, 0.35);
}
.panel__score span { color: var(--dim); font-size: 0.76rem; letter-spacing: 0.1em; text-transform: uppercase; }
.panel__score span b { color: var(--muted); }

/* ---------- controls under the board ---------- */
.keys {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
}
.keys li {
  padding: 6px 11px;
  border: var(--bw) solid var(--line);
  background: var(--panel);
  color: var(--muted);
  font-size: 0.78rem;
}
.keys b {
  color: var(--on-fill);
  background: var(--green);
  padding: 2px 6px;
  margin-right: 5px;
  font-size: 0.74rem;
  font-weight: 700;
}

/* The controls reference under a game. Same hard rules as any other
   table here, but it sits outside .prose, so it carries its own copy
   of the styling instead of inheriting it. Collapses to a stacked
   list on a phone, where a two-column table would squeeze the
   descriptions to two words a line. */
.keytable {
  width: 100%;
  margin: 16px 0 0;
  border-collapse: collapse;
  font-size: 0.8rem;
}
.keytable caption {
  margin-bottom: 7px;
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: left;
}
.keytable th, .keytable td {
  padding: 8px 11px;
  border: var(--bw) solid var(--line);
  text-align: left;
  vertical-align: top;
}
.keytable th {
  background: var(--panel-2);
  color: var(--green);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.keytable td:first-child { color: var(--text); }
.keytable td:last-child { color: var(--muted); }
.keytable b {
  color: var(--on-fill);
  background: var(--green);
  padding: 1px 5px;
  font-size: 0.72rem;
  font-weight: 700;
}
@media (max-width: 560px) {
  .keytable, .keytable tbody, .keytable tr, .keytable td { display: block; width: 100%; }
  .keytable thead { display: none; }
  .keytable tr { border: var(--bw) solid var(--line); margin-bottom: -2px; }
  .keytable td { border: 0; padding: 6px 10px; }
  .keytable td:first-child { padding-bottom: 0; }
}

/* ============================================================
   The 3D pages

   Two of the sixteen games are drawn in WebGL — the racer and the
   flak battery — and between them they bring what a 2D board does
   not have: gauges over the field, a garage that takes coins, thumb
   controls under the frame on a phone, a wave banner and a hit
   flash. All of it is built from the tokens above — hard rules,
   square corners, nothing blurred.
   ============================================================ */

.gauge {
  position: absolute;
  bottom: 18px;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 9px;
  border: var(--bw) solid var(--line-2);
  background: var(--plate);
}
.gauge--hp { left: 20px; }
.gauge--nitro { right: 20px; }
.gauge__k {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}
.gauge__bar {
  display: block;
  width: 118px;
  height: 12px;
  border: 1px solid var(--line-2);
  /* The empty part of a gauge is a piece of the screen, not a piece
     of the chrome: --screen rather than --black, so the track stays
     dark in the light theme instead of turning into paper the same
     colour as the plate behind it. */
  background: var(--screen);
}
.gauge__bar i {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--green);
  transition: width 0.08s linear;
}
.gauge--nitro .gauge__bar i { background: var(--cyan); }

/* ---------- the flak battery's two bars ----------
   Health on the left is the same gauge the racer uses. Heat on the
   right is this game's own: it fills as you fire and drains whether
   you do or not, and it turns red for as long as the gun is locked,
   which is the only feedback the player gets for the second or so
   they cannot shoot. */
.gauge--heat { right: 20px; }
.gauge--heat .gauge__bar i { background: var(--amber); }
.gauge--heat.is-locked .gauge__bar i { background: var(--red); }

/* The damage flash. A hard inset rule rather than a wash of colour,
   because a translucent red over the field is exactly the kind of
   soft thing this design does not do — and because a border can
   pulse without hiding the drone that just got through. Its opacity
   is driven from the game loop, so it fades on the same clock as the
   thing that caused it. */
.flak__hurt {
  position: absolute;
  inset: 8px;
  z-index: 5;
  pointer-events: none;
  opacity: 0;
  border: 5px solid var(--red);
  box-shadow: inset 0 0 0 2px rgba(255, 82, 82, 0.35);
}

/* The wave banner, over the middle of the field for a second and a
   half at the top of each wave. Panel-sized type, no ground of its
   own — it is the one thing on this site that is legible straight
   off the running board. */
.flak__banner {
  position: absolute;
  left: 50%;
  top: 22%;
  z-index: 6;
  pointer-events: none;
  transform: translateX(-50%);
  padding: 6px 18px;
  border: var(--bw) solid var(--green);
  background: var(--plate);
  color: var(--green);
  font-family: var(--display);
  font-size: 1.9rem;
  letter-spacing: 0.14em;
  opacity: 0;
  transition: opacity 0.18s linear;
}
.flak__banner.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  /* The banner still appears and still goes; only the fade goes. */
  .flak__banner { transition: none; }
}

/* The start panel here is taller than any other game's — three cars, a
   note about each, a hint and a key list — and a 16:10 frame is not
   very tall. A centred column that overflows an absolutely positioned
   box spills in both directions at once, which put the garage over the
   headline above the frame and the buttons over the thumb controls
   below it. Scrolling, with the centring moved onto auto margins, so
   that when it does not fit it starts at the top and stays reachable. */
.page3d .panel { justify-content: flex-start; overflow-y: auto; }
.page3d .panel > :first-child { margin-top: auto; }
.page3d .panel > :last-child { margin-bottom: auto; }

/* ---------- the garage on the start panel ---------- */
.garage {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 2px 0 10px;
}
.garage__car {
  min-width: 152px;
  padding: 10px 12px;
  border: var(--bw) solid var(--line);
  background: var(--panel);
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.1s linear, box-shadow 0.1s linear, transform 0.08s linear;
}
.garage__car:hover { border-color: var(--green); }
.garage__car[aria-pressed="true"] {
  border-color: var(--green);
  box-shadow: var(--sh) var(--edge);
}
.garage__car[data-owned="no"] .garage__name { color: var(--muted); }
.garage__name {
  display: block;
  font-family: var(--display);
  font-size: 1.32rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.garage__bars { display: block; margin: 7px 0 6px; }
.garage__bars i,
.garage__bars em {
  display: block;
  height: 5px;
  margin-bottom: 3px;
  background: var(--green-2);
}
.garage__bars em { background: var(--amber); }
.garage__note {
  display: block;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: var(--dim);
}
.garage__hint {
  margin: 0 0 12px;
  font-size: 0.77rem;
  color: var(--muted);
}

/* ---------- the run, in numbers ---------- */
.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  width: 100%;
  max-width: 540px;
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
}
.stats li {
  padding: 8px;
  border: var(--bw) solid var(--line);
  background: var(--panel);
}
.stats span {
  display: block;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
}
.stats b {
  display: block;
  font-size: 1rem;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ---------- no WebGL, and thumbs ---------- */
.no3d {
  position: absolute;
  inset: 8px;
  z-index: 7;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: clamp(18px, 4vw, 34px);
  background: var(--black);
  border: var(--bw) solid var(--line-2);
}
.no3d[hidden] { display: none; }
.no3d h2 { color: var(--amber); }
.no3d p { margin: 0 0 10px; color: var(--muted); max-width: 58ch; font-size: 0.88rem; }
.no3d__alt a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }

.touch {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 12px;
}
.touch[hidden] { display: none; }
.touch__btn {
  flex: 1;
  padding: 20px 10px;
  border: var(--bw) solid var(--line-2);
  background: var(--panel-2);
  color: var(--green);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  cursor: pointer;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
.touch__btn:active { background: var(--green); color: var(--on-fill); }
.touch__btn--nitro {
  flex: 1.5;
  color: var(--amber);
  border-color: var(--amber);
}
.touch__btn--nitro:active { background: var(--amber); color: var(--on-fill); }

/* ---------- fullscreen ----------
   The canvas fills the screen and the camera is re-aimed at the new
   shape, which is the one thing a WebGL canvas can do that a bitmap
   cannot: no letterbox, no stretching. */
.frame:fullscreen {
  display: block;
  padding: 0;
  border: 0;
  box-shadow: none;
  background: var(--black);
}
.frame:fullscreen::after { inset: 0; }
.frame:fullscreen canvas { width: 100%; height: 100%; }

/* ---------- the same page on a phone ----------
   A phone frame is about 350 by 220 pixels, so the start panel stops
   being an overlay and takes its place under the canvas instead: the
   three cars, the hint and the button all fit that way, nothing has to
   be scrolled inside a box the size of a postcard, and the attract loop
   keeps running above it while you choose. Only the key list goes: the
   one under the frame already says the same thing, and on a phone the
   thumbs are on the buttons anyway. The gauges stay over the canvas,
   which is the only place they are ever shown. */
@media (max-width: 700px) {
  .page3d .panel {
    position: static;
    inset: auto;
    margin-top: 8px;
    padding: 14px;
    overflow: visible;
  }
  .page3d .panel__keys { display: none; }
  .page3d .garage { gap: 5px; margin-bottom: 8px; }
  .page3d .garage__car {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
    padding: 7px 10px;
  }
  .page3d .garage__name { font-size: 1.08rem; }
  .page3d .garage__bars { width: 52px; margin: 0; }
  .page3d .garage__note { font-size: 0.66rem; }
  .page3d .garage__hint { font-size: 0.7rem; margin-bottom: 8px; }
  .page3d .gauge { padding: 4px 7px; }
  .page3d .gauge--hp { left: 14px; }
  .page3d .gauge--nitro { right: 14px; }
  .page3d .gauge--heat { right: 14px; }
  .page3d .gauge__k { display: none; }
  .page3d .gauge__bar { width: 76px; }
}

/* ---------- below the fold ---------- */
.how { padding: clamp(26px, 4vw, 46px) 0 clamp(34px, 5vw, 60px); }.how__in {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 0.9fr);
  gap: clamp(24px, 4vw, 48px);
  align-items: start;
}

.side { display: grid; gap: 10px; }
.side__card {
  display: block;
  padding: 14px;
  border: var(--bw) solid var(--line);
  background: var(--panel);
  text-decoration: none;
  transition: border-color 0.1s linear, transform 0.08s linear;
}
.side__card:hover { border-color: var(--green); transform: translate(-2px, -2px); box-shadow: var(--sh) var(--edge); }
.side__k {
  display: block;
  margin-bottom: 5px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
}
.side__card b {
  display: block;
  font-family: var(--display);
  font-size: 1.4rem;
  letter-spacing: 0.03em;
  color: var(--text);
  text-transform: uppercase;
}
.side__d { display: block; margin-top: 4px; color: var(--muted); font-size: 0.8rem; line-height: 1.6; }

/* ---------- prose ---------- */
.prose { max-width: 70ch; }
.prose h2 { margin-top: 1.5em; color: var(--green); }
.prose h2:first-child { margin-top: 0; }
.prose p { color: var(--muted); }
.prose a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }
.prose ul, .prose ol { padding-left: 1.2em; color: var(--muted); }
.prose li { margin-bottom: 0.45em; }
.prose strong { color: var(--text); }

/* FAQ questions sit inside the prose, so they are quieter than a
   section heading and take the cyan rather than the green. Two
   levels of heading in one column is enough without them competing. */
.prose h3 {
  margin-top: 1.7em;
  margin-bottom: 0.35em;
  font-size: 1.22rem;
  letter-spacing: 0.03em;
  color: var(--cyan);
}
.prose h3 + p { margin-top: 0; }

/* A controls table. Hard rules, no zebra striping — the colour of the
   key name is the only thing doing the work. */
.prose table {
  width: 100%;
  margin: 0 0 1.4em;
  border-collapse: collapse;
  font-size: 0.84rem;
}
.prose th, .prose td {
  padding: 9px 12px;
  border: var(--bw) solid var(--line);
  text-align: left;
  vertical-align: top;
}
.prose th {
  background: var(--panel-2);
  color: var(--green);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.prose td { color: var(--muted); }
.prose td:first-child { color: var(--text); white-space: nowrap; }
.prose code {
  padding: 2px 6px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font-size: 0.86em;
}

/* ---------- a trail back up the tree ---------- */
.crumb {
  margin: 0 0 10px;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
}
.crumb a { color: var(--muted); text-decoration: none; }
.crumb a:hover { color: var(--green); }
.crumb span { color: var(--line); padding: 0 6px; }

/* ---------- the blog: the list of posts ----------
   A list, not a grid of cards. There are no pictures here on
   purpose: twenty-two thumbnails would be twenty-two images that
   say nothing, and the thing a reader picks a piece by is the
   sentence under its title. Each row is a rule and four lines of
   text, which is the whole design. */
.postlist {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: var(--bw) solid var(--line);
}
.postlist > li { border-bottom: var(--bw) solid var(--line); }
.postlist a {
  display: block;
  padding: 18px 2px 20px;
  color: inherit;
  text-decoration: none;
  transition: background 0.1s linear;
}
.postlist a:hover, .postlist a:focus-visible { background: var(--panel); }
.postlist a:hover .postlist__title, .postlist a:focus-visible .postlist__title { color: var(--green); }
.postlist__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  margin-bottom: 7px;
}
.postlist__date {
  color: var(--dim);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.postlist__topic {
  padding: 3px 8px;
  border: 1px solid var(--line-2);
  color: var(--muted);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.postlist__title {
  margin: 0 0 6px;
  font-size: 1.32rem;
  line-height: 1.2;
  color: var(--text);
  transition: color 0.1s linear;
}
.postlist__d { margin: 0; max-width: 78ch; color: var(--muted); font-size: 0.86rem; line-height: 1.7; }
.postlist__count {
  margin: 0 0 14px;
  color: var(--dim);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.postlist > li[hidden] { display: none; }

/* ---------- the blog: the topic filter ----------
   Twelve topics with nothing to choose between them, so they sit in
   one wrapped row under the heading rather than in a sidebar: the
   reader sees the shape of the whole blog before reading a word of
   it, and on a phone the row is three lines instead of a drawer.
   They are buttons, and they look like buttons — the selected one is
   filled in, so the state is visible without a second colour.

   Named `filt` rather than `chip` because a `.chip` already exists:
   it is the HUD badge a game wears while it is running, and the two
   have nothing in common but a shape. Sharing the name meant the
   later block won on every property the two disagreed about. */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 0;
}
.filt {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 7px 12px;
  border: var(--bw) solid var(--line);
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.1s linear, border-color 0.1s linear, background 0.1s linear;
}
.filt:hover, .filt:focus-visible { border-color: var(--green); color: var(--text); }
.filt--on, .filt--on:hover, .filt--on:focus-visible {
  border-color: var(--green);
  background: var(--green);
  color: var(--on-fill);
}
.filt__n { font-size: 0.62rem; opacity: 0.7; }

/* The line under an article's title: when it was last touched, and
   what it is about. One date, because there is only one date this
   site can honestly show — the day the piece was last rewritten. */
.post__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
  margin: 0;
  color: var(--dim);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.post__meta time { color: var(--muted); }
.post__meta .post__topic { color: var(--text); }

/* Two lists side by side, under one heading. The colours do the
   labelling: green for what a thing is good at, amber for what it
   costs you. They stack on a phone, where a two-column list of
   sentences is unreadable. */
.proscons { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin: 0 0 1.6em; }
.proscons__col { padding: 14px 16px 16px; border: var(--bw) solid var(--line); background: var(--panel); }
.proscons__col--pro { border-color: var(--line-2); }
.proscons__col--con { border-color: var(--line-2); }
.proscons h3 {
  margin: 0 0 10px;
  font-family: var(--display);
  font-size: 1.24rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.proscons__col--pro h3 { color: var(--green); }
.proscons__col--con h3 { color: var(--amber); }
.proscons ul { margin: 0; padding-left: 1.1em; }
.proscons li { margin-bottom: 0.5em; color: var(--muted); font-size: 0.86rem; line-height: 1.65; }
.proscons li:last-child { margin-bottom: 0; }
@media (max-width: 640px) { .proscons { grid-template-columns: 1fr; } }

/* A data table with three or four columns of real numbers is wider
   than a phone, and a table wider than the viewport takes the whole
   page with it: the reading column goes off-centre and the header
   scrolls away sideways. Eleven of the twenty-two posts did this at
   390px, by up to 129px, which is what a measurement found that a
   reading of the pages had not. The table keeps its own box and
   scrolls inside it instead — same display:block move the controls
   legend already makes at this width. */
@media (max-width: 640px) {
  .prose table { display: block; overflow-x: auto; }
}

/* A data table in an article has a phrase in its first column as
   often as a single word, so the no-wrap rule that suits a controls
   legend is switched off for the tables that say so. */
.prose table.rows td:first-child { white-space: normal; }

/* ---------- a form ---------- */
.form { display: grid; gap: 16px; max-width: 56ch; }
.field { display: grid; gap: 7px; }
.field label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.field input,
.field textarea,
.field select {
  width: 100%;
  padding: 11px 13px;
  border: var(--bw) solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 0.88rem;
}
.field textarea { min-height: 150px; resize: vertical; }
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: 0;
  border-color: var(--green);
}
.field__note { margin: 0; color: var(--dim); font-size: 0.74rem; }
.form__status {
  margin: 0;
  padding: 11px 13px;
  border: var(--bw) solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  font-size: 0.82rem;
}
.form__status[data-state="ok"] { border-color: var(--green); color: var(--green); }
.form__status[data-state="bad"] { border-color: var(--red); color: var(--red); }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- a plain grid of links, for the legal pages ---------- */
.linklist { display: grid; gap: 2px; padding: 0; margin: 0 0 1.4em; list-style: none; }
.linklist a { color: var(--green); text-decoration: underline; text-underline-offset: 3px; }

/* The category strip on the landing page. Same hard-edged chip as a
   game card's tag, because it is the same idea: a short label that
   says what shelf a thing belongs on. */
.cats { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 1.8em; padding: 0; list-style: none; }
.cats a {
  display: inline-block;
  padding: 7px 13px;
  border: var(--bw) solid var(--line-2);
  color: var(--text);
  text-decoration: none;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.cats a:hover, .cats a:focus-visible { border-color: var(--green); color: var(--green); }

/* ---------- footer ---------- */
.foot {
  border-top: var(--bw) solid var(--line);
  background: var(--panel);
  padding: 34px 0 0;
}
.foot__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr));
  gap: 30px 22px;
  padding-bottom: 30px;
}
.foot__brand .brand { text-decoration: none; }
.foot__brand p {
  margin: 14px 0 0;
  color: var(--dim);
  font-size: 0.78rem;
  line-height: 1.7;
  max-width: 32ch;
}
.foot__col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.foot__head {
  margin: 0 0 12px;
  font-family: var(--body);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--green);
  border: 0;
  padding: 0;
}
.foot__col a {
  text-decoration: none;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 4px 0;
  border-bottom: var(--bw) solid transparent;
}
.foot__col a:hover, .foot__col a:focus-visible {
  color: var(--green);
  border-bottom-color: var(--line);
}
.foot__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 18px;
  border-top: var(--bw) solid var(--line);
  padding: 15px 0 18px;
}
.foot__base p {
  margin: 0;
  color: var(--dim);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
}

/* ============================================================
   The landing page: the bands under the cabinet

   Four sections that all follow the same rule: a heading, a hairline
   under it, and one band of content. They are here because the page
   used to stop about a third of the way down a long scroll, and
   because a visitor who has just looked at sixteen games wants to
   know what they have played, what they are getting, what has been
   written about it, and what the catch is. In that order.
   ============================================================ */

/* A line of explanation under a section heading, rather than beside
   it. The side-of-the-heading paragraph is what the cabinet section
   had and it is what got cut: at this width it turned a heading into
   a two-column argument. */
.section__cap {
  margin: -6px 0 20px;
  max-width: 66ch;
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.65;
}

/* ---------- your scores ----------
   The only thing on the site that is about the reader rather than
   about the games: sixteen rows read back out of their own browser.
   A list and not a chart, because the numbers in it are not the same
   kind of number as each other — metres, a clock, a move count — and
   a bar chart of those would be a lie with a nice colour. */
.scores { padding: clamp(28px, 4vw, 48px) 0; border-top: var(--bw) solid var(--line); }
.board__empty {
  margin: 0;
  padding: 18px;
  border: var(--bw) dashed var(--line);
  color: var(--dim);
  font-size: 0.84rem;
}
.board {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: var(--bw) solid var(--line);
}
.board__row { border-bottom: var(--bw) solid var(--line); }
.board__row a {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) 8rem;
  align-items: baseline;
  gap: 4px 14px;
  padding: 11px 2px;
  color: inherit;
  text-decoration: none;
  transition: background 0.1s linear;
}
.board__row a:hover, .board__row a:focus-visible { background: var(--panel); }
.board__name {
  font-family: var(--display);
  font-size: 1.25rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text);
}
.board__row a:hover .board__name, .board__row a:focus-visible .board__name { color: var(--green); }
.board__best { color: var(--green); font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.board__when {
  color: var(--dim);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-align: right;
}
/* A game with no score yet is still a row, and still a way in. It is
   dimmed rather than hidden so the board reads as a full cabinet. */
.board__row--off .board__name, .board__row--off .board__best { color: var(--dim); }

/* ---------- the spec sheet ----------
   The side of the cabinet, in the order a buyer would read it. */
.spec { padding: clamp(28px, 4vw, 48px) 0; border-top: var(--bw) solid var(--line); }
.spec__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--gap);
  margin: 0;
}
.spec__row {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line-2);
}
.spec__row dt {
  color: var(--green);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.spec__row dd { margin: 0; color: var(--muted); font-size: 0.86rem; line-height: 1.6; }

/* ---------- the newest notes ---------- */
.notes { padding: clamp(28px, 4vw, 48px) 0; border-top: var(--bw) solid var(--line); }
.notes__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}
.note {
  display: block;
  padding: 16px;
  border: var(--bw) solid var(--line);
  background: var(--panel);
  text-decoration: none;
  transition: border-color 0.1s linear, transform 0.08s linear;
}
.note:hover { border-color: var(--green); transform: translate(-2px, -2px); box-shadow: var(--sh) var(--edge); }
.note__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin-bottom: 8px;
  color: var(--dim);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.note__topic { padding: 2px 7px; border: 1px solid var(--line-2); }
.note b {
  display: block;
  margin-bottom: 6px;
  font-family: var(--display);
  font-size: 1.35rem;
  letter-spacing: 0.03em;
  line-height: 1.15;
  color: var(--text);
}
.note:hover b { color: var(--green); }
.note__d { display: block; color: var(--muted); font-size: 0.83rem; line-height: 1.65; }

/* ---------- questions ----------
   Native <details>, so it opens with no script and is reachable by
   keyboard without anything being added for it. The marker is
   replaced because the default triangle is the one rounded, grey
   thing on the site. */
.faq { padding: clamp(28px, 4vw, 48px) 0 clamp(34px, 5vw, 56px); border-top: var(--bw) solid var(--line); }
.faq__list { display: grid; gap: 8px; }
.faq__item { border: var(--bw) solid var(--line); background: var(--panel); }
.faq__item summary {
  padding: 13px 16px;
  cursor: pointer;
  font-family: var(--display);
  font-size: 1.3rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text);
  /* Chrome and Firefox draw their own marker for a summary; both are
     a rounded grey triangle, which is the one rounded thing here. The
     +/− at the right edge is the affordance instead. */
  list-style: none;
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after { content: "+"; float: right; color: var(--green); }
.faq__item[open] summary::after { content: "\2212"; }
.faq__item summary:hover, .faq__item[open] summary { color: var(--green); }
.faq__item[open] summary { border-bottom: var(--bw) solid var(--line); }
.faq__item p {
  margin: 0;
  padding: 14px 16px 16px;
  /* The question row runs the full width of the page so the boxes
     line up with the board and the spec grid above; the answer does
     not, because a 200-character line is not a line anybody reads. */
  max-width: 78ch;
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.7;
}

/* ============================================================
   Responsive
   ============================================================ */

@media (max-width: 940px) {
  .hero__in { grid-template-columns: minmax(0, 1fr); }
  .cabinet { order: -1; }
  .how__in { grid-template-columns: minmax(0, 1fr); }
  .notes__grid { grid-template-columns: minmax(0, 1fr); }
  .spec__list { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 700px) {
  body { font-size: 14px; }
  .grid-games { grid-template-columns: minmax(0, 1fr); }
  .points__in { grid-template-columns: minmax(0, 1fr); }
  .section__head { flex-direction: column; align-items: flex-start; }
  .nav a { font-size: 0.74rem; padding: 7px 9px; }
  .brand { font-size: 1.4rem; }
  .hud { top: 16px; left: 16px; right: 16px; }
  .chip { padding: 4px 8px; }
  .chip__k { display: none; }
  .panel { padding: 16px; }
  .panel__score > b { font-size: 2.4rem; }
  /* The board keeps its score on the same line as the name and drops
     the "when" underneath: at this width the three columns squeeze
     the label into two words a line. */
  .board__row a { grid-template-columns: minmax(0, 1fr) auto; }
  .board__name { grid-column: 1; }
  .board__when { grid-column: 2; }
  .board__best { grid-column: 1 / -1; }
}

@media (max-width: 860px) {
  .foot__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .foot__brand { grid-column: 1 / -1; }
}

@media (max-width: 520px) {
  .foot__grid { grid-template-columns: minmax(0, 1fr); }
  .spec__row { grid-template-columns: minmax(0, 1fr); gap: 4px; }
}

@media (max-width: 420px) {
  .keys li { font-size: 0.72rem; padding: 5px 9px; }
}
