/* ==========================================================================
   games.css — the table every game under /games/<slug>/ is played at: the home
   form, the lobby, the top of the game and its results (src/games/game.njk,
   src/assets/js/games/shell.mjs). 24's sibling on base.css v3 ("tilde rail"):
   Source Serif 4 for prose, IBM Plex Mono for anything operational or numeric;
   one column right of the rail; hairlines, not cards; green the working accent,
   pink on hover. Every class here is .g-*; a game's own arena has its own sheet
   beside this one (assets/css/games/<slug>.css) and its own prefix. Nothing is
   shared with 24.css or zetamac.css, which these pages never load.
   ========================================================================== */

/* Sizes are design pixels, calc(N * var(--s, 1px)): exactly N px up to a 1512px-wide window, growing
   with wider ones like the rest of the site (--s lives on :root in base.css). 1px hairlines and media
   queries stay in true pixels. */

/* shell.mjs toggles screens and panels with the hidden attribute; author display values (flex/grid)
   would otherwise beat the UA's [hidden]{display:none}. */
.g-root [hidden] { display: none !important; }
/* Phones: a quick second tap on a control is another tap, never iOS Safari's double-tap zoom. */
.g-root { touch-action: manipulation; }

/* A screen takes the focus when it replaces the one that held it (shell.mjs show): it is a place,
   not a control, so it draws no ring (as base.css has it for main). */
.g-screen:focus { outline: none; }

/* Numbers sit in mono with fixed-width digits, so none shimmy. */
.g-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---- Type ----------------------------------------------------------------- */
.g-heading {
  font-size: calc(24 * var(--s, 1px));
  font-weight: 600;
  margin-bottom: calc(20 * var(--s, 1px));
}
.g-subheading {
  font-size: calc(17 * var(--s, 1px));
  font-weight: 600;
  margin-bottom: calc(10 * var(--s, 1px));
}
.g-intro {
  color: var(--ink-muted);
  margin-bottom: calc(36 * var(--s, 1px));
  max-width: 36rem;
  text-wrap: pretty;
}
.g-dim {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  color: var(--ink-muted);
}
/* Create/join errors under the home form; red only, the text says what went wrong. */
.g-error {
  margin-top: calc(24 * var(--s, 1px));
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  color: var(--red);
  max-width: 32rem;
  text-wrap: pretty;
}
.g-error:empty { display: none; }
/* The same line, but a note rather than a complaint ("type your name to join ABCDE"). */
.g-error--note { color: var(--ink-muted); }

/* ---- Forms / controls ----------------------------------------------------- */
.g-btn {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  text-transform: lowercase;
  -webkit-tap-highlight-color: transparent;
  color: var(--ink);
  background: none;
  border: 1px solid var(--hairline);
  padding: calc(7 * var(--s, 1px)) calc(14 * var(--s, 1px));
  cursor: pointer;
  transition:
    color 150ms ease,
    border-color 150ms ease,
    background-color 150ms ease;
}
.g-btn--primary {
  color: var(--green);
  border-color: var(--green);
  font-weight: 500;
}
/* Hover colours only where there is a hover: a touch screen keeps :hover on whatever was tapped last,
   and pink there reads as a state that isn't one. Every hover rule on these pages sits in this guard. */
@media (hover: hover) {
  .g-btn:hover:not(:disabled) {
    color: var(--pink-text);
    border-color: var(--pink);
  }
}
.g-btn:disabled {
  color: var(--ink-muted);
  border-color: var(--hairline-soft);
  cursor: default;
}
/* A press that cannot be taken back is asked twice: the button turns into its question, in red,
   and a second press is the answer (surrender). */
.g-btn--sure,
.g-btn--sure:hover:not(:disabled) {
  color: var(--red);
  border-color: var(--red);
}
/* A toggle that is down (ready): green and filled a touch — and it stays so under the pointer, since
   the next press lets go rather than picks. */
.g-btn[aria-pressed="true"],
.g-btn[aria-pressed="true"]:hover:not(:disabled) {
  color: var(--green);
  border-color: var(--green);
  background: color-mix(in srgb, var(--green) 10%, transparent);
}
.g-linkbtn {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--green);
  text-align: left;
  text-decoration: none;
  transition: color 150ms ease;
}
@media (hover: hover) {
  .g-linkbtn:hover {
    color: var(--pink-text);
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: calc(3 * var(--s, 1px));
  }
}
/* ≥16px on every control: iOS Safari zooms the page when a smaller field is focused. */
.g-input,
.g-select {
  font-family: var(--font-mono);
  font-size: calc(16 * var(--s, 1px));
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--hairline);
  padding: calc(4 * var(--s, 1px)) calc(8 * var(--s, 1px));
  max-width: 100%;
}
.g-input--code {
  width: 9ch;
  text-transform: uppercase;
  letter-spacing: 0.2ch;
}
.g-field { margin-bottom: 1rem; }
/* One label width lines the controls up across rows (only labels with `for`). */
.g-field > label[for] {
  display: inline-block;
  min-width: 7rem;
}
/* One convention on every screen: the way out first, the action last. */
.g-actions {
  display: flex;
  justify-content: flex-start;
  gap: calc(10 * var(--s, 1px));
  margin-top: calc(32 * var(--s, 1px));
  flex-wrap: wrap;
}
.g-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2.5rem;
  margin-top: 1.5rem;
}
@media (max-width: 600px) {
  .g-columns { grid-template-columns: 1fr; }
}

/* ---- Lobby ----------------------------------------------------------------- */
.g-roomcode {
  font-size: calc(32 * var(--s, 1px));
  font-weight: 500;
  letter-spacing: 0.3ch;
  margin-bottom: 0.5rem;
}
.g-linkrow {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}
/* The URL never pushes the column sideways on a phone: it breaks anywhere. */
.g-lobby-link {
  min-width: 0;
  overflow-wrap: anywhere;
}
.g-lobby-game { margin-bottom: 0.75rem; }
.g-lobby-match {
  margin-bottom: 1.25rem;
  max-width: 32rem;
  text-wrap: pretty;
}
/* The match line as a disclosure (the host's): collapsed it reads like the guests' line with "change"
   in green at its end; open, the same controls as the home form. */
.g-settings {
  margin-bottom: 1.25rem;
  max-width: 32rem;
}
.g-settings > summary {
  cursor: pointer;
  list-style: none;
}
.g-settings > summary::-webkit-details-marker { display: none; }
.g-settings-hint {
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  color: var(--green);
  margin-inline-start: 1.2ch;
  transition: color 150ms ease;
}
@media (hover: hover) {
  .g-settings > summary:hover .g-settings-hint { color: var(--pink-text); }
}
.g-settings[open] > summary { margin-bottom: 0.9rem; }
.g-settings[open] > summary .g-settings-hint { display: none; }
.g-settings[open] {
  padding-bottom: 0.25rem;
  border-bottom: 1px solid var(--hairline-soft);
}
.g-settings-actions {
  margin-top: calc(8 * var(--s, 1px));
  margin-bottom: calc(14 * var(--s, 1px));
}
/* The seat list: one row per player — presence dot, their animal, name, then the tags. */
.g-seats {
  margin: 0 0 1.25rem;
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  max-width: 26rem;
  border-top: 1px solid var(--hairline);
}
.g-seat {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--hairline-soft);
}
.g-seat-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.g-seat-you { font-weight: 600; }
.g-seat-wins {
  flex: none;
  font-size: calc(11 * var(--s, 1px));
  color: var(--ink-muted);
  white-space: nowrap;
}
.g-seat-tag {
  margin-inline-start: auto;
  color: var(--ink-muted);
  white-space: nowrap;
}
.g-seat-host .g-seat-tag,
.g-seat-ready .g-seat-tag { color: var(--green); }
/* "remove", on a seat that can be cleared (the host's kick; anyone's for a seat gone quiet). */
.g-seat-remove { flex: none; }
/* Presence dot: green while the heartbeat is fresh; the row dims and the dot goes red once it is
   stale. Not colour alone: the dot carries a title and the tag gains "away". */
.g-dot {
  display: inline-block;
  flex: none;
  inline-size: calc(7 * var(--s, 1px));
  block-size: calc(7 * var(--s, 1px));
  border-radius: 50%;
  background: var(--green);
  vertical-align: 0.05em;
}
.g-seat-away { opacity: 0.55; }
.g-seat-away .g-dot { background: var(--red); }
.g-lobby-note {
  max-width: 28rem;
  text-wrap: pretty;
}
/* A player's animal before their name (critters.mjs draws and styles it: mono, muted, never wrapped,
   no part of what a screen reader hears): here it only holds its width in a row. */
.g-critter { flex: none; }

/* ---- The game's top: whose turn, the timer, the standings ---------------------- */
.g-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem 2rem;
  flex-wrap: wrap;
  margin-bottom: calc(16 * var(--s, 1px));
}
.g-status-wrap {
  display: flex;
  align-items: baseline;
  gap: 1.2ch;
  min-height: calc(24 * var(--s, 1px));
}
.g-status {
  font-family: var(--font-mono);
  font-size: calc(15 * var(--s, 1px));
  font-weight: 500;
}
/* Your turn is the one state that asks something of you: green, as a picked tile is in 24. */
.g-status--you { color: var(--green); }
.g-status--wait {
  color: var(--ink-muted);
  font-weight: 400;
}
.g-timer {
  font-size: calc(15 * var(--s, 1px));
  color: var(--ink-muted);
}
.g-timer--low { color: var(--red); }
.g-timer:empty { display: none; }
/* The standings: "~ animal name: score" per seat, packed to the right and wrapping. */
.g-scores {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.35rem 1.5rem;
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  min-width: 0;
}
.g-score {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
  min-width: 0;
  gap: 0.7ch;
}
.g-score-name {
  min-width: 0;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.g-score-name::after { content: ":"; }
.g-score > .g-num { flex: none; }
.g-score-you { font-weight: 600; }
/* The seat whose turn it is: its name underlined in green (the status line says it in words). */
.g-score-turn .g-score-name {
  text-decoration: underline;
  text-decoration-color: var(--green);
  text-decoration-thickness: calc(2 * var(--s, 1px));
  text-underline-offset: calc(4 * var(--s, 1px));
}
.g-score-out { opacity: 0.55; }
/* The race strip (critters.mjs draws it and brings its own styles): just its place in the column. */
.g-race { margin-bottom: calc(16 * var(--s, 1px)); }

/* ---- The arena: the game's own view draws inside ------------------------------ */
.g-arena {
  position: relative;
  border-top: 1px solid var(--hairline-soft);
  border-bottom: 1px solid var(--hairline-soft);
  padding: calc(32 * var(--s, 1px)) 0;
  text-align: center;
}
/* Over: the last position stays on show under the results, as a picture. */
.g-arena--over { pointer-events: none; }
/* "+12" in green; a reason in red; a note in grey. Reserved height so nothing below jumps: one line
   of it is exactly the height kept for it (the page's own line height made a line a pixel and a
   half taller than the reserve, and the button under it moved whenever the line filled or emptied). */
.g-feedback {
  min-height: calc(20 * var(--s, 1px));
  margin-top: calc(14 * var(--s, 1px));
  font-family: var(--font-mono);
  font-size: calc(13 * var(--s, 1px));
  line-height: calc(20 * var(--s, 1px));
  font-weight: 500;
  color: var(--green);
  text-align: center;
  text-wrap: pretty;
}
.g-feedback--bad {
  color: var(--red);
  font-weight: 400;
}
.g-feedback--dim {
  color: var(--ink-muted);
  font-weight: 400;
}
.g-game-actions {
  justify-content: center;
  margin-top: calc(16 * var(--s, 1px));
}

/* ---- Pieces (parts.mjs): what the games draw with ------------------------------
   One die, one disc and one peg for every game, in the site's own terms: a hairline square like
   24's tiles, and plain round marks. Clarity before anything: a face is read by its pips, the two
   sides of a board by shape (filled against outlined), never by colour alone. */
/* A die: a square tile with its pips on a three-by-three grid. Sized by --die. */
.g-die {
  --die: calc(56 * var(--s, 1px));
  display: inline-grid;
  grid-template: repeat(3, 1fr) / repeat(3, 1fr);
  box-sizing: border-box;
  inline-size: var(--die);
  block-size: var(--die);
  padding: calc(var(--die) * 0.15);
  border: 1px solid var(--hairline);
  background: var(--bg);
  color: var(--ink);
  vertical-align: middle;
  flex: none;
}
.g-die--sm { --die: calc(34 * var(--s, 1px)); }
.g-die--lg { --die: calc(76 * var(--s, 1px)); }
/* A pip's place; only the ones the face has are shown. */
.g-die > i {
  place-self: center;
  inline-size: calc(var(--die) * 0.16);
  block-size: calc(var(--die) * 0.16);
  border-radius: 50%;
  background: currentColor;
  visibility: hidden;
}
.g-die > i.on { visibility: visible; }
/* Face down: someone else's die, still hidden. */
.g-die--down {
  color: var(--ink-muted);
  border-style: dashed;
}
.g-die--down::after {
  content: "?" / "";
  grid-area: 1 / 1 / -1 / -1;
  place-self: center;
  font-family: var(--font-mono);
  font-size: calc(var(--die) * 0.4);
  line-height: 1;
}
/* A die that counts (it matches what is being counted) and one that does not. */
.g-die--mark {
  color: var(--green);
  border-color: var(--green);
}
.g-die--dim {
  color: var(--ink-muted);
  border-color: var(--hairline-soft);
}
/* A counter: filled for one side, a ring for the other. It fills the cell it is put in (--disc). */
.g-disc {
  display: block;
  box-sizing: border-box;
  inline-size: var(--disc, 72%);
  aspect-ratio: 1;
  border-radius: 50%;
  color: var(--ink);
}
.g-disc--solid { background: currentColor; }
.g-disc--ring { border: calc(3 * var(--s, 1px)) solid currentColor; }
/* A score mark: filled, a ring, or a faint dot. */
.g-peg {
  display: inline-block;
  box-sizing: border-box;
  inline-size: calc(11 * var(--s, 1px));
  block-size: calc(11 * var(--s, 1px));
  border-radius: 50%;
  vertical-align: baseline;
  color: var(--ink);
}
.g-peg--place { background: currentColor; }
.g-peg--near { border: calc(2 * var(--s, 1px)) solid currentColor; }
.g-peg--none {
  inline-size: calc(5 * var(--s, 1px));
  block-size: calc(5 * var(--s, 1px));
  margin: calc(3 * var(--s, 1px));
  background: var(--hairline);
}
/* Forced colours repaint every background as the page's own, and a filled piece would be gone (a
   ring keeps its border). These keep their fill, in the system's text colour. */
@media (forced-colors: active) {
  .g-disc--solid,
  .g-peg--place,
  .g-die > i {
    forced-color-adjust: none;
    background: CanvasText;
  }
  .g-peg--none {
    forced-color-adjust: none;
    background: GrayText;
  }
}

/* ---- Results (above the last position, once the game is over) ---------------- */
.g-over { margin-bottom: calc(32 * var(--s, 1px)); }
/* Focused by the shell when a game ends (shell.mjs showResults), as a screen is: no ring of its own. */
.g-over:focus { outline: none; }
/* Ranked rows: rank, animal, name (you in weight), what the game says of them; the winner in green. */
.g-results {
  max-width: 26rem;
  margin-bottom: 1rem;
  font-family: var(--font-mono);
  font-size: calc(14 * var(--s, 1px));
  font-variant-numeric: tabular-nums;
  border-top: 1px solid var(--hairline);
}
.g-result {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--hairline-soft);
  white-space: nowrap;
}
.g-result-rank {
  flex: none;
  width: 2ch;
  color: var(--ink-muted);
}
.g-result-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.g-result-you { font-weight: 600; }
.g-result-score { flex: none; }
.g-result-winner {
  color: var(--green);
  font-weight: 600;
}
.g-over-note {
  max-width: 28rem;
  text-wrap: pretty;
}
.g-over-note:empty { display: none; }
.g-over .g-actions { margin-top: calc(24 * var(--s, 1px)); }

/* ---- Phones ---------------------------------------------------------------- */
@media (max-width: 600px) {
  .g-top { flex-direction: column; }
  .g-scores { justify-content: flex-start; }
  .g-field > label[for] { min-width: 6rem; }
  .g-arena { padding: calc(20 * var(--s, 1px)) 0; }
  /* Thumb-sized: 44px tall buttons, 40px fields. */
  .g-btn { min-height: calc(44 * var(--s, 1px)); }
  .g-input,
  .g-select { min-height: calc(40 * var(--s, 1px)); }
}
