/* ============ The Realm is in Jeopardy — styling ============ */

/* Realm design tokens, lifted from RealmTools/resource-locator.html so this
   file stays the single source of truth for the palette. */
:root {
  --realm-dark-2: #211f25;
  --realm-dark-3: #2d2b30;
  --realm-dark-4: #38363b;
  --realm-dark-5: #434146;
  --realm-neutral-1000: #0d0d0d;

  --realm-bg: #16141a;
  --realm-card: #38363b;
  --realm-input: #434146;

  --realm-primary-200: #bb86fc;
  --realm-primary-500: #6200ee;
  --realm-secondary-200: #03dac5;
  --realm-accent: #0b99ff;
  --realm-yellow: #edb200;
  --realm-error: #cf6679;

  --realm-text: #f3f2f8;
  --realm-text-secondary: #b8b8b8;
  --realm-text-disabled: #797878;

  --realm-radius-sm: 0.25rem;
  --realm-radius-lg: 0.5rem;
}

:root {
  /* The board keeps its deep blue and gold — that silhouette IS the game, and a
     Realm-grey board would just read as a broken Jeopardy board. Realm's palette
     drives everything around it: chrome, panels, buttons, type and states. */
  --jeopardy-blue: #060ce9;
  --board-blue: #0a1cc9;
  --tile-blue: #060ce9;
  --tile-blue-hover: #1b2fff;
  --deep-blue: #04086e;

  /* Realm's yellow-500 is close enough to Jeopardy gold to swap in directly. */
  --gold: #c99500;
  --gold-bright: var(--realm-yellow);
  --clue-value: var(--realm-yellow);

  --white: var(--realm-text);
  --used: #0a0a12;

  /* Chrome now comes from Realm rather than the old navy. */
  --panel: var(--realm-dark-2);
  --panel-line: var(--realm-dark-4);

  --correct: var(--realm-secondary-200);
  --wrong: var(--realm-error);

  /* Inter is Realm's face; the serif stays for the JEOPARDY! wordmark only. */
  --board-font: Inter, Mulish, Roboto, "Helvetica Neue", Arial, sans-serif;
  --logo-font: Georgia, "Times New Roman", serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--realm-bg);
  color: var(--white);
  font-family: var(--board-font);
}

.screen { display: none; }
.screen.active { display: block; }

/* ---------- Brand / logo ---------- */
.brand {
  font-family: var(--logo-font);
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--gold-bright);
  text-shadow: 3px 3px 0 #000, -1px -1px 0 var(--deep-blue);
  margin: 0;
  line-height: 0.95;
  transform: skewX(-6deg);
}
.brand span { display: block; }

/* "The Realm is in" rides above the wordmark as a small gold kicker, the way
   the show stacks its own logo. Sized in em so it tracks whatever font-size the
   context sets rather than needing a value per breakpoint. */
.brand-kicker {
  font-size: 0.3em;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold-bright);
  margin-bottom: 0.15em;
  text-shadow: 2px 2px 0 #000;
}
.brand-main { color: var(--white); }

.brand.small { font-size: 1.3rem; }
/* At topbar size a stacked kicker would be ~0.4rem and illegible, so the two
   halves sit on one line instead. */
.brand.small .brand-kicker {
  display: inline;
  font-size: 0.5em;
  letter-spacing: 0.08em;
  margin-right: 0.4em;
}
.brand.small .brand-main { display: inline; }

/* Fluid, not a fixed 3.4rem: "JEOPARDY!" is nine wide glyphs plus a -6deg skew,
   which overflowed the card on a narrow phone. */
.start-card .brand {
  font-size: clamp(1.5rem, 10.5vw, 3.4rem);
  text-align: center;
  max-width: 100%;
}

/* ---------- Start screen ---------- */
#start-screen {
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle at 50% -10%, rgba(11, 153, 255, 0.16) 0%, var(--realm-bg) 60%);
}
#start-screen.active { display: flex; }

.start-card {
  width: min(520px, 92vw);
  background: var(--panel);
  border: 2px solid var(--panel-line);
  border-radius: 14px;
  padding: 34px 30px 28px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
.tagline {
  text-align: center;
  color: var(--realm-text-secondary);
  font-style: italic;
  margin: 6px 0 24px;
}
.field { display: block; margin: 0 0 18px; }
.field > span { display: block; font-size: 0.85rem; color: var(--realm-text-secondary); margin-bottom: 6px; }
.field input {
  width: 100%;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--panel-line);
  background: var(--realm-input);
  color: var(--white);
  font-size: 1rem;
}
.note { font-size: 0.8rem; color: var(--realm-text-secondary); margin-top: 14px; text-align: center; min-height: 1em; }

/* ---------- Buttons ---------- */
.btn {
  cursor: pointer;
  border: none;
  border-radius: 8px;
  padding: 12px 16px;
  font-size: 0.95rem;
  font-weight: 700;
  font-family: var(--board-font);
  transition: transform 0.05s ease, filter 0.15s ease;
}
.btn:hover { filter: brightness(1.1); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-gold { background: var(--gold); color: #1a1200; }
.btn-blue { background: var(--tile-blue); color: var(--white); border: 1px solid #2a3cff; }
.btn-ghost { background: transparent; color: var(--realm-text); border: 1px solid var(--panel-line); }
.btn-correct { background: var(--correct); color: var(--realm-neutral-1000); }
.btn-wrong { background: var(--wrong); color: var(--realm-neutral-1000); }
.btn.tiny { padding: 7px 11px; font-size: 0.8rem; }

/* ---------- Topbar ---------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  background: var(--realm-bg);
  border-bottom: 1px solid var(--panel-line);
}
.topbar-actions { display: flex; gap: 10px; }
.round-indicator {
  font-family: var(--logo-font);
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--gold-bright);
  text-transform: uppercase;
  letter-spacing: 1px;
  padding: 4px 14px;
  border: 1px solid var(--panel-line);
  border-radius: 999px;
  background: var(--realm-dark-3);
  text-shadow: 1px 1px 0 #000;
}

/* ---------- Host layout ---------- */
.host-main {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 16px;
  padding: 16px;
  align-items: start;
}

/* ---------- Board (a wall of CRT screens set in a dark cabinet) ---------- */
.board {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
  background: linear-gradient(160deg, var(--realm-dark-2), var(--realm-neutral-1000) 70%);
  padding: 12px;
  border-radius: 12px;
  box-shadow:
    inset 0 0 0 2px #000,
    inset 0 0 26px rgba(0, 0, 0, 0.9),
    0 14px 40px rgba(0, 0, 0, 0.6);
}

/* Shared CRT tube styling for every tile */
.cat-cell,
.value-cell {
  position: relative;
  overflow: hidden;
  border-radius: 7px;
  isolation: isolate; /* keep pseudo-element blending contained to the tile */
  /* black bezel + inset tube curvature */
  box-shadow:
    0 0 0 2px #000,
    inset 0 0 0 2px rgba(0, 0, 0, 0.55),
    inset 0 3px 10px rgba(255, 255, 255, 0.12),
    inset 0 -14px 26px rgba(0, 0, 0, 0.55);
}

/* Phosphor glow at the center + darkened corners = curved-glass illusion */
.cat-cell::before,
.value-cell::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background:
    radial-gradient(120% 75% at 50% 8%, rgba(255, 255, 255, 0.16), transparent 55%),
    radial-gradient(150% 130% at 50% 130%, rgba(0, 0, 0, 0.6), transparent 55%);
  z-index: -1; /* behind the tile text, above its background */
}

/* Horizontal scanlines */
.cat-cell::after,
.value-cell::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.22) 0px,
    rgba(0, 0, 0, 0.22) 1px,
    transparent 1px,
    transparent 3px
  );
  opacity: 0.55;
  mix-blend-mode: multiply;
  z-index: -1; /* behind the tile text */
}

.cat-cell {
  background: radial-gradient(120% 120% at 50% 30%, #1330e0 0%, var(--board-blue) 55%, #050a6b 100%);
  color: var(--white);
  text-align: center;
  padding: 14px 6px;
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(0.7rem, 1.1vw, 1rem);
  letter-spacing: 0.5px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 84px;
  text-shadow: 1px 1px 0 #000, 0 0 10px rgba(120, 150, 255, 0.5);
}
.value-cell {
  background: radial-gradient(120% 120% at 50% 30%, #1a34ff 0%, var(--tile-blue) 55%, #03076e 100%);
  color: var(--clue-value);
  text-align: center;
  font-family: var(--logo-font);
  font-weight: 800;
  font-size: clamp(1.3rem, 2.4vw, 2.4rem);
  padding: 20px 6px;
  min-height: 84px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  text-shadow: 2px 2px 0 #000, 0 0 12px rgba(255, 190, 60, 0.45);
  transition: filter 0.12s ease, transform 0.08s ease;
  user-select: none;
}
.value-cell:hover { filter: brightness(1.18) saturate(1.1); }
.value-cell:active { transform: scale(0.985); }
.value-cell.used {
  background: radial-gradient(120% 120% at 50% 30%, #0a0c33 0%, var(--used) 70%);
  color: transparent;
  cursor: default;
  text-shadow: none;
}
.value-cell.used::before {
  /* powered-down tube: dim, no phosphor glow */
  background: radial-gradient(150% 130% at 50% 130%, rgba(0, 0, 0, 0.75), transparent 60%);
}
.value-cell.used:hover { filter: none; }

/* ---------- Sidebar: teams ---------- */
.sidebar { display: flex; flex-direction: column; gap: 16px; }
.teams-panel, .buzz-panel {
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: 10px;
  padding: 14px;
}
.teams-header, .buzz-panel h2 { margin: 0; }
.teams-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
.teams-header h2, .buzz-panel h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 1px; color: var(--gold-bright); }
.buzz-panel h2 { margin-bottom: 10px; }

.teams-list { display: flex; flex-direction: column; gap: 10px; }
.team-row {
  background: var(--realm-input);
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  padding: 10px;
}
.team-row.active { border-color: var(--gold-bright); box-shadow: 0 0 0 1px var(--gold-bright); }
.team-top { display: flex; align-items: center; gap: 8px; }
.team-name {
  flex: 1;
  background: transparent;
  border: none;
  color: var(--white);
  font-weight: 700;
  font-size: 0.95rem;
  min-width: 0;
}
.team-name:focus { outline: 1px solid var(--panel-line); border-radius: 4px; }
.team-score {
  font-family: var(--logo-font);
  font-weight: 800;
  font-size: 1.3rem;
  color: var(--gold-bright);
  min-width: 70px;
  text-align: right;
}
.team-score.negative { color: #ff7a7a; }
.team-controls { display: flex; gap: 6px; margin-top: 8px; }
.team-controls button {
  flex: 1;
  padding: 5px;
  font-size: 0.75rem;
  border-radius: 6px;
  border: 1px solid var(--panel-line);
  background: #0c1442;
  color: var(--realm-text);
  cursor: pointer;
}
.team-controls button:hover { filter: brightness(1.2); }
.team-remove {
  background: transparent;
  border: none;
  color: #ff7a7a;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 4px;
}

/* ---------- Buzz queue ---------- */
.buzz-status { font-size: 0.8rem; color: var(--realm-text-secondary); margin-bottom: 10px; }
.buzz-status.live { color: var(--realm-secondary-200); font-weight: 700; }
.buzz-queue { list-style: none; margin: 0; padding: 0; counter-reset: buzz; }
.buzz-queue li {
  counter-increment: buzz;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 10px;
  background: var(--realm-input);
  border-radius: 6px;
  margin-bottom: 6px;
}
.buzz-queue li::before {
  content: counter(buzz);
  font-family: var(--logo-font);
  font-weight: 800;
  color: var(--gold-bright);
}
.buzz-queue li.first { border: 1px solid var(--gold-bright); }
.buzz-queue .bz-name { font-weight: 700; }
.buzz-queue .bz-team { font-size: 0.75rem; color: var(--realm-text-secondary); margin-left: auto; }

/* ---------- Clue overlay ---------- */
.overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(2, 3, 20, 0.9);
  z-index: 50;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.overlay.active { display: flex; }

/* Screen-space scanline field: lives on the overlay, NOT the card, so it stays
   at a constant scale during the zoom instead of stretching with the card. */
.overlay::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 60; /* above the card, same CRT lines as the board tiles */
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.22) 0px,
    rgba(0, 0, 0, 0.22) 1px,
    transparent 1px,
    transparent 3px
  );
  opacity: 0.45;
  mix-blend-mode: multiply;
}

.clue-card {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  width: min(1000px, 96vw);
  min-height: 60vh;
  background: radial-gradient(120% 120% at 50% 25%, #1a34ff 0%, var(--jeopardy-blue) 55%, #03076e 100%);
  border-radius: 12px;
  padding: 30px;
  display: flex;
  flex-direction: column;
  will-change: transform, opacity;
  /* Cabinet frame: the same layered dark bezel that surrounds the board,
     wrapped around this single screen. */
  box-shadow:
    0 0 0 10px #12142c,
    0 0 0 12px #000,
    0 30px 80px rgba(0, 0, 0, 0.6),
    inset 0 0 0 2px rgba(0, 0, 0, 0.55),
    inset 0 4px 22px rgba(255, 255, 255, 0.12),
    inset 0 -26px 60px rgba(0, 0, 0, 0.45);
}
/* CRT glass curvature (phosphor glow + darkened corners), matching the tiles */
.clue-card::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background:
    radial-gradient(120% 70% at 50% 4%, rgba(255, 255, 255, 0.14), transparent 55%),
    radial-gradient(150% 130% at 50% 130%, rgba(0, 0, 0, 0.5), transparent 55%);
  z-index: -1;
}
.clue-meta {
  text-align: center;
  color: var(--gold-bright);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 10px;
}
.clue-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 20px;
}
.clue-text {
  font-size: clamp(1.4rem, 3.2vw, 2.8rem);
  font-weight: 700;
  text-transform: uppercase;
  text-shadow: 2px 2px 0 #000;
  line-height: 1.25;
  margin: 0;
}
.answer-text {
  font-size: clamp(1.2rem, 2.6vw, 2.2rem);
  color: var(--gold-bright);
  font-style: italic;
  margin: 0;
}
.hidden { display: none !important; }

.clue-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  align-items: center;
  margin-top: 20px;
}
.award-row { display: flex; gap: 8px; align-items: center; }
.award-select {
  padding: 10px;
  border-radius: 8px;
  border: 1px solid #2a3cff;
  background: var(--realm-input);
  color: #fff;
  font-weight: 700;
}

/* Daily Double */
.dd-wager { text-align: center; }
.dd-banner {
  font-family: var(--logo-font);
  font-size: clamp(2rem, 6vw, 4rem);
  font-weight: 800;
  color: var(--gold-bright);
  text-shadow: 3px 3px 0 #000;
  margin-bottom: 20px;
  letter-spacing: 2px;
}
.dd-wager .field { max-width: 320px; margin: 0 auto 16px; text-align: left; }

.buzz-inline { text-align: center; color: var(--gold-bright); font-weight: 700; min-height: 1.2em; margin-top: 10px; }

/* ---------- Final Jeopardy ---------- */
.final-card { text-align: center; justify-content: center; }
.final-stage { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.final-instr { color: #cdd4ff; margin: 0; }
.final-wagers, .final-results { width: min(560px, 92%); display: flex; flex-direction: column; gap: 10px; }
.final-wager-row, .final-result-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(3, 7, 60, 0.6);
  border: 1px solid #2a3cff;
  border-radius: 8px;
  padding: 10px 14px;
}
.final-result-row.resolved { opacity: 0.55; }
.fw-name { font-weight: 800; flex: 1; text-align: left; }
.fw-score, .fw-wager { color: var(--gold-bright); font-family: var(--logo-font); font-weight: 800; }
.fw-input {
  width: 120px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid #2a3cff;
  background: var(--realm-input);
  color: #fff;
  font-weight: 700;
}

/* ---------- Player view ---------- */
#player-view.active { display: flex; }
#player-view {
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% -10%, rgba(11, 153, 255, 0.16) 0%, var(--realm-bg) 60%);
}
.player-inner {
  width: min(520px, 94vw);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 20px;
}
.player-team-tag { color: var(--gold-bright); font-weight: 800; text-transform: uppercase; letter-spacing: 1px; }
.buzz-button {
  width: min(300px, 70vw);
  height: min(300px, 70vw);
  border-radius: 50%;
  border: 8px solid #000;
  background: radial-gradient(circle at 40% 35%, #ff5a5a, #b0000f);
  color: #fff;
  font-family: var(--logo-font);
  font-size: 2rem;
  font-weight: 800;
  cursor: pointer;
  box-shadow: 0 14px 40px rgba(176,0,15,0.5);
  transition: transform 0.06s ease, filter 0.15s ease;
}
.buzz-button:disabled {
  background: radial-gradient(circle at 40% 35%, #4a4f70, #2a2d45);
  cursor: not-allowed;
  box-shadow: none;
}
.buzz-button.armed { animation: pulse 1.1s infinite; }
.buzz-button.armed .buzz-label::after { content: ""; }
.buzz-button:not(:disabled):active { transform: scale(0.96); }
@keyframes pulse {
  0%, 100% { box-shadow: 0 14px 40px rgba(176,0,15,0.5); }
  50% { box-shadow: 0 14px 60px rgba(255,90,90,0.9); }
}
.buzz-button.buzzed { background: radial-gradient(circle at 40% 35%, #ffd24a, #d69f4c); color: #1a1200; }

.player-scores { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.player-scores .ps {
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 0.85rem;
}
.player-scores .ps b { color: var(--gold-bright); font-family: var(--logo-font); }

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  .host-main { grid-template-columns: 1fr; }
  .sidebar { flex-direction: row; flex-wrap: wrap; }
  .teams-panel, .buzz-panel { flex: 1; min-width: 260px; }
}

/* =====================================================================
   v2 ADDITIONS
   Board fill animation, timer strip, per-role views, responsive fixes.
   Durations come from timing.js via custom properties on :root, so a
   duration is never restated in two places (v1 had 0.32s in CSS and
   330ms in JS, which silently drifted).
   ===================================================================== */

/* ---------- chrome ---------- */

.status-ribbon {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 900;
  padding: 6px 12px;
  text-align: center;
  font-weight: 700;
  font-size: 0.85rem;
  color: #1b1300;
  background: linear-gradient(#ffcf5a, #e0a520);
  /* Never a modal: nothing may cover the buzz button. */
  pointer-events: none;
}

.sound-toggle {
  position: fixed;
  top: 8px; right: 10px;
  z-index: 901;
  padding: 6px 10px;
  font: inherit;
  font-size: 0.78rem;
  border-radius: 999px;
  border: 1px solid var(--panel-line);
  background: var(--realm-dark-3);
  color: var(--white);
  cursor: pointer;
}
.sound-toggle.warn { border-color: #e0a520; color: #ffcf5a; }

.note.error { color: #ff8a7a; }
.note.warn { color: #ffcf5a; }
.note.small { font-size: 0.78rem; opacity: 0.8; }
.btn.big { font-size: 1.3rem; padding: 18px 32px; }

/* ---------- board grid sizing ---------- */

.board { grid-template-columns: repeat(var(--cols, 6), 1fr); }

/* ---------- board fill: a CRT tube powering on ----------
   Only opacity, transform and filter are animated. box-shadow is NOT
   interpolated: the tile has five shadow layers and interpolating them
   drops frames on the machine driving the projector. */

@keyframes tube-on {
  0%   { opacity: 0; transform: scaleY(0.04); filter: brightness(3.2) saturate(0.15); }
  45%  { opacity: 1; transform: scaleY(1.06); filter: brightness(2.1) saturate(0.6); }
  72%  { transform: scaleY(0.985); filter: brightness(1.22); }
  100% { opacity: 1; transform: none; filter: none; }
}

@keyframes cv-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Tiles start invisible only while a fill is queued or running. */
.board.filling .cat-cell,
.board.filling .value-cell {
  animation: tube-on var(--fill-dur, 340ms) cubic-bezier(0.2, 0.8, 0.25, 1) both;
  /* The existing filter/transform transition on .value-cell would fight the
     keyframes, and sticky touch-hover would leave a tile lit. */
  transition: none;
}
.board.filling { pointer-events: none; }

.board.filling .cat-cell {
  animation-delay: calc(var(--fill-i, 0) * var(--fill-head-step, 55ms));
}
.board.filling .value-cell {
  animation-delay: calc(var(--fill-lead, 330ms) + var(--fill-i, 0) * var(--fill-step, 170ms));
}

/* Text lives in a child because scaleY(0.04) would deform a bare text node. */
.board.filling .cc-text,
.board.filling .cv {
  animation: cv-in 200ms ease both;
  animation-delay: calc(
    var(--fill-lead, 330ms) + var(--fill-i, 0) * var(--fill-step, 170ms) + var(--fill-text-delay, 180ms)
  );
}

.value-cell.empty { pointer-events: none; }
.cv, .cc-text { display: block; }

/* ---------- timer strip: the show's light bar ---------- */

.timer-strip {
  --p: 0;
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 0;
  opacity: 0.25;
  transition: opacity 200ms ease;
}
.timer-strip.live { opacity: 1; }

.tl-mask {
  flex: 1;
  overflow: hidden;
  /* One style write per frame, composited on the GPU — far cheaper than
     animating nine separate opacities. */
  clip-path: inset(0 calc(var(--p) * 50%) 0 calc(var(--p) * 50%));
}
.tl-wrap { display: flex; gap: 4px; }
.tl {
  flex: 1;
  height: 12px;
  border-radius: 3px;
  background: linear-gradient(#ffd97a, #e0a520);
  box-shadow: 0 0 0 1px #000, 0 0 8px rgba(255, 190, 60, 0.5);
  position: relative;
  overflow: hidden;
}
/* Same scanline treatment as the tiles, so it reads as part of the cabinet. */
.tl::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    to bottom, rgba(0,0,0,0.25) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
}

.timer-strip[data-urgency="mid"] .tl { background: linear-gradient(#ffb454, #d97b10); }
.timer-strip[data-urgency="high"] .tl,
.timer-strip[data-urgency="out"] .tl {
  background: linear-gradient(#ff7a5c, #c62828);
  animation: timer-urgent 400ms ease-in-out infinite alternate;
}
@keyframes timer-urgent { from { filter: brightness(1); } to { filter: brightness(1.5); } }

.tl-readout {
  font-family: var(--logo-font);
  font-weight: 800;
  color: var(--gold-bright);
  min-width: 3.4em;
  text-align: right;
}
.timer-strip.tv .tl { height: 20px; }
.timer-strip.tv { margin: 4px 16px; }

/* ---------- scores ---------- */

.scores-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  padding: 8px;
}
.score-chip {
  display: flex;
  gap: 8px;
  align-items: baseline;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--realm-dark-3);
  border: 1px solid var(--panel-line);
}
.score-chip.is-you { border-color: var(--gold-bright); }
.score-chip.negative .sc-val { color: #ff8a7a; }
.sc-name { font-size: 0.8rem; opacity: 0.85; }
.sc-val { font-family: var(--logo-font); font-weight: 800; color: var(--gold-bright); }

/* ---------- host presence panel ---------- */

.presence-list { display: flex; flex-direction: column; gap: 6px; }
.presence-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 8px;
  align-items: center;
  padding: 6px 8px;
  border-radius: 8px;
  background: var(--realm-dark-3);
  font-size: 0.82rem;
}
.presence-row.offline { opacity: 0.45; }
.pr-meta { grid-column: 1 / -1; font-size: 0.72rem; opacity: 0.7; }
.pr-team { grid-row: 1; }

/* ---------- final panels ---------- */

.final-panel {
  margin-top: 14px;
  padding: 16px;
  border-radius: 12px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
}
.final-rows, .final-stage { display: flex; flex-direction: column; gap: 8px; }
.final-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding: 6px 8px;
  border-radius: 8px;
  background: var(--realm-dark-3);
}
.final-row.resolved { opacity: 0.6; }
.fw-answer { font-style: italic; opacity: 0.9; }

/* ---------- BOARD / TV VIEW ----------
   v1's .board had no grid-template-rows at all and leaned on
   min-height: 84px, so on a 1080p or 4K TV the board floated as a short
   block at the top of a black screen. */

.board-screen {
  height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  gap: 6px;
  padding: 10px;
}
.tv-topbar { display: flex; justify-content: space-between; align-items: center; padding: 0 12px; }
.tv-banner {
  font-family: var(--logo-font);
  font-weight: 800;
  color: var(--gold-bright);
  letter-spacing: 2px;
}
.board.tv {
  height: 100%;
  grid-template-rows: 1.15fr repeat(var(--rows, 5), 1fr);
}
/* vmin, not vw: the right unit for a fill-the-screen grid at any aspect
   ratio. 2.4vw both undershoots on a 65" 16:9 and overshoots on 21:9. */
.board.tv .value-cell { font-size: clamp(1.5rem, 3.2vmin, 5rem); min-height: 0; }
.board.tv .cat-cell {
  font-size: clamp(0.9rem, 1.9vmin, 2.4rem);
  min-height: max(84px, 9vmin);
  line-height: 1.05;
  hyphens: auto;
}
.tv-card .clue-text { font-size: clamp(1.6rem, 4.4vmin, 4rem); }

/* ---------- PLAYER VIEW ---------- */

#player-view {
  /* dvh, not vh: iOS includes the collapsing toolbar in vh, which pushes the
     buzz button partly off-screen on first paint. */
  height: 100dvh;
  overflow: hidden;
  /* Pull-to-refresh during a buzz window would reload the page mid-game. */
  overscroll-behavior: none;
  padding-bottom: env(safe-area-inset-bottom);
}
.player-grid {
  height: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  gap: 6px;
  padding: 8px;
}
.player-head { display: flex; flex-direction: column; gap: 6px; align-items: center; }
.player-board-wrap { width: 100%; max-height: 26vh; overflow: hidden; }
.board.mini { gap: 3px; padding: 4px; }
.board.mini .value-cell { min-height: 0; padding: 6px 2px; font-size: clamp(0.6rem, 2.4vw, 0.95rem); }
.board.mini .cat-cell { min-height: 0; padding: 5px 2px; font-size: clamp(0.42rem, 1.7vw, 0.7rem); }
.player-mid { display: flex; flex-direction: column; justify-content: center; gap: 4px; text-align: center; }
.player-buzz-zone { display: grid; place-items: center; }
.team-picker { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }

.buzz-button {
  /* manipulation kills double-tap zoom without disabling pinch, so the page
     stays accessible. */
  touch-action: manipulation;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
}
.buzz-button.reject { animation: buzz-reject-flash var(--reject-flash, 400ms) ease; }
@keyframes buzz-reject-flash {
  0%, 100% { box-shadow: none; }
  50% { box-shadow: 0 0 0 6px rgba(198, 40, 40, 0.8); }
}

/* Phase-driven presentation; JS only sets data-phase. */
#player-view[data-phase="idle"] #player-timer { opacity: 0.15; }
#player-view[data-phase="buzzed-self"] .buzz-button { background: radial-gradient(circle at 50% 35%, #ffd97a, #e0a520); }
#player-view[data-phase="buzzed-other"] .buzz-button,
#player-view[data-phase="locked"] .buzz-button,
#player-view[data-phase="no-team"] .buzz-button,
#player-view[data-phase="timeout"] .buzz-button { filter: grayscale(0.7) brightness(0.7); }
#player-view[data-phase="timeout"] .buzz-button { box-shadow: 0 0 0 3px #c62828; }
#player-view[data-phase="dd-active"] .player-buzz-zone { display: none; }
#player-view[data-phase="final-wager"] .player-buzz-zone,
#player-view[data-phase="final-clue"] .player-buzz-zone { display: none; }

/* ---------- responsive ---------- */

@media (max-width: 1200px) { .host-main { grid-template-columns: 1fr 280px; } }

@media (max-width: 1024px) {
  /* A bottom sheet rather than side-by-side wrap, which left the board too
     short to click accurately. */
  .host-main { grid-template-columns: 1fr; }
  .sidebar { flex-direction: column; }
}

/* Host on an iPad is a real scenario and these were all well under 44px. */
@media (pointer: coarse) {
  .btn.tiny { padding: 12px 16px; }
  .team-controls button { padding: 12px 10px; min-height: 44px; }
  .team-remove { min-width: 44px; min-height: 44px; }
  .award-select, .pr-team { min-height: 44px; }
}

/* Sticky hover leaves a tile permanently lit after a tap. */
@media (hover: none) {
  .value-cell:hover { filter: none; }
}

/* v1's buzz button was min(300px, 70vw) — taller than a landscape phone. */
@media (orientation: landscape) and (max-height: 520px) {
  .player-grid {
    grid-template-columns: 1fr auto;
    grid-template-rows: auto 1fr auto;
  }
  .player-head { grid-column: 1 / -1; }
  .player-board-wrap { max-height: 20vh; }
  .player-buzz-zone { grid-column: 2; grid-row: 2 / 4; }
  .buzz-button { width: min(38vh, 200px); height: min(38vh, 200px); }
}

@media (min-width: 1600px) {
  /* The CRT glow reads thin at TV distance. */
  .board.tv .value-cell { text-shadow: 3px 3px 0 #000, 0 0 20px rgba(255, 190, 60, 0.55); }
  .board.tv { text-rendering: optimizeLegibility; }
}

@media (prefers-reduced-motion: reduce) {
  .board.filling .cat-cell,
  .board.filling .value-cell,
  .board.filling .cc-text,
  .board.filling .cv {
    animation: none;
    opacity: 1;
    transform: none;
    filter: none;
  }
  .buzz-button.armed { animation: none; }
  .timer-strip[data-urgency="high"] .tl,
  .timer-strip[data-urgency="out"] .tl { animation: none; }
}

/* ---------- per-buzzer award rows ----------
   The host scores the person who actually buzzed. Replaces a team dropdown
   that had to be cross-checked against the queue. */

.award-candidates { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.award-cand {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--realm-dark-3);
  border: 1px solid var(--panel-line);
}
/* The first buzzer is who the host should normally be scoring. */
.award-cand.first { border-color: var(--gold-bright); box-shadow: 0 0 12px rgba(255,190,60,0.25); }
.award-cand.spent { opacity: 0.5; }
.ac-pos {
  font-family: var(--logo-font);
  font-weight: 800;
  color: var(--gold-bright);
  min-width: 2em;
}
.ac-name { font-weight: 700; }
.ac-team { font-size: 0.8rem; opacity: 0.75; }
.ac-note { font-size: 0.78rem; opacity: 0.7; font-style: italic; }
.award-cand .btn { margin-left: auto; }
.award-cand .btn + .btn { margin-left: 0; }

/* The host reads the answer aloud, so it is always on screen for them. */
#host-clue-overlay .answer-text {
  color: var(--gold-bright);
  border-top: 1px solid var(--panel-line);
  padding-top: 10px;
  margin-top: 10px;
}

@media (pointer: coarse) {
  .award-cand .btn.tiny { padding: 12px 14px; }
}

/* ---------- Realm wordmark ----------
   REALM_HORIZONTAL_WHITE.svg from realm.security. It is pure white with a
   1500x489 viewBox, so it sits on the dark chrome without any treatment and
   only needs a height plus a little breathing room. */

.realm-mark {
  display: block;
  height: clamp(22px, 5.5vw, 34px);
  width: auto;
  margin: 0 auto 18px;
  /* The board's blue-and-gold is loud; keep the corporate mark quiet so it
     frames the game rather than competing with it. */
  opacity: 0.82;
}
.realm-mark.tiny {
  height: 18px;
  margin: 0 14px 0 0;
  opacity: 0.7;
  flex: 0 0 auto;
}
.tv-topbar .realm-mark.tiny { height: clamp(18px, 2.2vmin, 30px); }

@media (max-width: 640px) {
  /* The host topbar is already crowded on a phone; the JEOPARDY! lockup and the
     round indicator matter more than the corporate mark. */
  .topbar .realm-mark.tiny { display: none; }
}

/* ---------- Host: clue overlay scoped to the board ----------
   The player and TV views want a full-screen clue. The host does not: while a
   clue is up the host still needs the buzz queue to see who came in, the award
   buttons, and the team scores. So on the host the overlay is positioned
   against the board column rather than the viewport, and the sidebar stays
   live underneath it. */

.board-column {
  position: relative;
  /* Establishes the containing block for the absolutely-positioned overlay. */
  min-width: 0;
}

.overlay.overlay-scoped {
  position: absolute;
  inset: 0;
  padding: 0;
  border-radius: 12px;
  /* The card's cabinet frame extends ~12px past its box; clip it so the bezel
     can't spill over the sidebar gutter. */
  overflow: hidden;
}

/* The full-screen card sizes itself in vw/vh, which would overflow a column
   that is only part of the viewport. Fill the board area instead. */
.overlay.overlay-scoped .clue-card {
  width: 100%;
  height: 100%;
  min-height: 0;
  padding: clamp(16px, 2.2vw, 30px);
  /* A thinner bezel: the heavy 10px+12px frame reads as oversized once the card
     is board-sized rather than screen-sized. */
  box-shadow:
    0 0 0 6px #12142c,
    0 0 0 8px #000,
    inset 0 0 0 2px rgba(0, 0, 0, 0.55),
    inset 0 4px 22px rgba(255, 255, 255, 0.12),
    inset 0 -26px 60px rgba(0, 0, 0, 0.45);
}

/* Long clues plus the award rows can exceed the board's height, so let the
   card scroll internally rather than pushing its actions out of reach. */
.overlay.overlay-scoped .clue-card > * { flex: 0 0 auto; }
.overlay.overlay-scoped .clue-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
