/* ==========================================================================
   Maple Frag Fest — the club's front door (prototype).
   Dark-first, desktop-first: this is a thing four people open on a PC before
   a Friday night, not a phone app. Palette keeps the site family's lime
   (--fx-accent from /theme/theme.css) and adds maple orange for "new".
   The shared theme sheet loads LAST and supplies the background pattern, the
   --card vars and the hover/press FX on .card / .tile — so those class names
   are load-bearing, not decoration.
   ========================================================================== */

:root {
  --bg:       #0b0d10;
  --panel:    #14181e;
  --panel-2:  #1b2028;
  --line:     #262d38;
  --line-2:   #333c4a;
  --text:     #e9edf4;
  --dim:      #a3adbe;
  --faint:    #6e7889;

  --lime:     #d7f36a;   /* the family accent — matches --fx-accent */
  --lime-ink: #1a1f0a;
  --maple:    #e2703a;   /* club secondary: new / changed */

  --ok:       #4ade80;
  --wait:     #94a3b8;

  --r:    14px;
  --r-s:  9px;
  --shadow: 0 16px 40px rgba(0, 0, 0, .45);
}

/* ⚠️ Dark is the DEFAULT, on every machine (Ben, 2026-10-27: "lets make the dashboard page black
   background by default"). This used to be @media (prefers-color-scheme: light), which meant the club
   page went white on any machine whose OS was in light mode — and the cabinet is hardware: it does not
   re-theme. The light palette is kept, but only behind an explicit opt-in on <html data-theme="light">,
   so nothing switches by accident and a real toggle stays cheap to add later. */
:root { color-scheme: dark; }

:root[data-theme="light"] {
  color-scheme: light;
  --bg:      #f3f4ee;
  --panel:   #ffffff;
  --panel-2: #f4f5ef;
  --line:    #e2e4da;
  --line-2:  #cfd2c5;
  --text:    #12151a;
  --dim:     #57606f;
  --faint:   #8a92a1;
  --lime:    #4d7c0f;
  --lime-ink:#ffffff;
  --maple:   #c2521c;
  --shadow:  0 10px 28px rgba(0, 0, 0, .10);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background-color: var(--bg);
  color: var(--text);
  font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow-wrap: break-word;
  /* fallback room under the fixed player-up dock; js/dock.js sets the exact height */
  padding-bottom: 132px;
}

/* ---------- header: the cabinet marquee ----------
   Pimped-out retro (Ben, 2026-10-25): a chrome-gradient wordmark with the 80s red/cyan fringe,
   a marquee saying in tracked mono, and a lit badge — all on a dark plate with faint scanlines.
   The dock stays the loudest thing on screen; this is the sign above it. */

/* not sticky (Ben, 2026-10-25): the marquee scrolls away with the page — the dock is the only
   thing that stays put */
.top {
  position: relative; z-index: 1;
  background:
    radial-gradient(130% 150% at 8% -50%, rgba(215, 243, 106, .12), transparent 58%),
    radial-gradient(90% 120% at 92% -60%, rgba(226, 112, 58, .14), transparent 60%),
    linear-gradient(180deg, #0d1218 0%, #080b0f 100%);
  border-bottom: 1px solid #26303c;
  box-shadow: inset 0 -1px 0 rgba(215, 243, 106, .16), 0 14px 34px rgba(0, 0, 0, .45);
}
.top::after {                       /* scanlines — decorative only */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(180deg, rgba(255, 255, 255, .05) 0 1px, transparent 1px 3px);
  opacity: .35;
}
.top-inner {
  position: relative; z-index: 1;
  max-width: 1080px; margin: 0 auto; padding: 17px 20px;
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
}
.brand { display: flex; gap: 14px; align-items: center; min-width: 0; flex: 1 1 24ch; }
.leaf {
  font-size: 34px; line-height: 1; flex: none;
  filter: drop-shadow(0 2px 8px rgba(226, 112, 58, .45));
}
.brand-text { min-width: 0; }

/* wordmark + its version chip sit on one line; the chip wraps under the logo on a phone
   rather than shrinking the wordmark (rule: the logo never squeezes) */
.brand-line { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.ver-chip {
  flex: none; margin-bottom: 2px; padding: 3px 7px; border-radius: 4px;
  font: 700 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: var(--dim);
  background: rgba(255, 255, 255, .04); border: 1px solid var(--line-2);
}

h1.wordmark {
  margin: 0;
  font-family: "Arial Black", "Helvetica Neue", Impact, Haettenschweiler, system-ui, sans-serif;
  font-weight: 900;
  font-size: clamp(25px, 3.7vw, 41px);
  line-height: .95; letter-spacing: .004em; text-transform: uppercase;
  background: linear-gradient(180deg, #ffffff 0%, #edffae 20%, #d7f36a 46%, #64800f 58%, #a9cc3d 76%, #e9ff9f 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  /* ⚠️ The fringe is a FILTER, not a text-shadow. With `background-clip: text` the fill is the
     background, and Chrome paints text-shadow ON TOP of it — so any 0-offset shadow erased the lime
     and the wordmark rendered pink. drop-shadow() is applied after the element is painted, so the
     chrome survives and the magenta/cyan ghosts sit behind it. */
  filter:
    drop-shadow(3px 0 0 rgba(255, 47, 134, .62))
    drop-shadow(-3px 0 0 rgba(39, 216, 255, .52))
    drop-shadow(0 4px 0 rgba(3, 6, 9, .95))
    drop-shadow(0 10px 20px rgba(215, 243, 106, .18));
  transform: skewX(-5deg);                 /* the racing slant */
  transform-origin: left center;
  display: inline-block;
}
h1 {                                  /* any page without the wordmark keeps the plain heading */
  margin: 0; font-size: 25px; letter-spacing: -.015em;
}

/* plain pages (the charter) keep a normal subtitle */
.sub { margin: 3px 0 0; color: var(--dim); font-size: 13.5px; }

/* the dashboard's marquee line — ⚠️ scoped to #tagline, or the charter page's subtitle inherits
   mono caps and the ★ rules and wraps into four shouting lines */
#tagline {
  margin: 7px 0 0; color: #a9b8cb;
  font: 600 11.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .17em; text-transform: uppercase;
}
#tagline::before, #tagline::after { color: var(--maple); content: "\2605"; padding: 0 .55em; }

/* the lit badge — an arcade insert, not a badge you can press */
.cab-badge {
  margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 11px 6px 9px; border-radius: 6px;
  font: 700 10.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .16em; text-transform: uppercase; color: #d7f36a;
  background: rgba(215, 243, 106, .07);
  border: 1px solid rgba(215, 243, 106, .38);
  box-shadow: inset 0 0 14px rgba(215, 243, 106, .12), 0 0 18px -6px rgba(215, 243, 106, .5);
}
.cab-badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: #d7f36a; box-shadow: 0 0 8px #d7f36a;
}

.who { display: flex; align-items: center; gap: 10px; }
.who-hint { margin: 0; color: var(--faint); font-size: 12px; }
.who-btn {
  font: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 9px;
  padding: 10px 15px; border-radius: 999px; text-decoration: none;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--line-2);
}
.who-btn:hover { border-color: var(--lime); }
.who-lbl { color: var(--dim); font-size: 14px; }
.who-btn[data-set="1"] .who-lbl { color: var(--faint); }
.who-name { font-weight: 600; }
.who-btn[data-set="1"] { border-color: var(--lime); }
.who-btn[data-set="1"] .who-name { color: var(--lime); }

/* ---------- layout ---------- */

main { max-width: 1080px; margin: 0 auto; padding: 22px 20px 8px; }
.block { margin: 0 0 30px; }
.block-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
h2 {
  margin: 0; font-size: 14px; letter-spacing: .08em; text-transform: uppercase; color: var(--dim);
}
.hint, .lede { color: var(--dim); font-size: 13px; }
.hint { margin-left: auto; }
.lede { margin: 0 0 14px; max-width: 68ch; }
.dim { color: var(--faint); }
.small { font-size: 12.5px; }
.empty { color: var(--dim); }

.banner {
  margin: 0 0 26px; padding: 12px 15px; font-size: 13.5px; color: var(--dim);
  background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--maple);
  border-radius: var(--r-s);
}
.banner b { color: var(--text); }
/* the dashboard's prototype note sits under the content, not over it */
.banner.bottom { margin: 26px 0 0; }

.tag {
  font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px; border: 1px solid var(--line-2); color: var(--faint);
}
.tag.mock { border-style: dashed; }
.tag.new { color: var(--maple); border-color: color-mix(in srgb, var(--maple) 45%, var(--line-2)); }

/* ---------- player up dock (the arcade cabinet panel) ----------
   A fixed panel along the bottom of the screen: one seat per crew member, in the four-player
   cabinet colours, states worded the way a cabinet words them (IN · PRESS START · OUT).
   It stays DARK in both themes — it's hardware, and hardware doesn't re-theme. */

.dock {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  /* ⭐ Lifted 2026-10-27 (Ben: "the playerbar almost disappears"). The whole panel used to be
     #101419 → #090b0f, which is the same black as the page behind it, so the cabinet had no edge.
     It is still dark — it is hardware — but it now reads as a lit panel on a black wall. */
  background: linear-gradient(180deg, #202833 0%, #161c24 55%, #12171e 100%);
  border-top: 1px solid #46536a;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16), inset 0 14px 24px rgba(255, 255, 255, .04),
              0 -18px 44px rgba(0, 0, 0, .5);
  color: #e9edf4;
}
/* ⭐ the cabinet's light strip (Ben: "push it over the top") — the four player colours, left to right,
   so the panel reads as hardware that is switched on rather than a grey bar. */
.dock::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; pointer-events: none;
  /* the strip is a 200%-wide tile that starts and ends on the same colour, so sliding it one full
     tile is a seamless lap — the four crew colours stay pure instead of hue-rotating through greens */
  background: linear-gradient(90deg, #54a6ff 0%, #ff5d5d 25%, #ffb03a 50%, #b07bff 75%, #54a6ff 100%);
  background-size: 200% 100%;
  box-shadow: 0 0 20px rgba(255, 176, 58, .5);
  animation: strip-cycle 7s linear infinite;
}
@keyframes strip-cycle {
  from { background-position: 0% 50%; }
  to   { background-position: -200% 50%; }
}
/* a moving light on the hardware is exactly the kind of thing motion sensitivity is about */
@media (prefers-reduced-motion: reduce) {
  .dock::before { animation: none; }
  .seat[data-state="in"]::after { animation: none; opacity: .6; }
}
.dock-inner { max-width: 1080px; margin: 0 auto; padding: 8px 20px 12px; }

.dock-head { display: flex; align-items: center; gap: 11px; margin: 0 0 8px; }
.dock-title {
  font: 700 10.5px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .22em; text-transform: uppercase; color: #a2aec0;
}
.dock-count {
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .1em; text-transform: uppercase; color: #a9b3c2;
}
/* the dock is always dark, so it uses the cabinet's own lime, not the theme's (which darkens
   in light mode and goes muddy on black) */
.dock-count b { font-size: 13px; color: #d7f36a; }
.dock-count[data-full="1"] b { color: #4ade80; }
.dock .tag.mock { border-color: #4a566a; color: #93a0b3; }
.dock-hint { margin-left: auto; font-size: 11px; color: #8b96a8; }
.dock-hint kbd {
  font-size: 10px; padding: 2px 4px; background: #232b36;
  border-color: #46536a; color: #b3bece;
}

.seats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }

/* one seat = one controller panel */
.seat {
  --pc: #7c8899;
  position: relative; display: grid; grid-template-columns: auto auto 1fr auto;
  align-items: center; gap: 9px; padding: 8px 10px;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
  /* ⭐ over the top, on purpose (Ben, 2026-10-27): four grey plates became four arcade panels,
     each lit in its own player colour. */
  background:
    radial-gradient(130% 170% at 8% -10%, color-mix(in srgb, var(--pc) 62%, transparent), transparent 62%),
    linear-gradient(180deg, color-mix(in srgb, var(--pc) 22%, #1b222c) 0%, #141a22 100%);
  border: 1.5px solid color-mix(in srgb, var(--pc) 70%, #2b3341); border-radius: 10px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .22),
    inset 0 -3px 0 color-mix(in srgb, var(--pc) 34%, transparent),
    0 0 18px -8px var(--pc);
  transition: border-color .12s, box-shadow .12s, transform .06s, filter .12s;
}
.seat:hover { border-color: var(--pc); filter: brightness(1.14) saturate(1.1); }
.seat:active { transform: translateY(1px); }
.seat:focus-visible { outline: 2px solid #d7f36a; outline-offset: 2px; }

/* in — the seat is lit */
.seat[data-state="in"] {
  border-color: var(--pc);
  background:
    radial-gradient(130% 180% at 6% -14%, color-mix(in srgb, var(--pc) 92%, transparent), transparent 64%),
    linear-gradient(180deg, color-mix(in srgb, var(--pc) 30%, #1b222c) 0%, #131920 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .34),
    inset 0 -3px 0 color-mix(in srgb, var(--pc) 55%, transparent),
    0 0 0 1px color-mix(in srgb, var(--pc) 45%, transparent),
    0 0 28px -2px color-mix(in srgb, var(--pc) 70%, transparent),
    0 14px 30px -18px var(--pc);
}
/* ⭐ locked IN = a pulse, not a static glow (Ben, 2026-10-27). An ::after ring so the animation is
   opacity only — the compositor handles it, no box-shadow repaint every frame. Seats are offset so
   two players do not breathe in lockstep. */
.seat[data-state="in"]::after {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 34px 3px color-mix(in srgb, var(--pc) 88%, transparent),
              inset 0 0 26px color-mix(in srgb, var(--pc) 38%, transparent);
  animation: seat-pulse 2.6s ease-in-out infinite;
}
.seat:nth-child(2)[data-state="in"]::after { animation-delay: .32s; }
.seat:nth-child(3)[data-state="in"]::after { animation-delay: .64s; }
.seat:nth-child(4)[data-state="in"]::after { animation-delay: .96s; }
@keyframes seat-pulse {
  0%, 100% { opacity: .28; }
  50%      { opacity: 1; }
}

/* empty seat — open, and it says so */
.seat[data-state="press"] {
  border-style: dashed; border-color: #6f7d94;
  background: linear-gradient(180deg, #1e2630 0%, #161c24 100%);
}
/* maybe — a shoulder shrug, not an arrival */
.seat[data-state="maybe"] { border-color: color-mix(in srgb, var(--pc) 38%, #414d61); }
/* out — wiped off the cabinet */
.seat[data-state="out"] { opacity: .72; }

.seat-no {
  font: 700 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; padding: 3px 5px; border-radius: 4px;
  color: #0b0d10;
  border: 1px solid color-mix(in srgb, var(--pc) 55%, #fff);
  background: var(--pc);
  box-shadow: 0 0 14px -4px var(--pc);
}
.seat[data-state="out"] .seat-no { color: #6b7688; border-color: #2b3341; background: transparent; }

/* the player's portrait — shared by the dock and the cards (js/faces.js) */
.face {
  --pc: #7c8899;
  display: inline-grid; place-items: center; flex: none;
  width: 22px; height: 22px; border-radius: 5px; overflow: hidden;
  color: var(--pc); background: #090c11;
  border: 1px solid color-mix(in srgb, var(--pc) 34%, #29313d);
}
.face svg { width: 82%; height: 82%; display: block; }
.face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.face .ink { fill: #0a0e13; }
.face-letter { font: 700 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }

/* in the dock a seat gets the full 30px tile */
.seat .face {
  width: 30px; height: 30px;
  background: color-mix(in srgb, var(--pc) 24%, #0a0f15);
  border-color: color-mix(in srgb, var(--pc) 62%, #2b3341);
  box-shadow: inset 0 0 12px color-mix(in srgb, var(--pc) 32%, transparent);
}
.seat .face svg { width: 24px; height: 24px; }
.seat[data-state="press"] .face { color: #46505f; border-color: #333c4a; }
.seat[data-state="maybe"] .face { color: #6e7a8c; }
.seat[data-state="out"] .face { color: #39414d; border-color: #2b3341; }

.seat-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.seat-name {
  font-weight: 650; font-size: 13.5px; color: #fff;
  text-shadow: 0 0 16px color-mix(in srgb, var(--pc) 55%, transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.seat-state {
  font: 600 9.5px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .13em; text-transform: uppercase; color: #7f8b9e;
}
.seat[data-state="in"] .seat-state { color: color-mix(in srgb, var(--pc) 45%, #fff); }
.seat[data-state="maybe"] .seat-state { color: #8b93a3; }
.seat[data-state="out"] .seat-state { color: #5f6a7b; }

.seat-you {
  font: 700 9px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .14em; text-transform: uppercase;
  color: #12160a; background: #d7f36a; border-radius: 3px; padding: 3px 5px;
}

/* ⚠️ No blinking. An empty seat still reads open because of the dashed slot and the bright
   PRESS START lettering — a hard blink in the corner of the eye for a whole evening is just a nag. */
.seat[data-state="press"] .seat-state { color: #d7dee9; }

/* pressing W brings you to the panel */
.dock[data-pulse="1"] .seat {
  box-shadow: 0 0 0 1px #d7f36a, inset 0 1px 0 rgba(255, 255, 255, .06);
}

/* ---------- game listings ----------
   One row per game: sample art, the title and who made it, and one control — a thumb that says
   "I'll play this tonight". Nothing else. The facts that used to sit here (players, length, last
   played) were cut: on a Friday you decide who is in, not what the spec says. */

.games { display: flex; flex-direction: column; gap: 12px; }

.card {
  display: grid; grid-template-columns: 224px minmax(0, 1fr) auto;
  align-items: center; gap: 18px; padding: 12px;
  background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--line-2);
  border-radius: var(--r); box-shadow: var(--shadow);
  transition: border-color .12s;
}
.card[data-ready="1"] { border-left-color: var(--lime); }

/* the sample art */
.shot {
  position: relative; border-radius: 10px; overflow: hidden;
  aspect-ratio: 3 / 2; background: #0a0d12; border: 1px solid var(--line);
}
.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot-ver, .shot-new, .shot-soon {
  position: absolute; font: 700 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em; text-transform: uppercase; padding: 4px 7px; border-radius: 4px;
}
.shot-ver {
  left: 8px; bottom: 8px; color: #e9edf4; background: rgba(8, 10, 14, .72);
  border: 1px solid rgba(255, 255, 255, .18);
}
.shot-new {
  right: 8px; top: 8px; color: #201005; background: var(--maple); border: 1px solid var(--maple);
}
.shot-soon {
  right: 8px; bottom: 8px; color: #cfd6e2; background: rgba(8, 10, 14, .72);
  border: 1px dashed rgba(255, 255, 255, .24);
}

.gmain { min-width: 0; }
.ghead { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.card h3 { margin: 0; font-size: 19px; letter-spacing: -.01em; }
.card .shape {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--faint);
  border: 1px solid var(--line-2); border-radius: 999px; padding: 3px 8px;
}
.card .tagline { margin: 7px 0 0; color: var(--dim); font-size: 14px; max-width: 58ch; }
.card .soon-tag { display: none; border-style: dashed; color: var(--faint); }

.byline {
  display: flex; align-items: center; gap: 7px; margin: 11px 0 0;
  font-size: 12.5px; color: var(--faint);
}
.byline .by-name { color: var(--text); font-weight: 620; }

/* the thumb */
.gside { display: flex; flex-direction: column; align-items: flex-end; gap: 9px; }
.ready-btn {
  display: inline-flex; align-items: center; gap: 9px; white-space: nowrap;
  font: inherit; font-size: 14px; font-weight: 650; cursor: pointer;
  padding: 10px 15px; border-radius: 10px; color: var(--text); background: var(--panel-2);
  border: 1px solid var(--line-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
  transition: border-color .12s, background .12s, color .12s;
}
.ready-btn svg { width: 18px; height: 18px; flex: none; }
.ready-btn:hover { border-color: var(--lime); }
.ready-btn.needs-seat { color: var(--dim); border-style: dashed; font-weight: 500; }
.ready-btn.on {
  background: var(--lime); border-color: var(--lime); color: var(--lime-ink);
  box-shadow: 0 8px 20px -12px var(--lime);
}
.ready-btn.on:hover { filter: brightness(1.06); }
.ready-btn:focus-visible { outline: 2px solid var(--lime); outline-offset: 2px; }

.ready-row { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--faint); }
.ready-row .face { width: 19px; height: 19px; border-radius: 4px; }
.ready-row b { color: var(--text); }
.ready-row .none { font-style: italic; }

@media (max-width: 820px) {
  /* image STAYS beside the text — a stacked card on a narrow pane pushed the picture to
     full width and the button off the screen. It just gets smaller. */
  .card { grid-template-columns: 128px minmax(0, 1fr); align-items: start; gap: 12px; }
  .shot { aspect-ratio: 1 / 1; }
  .shot-ver, .shot-new, .shot-soon { font-size: 8.5px; padding: 3px 5px; }
  .shot-ver { left: 6px; bottom: 6px; }
  .shot-new { right: 6px; top: 6px; }
  .shot-soon { right: 6px; bottom: 6px; }
  .card h3 { font-size: 17px; }
  .card .tagline { font-size: 13px; }
  .byline { margin-top: 8px; }
  /* the thumb drops under both columns, but stays its own size — a full-width bar read as a
     giant call to action on a 400px screen, and it is one of four identical buttons */
  .gside {
    grid-column: 1 / -1; flex-direction: row; align-items: center;
    justify-content: space-between; gap: 10px;
    border-top: 1px dashed var(--line-2); padding-top: 9px;
  }
  .ready-btn { padding: 8px 12px; font-size: 13px; }
}

@media (max-width: 420px) {
  .card { grid-template-columns: 104px minmax(0, 1fr); gap: 10px; padding: 10px; }
  /* two chips do not fit on a 104px image — the not-built tag moves beside the title */
  .shot-soon { display: none; }
  .card .soon-tag { display: inline-block; }
  .card h3 { font-size: 16px; }
  .card .tagline { font-size: 12.5px; }
  .card .shape { font-size: 10px; padding: 2px 6px; }
  .byline { font-size: 12px; }
  .ready-btn { padding: 7px 10px; font-size: 12.5px; }
  .ready-btn svg { width: 15px; height: 15px; }
}

/* ---------- charter card (dashboard) ---------- */

.charter-card {
  display: flex; align-items: center; gap: 14px; padding: 16px 18px; text-decoration: none;
  color: inherit; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r); border-left: 3px solid var(--lime);
}
.charter-card:hover { border-color: var(--lime); }
.cc-mark { font-size: 26px; }
.cc-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cc-text b { font-size: 15.5px; }
.cc-text span { color: var(--dim); font-size: 13.5px; font-style: italic; }
.cc-arrow { margin-left: auto; font-size: 20px; color: var(--faint); }

/* ---------- charter page ---------- */

.charter-line {
  margin: 0; padding: 22px 24px; border: 1px solid var(--line); border-left: 3px solid var(--lime);
  border-radius: var(--r); background: var(--panel);
  font-size: 20px; line-height: 1.5; letter-spacing: -.01em;
}
.charter-line b { color: var(--lime); }

ol.rules { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
ol.rules > li {
  display: grid; grid-template-columns: 46px 1fr; gap: 14px;
  padding: 14px 16px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-s);
}
ol.rules > li.dropped { opacity: .55; border-style: dashed; }
.rule-letter {
  font: 700 15px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--lime); padding-top: 2px;
}
li.dropped .rule-letter { color: var(--faint); text-decoration: line-through; }
.rule-title { font-weight: 650; }
li.dropped .rule-title { text-decoration: line-through; }
.rule-body { color: var(--dim); font-size: 14.5px; }
.rule-body code { font-size: .92em; }
.rule-flag {
  display: inline-block; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--faint); border: 1px dashed var(--line-2); border-radius: 999px;
  padding: 2px 7px; margin-left: 7px; vertical-align: 1px;
}

.ritual { display: grid; grid-template-columns: 120px 1fr; gap: 0; }
.ritual > div { padding: 12px 4px; border-bottom: 1px solid var(--line); font-size: 14.5px; }
.ritual .when { color: var(--dim); font-weight: 600; }
.ritual > div:nth-last-child(-n+2) { border-bottom: 0; }

/* ---------- repo-structure page ---------- */

.tree {
  list-style: none; margin: 0; padding: 12px 16px;
  background: #0a0d12; border: 1px solid var(--line); border-left: 3px solid var(--line-2);
  border-radius: var(--r-s);
  font: 500 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #c6d0de;
}
/* ⚠️ A list, not an ASCII-art <pre>: box-drawing characters forced a sideways scroll on a phone,
   which put every note off-screen. Nested lists wrap the note instead. */
.tree ul {
  list-style: none; margin: 0; padding: 2px 0 0 14px;
  border-left: 1px dashed #2a3340;
}
.tree li { position: relative; padding: 2px 0 2px 13px; }
.tree li::before {
  content: ""; position: absolute; left: 0; top: 13px; width: 8px; height: 1px; background: #2a3340;
}
.tree > li { padding-left: 0; }
.tree > li::before { display: none; }
.tree b { color: #d7f36a; font-weight: 700; }
.tree span { color: #6e7889; }              /* the note beside a folder */
.tree code { background: none; padding: 0; color: #c6d0de; font-size: inherit; }

.cfg {
  margin: 0 0 14px; border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; background: #0a0d12; box-shadow: var(--shadow);
}
.cfg-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 9px 14px; background: #101519; border-bottom: 1px solid var(--line);
  font: 700 12px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; color: #dbe3ee;
}
.cfg-head .why {
  margin-left: auto; color: var(--faint); font-weight: 400;
  font-family: ui-sans-serif, system-ui, sans-serif; font-size: 12px;
}
.cfg pre {
  margin: 0; padding: 14px 16px; overflow-x: auto;
  font: 500 12.5px/1.65 ui-monospace, SFMono-Regular, Menlo, monospace; color: #c6d0de;
}
.tok-key { color: #d7f36a; }
.tok-str { color: #ffb07a; }
.tok-num { color: #8fd0ff; }
.tok-lit { color: #ff8fbf; }

.fields { width: 100%; border-collapse: collapse; margin: 6px 0 18px; }
.fields th {
  text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--line-2);
  font: 600 10.5px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .08em; text-transform: uppercase; color: var(--faint);
}
.fields td {
  padding: 10px; border-bottom: 1px solid var(--line);
  font-size: 13.5px; color: var(--dim); vertical-align: top;
}
.fields td:first-child { color: var(--text); font-weight: 640; white-space: nowrap; }
.fields code { font-size: 12.5px; }
.fields tbody tr:last-child td { border-bottom: 0; }

.notes { margin: 0; padding-left: 20px; color: var(--dim); font-size: 14px; }
.notes li { margin: 0 0 10px; }
.notes b { color: var(--text); }

@media (max-width: 620px) {
  .tree { font-size: 11.5px; padding: 10px 12px; }
  .cfg-head .why { display: none; }
  .fields td:first-child { white-space: normal; }
}

/* ---------- footer ---------- */

.foot {
  max-width: 1080px; margin: 14px auto 0; padding: 18px 20px 30px;
  border-top: 1px solid var(--line); color: var(--dim); font-size: 13.5px;
}
.foot p { margin: 0 0 7px; max-width: 78ch; }
.foot .keys { font-size: 12.5px; }

kbd {
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 3px 5px; border-radius: 5px; background: var(--panel-2);
  border: 1px solid var(--line-2); color: var(--dim);
}
code {
  font: 500 .92em/1.3 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--panel-2); padding: 1px 5px; border-radius: 4px;
}
a { color: var(--lime); }

/* a class rule must never beat [hidden] */
[hidden] { display: none !important; }

@media (max-width: 480px) {
  /* the games hint drops to its own line at this width and reads as a stray sentence */
  #h-games ~ .hint { display: none; }
}

@media (max-width: 620px) {
  /* the badge is the first thing to go when the marquee has to share a phone screen */
  .cab-badge { display: none; }
  /* sized so the whole wordmark stays on ONE line at 360px — a lone "FEST" on line two looked
     like a wrap, not a design choice */
  h1.wordmark { font-size: clamp(20px, 6.8vw, 34px); }
  #tagline { font-size: 10px; letter-spacing: .12em; }
}

@media (max-width: 720px) {
  /* four plates in ONE row, the way a cabinet has four controllers in one row. A vertical plate
     (face over handle over state) is what makes 4-across work on a phone: the handle gets the
     full width of its column instead of fighting a P-badge and a face for 88px of space. */
  .seats { grid-template-columns: repeat(4, 1fr); gap: 5px; }
  .dock-inner { padding: 7px 10px 9px; }
  .dock-head { margin-bottom: 6px; }
  .dock-hint { display: none; }

  .seat {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 6px 3px; text-align: center;
  }
  .seat-no { display: none; }                 /* P2 is still in the aria-label */
  .seat .face { width: 26px; height: 26px; }
  .seat .face svg { width: 21px; height: 21px; }
  /* the empty seat's grey face disappeared at this size — lift it a step */
  .seat[data-state="press"] .face { color: #5d6a7d; }
  .seat-body { align-items: center; gap: 2px; }
  .seat-name { font-size: 10.5px; letter-spacing: 0; max-width: 100%; }
  .seat-state { font-size: 8.5px; letter-spacing: .05em; }
  /* the YOU mark becomes a corner dot on the plate */
  .seat-you {
    position: absolute; top: 4px; right: 4px;
    width: 7px; height: 7px; padding: 0; border-radius: 50%;
    background: #d7f36a; font-size: 0; letter-spacing: 0;
  }
  body { padding-bottom: 150px; }
}

@media print {
  .dock { display: none; }
  body { padding-bottom: 0; }
}

@media (max-width: 620px) {
  .top-inner { padding: 13px 15px; }
  main { padding: 16px 15px 8px; }
  h1 { font-size: 21px; }
  .charter-line { font-size: 17px; padding: 17px 16px; }
  ol.rules > li { grid-template-columns: 34px 1fr; gap: 10px; padding: 12px; }
  .ritual { grid-template-columns: 92px 1fr; }
}

/* ---------- embers up the edges (js/sparks.js) ----------
   A fixed canvas hugging the left and right of the viewport. It sits above the page so the sparks
   are visible where they belong, but it is pointer-events:none and the CSS mask below keeps the
   middle of the screen clean — nothing ever draws over a game card. */

.fx-sparks {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: 3;
  pointer-events: none;
  /* the whole trick: only the outer bands can contain paint, whatever the JS does */
  -webkit-mask-image: linear-gradient(to right,
      #000 0, #000 5.5vw, transparent 13vw, transparent 87vw, #000 94.5vw, #000 100%);
  mask-image: linear-gradient(to right,
      #000 0, #000 5.5vw, transparent 13vw, transparent 87vw, #000 94.5vw, #000 100%);
  mix-blend-mode: screen;      /* embers add light, they don't cover the page */
}

:root[data-theme="light"] .fx-sparks { opacity: .45; mix-blend-mode: multiply; }

@media (prefers-reduced-motion: reduce) {
  .fx-sparks { display: none; }
}

@media print {
  .fx-sparks { display: none; }
}

/* ⚠️ debug shortcut (Ben, 2026-10-27): a game card links to the game's own entry for now, so the title
   and the art are anchors. The real front door will launch through the dock, not a link. */
.ghead h3 a { color: inherit; text-decoration: none; }
.ghead h3 a:hover { text-decoration: underline; text-decoration-color: var(--lime); }
.ghead .open-hint { margin-left: 8px; font: 10.5px/1 ui-monospace, monospace; letter-spacing: .1em;
                    text-transform: uppercase; color: var(--faint); }
.shot a { display: block; line-height: 0; }
.shot a:focus-visible { outline: 2px solid var(--lime); outline-offset: 2px; }
