/* ===========================================================================
   quiz.css — components specific to the quiz surface.
   Loaded after tokens.css by all three quiz views. Every size scales off
   var(--scale), so display.css rescales these for the projector too.

   No raw colours below this line — scripts/check-ui-tokens.js enforces it.
   =========================================================================== */

/* .conn moved to tokens.css — it is used by four pages across BOTH surfaces,
   and living here meant the awards big screen, which loads awards.css instead,
   rendered it with no styles at all: "offline" as 33px lowercase body text
   with no status dot, in the corner of a projector. @keyframes pulse moved
   with it because .conn.connecting is what uses it. */

/* ===== Module A — multiple choice ===== */

.options { display: grid; gap: 0.7rem; grid-template-columns: 1fr; width: 100%; }
@media (min-width: 620px) { .options { grid-template-columns: 1fr 1fr; } }

.option { display: flex; align-items: center; gap: 0.8rem; width: 100%;
  padding: 1rem 1.1rem; border-radius: 12px; cursor: pointer; text-align: left;
  background: var(--bg-card); border: 1.5px solid var(--border-strong);
  color: var(--text-main); font-size: var(--fs-base); font-weight: 500;
  transition: border-color 0.14s, background 0.14s, transform 0.1s; }
.option:hover { border-color: var(--sport-ring); background: var(--sport-soft); }
.option:active { transform: scale(0.99); }
.option.selected { border-color: var(--sport); background: var(--sport-soft);
  color: var(--sport-on-soft); font-weight: 700; }
.option.correct { border-color: var(--success);
  background: color-mix(in srgb, var(--success) 16%, var(--bg-card));
  color: color-mix(in srgb, var(--success) 75%, var(--text-main)); font-weight: 700; }
.option.wrong { border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, var(--bg-card));
  color: color-mix(in srgb, var(--danger) 75%, var(--text-main)); }
.option:disabled { cursor: default; }

.option-key { flex: none; width: 2em; height: 2em; border-radius: 8px;
  display: grid; place-items: center; font-family: 'Oswald', sans-serif;
  font-weight: 700; background: var(--bg-inset); color: var(--text-muted); }
.option.selected .option-key, .option.correct .option-key { background: var(--sport);
  color: var(--on-sport); }

/* ===== Module F — the buzzer ===== */

.buzzer { display: grid; place-items: center; width: min(72vw, 300px);
  aspect-ratio: 1; margin: 1.5rem auto; border-radius: 50%; border: none;
  background: var(--sport-grad); color: var(--on-sport); cursor: pointer;
  font-family: 'Oswald', sans-serif; font-size: var(--fs-xl);
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  box-shadow: 0 0 0 10px var(--sport-ring), 0 18px 44px var(--sport-ring);
  transition: transform 0.08s, box-shadow 0.15s, filter 0.15s;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
.buzzer:active { transform: scale(0.94); box-shadow: 0 0 0 4px var(--sport-ring); }
.buzzer:disabled { cursor: not-allowed; filter: grayscale(0.8) brightness(0.5);
  box-shadow: none; }
/* Won and lost are RESULTS, and the button is disabled in both cases — so the
   dimming above must not apply, or the team that actually won the buzz sees a
   grey slab instead of a green one. */
.buzzer.won, .buzzer.lost { filter: none; }
.buzzer.won { background: var(--success); color: var(--on-danger);
  box-shadow: 0 0 0 12px color-mix(in srgb, var(--success) 30%, transparent); }
.buzzer.lost { background: var(--bg-inset); color: var(--text-faint); box-shadow: none; }

/* ===== Module E — the wager ===== */

.wager-box { display: flex; flex-direction: column; gap: 0.9rem; }
.wager-amount { font-family: 'Oswald', sans-serif; font-size: var(--fs-2xl);
  font-weight: 700; text-align: center; color: var(--sport-ink);
  font-variant-numeric: tabular-nums; line-height: 1; }
.wager-range { width: 100%; accent-color: var(--sport); }
.wager-limits { display: flex; justify-content: space-between;
  font-size: var(--fs-xs); color: var(--text-faint); }

/* ===== Module G — spatial tap ===== */

.tap-wrap { position: relative; display: inline-block; max-width: 100%;
  line-height: 0; border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--border-color); touch-action: manipulation; }
.tap-img { display: block; max-width: 100%; height: auto; cursor: crosshair;
  user-select: none; -webkit-user-drag: none; }
/* Markers are positioned from normalised 0..1 coords, so they land in the same
   spot on a phone and on the projector regardless of rendered size. */
.tap-marker { position: absolute; width: 22px; height: 22px; margin: -11px 0 0 -11px;
  border-radius: 50%; border: 2px solid var(--on-sport); background: var(--sport);
  pointer-events: none; box-shadow: 0 2px 8px var(--sport-ring); }
.heat-dot { position: absolute; width: 20px; height: 20px; margin: -10px 0 0 -10px;
  border-radius: 50%; pointer-events: none; opacity: 0.85;
  background: var(--team-color, var(--team-default));
  border: 2px solid var(--bg-main); }
.tap-target { position: absolute; border-radius: 50%; pointer-events: none;
  border: 3px dashed var(--success);
  background: color-mix(in srgb, var(--success) 18%, transparent); }

/* ===== Participant play view ===== */

/* height, not min-height — and dvh, not vh.
   On a phone 100vh is the LARGE viewport: the height the page would have if the
   address bar were hidden. A 100vh column is therefore taller than what the
   captain can actually see, and the first thing pushed off the bottom is
   .play-foot — the only place the handset says what it wants of you ("Answer
   now", "Answer locked in", "Time up"). Measured at 375x812 the page ran to
   862 and the status line sat below the fold with the bar still showing.
   A fixed height with an internally scrolling body pins the score at the top
   and the status line at the bottom, whatever the round puts between them. */
.play { height: 100vh; height: 100dvh; display: flex; flex-direction: column; }
.play-head { flex: none; display: flex; align-items: center; justify-content: space-between;
  gap: 0.8rem; padding: 0.7rem 1rem; border-bottom: 1px solid var(--border-color);
  background: var(--topbar-bg); z-index: 5; }
.play-team { display: flex; align-items: center; gap: 0.5rem; font-weight: 700;
  font-size: var(--fs-base); }
.play-score { font-family: 'Oswald', sans-serif; font-weight: 700;
  font-size: var(--fs-md); color: var(--sport-ink);
  font-variant-numeric: tabular-nums; }
/* "safe center" is the whole trick. A centred flex column whose content is
   taller than the box overflows in BOTH directions, and the part above the top
   edge is unreachable by scrolling — so on a long round the question number
   would be permanently gone. "safe" falls back to flex-start the moment the
   content stops fitting. The unprefixed value is left in front for anything
   that does not understand it. */
.play-body { flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column;
  justify-content: center; justify-content: safe center;
  gap: 1.2rem; padding: 1.25rem; text-align: center; }
.play-prompt { font-size: var(--fs-xl); font-weight: 600;
  line-height: 1.35; text-wrap: balance; }
.play-category { font-size: var(--fs-micro); text-transform: uppercase;
  letter-spacing: 0.16em; font-weight: 700; color: var(--sport-ink); }
.play-status { font-size: var(--fs-sm); color: var(--text-muted); }
/* env() is not decoration here: index.html asks for viewport-fit=cover, so on a
   notched phone the page really does extend under the home indicator, and
   without this the status line is the thing sitting beneath it. */
.play-foot { flex: none; border-top: 1px solid var(--border-color);
  padding: 0.8rem 1rem calc(1.4rem + env(safe-area-inset-bottom, 0px)); }

/* The "nothing to do yet" state — most of a participant's evening is spent
   here, so it has to look intentional rather than broken. */
.waiting { display: flex; flex-direction: column; align-items: center; gap: 1rem;
  color: var(--text-faint); }
.waiting-mark { width: 64px; height: 64px; border-radius: 50%;
  border: 2px solid var(--border-strong); border-top-color: var(--sport);
  animation: spin 1.4s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.verdict { font-family: 'Oswald', sans-serif; font-size: var(--fs-xl);
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; }
.verdict.correct { color: var(--success); }
.verdict.wrong { color: var(--danger); }

/* ===== Master dashboard ===== */

/* ===== The control room fills the screen and never scrolls =====
   Three short cards floating at the top of a tall window left the bottom half
   of the screen empty while the team list ran off the end of a card. A screen
   driven in front of a room should not be scrolled at all: the columns take
   the full height and each one scrolls inside itself if it must. */
.fill-screen { display: flex; flex-direction: column; height: 100vh; }
/* max-width: none, deliberately. .wrap caps at 1180px because that is a
   comfortable measure for READING, and every other page here is read. This one
   is operated: three dense columns at once. Capped, a 1600px screen left the
   sides empty AND squeezed the middle column to 463px — narrow enough that the
   team rows, the thing being clicked, overflowed it sideways. */
.fill-screen > .wrap { flex: 1; min-height: 0; max-width: none; }

.master-grid { display: grid; grid-template-columns: 1fr; gap: 1.25rem;
  height: 100%; min-height: 0; }
@media (min-width: 1000px) { .master-grid { grid-template-columns: 300px 1fr 320px; } }
/* min-height:0 is the part that actually matters — without it a grid child
   refuses to shrink below its content and the overflow never engages. */
/* overflow-x: hidden matters as much as the y. These cards are scroll
   containers, and a hover tooltip is absolutely positioned — so even sitting
   invisible at opacity 0 it counts toward scrollWidth and hung a horizontal
   scrollbar under the panel with no element actually out of bounds. */
.master-grid > .card { min-height: 0; overflow-y: auto; overflow-x: hidden; }

.q-list { display: flex; flex-direction: column; gap: 0.3rem; }
.q-round { font-size: var(--fs-micro); text-transform: uppercase;
  letter-spacing: 0.1em; font-weight: 700; color: var(--text-faint);
  padding: 0.7rem 0.5rem 0.3rem; }
.q-item { display: flex; align-items: center; gap: 0.6rem; width: 100%;
  padding: 0.55rem 0.7rem; border-radius: 9px; cursor: pointer; text-align: left;
  background: none; border: 1px solid transparent; color: var(--text-muted);
  font-size: var(--fs-sm); }
.q-item:hover { background: var(--bg-subtle); color: var(--text-main); }
.q-item.active { background: var(--sport-soft); border-color: var(--sport-ring);
  color: var(--sport-on-soft); font-weight: 600; }
.q-num { flex: none; font-family: 'Oswald', sans-serif; font-weight: 600;
  color: var(--text-faint); min-width: 1.6em; }
.q-item.active .q-num { color: var(--sport-ink); }

/* The Master's private half. Deliberately marked so it is obvious at a glance
   that this pane must never face the room. */
.secret-panel { border: 1px solid var(--warning); border-radius: var(--radius);
  background: color-mix(in srgb, var(--warning) 8%, var(--bg-card));
  padding: 1.1rem 1.2rem; display: flex; flex-direction: column; gap: 0.7rem; }
.secret-head { font-size: var(--fs-micro); text-transform: uppercase;
  letter-spacing: 0.14em; font-weight: 800; color: var(--warning); }
/* The answer is read off this screen constantly, at a glance, by someone
   mid-sentence. One step up from a card title is not enough — it is the
   largest thing in the column now, which is what its job deserves. */
.secret-answer { font-family: 'Oswald', sans-serif; font-size: var(--fs-xl);
  font-weight: 700; color: var(--text-main); line-height: 1.15; }
.secret-note { font-size: var(--fs-sm); color: var(--text-muted); }

/* One line, scrolling if it must. Wrapped onto two rows it read as a map of
   the state machine rather than as "you are here", and ate the space the
   answer now occupies. */
.phase-bar { display: flex; flex-wrap: nowrap; gap: 0.3rem;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; }
.phase-bar::-webkit-scrollbar { display: none; }
.phase-step { flex: none; }
.phase-step { padding: 0.3rem 0.6rem; border-radius: 7px;
  font-size: var(--fs-micro); font-weight: 600;
  background: var(--bg-inset); color: var(--text-faint); border: 1px solid var(--border-color); }
.phase-step.active { background: var(--sport); color: var(--on-sport); border-color: var(--sport); }
.phase-step.past { color: var(--text-muted); border-color: var(--border-strong); }

.ctl-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.judge-row { display: grid; grid-template-columns: 1fr auto auto; align-items: center;
  gap: 0.6rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border-color); }
.judge-row:last-child { border-bottom: none; }
.judge-team { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.judge-name { font-weight: 600; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
/* What the team actually typed. It has to be allowed to SHRINK: a long answer
   was pushing the whole row past the edge of the card and putting a horizontal
   scrollbar under the one panel nobody can afford to scroll mid-question.
   min-width:0 is what lets a flex child go below its content width. */
.judge-answer { font-size: var(--fs-sm); color: var(--text-muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.judge-actions { display: flex; gap: 0.3rem; flex: none; }

.tile-grid { display: grid; gap: 4px; }
.tile-btn { aspect-ratio: 1; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--border-strong); background: var(--bg-inset);
  color: var(--text-faint); font-size: var(--fs-micro); }
.tile-btn.gone { background: var(--sport-soft); border-color: var(--sport-ring);
  color: var(--sport-on-soft); }

.mini-timer { font-family: 'Oswald', sans-serif; font-weight: 700;
  font-size: var(--fs-lg); font-variant-numeric: tabular-nums;
  color: var(--sport-ink); }
.mini-timer.urgent { color: var(--danger); }

.buzz-order { display: flex; flex-direction: column; gap: 0.3rem; }
.buzz-row { display: flex; align-items: center; justify-content: space-between;
  gap: 0.6rem; font-size: var(--fs-sm); }
.buzz-row .lag { color: var(--text-faint); font-variant-numeric: tabular-nums; }
.buzz-row.first { color: var(--success); font-weight: 700; }

/* ===== Auto-scoring proposal ===== */

.prop-row { display: grid; grid-template-columns: 1fr auto auto; align-items: center;
  gap: 0.5rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);
  font-size: var(--fs-sm); }
.prop-row:last-child { border-bottom: none; }
.prop-who { display: flex; flex-direction: column; min-width: 0; }
.prop-given { font-size: var(--fs-xs); color: var(--text-faint);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.prop-verdict { font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em; }
.prop-verdict.correct { color: var(--success); }
.prop-verdict.partial { color: var(--warning); }
.prop-verdict.wrong { color: var(--danger); }
.prop-verdict.none { color: var(--text-faint); }
.prop-points { font-variant-numeric: tabular-nums; font-weight: 700;
  min-width: 3ch; text-align: right; }
.prop-points.plus { color: var(--success); }
.prop-points.minus { color: var(--danger); }

/* A settled question must look settled — the commonest live-show mistake is
   pressing Score twice because nothing obviously happened. */
.scored-note { font-size: var(--fs-xs); color: var(--warning);
  font-weight: 600; }

.score-log { display: flex; flex-direction: column; gap: 0.25rem; }
.score-log-row { display: grid; grid-template-columns: 1fr auto auto; gap: 0.5rem;
  align-items: center; font-size: var(--fs-xs);
  padding: 0.3rem 0; border-bottom: 1px solid var(--border-color); }
.score-log-row:last-child { border-bottom: none; }
.score-log-row.undone { opacity: 0.45; text-decoration: line-through; }
.score-log-row .pts { font-variant-numeric: tabular-nums; font-weight: 700; }

/* The Run panel's controls, split into labelled groups: the next step, the
   clock, and the escapes. They used to be one undifferentiated row of seven
   buttons — including two both labelled "Reveal" — which is a poor thing to
   hand someone who is mid-sentence in front of a room. */
.ctl-groups { display: flex; flex-wrap: wrap; gap: 1.1rem 1.75rem; align-items: flex-start; }
.ctl-groups > div { min-width: 0; }

/* ===== Quiz setup (authoring) =====
   A desk screen: long forms, two columns, read at arm's length. Separate from
   the control room on purpose — that one is a stage screen with one decision
   at a time. */

.setup-grid { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
@media (min-width: 1000px) { .setup-grid { grid-template-columns: 320px 1fr; } }

.round-row { display: flex; align-items: flex-start; gap: var(--sp-3); width: 100%;
  padding: var(--sp-3); border-radius: 9px; cursor: pointer; text-align: left;
  background: none; border: 1px solid transparent; color: var(--text-muted);
  font-size: var(--fs-sm); }
.round-row:hover { background: var(--bg-subtle); color: var(--text-main); }
.round-row.active { background: var(--sport-soft); border-color: var(--sport-ring);
  color: var(--sport-on-soft); }
.round-row .seq { flex: none; font-family: 'Oswald', sans-serif;
  color: var(--text-faint); min-width: 1.4em; }
.round-text { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.round-name { font-weight: 600; }
.round-meta { font-size: var(--fs-micro); color: var(--text-faint); }

/* .field-grid moved to tokens.css — the admin screen uses it too. */

/* A question folds away. A round of ten open editors is unreadable; a round of
   ten one-line summaries is a running order. */
.q-edit { border: 1px solid var(--border-color); border-radius: 10px;
  background: var(--bg-subtle); margin-bottom: var(--sp-3); }
.q-edit[open] { border-color: var(--border-strong); }
.q-edit-head { display: flex; align-items: center; gap: var(--sp-3); cursor: pointer;
  padding: var(--sp-3); font-size: var(--fs-sm); list-style: none; }
.q-edit-head::-webkit-details-marker { display: none; }
.q-edit-head .seq { flex: none; font-family: 'Oswald', sans-serif; color: var(--text-faint); }
.q-edit-title { flex: 1; min-width: 0; font-weight: 600; color: var(--text-main);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-edit-pts { flex: none; font-size: var(--fs-micro); color: var(--text-faint);
  font-variant-numeric: tabular-nums; }
.q-edit-body { display: flex; flex-direction: column; gap: var(--sp-4);
  padding: 0 var(--sp-3) var(--sp-3); border-top: 1px solid var(--border-color);
  padding-top: var(--sp-3); }

.opt-row { display: flex; align-items: center; gap: var(--sp-2); }
.opt-row input[type="text"] { flex: 1; min-width: 0; }
.opt-row input[type="radio"] { flex: none; width: 18px; height: 18px;
  accent-color: var(--sport); }

/* Free-form score adjustment, per team, in the judging row. Narrow on purpose:
   it sits beside the one-click buttons and must not out-shout them. */
input.adjust { width: 4.2rem; flex: none; text-align: center;
  font-variant-numeric: tabular-nums; padding: 0.3rem 0.4rem;
  font-size: var(--fs-sm); }

/* Spreadsheet import. Monospaced, because what goes in here is columns, and
   proportional text makes a misaligned paste impossible to spot. */
.import-box { width: 100%; font-family: ui-monospace, monospace;
  font-size: var(--fs-xs); line-height: 1.5; white-space: pre;
  overflow-x: auto; resize: vertical; }

/* The result stays on screen rather than in a toast: on failure it is a list
   of spreadsheet row numbers, which is something to work from, not to glance
   at and lose. */
.import-status { margin-top: var(--sp-3); padding: var(--sp-3);
  border-radius: 9px; border: 1px solid var(--border-color);
  background: var(--bg-inset); color: var(--text-muted);
  font-family: ui-monospace, monospace; font-size: var(--fs-xs);
  line-height: 1.6; white-space: pre-wrap; max-height: 14rem; overflow-y: auto; }
.import-status.is-error { border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, var(--bg-card));
  color: color-mix(in srgb, var(--danger) 30%, var(--text-main)); }

/* ===== Captain's handset: number, not question =====
   The phone shows which question is up and what the scores are. The question
   itself is on the big screen, because a captain who can read it on a handset
   can paste it into an AI and read the answer back. */
.q-head { display: flex; align-items: baseline; justify-content: center;
  gap: var(--sp-3); flex-wrap: wrap; }
.q-head-round { font-size: var(--fs-micro); text-transform: uppercase;
  letter-spacing: 0.12em; font-weight: 700; color: var(--text-faint); }
.q-head-num { font-family: 'Oswald', sans-serif; font-weight: 700;
  font-size: var(--fs-2xl); line-height: 1; color: var(--sport-ink);
  text-shadow: 0 0 18px var(--sport-glow); }
.play-watch { text-align: center; font-size: var(--fs-sm); color: var(--text-muted); }

/* Every team's score, on the handset. With the question text gone there is
   room for it, and a captain weighing a bonus wants to know where they stand. */
.score-strip { display: flex; flex-direction: column; gap: 0.3rem; width: 100%; }
.score-chip { display: flex; align-items: center; gap: var(--sp-2);
  padding: 0.35rem 0.6rem; border-radius: 8px; background: var(--bg-inset);
  border: 1px solid var(--border-color); font-size: var(--fs-sm); }
.score-chip.is-me { border-color: var(--sport-ring); background: var(--sport-soft); }
.score-chip-name { flex: 1; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.score-chip-pts { font-weight: 700; color: var(--sport-on-soft); }

/* An MCQ with the text withheld is four big letters. They match the letters
   beside the choices on the big screen. */
.option.is-letter { justify-content: center; padding: var(--sp-4) var(--sp-3); }
.option.is-letter .option-key { width: auto; height: auto; background: none;
  font-size: var(--fs-xl); color: var(--sport-ink); }


/* ===== Before doors =====
   Join codes and the big-screen link: needed once, then never again. Folded
   away so they stop holding permanent space on a screen driven mid-sentence,
   but one click from the person who needs them. */
.setup-fold { border-top: 1px solid var(--border-color); padding-top: var(--sp-3); }
.setup-fold > summary { cursor: pointer; list-style: none;
  font-size: var(--fs-micro); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-faint); }
.setup-fold > summary::-webkit-details-marker { display: none; }
/* Literal glyphs, not B8-style escapes: the escape was eaten in transit
   and shipped as the text "B8" beside the heading. A character visible in
   the source cannot be mangled by whatever writes the file. */
.setup-fold > summary::before { content: "▸ "; color: var(--sport-ink); }
.setup-fold[open] > summary::before { content: "▾ "; }
.setup-fold > summary:hover { color: var(--text-main); }
.setup-fold-body { display: flex; flex-direction: column; gap: var(--sp-4);
  padding-top: var(--sp-3); }

/* ===== The control room's colour language =====

   Every control on this screen used to be the same grey pill, so the only way
   to tell "opens fifty phones" from "freezes them" was to read the label — in
   a dark room, mid-sentence, while talking to a hall. The operator's question
   is never "which button is this", it is "what will this do to the room", and
   there are only four answers.

   The PHASE BAR takes the same four, so the state at the top of the screen and
   the button under it always agree. That is what makes it readable at a
   glance: green anywhere on this screen means the phones are open.

   Deliberately not new hues. Green already means "correct" and "won" on the
   quiz screens, amber already means "waiting", gold already means "the thing
   to press" — these carry those same meanings one level up, to the room. */

/* LIVE — the teams can act right now: phones open, clock running. */
.btn.is-live { background: var(--success); color: var(--on-danger);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--success) 30%, transparent); }
.btn.is-live:hover { background: color-mix(in srgb, var(--success) 85%, white); }

/* HOLD — the teams are frozen: locked, paused, time up. */
.btn.is-hold { background: var(--warning); color: var(--on-sport);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--warning) 30%, transparent); }
.btn.is-hold:hover { background: color-mix(in srgb, var(--warning) 85%, white); }

/* GO — the show moves on; nothing changes for the teams. The house gold. */
.btn.is-go { background: var(--sport); color: var(--on-sport); }

/* UNDO — reversing. Quiet at rest and red only on approach, the same rule
   .btn-danger follows: a button that is red before you reach for it reads as
   an error message rather than as a choice. */
.btn.is-undo { background: var(--bg-card); color: var(--text-muted);
  border: 1px solid var(--border-strong); box-shadow: none; font-weight: 600; }
.btn.is-undo:hover { border-color: var(--sport); background: var(--sport-soft);
  color: var(--sport-on-soft); }

/* The phase bar, in the same four. */
.phase-step.active.is-live { background: var(--success); border-color: var(--success);
  color: var(--on-danger); }
.phase-step.active.is-hold { background: var(--warning); border-color: var(--warning);
  color: var(--on-sport); }
.phase-step.active.is-go { background: var(--sport); border-color: var(--sport);
  color: var(--on-sport); }

/* The next step, the step back, and the undo, in one band. The big button keeps
   its own line on a narrow console so the two small ones never end up sharing a
   row with it and looking like alternatives to it. */
.primary-row { display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sp-3); }
.primary-row .btn-lg { flex: 1 1 14rem; }

.undo-bar { display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-3); min-height: 2rem; }
/* Nothing to undo. Present rather than absent, so the row does not appear and
   disappear under the operator's cursor between actions. */
.undo-idle { font-size: var(--fs-micro); color: var(--text-faint);
  font-style: italic; }

/* Pressed from the keyboard. A click has a cursor on it to say where it went;
   the space bar has nothing, so the button says so itself. */
.btn.is-pressed { transform: translateY(1px);
  box-shadow: 0 0 0 3px var(--focus); }
