/* Cards Hub Pro v2 — "card table" theme.
 *
 * Deep green felt, warm cream ink, brass accents. Played on phones, in the
 * evening, around a table, for three hours at a stretch.
 *
 * Three rules this file is built on:
 *
 *   1. LEGIBILITY WINS. Where theme and readability pull against each other,
 *      readability takes it. This is a scorecard before it is anything else.
 *   2. Colour carries MEANING, never decoration. Each section has a role, worn
 *      as a coloured left-edge bar — never a background wash, because a wash
 *      behind a column of numbers is precisely where legibility goes.
 *   3. A status colour NEVER carries meaning alone. Every warning ships an icon
 *      and words as well. Roughly one man in twelve has some colour vision
 *      deficiency, and a card table is where that goes unmentioned.
 *
 * Every text/background pair below clears WCAG 4.5:1 — checked, not eyeballed;
 * the ratios are in REPORT.md. That is why the accents appear on edges, badges
 * and pills rather than as body text: amber and gold *text* on felt do not
 * clear it, and would be the first thing to fail on a dim phone.
 *
 * Change a colour in ONE place — the tokens below. Nothing downstream should
 * ever carry a raw hex value; the theme has to reach Grand Fan and the
 * leaderboard later without a second rewrite.
 */

:root {
  /* surfaces */
  --felt:        #0d2b1d;   /* the table */
  --card:        #143a27;   /* a card laid on it */
  --sunk:        #102f20;   /* inputs, recessed rows */
  --line:        #255c3e;   /* hairline rules */

  /* ink */
  --ink:         #f4ecdc;   /* warm cream — body */
  --ink-muted:   #b9cbbd;   /* secondary */
  --on-accent:   #0d2b1d;   /* reverse text sitting ON an accent */

  /* paper — the History ledger, and the only surface in the app that is not
     dark. A scorecard laid on the felt. Dark ink on cream is a HIGHER contrast
     pairing than cream on green, which is why the ledger can afford the
     handwriting face the rest of the app cannot. */
  --paper:       #f2ead8;   /* the card stock */
  --paper-ink:   #23301f;   /* what is written on it — 11.6:1 */
  --paper-muted: #5a6b52;   /* the detail line — 4.8:1 */
  --paper-note:  #6b5326;   /* pencil annotation: running subtotals — 6.1:1 */
  --paper-line:  #cfc0a2;   /* ruled lines */
  /* The two trick marks, in their paper-surface tones. The role accents are
     pitched for the FELT: on cream they measure 2.20:1 and 1.85:1, under the
     3:1 WCAG asks of a non-text graphical mark, which is most of why the first
     pass "read as almost nothing". Same hue and saturation, walked down in
     lightness until they clear it — 3.20:1 and 3.25:1. */
  /* WINNING IS GREEN from M6.8, everywhere: the card currently taking the trick
     on the table, and the winner's ring in the ledger. Red is no longer a mark on
     any card — it now means only "a tally off its bid", on the seat plate.
     Three values of one hue, one per surface, the way the two marks already
     work: #5cf07a on the dark felt, #28c840 on the near-black plate, and this
     deep one on cream, where a light green would disappear. */
  --paper-won:   #0f5c22;   /* won the trick — deep green, on paper — 6.81:1 */
  /* LED IS BLUE from M6.6. The brass rule did not read — too close to the paper
     and too close to the card's own edge — so it became a stark third colour,
     clear of the red ring. Darkened from --role-safe, which measures 1.78:1 on
     cream and would have been the same mistake in a different hue. */
  /* LED — DEEP blue, third attempt. #2183e5 met the 3:1 bar at 3.22:1 and Mufi
     still could not see it on a phone, which settles what the bar is worth: the
     test is legibility at arm's length, not the ratio. This is the same hue
     walked down until it is unmistakably a different THING from both the cream
     paper and the card's own white face — 7.66:1 on the paper, 9.17:1 on the
     card face. */
  --paper-led:   #0a3fa8;   /* led the trick — deep blue, on paper — 7.66:1 */

  /* role accents — edges, badges, pills and rules. NOT body text. */
  --role-live:      #e8a33d;   /* in progress — the round being played */
  --role-standings: #d4a843;   /* brass — where everyone stands */
  --role-settled:   #4d7a5f;   /* history — recedes */
  --role-status:    #e8837a;   /* warnings, errors, destructive actions */
  --role-safe:      #84b6e8;   /* non-destructive actions (rename) */
  /* ON TARGET, on the felt — the card currently winning the trick. Light against
     the dark green table so it cannot merge with it: 6.88:1 at the felt's
     lightest point, 10.4:1 out at the rim. */
  --role-win:       #5cf07a;
  /* The seat plate's tally, on its near-black ground. Mufi sent the macOS
     traffic-light palette as the reference; measured here, red is 5.66:1 and
     green 7.59:1. */
  --mark-red:       #ff5f57;
  --mark-green:     #28c840;

  /* spacing scale — one ladder, used everywhere. Inconsistent gaps are most
     of why the old screens read as unfinished. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;

  --radius: 12px;
  --radius-sm: 8px;

  /* Phones, evening, one hand, possibly a drink in the other. */
  --tap: 44px;

  /* Tells the browser its own furniture is dark: number-input spinners, the
     checkbox, scrollbars and the autofill wash. Without it iOS paints white
     chrome onto the felt. */
  color-scheme: dark;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--felt);
  color: var(--ink);
  -webkit-text-size-adjust: 100%;
}

/* Caveat, served from our own origin. The app is reached through the tunnel
   and must not depend on a third-party host at game time. This is the only
   webfont, and it is used for exactly one thing: the running subtotals. */
@font-face {
  font-family: "Caveat";
  src: url("/static/fonts/Caveat-Regular.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
}

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

.topbar {
  padding: var(--s3) var(--s5);
  border-bottom: 1px solid var(--line);
  background: var(--card);
}

.brand {
  font-weight: 700;
  font-size: 1.1rem;
  text-decoration: none;
  color: var(--ink);
}

.container {
  max-width: 640px;
  margin: 0 auto;
  /* Bottom room so a sticky action bar never sits on the last card. */
  padding: var(--s5) var(--s4) var(--s6);
}

.game-head { margin-bottom: var(--s4); }

h1 { font-size: 1.5rem; margin: 0 0 var(--s1); }
h2 {
  font-size: 1.1rem;
  margin: 0 0 var(--s3);
  display: flex;
  align-items: center;
  gap: var(--s2);
}

p { margin: 0 0 var(--s3); }
p:last-child { margin-bottom: 0; }

.muted { color: var(--ink-muted); font-size: 0.92rem; }
.small { font-size: 0.8rem; }

.error {
  color: var(--ink);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: var(--s2);
  background: var(--card);
  border: 1px solid var(--role-status);
  border-left: 4px solid var(--role-status);
  border-radius: var(--radius-sm);
  padding: var(--s3);
}

/* Inline status icons. Sized in ems so they track the text they sit beside. */
.icon {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  vertical-align: -0.15em;
}

/* cards and their role bars ------------------------------------------------
   The left edge is the whole colour system. Everything inside stays on the
   same surface at the same contrast. */

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4);
  margin: 0 0 var(--s4);
}

.card-live      { border-left-color: var(--role-live); }
.card-standings { border-left-color: var(--role-standings); }
.card-history   { border-left-color: var(--role-settled); }
.card-warn      { border-left-color: var(--role-status); border-color: var(--role-status); }

/* A quiet pulse on the panel that is live. Motion, not a second colour, so it
   still reads for anyone who cannot separate amber from gold. */
.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--role-live);
  flex: none;
  animation: live-pulse 2.4s ease-in-out infinite;
}
@keyframes live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
}

/* forms -------------------------------------------------------------------- */

label {
  display: block;
  font-size: 0.85rem;
  color: var(--ink-muted);
  margin-bottom: var(--s1);
}

/* Every text-ish input, not a list of the ones that happened to exist when this
   was written: `tel` was added for the recovery field and rendered at its
   browser default width, which on the one screen a locked-out novice sees is
   exactly where it must not happen. */
input[type="text"], input[type="number"], input[type="tel"],
input[type="email"], input[type="search"], select {
  width: 100%;
  min-height: var(--tap);
  padding: var(--s2) var(--s3);
  /* 16px minimum, or iOS Safari zooms the page on focus and the scorer spends
     the round pinching back out. */
  font-size: 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  margin-bottom: var(--s3);
  background: var(--sunk);
  color: var(--ink);
}

input:focus-visible, select:focus-visible, button:focus-visible {
  outline: 2px solid var(--role-standings);
  outline-offset: 2px;
}

button {
  min-height: var(--tap);
  padding: var(--s2) var(--s4);
  font-size: 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--sunk);
  color: var(--ink);
  cursor: pointer;
}

button.primary {
  background: var(--role-standings);
  color: var(--on-accent);
  border-color: var(--role-standings);
  font-weight: 700;
  width: 100%;
  min-height: 52px;
  font-size: 1.05rem;
}

/* Pressed, and staying pressed while the work happens. --role-safe rather than
   a deeper brass: amber and brass are a hair apart on a dim phone, and a
   pressed state that could be mistaken for the idle one is not a pressed
   state. Blue also says plainly that nothing destructive is under way.
   home.js holds the class from the tap until the next page paints, because
   :active ends when the finger lifts and creating a game takes longer. */
button.primary:active,
button.primary.is-pressing {
  background: var(--role-safe);
  border-color: var(--role-safe);
  color: var(--on-accent);
}

button:disabled { opacity: 0.45; cursor: not-allowed; }

/* The primary action closes the form: full width, taller than anything else on
   the screen, and the last thing under the thumb after the final entry.
 *
 * This was a sticky bar pinned to the bottom of the viewport, which is the
 * obvious way to make it reachable one-handed — and it was wrong. A sticky
 * footer is lifted out of its flow position, so on a four-player card it
 * painted over the bid inputs for players two, three and four: the button was
 * always to hand, and the fields it submits were hidden behind it. A control
 * that occludes its own form is worse than one you scroll to. */
.form-actions {
  padding-top: var(--s4);
  margin-top: var(--s2);
  border-top: 1px solid var(--line);
}
.form-actions > * + * { margin-top: var(--s2); }

/* One entry row per player: name left, number right, aligned down the column. */
.entry-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--tap);
  margin-bottom: var(--s2);
}
.entry-row label { flex: 1; margin-bottom: 0; font-size: 0.98rem; color: var(--ink); }
.entry-row input {
  flex: 0 0 92px;
  margin-bottom: 0;
  text-align: center;
  font-size: 1.15rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s3);
}
.row label { flex: 1; margin-bottom: 0; }
.row input, .row select { margin-bottom: 0; width: auto; flex: 1 1 100px; min-width: 0; }
.row button { flex: none; }

.checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--tap);
  font-size: 0.95rem;
  color: var(--ink);
  margin-bottom: var(--s2);
}
.checkbox-row input {
  width: 22px;
  height: 22px;
  min-height: 0;
  margin: 0;
  flex: none;
  accent-color: var(--role-standings);
}

.total-line {
  font-size: 0.95rem;
  margin: var(--s3) 0;
  font-variant-numeric: tabular-nums;
}
.total-line strong { font-size: 1.15rem; }

/* The doubled half of "Pot: 60 → 120 (×2)". Same weight and size as the raw
   figure beside it: both are the pot, and the second one is the one being
   written. */
#pot-doubled { font-size: 1.15rem; font-weight: 700; }

/* A read-back that no longer describes the round it was read from. Icon and
   words, never colour alone. */
.stale-note {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}
.stale-note[hidden] { display: none; }

/* The devil's cut, armed. The round head takes the live edge so a doubled
   round is visible from wherever the scorer's eye is on a long card. Colour
   is never alone — the tick box beside it says it in words, and the pot line
   shows both figures. */
.card.is-doubled {
  border-color: var(--role-live);
  border-left-color: var(--role-live);
}

/* How far off the actuals are, in words. The hard rule refuses the round, so
   say the arithmetic before the submit rather than after it. */
.tally-state { display: block; margin-top: var(--s1); font-size: 0.88rem; }
.tally-ok  { color: var(--ink-muted); }
.tally-off { color: var(--ink); font-weight: 600; }

.inline-form { display: inline-block; margin: 0 0 var(--s4); }

/* pills — small, but still 44px of tappable height ------------------------- */

.pill {
  min-height: var(--tap);
  /* Never narrower than it is tall either: "−1" and "+1" are two characters,
     and padding alone left them 42px wide — under the target on the one
     control whose whole job is to be tapped correctly at a table. */
  min-width: var(--tap);
  /* Snug horizontally, full height vertically. Two pills and a name have to
     share 311px inside a card on a 375px phone, and the whole point of this
     row is that they sit on it together. */
  padding: var(--s2) var(--s3);
  white-space: nowrap;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 700;
  border: 1px solid transparent;
  color: var(--on-accent);
  cursor: pointer;
}
.pill-rename { background: var(--role-safe); border-color: var(--role-safe); }
.pill-remove { background: var(--role-status); border-color: var(--role-status); }
.pill-add    { background: var(--role-standings); border-color: var(--role-standings); }
.pill-quiet  { background: var(--sunk); color: var(--ink); border-color: var(--line); }

/* the setup screen --------------------------------------------------------- */

.code-chip {
  display: inline-block;
  padding: 2px var(--s2);
  border-radius: var(--radius-sm);
  background: var(--sunk);
  border: 1px solid var(--line);
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
}

.seat-list { list-style: none; padding: 0; margin: 0 0 var(--s4); }

.seat {
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line);
}
.seat:last-child { border-bottom: none; }

/* Name on the left, actions on the right, on the same row — the whole point
   of this section. Wraps rather than squashing on a narrow phone. */
.seat-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2);
  min-height: var(--tap);
}
.seat-name {
  /* No flex-basis: the name yields to the buttons rather than pushing them
     onto a line of their own, which was the original complaint. */
  flex: 1 1 auto;
  min-width: 0;
  font-size: 1.05rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.seat-actions {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-left: auto;
}
.seat-remove { margin: 0; }

.seat-edit {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: var(--s2) 0 var(--s1);
}
.seat-edit input { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.seat.is-editing .seat-head { display: none; }

/* `hidden` is only `display: none` in the UA sheet, so the author `display:
   flex` above beats it and every editor renders open — which is precisely the
   stack of boxes this section exists to get rid of. Say it here instead. The
   <noscript> block in setup.html re-opens them, later in the cascade, for the
   case where nothing can toggle them. */
.seat-edit[hidden] { display: none; }

.add-seat {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: 0;
}
.add-seat input { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }

.tag {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px var(--s2);
  border-radius: 999px;
  background: var(--sunk);
  border: 1px solid var(--line);
  color: var(--ink-muted);
}

/* tables — the scorecard proper -------------------------------------------- */

.table-scroll { overflow-x: auto; }

table { border-collapse: collapse; width: 100%; }

th, td {
  padding: var(--s2) var(--s3);
  text-align: center;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }

th {
  color: var(--ink-muted);
  font-weight: 600;
  font-size: 0.85rem;
}

/* Tabular figures on every score column. Proportional digits make a column of
   scores ragged and genuinely harder to compare down the page; this one
   declaration is the largest readability gain in the file. */
.totals td, .history td, .running-total, .round-points, .standing-score {
  font-variant-numeric: tabular-nums;
}

.totals td { font-weight: 700; font-size: 1.15rem; }

/* Running totals: one ROW per player ---------------------------------------
   Grand Fan seats eight or nine. A column each ran the fifth name off the card
   at 375px and nine would have been hopeless, so the standings turn on their
   side: name left, score right, badge alongside.

   auto-fit rather than a breakpoint on player count. The layout takes as many
   columns as the width can hold — one on a phone held upright, more in
   landscape or on a tablet — and there is no player count at which it changes
   behaviour, so there is no count at which it can be wrong. */
.standings {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--s1) var(--s3);
}

.standing {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 34px;
  padding: var(--s1) var(--s2);
  border-radius: var(--radius-sm);
}
.standing-name {
  /* Yields to the score rather than pushing it off the row; a long name wraps
     inside its own space. */
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 0.98rem;
}
.standing-score {
  flex: none;
  margin-left: auto;
  font-size: 1.15rem;
  font-weight: 700;
}

/* The leader, reversed. This is a BINARY distinction — leading or not — and
   not the rank scale §11 rules out: when the lead changes exactly two rows
   repaint, never the whole set. Everyone tied on the top score is highlighted;
   the badge inverts with the row because brass on brass is invisible. */
.standing.is-leader {
  background: var(--role-standings);
  color: var(--on-accent);
}
.standing.is-leader .pos-badge {
  background: var(--on-accent);
  color: var(--role-standings);
}

/* Bids in play. Still a column per player — a bid is one digit and the row
   reads across in a glance, which is not true of a column of totals. It no
   longer lines up with the standings below, because those turned into rows;
   that is the right trade, since the standings had to survive nine players and
   a row of bids does not. */
.bids-table td { color: var(--ink); }

/* One accent for every position — the NUMBER carries the rank. Colouring
   1st/2nd/3rd differently would repaint the badges every time the standings
   shuffle, which is a flicker of noise on every single round. */
.pos-badge {
  display: inline-block;
  min-width: 1.6em;
  padding: 1px var(--s2);
  margin-left: var(--s1);
  border-radius: 5px;
  background: var(--role-standings);
  color: var(--on-accent);
  font-size: 0.78rem;
  font-weight: 700;
  vertical-align: middle;
  font-variant-numeric: tabular-nums;
}

/* Round header: cards dealt and trump, spelled out. */
.round-line {
  font-size: 1rem;
  margin: 0 0 var(--s3);
  color: var(--ink);
}

/* Over/under indicator. Equal is the one that means something is wrong, so it
   alone wears the status colour — and it carries an icon and the word EQUAL,
   never the colour alone. */
.balance {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 var(--s1);
  font-size: 0.9rem;
  line-height: 1.45;
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--line);
  background: var(--sunk);
  padding: var(--s2) var(--s3);
  margin: 0;
  color: var(--ink);
}
.balance .icon { align-self: center; }
.balance-over  { border-left-color: var(--role-safe); }
.balance-under { border-left-color: var(--role-live); }
.balance-equal { border-left-color: var(--role-status); }

.ended-banner {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--role-standings);
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3);
  margin: 0 0 var(--s4);
}

/* End game: its own ground, not another card in the stack. The heavy rule and
   the empty space above it are the whole point — this is the one thing on the
   screen that cannot be undone, and it has to be findable without being
   adjacent to anything routine. */
.end-region {
  margin-top: var(--s6);
  padding-top: var(--s5);
  border-top: 2px solid var(--line);
}
.end-region h2 { color: var(--ink); }

button.warn-action,
button.danger {
  width: 100%;
  background: var(--sunk);
  border-color: var(--role-status);
  color: var(--ink);
  font-weight: 600;
}

/* History — the paper scorecard ---------------------------------------------
   The one light surface in the app, in the handwriting face, ruled like a
   notepad. It makes the ledger a distinct OBJECT laid on the table rather than
   another dark card in a stack of them.

   The hazard is that Caveat is a handwriting face with no tabular figures, so
   dropping it on the numbers would undo the tabular-nums alignment that was
   M2.3's biggest readability gain. THE ALIGNMENT COMES FROM THE LAYOUT
   INSTEAD: every number is a block of its own, right-aligned in a cell of
   fixed minimum width, so the column lines up whatever the glyph widths are.
   Do not "restore" tabular-nums here and drop the fixed width — the width is
   what is holding the column together. */
.card-history {
  /* THE OTHER LIGHT SURFACE, armoured the same way as the card face — see the
     long note above `.pcard` in cards/card.css. A scorecard laid on the felt is
     a light object in a dark app, which is precisely the thing a browser's
     darkening pass exists to repaint. Declaring what it is keeps the cream
     stock cream and the pencil dark, together, whatever the page around it. */
  color-scheme: only light;
  background: var(--paper);
  color: var(--paper-ink);
  border-color: var(--paper-line);
  border-left-color: var(--role-settled);
}
.card-history h2 { color: var(--paper-ink); }

.history th,
.history td {
  border-bottom: 1px solid var(--paper-line);
  /* Right-aligned in a fixed minimum width: this is the tabular alignment,
     and it does not depend on the typeface. */
  text-align: right;
  min-width: 6.4rem;
  vertical-align: top;
  line-height: 1.3;
  padding-top: var(--s2);
}
.history th { color: var(--paper-muted); }
.history tbody tr:last-child td { border-bottom: none; }

/* The round label reads as a row header, so it keeps the left edge. */
.history th:first-child,
.history .history-rd {
  text-align: left;
  min-width: 3.4rem;
  font-family: "Caveat", cursive;
  font-size: 1.3rem;
  font-weight: 400;
  color: var(--paper-ink);
}

.round-points {
  display: block;
  font-family: "Caveat", cursive;
  /* Caveat sits small on the em, so it needs the size to match the sans it
     replaces rather than to shout. */
  font-size: 1.55rem;
  line-height: 1.1;
  font-weight: 400;
  color: var(--paper-ink);
}
.round-detail {
  display: block;
  font-size: 0.78rem;
  color: var(--paper-muted);
}
/* The round score is the event, the running total is the reference, so the
   total sits under a rule and reads as a pencil annotation rather than data. */
.running-total {
  display: block;
  margin-top: var(--s1);
  padding-top: 3px;
  border-top: 1px solid var(--paper-line);
  font-family: "Caveat", cursive;
  font-size: 1.35rem;
  line-height: 1.15;
  color: var(--paper-note);
}

.scorer-line {
  font-size: 0.92rem;
  color: var(--ink-muted);
  margin: 0 0 var(--s4);
}

/* Start time and elapsed, filled in by clock.js in the viewer's local time.
   Hidden until then: an empty line is better than a wrong one. */
.game-clock {
  font-size: 0.92rem;
  color: var(--ink-muted);
  margin: 0 0 var(--s2);
  font-variant-numeric: tabular-nums;
}

/* Grand Fan ----------------------------------------------------------------
   No new palette: every colour below is one of the tokens Judgement already
   uses, so the two games read as one app. --role-live is still "the round in
   play", which is what the winner mark and the command read-back are. */

/* Winner: a radio, but sized and shaped like the rest of the entry rows
   rather than like a form control from another decade. */
.winner-choice {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: 1;
  margin-bottom: 0;
  min-height: var(--tap);
  font-size: 0.98rem;
  color: var(--ink);
  cursor: pointer;
}
.winner-input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.winner-mark {
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--line);
  background: var(--sunk);
}
.winner-input:checked + .winner-mark {
  border-color: var(--role-live);
  /* Filled centre, not a colour swap alone: the ring reads at a glance and
     still reads without colour. */
  box-shadow: inset 0 0 0 4px var(--role-live);
}
.winner-input:focus-visible + .winner-mark {
  outline: 2px solid var(--role-standings);
  outline-offset: 2px;
}

.amount-input {
  flex: 0 0 92px;
  margin-bottom: 0;
  text-align: center;
  font-size: 1.15rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Picks: pips plus the figure. The pips are quick to read across the table,
   the figure is the one that is unambiguous. */
.pick-count {
  display: flex;
  align-items: center;
  gap: var(--s1);
  font-variant-numeric: tabular-nums;
}
.pick-pip {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--sunk);
}
.pick-pip.is-on { background: var(--role-live); border-color: var(--role-live); }
.pick-number {
  margin-left: var(--s1);
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.picks-line {
  font-size: 0.92rem;
  color: var(--ink-muted);
  margin: 0 0 var(--s3);
}
.picks-entry { margin-right: var(--s3); font-variant-numeric: tabular-nums; }
.picks-entry strong { color: var(--ink); }

/* What the command was understood to mean. Larger than body text because it is
   the thing being checked, and the only defence against a typo becoming a
   score. */
.command-preview {
  font-size: 1.05rem;
  line-height: 1.45;
  font-variant-numeric: tabular-nums;
}

/* Room around it. It is the one card on the screen that has to be read rather
   than filled in, and it was previously crowded by the form above it. */
.command-check {
  padding: var(--s5) var(--s4);
  margin-bottom: var(--s5);
}

/* The command box, folded away ---------------------------------------------
   It is the SECOND way to enter a round; the form is the default. Folded, it
   stops competing with the form for the lower half of the screen, and open it
   gets the full width and proper padding it never had. <details> so it works
   with no scripting, and so the server can render it `open` whenever there is
   typed text to come back to. */
.command-box { padding: 0; }
.command-box > summary {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--tap);
  padding: var(--s3) var(--s4);
  font-weight: 600;
  font-size: 0.98rem;
  color: var(--ink);
  cursor: pointer;
  /* Both halves of hiding the default marker: Firefox reads list-style,
     WebKit needs its own pseudo-element. */
  list-style: none;
}
.command-box > summary::-webkit-details-marker { display: none; }
/* Our own, which points down once it is open. No transition — the file already
   keeps motion to the one thing that means something. */
.command-box > summary::before {
  content: "";
  flex: none;
  width: 0;
  height: 0;
  border-left: 6px solid currentColor;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}
.command-box[open] > summary::before {
  transform: rotate(90deg);
}
.command-body {
  padding: var(--s4);
  border-top: 1px solid var(--line);
}
.command-body input[type="text"] { width: 100%; }
/* The syntax examples are inline <code> chips with a border, and an inline
   border does not push the line box apart — so at the default line height the
   chip on one line struck through the words on the next. Room, rather than
   dropping the border. */
.command-body .small { line-height: 2; }

code {
  font-size: 0.85rem;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--sunk);
  border: 1px solid var(--line);
  color: var(--ink);
}

/* The Daily Hand ------------------------------------------------------------
   CHALLENGE-SPEC. No new palette: every colour here is a token the scorer
   already defined, so the challenge and the scorer read as one app. */

/* A link that has to look and measure like the primary button beside it. */
.button-link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: var(--s2) var(--s4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--sunk);
  color: var(--ink);
  text-decoration: none;
  font-size: 1rem;
}
a.primary.button-link {
  background: var(--role-standings);
  border-color: var(--role-standings);
  color: var(--on-accent);
  font-weight: 700;
  min-height: 52px;
  font-size: 1.05rem;
}

/* Whether they have played, said once and unmistakably. A hub that makes you
   work it out is a hub that gets opened twice. */
.hub-state {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 1rem;
  font-weight: 600;
  padding: var(--s3) 0 0;
}
.hub-done   { color: var(--ink); }
.hub-open   { color: var(--ink); }
.hub-missed { color: var(--ink-muted); }

/* Bidding: every legal bid as its own target, so choosing is one tap and there
   is no number field to mistype. A hand is at most ten cards, so eleven
   choices always fit. */
/* THE BID IS A DROPDOWN AND A CONFIRM. It was one button per number: a single
   tap committed the bid with no chance to pull back — the same mis-tap the
   two-tap card rule exists to prevent — and eleven buttons ate the screen on a
   ten-card hand. Choosing a number now does nothing until it is confirmed. */
.bid-select {
  font-size: 1.25rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border-color: var(--role-standings);
  margin-bottom: var(--s2);
}
.bid-note {
  margin-bottom: var(--s3);
  border-left: 3px solid var(--role-standings);
  padding-left: var(--s2);
}

/* A card in the trick, with who played it under it. */
.trick-slot { display: flex; flex-direction: column; align-items: center; gap: var(--s1); }
.trick-slot .pcard { --pcard-w: 72px; }
.trick-who { font-size: 0.75rem; color: var(--ink-muted); }

/* The hand, laid out to be TAPPED rather than fanned: an overlapped card is a
   card whose tap target is mostly underneath its neighbour. */
.hand-choices { gap: var(--s2); }
.pcard-button {
  display: block;
  padding: 0;
  border: none;
  background: none;
  min-height: 0;
  border-radius: calc(var(--pcard-w, 84px) * 0.06);
  cursor: pointer;
}
.hand-choices .pcard { --pcard-w: 84px; display: block; }
.card-play { display: inline-block; margin: 0; }
.pcard-button:focus-visible {
  outline: 3px solid var(--role-standings);
  outline-offset: 2px;
}
/* Not playable because suit must be followed. Dimmed AND inert — never hidden,
   because seeing the whole hand is part of reading the position. */
.pcard-button.is-unplayable {
  opacity: 0.4;
  cursor: not-allowed;
  filter: grayscale(0.6);
}

/* The suit pip, beside the word and in the suit's own colour. Never instead of
   the word: the symbol alone is smaller than a novice wants to squint at. */
.pip { font-size: 1.15em; line-height: 1; }
.pip-red { color: #e8837a; }

/* A practice hand, flagged unmissably on every screen it appears on. It must
   never be mistakable for the day's hand that counts. */
.practice-flag {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--role-safe);
  border-left: 4px solid var(--role-safe);
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3);
  margin: 0 0 var(--s3);
}

/* One sentence saying what this is, for a member who has never seen it. */
.hub-intro {
  font-size: 1rem;
  color: var(--ink);
  margin: 0 0 var(--s3);
}

/* THE NEXT ACTION, in words, as an instruction. Not muted — it is the thing to
   read. Half the IPL Braz players lost interest because they could not work out
   how to make a prediction; this line is the fix for that, applied everywhere. */
.next-action {
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 var(--s2);
}

/* THE PLAY PANEL'S NARRATION, under the cards rather than above them. Cards
   first was the whole change — on a 375px phone the heading, the instruction and
   the follow-suit note between them pushed the member's own hand off the bottom
   of the screen. These are training wheels for the first two hands, so they read
   as a footnote to the cards instead of as a preamble to them. */
.card-hand .hand-note {
  margin: var(--s2) 0 0;
  font-size: 0.88rem;
  font-weight: 500;
}
.card-hand .hand-note:last-child { margin-bottom: 0; }
/* The cards are the first thing in the box now, so the box's own top padding is
   all the room the lift needs. */
.card-hand .hand-choices { padding-top: 0; }

/* "Keep this on your phone" — one platform's steps, dismissible for good. */
.install-helper { border-left-color: var(--role-safe); }
.install-steps {
  padding-left: var(--s5);
  margin: 0 0 var(--s3);
  font-size: 0.98rem;
  line-height: 1.6;
}
.install-steps li { margin-bottom: var(--s2); }

/* THE IN-APP BROWSER RESCUE, and the member's own link wherever it is shown.
 *
 * A credential on a screen. It gets the plainest treatment in the app: a
 * monospaced face, the WHOLE string wrapped rather than truncated, and
 * `user-select: all` so one tap selects it. An ellipsis in the middle of a token
 * is a link that cannot be read off the screen, and reading it off the screen is
 * the fallback that every other route to it depends on.
 */
.install-rescue { border-left-color: var(--role-status); }
.link-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
  word-break: break-all;
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3);
  margin: 0 0 var(--s3);
  -webkit-user-select: all;
  user-select: all;
}

/* The QR code. ALWAYS ON WHITE, whatever the surface — a code drawn on the felt
 * does not scan, and this one sits on a dark green card. The name goes above it
 * because a member with a tablet and a phone open is looking at two of these. */
figure.qr {
  /* A QR CODE THAT IS NOT BLACK ON WHITE DOES NOT SCAN. Every other surface in
     this app can survive being repainted a bit; this one stops working. Same
     armour as the card face: the figure states that it is a light surface and
     the darkening pass leaves it alone. */
  color-scheme: only light;
  margin: 0 0 var(--s3);
  padding: var(--s3);
  border-radius: var(--radius-sm);
  background: #ffffff;
  max-width: 280px;
}
figure.qr svg { display: block; width: 100%; height: auto; }
figure.qr .qr-name {
  font-size: 0.9rem;
  font-weight: 700;
  color: #16211a;
  text-align: center;
  margin: 0 0 var(--s2);
}
figure.qr:not(:has(svg)) { display: none; }
.reveal[hidden] { display: none; }

/* `hidden` MUST WIN, everywhere. A class that sets `display` beats the browser's
 * own `[hidden] { display: none }`, so a button marked hidden in the markup is
 * drawn anyway — which is how "Show it again" appeared beside a link that had
 * not been hidden yet. */
[hidden] { display: none !important; }

/* The end-of-hand summary, revealed a seat at a time. */
.summary-line { font-size: 1.05rem; }
.summary-score { font-size: 1.05rem; }
.summary-score strong { font-size: 1.35rem; }
.summary-seats { list-style: none; padding: 0; margin: 0; }
.summary-seat {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.summary-seat:last-child { border-bottom: none; }
.summary-seat[hidden] { display: none; }
.summary-name { font-weight: 600; flex: 1 1 auto; min-width: 0; }
.summary-verdict { font-size: 0.85rem; color: var(--ink-muted); }
.summary-seat.is-made .summary-verdict { color: var(--ink); font-weight: 600; }
.summary-seat.is-me { background: rgb(212 168 67 / 0.08); border-radius: 6px; }
@keyframes summary-arrive {
  from { transform: translateY(6px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.summary-seat.is-arriving { animation: summary-arrive 200ms ease-out; }
@media (prefers-reduced-motion: reduce) {
  .summary-seat.is-arriving { animation: none; }
}

/* A card named in TEXT is a card nobody reads. The deck is built and the sprites
   are addressable, so wherever a played card is mentioned it is drawn. */
.card-in-text {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  vertical-align: -0.35em;
}
/* A .pcard is sized by width and an aspect-ratio, and BOTH are ignored on an
   inline box — so every card drawn in text was rendering 0x0. The faces were in
   the markup and simply were not there on screen, which is why the ledger looked
   like it had tiny cards in it: it had none. */
.card-in-text .pcard { --pcard-w: 24px; display: inline-block; }
.history .card-in-text .pcard { --pcard-w: 22px; }

/* THE HAND'S LEDGER, scoped so the scorer's round history is untouched.
   Two complaints, one cause: the cards were 22px and every column reserved
   6.4rem, so roughly a card's width of each gap was carrying nothing. The card
   is 40px now (+82%) and the column is sized to it. */
.history-hand .card-in-text .pcard { --pcard-w: 36px; }
/* Room for the leader's rule, which hangs 8px below the card's box. */
.history-hand td { padding-bottom: 16px; }
.history-hand th,
.history-hand td {
  min-width: 0;
  width: 3rem;
  padding-left: 1px;
  padding-right: 1px;
  text-align: center;
}
/* THE TRICK COLUMN. Two things from M6.6:

   ONE FONT. The `Trick` label and the numbers were in Caveat while the player
   names beside them were not, so the first column read as belonging to a
   different table. Both now use the body face, like the names.

   AN INVERTED BADGE. Reverse text on a solid fill, so each row's number is
   found rather than read. Kept TIGHT TO THE NUMERAL rather than filling the
   cell — an inverted block at 24px would dominate the row. */
.history-hand th:first-child,
.history-hand .history-rd {
  width: 1.9rem;
  min-width: 1.9rem;
  padding-left: 0;
  padding-right: 0;
  text-align: center;
  /* The body face, not the handwriting one. */
  font-family: inherit;
}
.history-hand th:first-child {
  font-size: 0.72rem;
  font-weight: 600;
}
.history-hand .history-rd { padding-bottom: 16px; }
.history-hand .history-rd span {
  display: inline-grid;
  place-items: center;
  min-width: 1.45rem;
  height: 1.45rem;
  padding: 0 0.2rem;
  border-radius: 0.35rem;
  background: var(--paper-ink);
  color: var(--paper);
  font-size: 0.95rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* THE LEDGER REACHES THE EDGES OF ITS CARD. Six seats at 38px cards need more
   width than the card's own padding leaves, and a six-handed table is the common
   case that should not have to be dragged sideways to read. Eight still scrolls,
   which a ledger may legitimately do — inside its own box, never taking the page
   with it. */
.card-history .table-scroll {
  margin-inline: calc(var(--s4, 16px) * -1);
}
/* Centred in the wider box, so reaching the edges buys width without the ledger
   sitting flush against them. Padding on the scroller itself would have counted
   towards its scrollWidth and made a table that fits look like one that does
   not. */
.card-history .history-hand { margin-inline: auto; }
.history-hand th {
  font-size: 0.72rem;
  white-space: nowrap;
}

/* The two marks. Deliberately different KINDS of mark — a ring around the card
   and a rule beneath it — so a card that both led and won wears both and reads
   as intentional rather than as one style losing to the other.

   THE COLOURS SWAPPED IN M6.4: the winner is RED and the leader is BRASS. Mufi
   asked for the winner in red, and putting both in red would have left shape as
   the only difference between them.

   The first pass was too faint to see: a 2px border and a 3px inset rule read as
   almost nothing at 36px. The ring is 3px with a soft halo and real corner
   radius so it reads as a ring rather than a border, and the rule is 5px. Both
   sit OUTSIDE the card's own edge — neither may overwhelm the face. */
.history-hand .card-in-text {
  display: inline-block;
  padding: 3px;
  border-radius: 9px;
  border: 3px solid transparent;
  line-height: 0;
}
/* THE WINNER'S RING IS GREEN. Mufi: "The card that is winning a green would
   probably do better than the red circle." Same mark, same weight — only the
   colour moved, and it moved on every surface at once. */
.history-hand .card-in-text.is-won {
  border-color: var(--paper-won);
  box-shadow: 0 0 0 2px rgb(15 92 34 / 0.3);
}
/* LED — a rule BELOW the card, clear of its bottom edge. Inset, it read as part
   of the card's own border and Mufi did not see it as a mark at all. Drawn under
   the box with a gap, in blue, it is unmistakably a separate thing. */
.history-hand .card-in-text.is-led {
  position: relative;
}
.history-hand .card-in-text.is-led::after {
  content: "";
  position: absolute;
  /* THE FULL WIDTH OF THE CARD. At 15% inset it was a short dash under a 36px
     card — 25px of 4px rule, which is what "hardly noticeable" was describing. */
  left: 0;
  right: 0;
  /* SEVEN PIXELS OF AIR BELOW THE CARD'S OWN EDGE — 1-2mm at this scale, which
     is what was asked for. Measured from the card in a browser, not inferred
     from this number, because `bottom` does not resolve against the card: it
     resolves against this box's PADDING BOX, which sits 3px lower. -7px here
     put the rule 3px from the card and read as part of its edge, which is the
     complaint. -11px is 7px of gap + 7px of rule - the 3px of padding. */
  bottom: -11px;
  height: 7px;
  border-radius: 3px;
  background: var(--paper-led);
}
/* A card that both led and won keeps both: the ring around it, the rule beneath
   it. Different kinds of mark, so neither hides the other, and the rule sits at
   the same distance from the card either way. */
.history-hand .card-in-text.is-won.is-led::after { bottom: -11px; }

.history-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2, 8px) var(--s4, 16px);
  margin-top: var(--s3, 12px);
  color: var(--paper-muted);
  font-size: 0.85rem;
}
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-mark { display: inline-block; width: 16px; height: 16px; flex: none; }
/* The swatches carry the same weights as the marks themselves, or the legend is
   explaining something the reader cannot find. */
.legend-won {
  border: 3px solid var(--paper-won);
  border-radius: 6px;
}
.legend-led {
  height: 7px;
  border-radius: 3px;
  background: var(--paper-led);
  align-self: flex-end;
  margin-bottom: 4px;
}

/* The spare key, kept modest. The front door is the URL. */
.lost-link {
  text-align: center;
  margin: var(--s5) 0 0;
  font-size: 0.92rem;
}
.lost-link a { color: var(--ink-muted); }

/* The trick that just finished, on its way out. */
.taken-card { opacity: 1; transition: opacity 220ms ease-in 420ms; }

/* The published bot rules. Folded, like the command box, and for the same
   reason: it must be there, and it must not crowd the hand. */
.bot-rules h3 {
  font-size: 0.92rem;
  margin: var(--s4) 0 var(--s1);
  color: var(--role-standings);
}
.rules-list { padding-left: var(--s5); margin: 0 0 var(--s3); }
.rules-list li { margin-bottom: var(--s1); }

/* Set when polling has been failing — the numbers on screen may be behind. */
body.is-stale #live-region {
  opacity: 0.55;
  transition: opacity 0.3s;
}
body.is-stale #live-region::before {
  content: "Reconnecting… scores may be out of date";
  display: block;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--role-live);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3);
  margin-bottom: var(--s3);
  font-size: 0.85rem;
}

/* Landscape on a phone: very little height, so give it back. The table is
   often laid out sideways on a coffee table and the phone follows. */
@media (max-height: 480px) and (orientation: landscape) {
  .container { padding-top: var(--s3); }
  .topbar { padding: var(--s2) var(--s4); }
  h1 { font-size: 1.25rem; }
  .card { padding: var(--s3); margin-bottom: var(--s3); }
  button.primary { min-height: var(--tap); }
}

/* Off-screen but read aloud. The tab's icon-like label needs a full sentence for
   a screen reader without putting one on the tab itself. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* THE ROUND'S BIDDING, in the header. Reverse text on a solid fill — Mufi's
   point was that another grey line would not be read, and he asked for it "in
   reverse text so that it's very obvious".

   OVERBID red, UNDERBID brass. NOT blue: blue means "led the trick" from M6.6
   and one colour carries one meaning. */
/* TWO STATUSES, ONE LINE. The trick number on the left, the round's bidding on
   the right, both the same size. Mufi asked for the second "towards the right
   side or right-aligned on the same line as the trick number".

   It WRAPS rather than squeezes. At 320px with an eight-word IMPOSSIBLE chip
   there is not room for both on one line, and a status compressed to
   unreadability is worse than a status on the next line. */
.head-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s1) var(--s2);
  margin-bottom: var(--s2);
}

/* THE STATUS CHIP — a brass pill with dark reverse text, shared by the trick
   number in the header and the "Bot 2 takes it" banner under the table. ONE
   rule, used by both, because Mufi's note was that they should read as the same
   thing and two rules that agree today are two rules that stop agreeing.
   .table-banner in cards/card.css adds only its shadow and its animation. */
.status-chip {
  display: inline-block;
  padding: 3px var(--s2);
  border-radius: 999px;
  background: var(--role-standings);
  color: var(--on-accent);
  font-weight: 700;
  white-space: nowrap;
}
/* AS A HEADING. It is still an <h1> — the screen keeps its heading and its
   outline — but it is a status, not a page title, and 1.5rem of white was the
   loudest thing on a screen whose subject is the table below it. */
h1.status-chip {
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  margin: 0;
  font-variant-numeric: tabular-nums;
}
h1.status-chip strong { font-size: 1rem; }

.bid-total {
  display: inline-block;
  padding: 2px var(--s2);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--on-accent);
  font-variant-numeric: tabular-nums;
}
.bid-total strong { font-size: 1rem; }
/* BOTH STATES ARE RED from M6.9b. M6.6 gave underbid the brass fill so the two
   would differ by colour as well as by word; on the deployed page Mufi found the
   brass did not catch the eye — "that catches the eye much better than the
   current yellow reverse text". The WORD carries the distinction, as it always
   did. The colour's only job is to say the bidding is lopsided, look.

   IT IS DELIBERATELY NOT THE PLATE'S RED. --role-status #e8837a is the app's
   soft warning red and this is a TABLE state; --mark-red #ff5f57 is the
   traffic-light red on a seat plate and that is a PLAYER state. They are
   different values of the same hue — the same same-hue-own-value approach as
   --paper-won and --paper-led — so a chip in the header cannot be read as
   somebody's tally. Measured: 5.77:1 for the dark text on the chip, and the chip
   is 5.77:1 against the page behind it. */
.bid-total-over,
.bid-total-under { background: var(--role-status); }
/* UNREACHABLE BY THE RULES. The dealer constraint forbids the total equalling
   the cards dealt, so this state means something upstream has failed. It is
   deliberately alarming rather than a calm chip, and it says so in words. */
.bid-total-equal {
  background: var(--role-status);
  color: var(--ink);
  border: 2px dashed var(--ink);
  font-weight: 800;
  text-transform: uppercase;
}

/* ==========================================================================
   ABOVE PHONE WIDTH — M7.2
   ==========================================================================

   Everything above this line is the PHONE, and none of it has changed. The app
   was built at 375 and 390 and that is still where its geometry is measured;
   what follows only applies once the screen is wider than the phone layout can
   use, and it is written so that it can never come into play below that.

   THE LINE IS 672px, and it is not a round number for the look of it. The
   reading column is `max-width: 640px` with 16px of padding on each side, and
   `.table-felt` reaches the container's edge with `100% + 32px`. So 672 is the
   exact width at which the column stops growing and the felt stops being the
   width of the screen. Below it, nothing here matches and the phone rules are
   the whole story; above it, the felt would otherwise go on inflating with
   nothing to stop it — which is how a Galaxy Z Fold ended up with a 640 x 833
   ellipse of empty green and the member's own hand pushed off the bottom.

   The one thing a bigger screen is FOR is bigger cards. That is the test
   applied to each rule below, and the numbers are in REPORT.md: at 900 x 1100 —
   the Fold this round was called for — the table card goes from the phone's
   68px to 94px and the member's hand from three wrapped rows of 84px cards to
   one row of 104px. */

/* HOW MUCH VERTICAL ROOM IS NOT THE TABLE'S.

   The felt is sized from the height as well as the width, so that what is under
   it is on screen rather than under the fold — and that needs a figure for
   everything on the play screen that is not felt.

   400px, and it is what is actually there, measured on the rendered page rather
   than guessed: 191px of top bar and heading above the felt, its own 16px of
   margin, and the hand panel down to the BOTTOM OF THE MEMBER'S CARD ROW. The
   two notes under the cards are deliberately not in it. They are narration —
   "tap a card to choose it" — and a member who has to scroll a line to read
   them has lost nothing, where a member who has to scroll to see their own hand
   has lost the game. That is the same order of priority M6.9b settled when it
   put the cards above the instructions. */
:root { --felt-room: 400px; }

@media (min-width: 672px) {
  /* THE READING COLUMN STOPS GROWING — it already did, at 640px, and it stays
     there. A paragraph set across 900px is the thing that makes screen 2 ugly,
     and the fix for it is to not do that. */

  /* THE TABLE COLUMN. The play screen is not a page of text; it is a felt with
     a hand under it, and both are better wider. Nothing else uses this. */
  .container-table { max-width: 960px; }

  /* THE HAND IN ONE ROW. On the phone a ten-card hand wraps to four rows of
     84px cards and most of it is below the fold. There is width here instead,
     so the cards take it: every card an equal share of the row, so the whole
     hand is one line whenever the room is there.

     THE COUNT COMES FROM THE TEMPLATE, as `--hand-n` — the same thing
     `--pcard-count` already does for the bid screen's fan. So this is one
     expression that is right at three cards and at ten, rather than a
     breakpoint on hand size that would be wrong at some particular number. */
  .hand-choices {
    /* The row measures ITSELF, so the card width below can be a share of the
       row rather than of whatever box each card happens to sit in. Same
       technique as `.table-felt`, and for the same reason: a percentage inside
       a flex item resolves against the item, which is exactly the wrong box. */
    container-type: inline-size;
  }
  .hand-choices > .card-play,
  .hand-choices > .pcard-button { flex: 0 0 auto; }
  .hand-choices .pcard {
    /* AN EQUAL SHARE OF THE ROW, floored and capped.

       The floor is the TAP TARGET. With the drawer open at 768px the column is
       356px wide and nine equal shares would be 32px — a card too narrow to hit
       with a thumb. Below the floor the cards stop shrinking and the row wraps
       instead, every card still the same width, which is what the phone does
       already. The cap stops a three-card hand being drawn enormous. */
    width: clamp(64px,
                 calc((100cqw - (var(--hand-n, 10) - 1) * var(--s2))
                      / var(--hand-n, 10)),
                 104px);
  }

  /* THE FELT, THE TAB AND THE DRAWER are all in `cards/card.css`, which every
     screen that has them loads AFTER this file — so their wide rules have to
     live there or the phone rules they are meant to replace would win on source
     order. Same breakpoint, same reasoning; see the block at the end of it. */
}

/* THE COMMAND CENTRE'S DOOR, on the hub, for the one member who has one.
   M7.3: Mufi had to type the address and got it wrong. Deliberately quiet — a
   door, not a feature of the game — and absent from the page entirely for
   everybody else rather than hidden in it. */
.admin-door {
  margin: var(--s5) 0 0;
  text-align: center;
  font-size: 0.85rem;
}
.admin-door a {
  color: var(--ink-muted);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  /* A link in a paragraph is still something a thumb has to hit. */
  display: inline-block;
  min-height: var(--tap);
  line-height: var(--tap);
}
