/* system.css — the shared design system.

   One place for the decisions every screen needs: how wide a column is, how big a
   label is, what a panel looks like, what a button looks like, and where an icon
   sits. Loaded after base.css (which owns fonts, palette and the CRT cabinet) and
   before the per-area sheets, so arcade.css / campaign.css / stats.css can still
   specialise but no longer need to re-invent.

   Audit that produced this: audit/AUDIT.md. The short version is that the site had
   five content widths, twenty max-widths in the CSS, three design languages and two
   icon families sharing button rows.

   Rules that matter and are testable:
     - Widths come from the scale below. A new max-width is a bug unless it is a
       genuine one-off (an image, a sprite) and says so in a comment.
     - Every icon sits in a FIXED slot that is reserved whether or not there is an
       icon in it, so labels in a stack line up.
     - Colour emoji are not icons. They ignore the palette, sit on their own
       baseline, and render differently per platform. Use core/icons.js.
     - A repeated row keeps its columns. Missing values dim, they do not collapse. */

/* ── the two faces ──────────────────────────────────────────────────────────
   Declared HERE, not in base.css, because the server-rendered dashboards load
   this sheet without the cabinet. One declaration, every surface. */
@font-face {
  font-family: "Press Start 2P";
  src: url("../fonts/PressStart2P.woff2") format("woff2"),
       url("../../PressStart2P.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "VT323";
  src: url("../fonts/VT323.woff2") format("woff2"),
       url("../../VT323.ttf") format("truetype");
  font-display: swap;
}

:root {
  /* ── palette + faces ───────────────────────────────────────────────────────
     Was declared three times: base.css for the game and stats.css for the
     dashboards, with a third partial copy in between. That is how a site ends up
     with three design languages — nothing forces the copies to agree. */
  --bg:#0a0a12; --panel:#10101c; --ink:#d7e0e6;
  --cyan:#34d6e6; --mag:#e056c8; --grn:#46d36a; --blu:#5a8cff;
  --ylw:#ffd23f; --red:#ff4d56; --dim:#8796ab;
  --pixel:"Press Start 2P", ui-monospace, "Courier New", monospace;
  --mono: "VT323", ui-monospace, "Courier New", monospace;
  /* The third face, and it earns its place rather than being an accident. The
     distro and desktop guides are read by people arriving from a search engine
     rather than from the game, so their running prose is set in the reader's own
     system sans until they click through to play. Nothing in the game uses it:
     it is scoped to the guides and its ladder is separate (below). */
  --read: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* ── width scale ───────────────────────────────────────────────────────────
     Four widths, and that is the whole set. --w-screen is the standard content
     column inside the cabinet: it is what the campaign screens already use, and
     they are the most developed surfaces on the site. */
  --w-cabinet: 960px;   /* the #app frame itself (base.css) */
  --w-screen:  886px;   /* standard content column inside the cabinet */
  /* Reading measure. Deliberately px, NOT ch: `ch` resolves against the element's
     OWN font-size, so 68ch on a block that inherited 17px came out at 462px while
     the same token on a 20px block would give 544px — the measure would drift with
     whatever the element happened to inherit. VT323 is monospace at 0.4em, so at
     --fs-body (20px) this is ~75 characters a line. */
  --w-prose:   600px;
  --w-column:  480px;   /* one narrow column: forms, empty states, single cards */

  /* ── spacing scale ─────────────────────────────────────────────────────── */
  --sp-1:6px; --sp-2:10px; --sp-3:14px; --sp-4:20px; --sp-5:28px; --sp-6:40px;

  /* ── type scale ────────────────────────────────────────────────────────────
     A ladder, not a set of favourites. Display sizes (--pixel) run
     hero > page > num > title > head > label > micro; reading sizes (--mono) run
     lead > body > small. tools/design-audit.js fails any visible text whose size
     is not on the ladder for its face, which is how a 24px KPI value or a 17px
     caption gets caught instead of shipping.

     The pixel face carries headings, labels, buttons and numbers. Body prose is
     the reading face (--mono, VT323) at --fs-body. Press Start 2P is a display
     face and multi-sentence prose set in it is the least readable thing on the
     site, so it does not go below --fs-label. */
  --fs-hero:clamp(30px,9.5vw,66px);  /* the wordmark, and nothing else */
  --fs-page:clamp(16px,4vw,26px);    /* a page or screen title */
  --fs-num:26px;        /* a big numeral: KPI values, roster tile counts */
  --fs-title:19px;      /* section rule above a group of panels */
  --fs-head:15px;       /* panel / card heading */
  --fs-label:13px;      /* button, tab, column header */
  --fs-micro:11px;      /* eyebrow, footnote, badge */
  /* One rung below micro, and it exists for a single job: a badge whose longest
     value is far longer than the rest of its column. On the distro index every
     base family is 4-6 characters except "Independent" at 11, which pushed three
     cards' names onto a second line. Do not reach for this to make something fit
     that should have been shortened. */
  --fs-nano:9px;

  /* Spectacle sizes (owner ruling, 2026-08-02): the game's full-screen moments —
     FIGHT!, GAME OVER, THE ROT IS DEAD, CHAMPION, round names, chapter cards —
     share ONE standard of impact across both modes instead of the 40/42/46/52/
     56/60/88 scatter they grew individually. Two tiers, both fluid with the
     same mobile floors the arcade proved:
       --fs-splash      the screen-owning word (66 caps the top of the ladder)
       --fs-splash-sub  the supporting spectacle line (round names' proven size) */
  --fs-splash:clamp(37px,9vw,66px);
  --fs-splash-sub:clamp(26px,7vw,52px);

  /* Reading sizes, in --mono (VT323). These are BIGGER than the pixel sizes above
     on purpose. VT323 is a narrow terminal face with a small x-height, so it reads
     roughly two steps smaller than Press Start 2P at the same nominal px — 17px of
     VT323 beside a 15px pixel heading looks like fine print. The site had already
     worked around this ad hoc: the roster links were 25px, the press quotes 18-22,
     the trophy count up to 27. This makes that relationship a rule.

     A codebase grep found 30+ distinct --mono sizes. Later batches collapse the
     ones they touch onto these three. */
  --fs-lead:23px;       /* lead line, prominent link, anything meant to pull the eye */
  --fs-body:20px;       /* default reading size */
  --fs-small:16px;      /* caption, state line, secondary detail */

  /* Sans ladder, for --read. Three rungs, taken from what the guides already
     shipped: 19 / 16.5 / 14. The live pages also used 13.5 for the breadcrumb and
     the footer and 14 for the related-distro descriptions; half a pixel apart is
     not a decision, so they collapse onto one rung. Sans needs no VT323-style
     correction — it reads at its nominal size. */
  --fs-read-lead:19px;  /* the line under a guide title */
  --fs-read:16.5px;     /* running prose */
  --fs-read-sm:14px;    /* breadcrumb, footer, card description, caption */

  /* ── surfaces ──────────────────────────────────────────────────────────── */
  --line:rgba(135,150,171,.22);
  --fill:rgba(255,255,255,.018);
  --radius:10px;
  --radius-sm:6px;

  /* The fixed icon slot. One value, everywhere, and deliberately an ABSOLUTE
     length rather than an em: a button label is 13px pixel-font and a link label
     is 17px reading-font, so an em-based slot came out 15px in one and 19px in
     the other and the labels in a stack sat on two different x-positions. */
  --icon-slot:18px;

  /* minimum comfortable tap target */
  --tap:48px;

  /* The page's side padding on a phone. ONE value for every surface. The game
     frame, the guides and the dashboards each had their own (28px, 20px and
     18px), which is why a guide tile on a 390px screen measured 296px and a
     quarter of the glass was margin. */
  --gutter:12px;
}

/* -- edge to edge on a phone ------------------------------------------------
   A card floating inside a side margin wastes the scarcest thing a phone has.
   These surfaces bleed to both edges instead and drop their side borders and
   side radius, so what is left is a horizontal band: top rule, content, bottom
   rule. Text still does not sit against the glass, because the card keeps its
   own internal padding and that padding becomes the page's margin.

   Only full-width blocks belong here. A tile inside a multi-column grid must
   NOT bleed: it would overrun its track and collide with its neighbour. */
@media (max-width:600px) {
  .ui-panel, .cmp-panel {
    margin-left:calc(-1 * var(--gutter)); margin-right:calc(-1 * var(--gutter));
    border-left:0; border-right:0; border-radius:0;
  }
}

/* ── panel ──────────────────────────────────────────────────────────────────
   Promoted from .cmp-panel, which the Hall / Weekly / reclaimed-list work
   established. .cmp-panel is kept as an alias so those screens are untouched;
   the duplicate definition in campaign.css is gone. */
.ui-panel, .cmp-panel {
  border:1px solid var(--line); border-radius:var(--radius);
  padding:16px 18px; background:var(--fill);
}
.ui-panel + .ui-panel, .cmp-panel + .cmp-panel { margin-top:var(--sp-3); }

/* Panel heading. Flex so it can carry an icon in the shared slot, and every panel
   heading on the site is this one rule with a colour modifier — that is what stops
   two headings in the same row disagreeing about case, size and colour. */
.ui-panel__h {
  display:flex; align-items:center; gap:var(--sp-2);
  font-family:var(--pixel); font-size:var(--fs-head); letter-spacing:1px;
  text-transform:uppercase; line-height:1;
  color:var(--cyan); margin:0 0 var(--sp-3);
}

/* ── empty state ────────────────────────────────────────────────────────────
   Promoted from .cmp-empty. An empty screen is normal, not a failure: it keeps
   the panel's shape and says what would fill it. */
.ui-empty, .cmp-empty {
  display:flex; flex-direction:column; gap:var(--sp-1); align-items:center;
  text-align:center; font-family:var(--mono); font-size:clamp(16px,1.9vw,19px);
  color:var(--dim); padding:30px 12px;
}
.ui-empty span, .cmp-empty span {
  font-family:var(--pixel); font-size:var(--fs-label); color:var(--ylw); letter-spacing:1px;
}

/* ── icon slot ──────────────────────────────────────────────────────────────
   Reserved whether or not it is filled. This is what stops a button without an
   icon from shifting its label left relative to the one beside it. */
.ui-i {
  width:var(--icon-slot); height:var(--icon-slot);
  display:inline-flex; align-items:center; justify-content:center;
  flex:0 0 var(--icon-slot);
}
.ui-i svg { width:100%; height:100%; display:block; fill:currentColor; }
/* a glyph kept IN the text but not on the screen: drawn icons replace ☣/☠/⚡
   visually (iOS renders those as colour emoji), while the original character
   stays in textContent — screen readers and the frozen transcript fixtures
   both keep reading the exact bytes they always did. */
.glyph-x { position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; }

/* ── buttons ────────────────────────────────────────────────────────────────
   ONE button, three weights, two sizes. The weight says what the button is for,
   not how much the designer liked it:

     --primary    the one action this panel exists for. One per panel.
     --secondary  a real alternative to the primary, same panel.
     --quiet      reversible, low-stakes, or a toggle.

   Left-aligned with a fixed icon column, so a stack of them lines up. */
.ui-btn {
  display:grid; grid-template-columns:var(--icon-slot) 1fr; gap:var(--sp-2);
  align-items:center; text-align:left; width:100%;
  font-family:var(--pixel); font-size:var(--fs-label); line-height:1.25;
  text-transform:uppercase; letter-spacing:1px; text-decoration:none;
  padding:14px 18px; min-height:var(--tap);
  /* Every variant carries the SAME 2px side border, transparent on the filled
     ones. Without it the outlined variant's real border pushed its icon 2px
     right of the filled button above it, so a stack had two icon columns. */
  border:2px solid transparent; border-radius:var(--radius-sm); cursor:pointer;
  transition:transform .05s, filter .1s, background .1s;
}
.ui-btn:active { transform:translateY(2px); }
.ui-btn:hover { filter:brightness(1.1); }

.ui-btn--primary {
  color:var(--bg); background:var(--ylw); border-bottom:4px solid #b8930a;
  box-shadow:0 0 20px rgba(255,210,63,.34);
}
.ui-btn--primary:active { border-bottom-width:2px; }

.ui-btn--secondary {
  color:var(--bg); background:var(--cyan); border-bottom:4px solid #1b8c99;
  box-shadow:0 0 18px rgba(52,214,230,.34);
}
.ui-btn--secondary:active { border-bottom-width:2px; }

/* 4px bottom border like the filled variants, so a stack of mixed weights has
   one row height. Without it the outlined button measured 50px next to a 52px
   primary. */
.ui-btn--quiet {
  color:var(--cyan); background:transparent; border-color:var(--cyan);
  border-bottom-width:4px;
}
.ui-btn--quiet:hover { background:rgba(52,214,230,.12); filter:none; }

/* a second line under the label: state, not decoration ("Alpine · 7 of 15") */
.ui-btn__sub {
  display:block; font-family:var(--mono); font-size:var(--fs-small);
  text-transform:none; letter-spacing:0; opacity:.78; margin-top:5px;
}
.ui-btn--quiet .ui-btn__sub { color:var(--dim); opacity:1; }
/* with a state line the button is two lines tall, and a centred icon drifts down
   between them — pin it to the first line, which is the one it labels */
.ui-btn:has(.ui-btn__sub) { align-items:start; }
.ui-btn:has(.ui-btn__sub) .ui-i { margin-top:1px; }

/* A button that sits in a row beside other content rather than filling a panel —
   a top-bar action, a link row under a call-out. Same box, same icon column, just
   not stretched. Added for the guide pages, which had their own `.play` button
   with its own padding, radius and font size for exactly this job. */
.ui-btn--inline { width:auto; display:inline-grid; }

.ui-btn-stack { display:grid; gap:var(--sp-2); }

/* ── secondary link list ────────────────────────────────────────────────────
   For "go and read something" destinations. Deliberately NOT buttons: the title
   screen previously had seven buttons in five styles, which left nothing looking
   like the main action. */
.ui-links { display:grid; gap:0; }
.ui-link {
  display:grid; grid-template-columns:var(--icon-slot) 1fr; gap:var(--sp-2);
  /* same border+padding box as .ui-btn, so a panel holding both has ONE icon
     column rather than two 16px apart */
  align-items:center; min-height:36px; padding:5px 18px;
  border:2px solid transparent; background:none;
  font-family:var(--mono); font-size:var(--fs-body); line-height:1.3;
  color:var(--cyan); text-decoration:none;
}
.ui-link:hover { color:#8ce8f4; text-decoration:underline; }

/* ── responsive ─────────────────────────────────────────────────────────────
   Touch targets grow rather than shrink; nothing here gets smaller on a phone. */
@media (max-width:600px) {
  .ui-btn { padding:15px 16px; }
  /* on touch a link is a tap target like any other, so it gets the full size */
  .ui-link { min-height:var(--tap); padding-left:16px; padding-right:16px; }
}

/* ── guide tiles ────────────────────────────────────────────────────────────
   A big number, a heading, a line of what is inside, and the whole tile is the
   link. Lives here rather than in arcade.css so any surface can use it; the title
   screen is the only caller today.

   .rp-tiles is two across by default; add .stack for one column, which is what a
   panel-width slot needs. */
.rp-tiles { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.rp-tile {
  /* number and title on the top row, description spanning the FULL width beneath.
     Boxing the description into the leftover column next to the number squeezed it
     to ~275px and it broke as "signing up / for". */
  display:grid; grid-template-columns:auto 1fr auto;
  grid-template-areas:"n t go" "d d d";
  align-items:center; gap:8px 14px;
  padding:16px 18px; border:1px solid var(--line); border-radius:var(--radius-sm);
  background:rgba(52,214,230,.05); text-decoration:none; color:var(--ink);
  transition:border-color .16s, background .16s, transform .16s;
}
.rp-tile:hover, .rp-tile:focus-visible {
  border-color:var(--cyan); background:rgba(52,214,230,.12);
  transform:translateY(-2px); outline:none;
}
.rp-n {
  grid-area:n; font-family:var(--pixel); font-size:var(--fs-num); line-height:1; color:var(--ylw);
  text-shadow:0 0 14px rgba(255,210,63,.45);
}
.rp-t {
  grid-area:t; font-family:var(--pixel); font-size:var(--fs-head); letter-spacing:1px;
  text-transform:uppercase; color:var(--cyan); line-height:1.2;
}
.rp-d { grid-area:d; font-family:var(--mono); font-size:var(--fs-body); color:var(--dim); line-height:1.3; }
/* --fs-num is a DISPLAY size, so the chevron declares the display face — inside a
   reading-face container it inherited VT323 and sat on the wrong ladder (the same
   fix .cf-go needed on the stats page) */
.rp-go { grid-area:go; font-family:var(--pixel); color:var(--cyan); font-size:var(--fs-num); line-height:1; opacity:.6; }
.rp-tile:hover .rp-go, .rp-tile:focus-visible .rp-go { opacity:1; }
.rp-tiles.stack { grid-template-columns:1fr; }
/* flat variant: no big number — title + line + chevron. For rows of outbound
   reading links (the result screen's "how this pick was reasoned").
   Long article titles, so: label size and the case as written (uppercase forced
   2-3 choppy lines), and the title row takes the slack (1fr) so every
   description sits on the tile floor — one line across the whole row. */
.rp-tile--flat { grid-template-columns:1fr auto; grid-template-areas:"t go" "d go";
  grid-template-rows:1fr auto; align-items:start; }
.rp-tile--flat .rp-t { font-size:var(--fs-label); text-transform:none; line-height:1.5; }
.rp-tile--flat .rp-go { align-self:center; }
@media (max-width:600px) { .rp-tiles { grid-template-columns:1fr; } }

/* ── THE RECORD: the record keeper's paper book (shared by VERSUS and the
   arcade's past-runs book — same stock as the judges' cards). ────────────── */
.vs-rec { width: 100%; background: rgba(10, 12, 22, .82); border: 1px solid #262638; border-radius: 10px;
  padding: 18px 18px 14px; margin-top: 26px; }
.rec-eyebrow { font-family: var(--pixel); font-size: var(--fs-label); letter-spacing: 2px;
  color: var(--mag); text-align: center; margin-bottom: 12px; }
/* the book's foot: the title screen's own two-up .ui-links arrangement */
.rec-foot { grid-template-columns: auto auto; justify-content: center; gap: 0 24px; margin-top: 10px; }
.lg-known { font-family: var(--mono); font-size: var(--fs-body); color: var(--grn);
  text-align: center; margin-bottom: 10px; }
.lg-sheet { background: #e2dac2; border-radius: 6px; padding: 10px 16px 12px; color: #1a1610;
  /* AGED PARCHMENT (owner pick, Aug 2026): deeper tan + a whisper of ruling —
     the CRT overlay supplies most of the texture on light surfaces */
  background-image: repeating-linear-gradient(0deg, transparent 0 43px, rgba(95, 84, 64, .08) 43px 44px);
  box-shadow: 0 2px 0 rgba(0, 0, 0, .35); }
.lg-row { display: flex; align-items: center; gap: 10px; min-height: 28px; line-height: 28px;
  font-family: var(--mono); font-size: var(--fs-body); color: #3c3428; text-decoration: none; }
.lg-row:hover .lg-pair b { color: #b3202a; }
.lg-date { color: #5f5440; width: 72px; flex: none; font-size: var(--fs-small); }
.lg-pair { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lg-pair b { color: #1a1610; font-weight: bold; }
.lg-win { color: #5f5440; flex: none; font-size: var(--fs-small); }
.lg-flip { flex: none; font-family: var(--mono); font-size: var(--fs-small); line-height: 1; letter-spacing: 1px;
  color: #b3202a; border: 1px solid #b3202a; border-radius: 3px; padding: 3px 6px; transform: rotate(-2deg); }
.lg-pager { display: flex; justify-content: space-between; margin-top: 6px; }
.lg-pager a { font-family: var(--mono); font-size: var(--fs-small); color: #5f5440; text-decoration: none;
  padding: 4px 6px; min-height: 30px; display: inline-flex; align-items: center; }
.lg-pager a:hover { color: #b3202a; }
.lg-pager .pg-old { margin-left: auto; }
/* the keeper's notice + the sign-up desk, all in ink on the same paper */
.lg-notice { text-align: center; padding: 6px 0 12px; border-bottom: 2px solid rgba(107, 95, 71, .35);
  margin-bottom: 8px; }
.lg-nk { font-family: var(--mono); font-size: var(--fs-small); color: #5f5440; letter-spacing: 2px; }
.lg-nt { font-family: var(--pixel); font-size: var(--fs-label); color: #1a1610; letter-spacing: 1px; margin-top: 6px; }
.lg-np { font-family: var(--mono); font-size: var(--fs-body); color: #3c3428; margin: 8px auto 0;
  max-width: 640px; line-height: 1.5; }
.lg-form { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.lg-in { font-family: var(--mono); font-size: var(--fs-body); background: #fffdf6; color: #1a1610;
  border: 1px solid #b3a888; border-radius: 6px; padding: 10px 12px; min-width: 230px; }
.lg-in:focus { outline: none; border-color: #1a1610; }
.lg-in.code { min-width: 140px; text-align: center; letter-spacing: 4px; }
.lg-btn { font-family: var(--pixel); font-size: var(--fs-label); letter-spacing: 1px; cursor: pointer;
  background: #1a1610; color: #f4efe2; border: 2px solid #1a1610; border-radius: 6px; padding: 10px 16px;
  min-height: 44px; }
.lg-btn:hover { background: #b3202a; border-color: #b3202a; }
.lg-optin { display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-family: var(--mono); font-size: var(--fs-small); color: #5f5440; margin-top: 8px; min-height: 32px; }
.lg-err { font-family: var(--mono); font-size: var(--fs-small); color: #b3202a; margin-top: 6px; min-height: 18px; }
.lg-sent { font-family: var(--mono); font-size: var(--fs-body); color: #3c3428; margin: 10px 0 0; }
/* the keeper's slip on the scorecard: a fourth paper piece under the judges */
.lg-slip { background: #e2dac2; border-radius: 6px; padding: 14px 18px 12px; margin-top: 16px;
  text-align: center; transform: rotate(.4deg); box-shadow: 0 2px 0 rgba(0, 0, 0, .35);
  position: relative; z-index: 1; }
.lg-fine { font-family: var(--mono); font-size: var(--fs-small); color: #5f5440; margin: 10px auto 0;
  max-width: 560px; line-height: 1.45; }
@media (max-width: 720px) {
  .lg-date { width: 58px; }
  .lg-win { display: none; }   /* small screens: the pair and the stamps carry the line */
  .lg-slip { transform: none; }
  /* thumb-sized book lines: taller rows, and the ruling widens to match */
  .lg-row { min-height: 44px; line-height: 44px; }
  .lg-pager a { min-height: 44px; }
}

/* the book's management row (clear-history) — dark furniture under the paper */
.lg-manage { text-align: center; margin-top: 6px; font-family: var(--mono); font-size: var(--fs-small); color: var(--dim); }
.lg-manage a { color: var(--dim); text-decoration: underline; padding: 8px 10px; min-height: 44px;
  display: inline-flex; align-items: center; }
.lg-manage a:hover { color: var(--red); }
.lg-manage .lg-keep:hover { color: var(--cyan); }
.lg-sure { display: block; margin-bottom: 2px; }
