/* ═══════════════ Trivia Rumble — the big screen ═══════════════
   OneScreen.dc.html, TV frames. Every other stylesheet in this project sizes
   type for a phone at arm's length. This one sizes it for a room at three
   metres, so the numbers are the board's own rather than a phone scaled up:
   room code 360 on join and 220 in the lobby, question 84, options 52,
   timer 96, countdown 560, side names 200.

   Sizes are in vw so the design holds on a 1920 television, a 1440 laptop and
   whatever the projector in somebody's office turns out to be. 1vw is 19.2px
   at 1920, which is the width the board was drawn at. */
* { margin: 0; padding: 0; box-sizing: border-box; }
body.tv {
  font-family: "Outfit", system-ui, sans-serif;
  background: var(--bg);
  background-image:
    radial-gradient(60vw 40vw at 85% -10%, oklch(0.30 0.06 25 / 0.55), transparent 60%),
    radial-gradient(50vw 35vw at -10% 110%, oklch(0.28 0.05 255 / 0.45), transparent 60%);
  color: var(--ink);
  min-height: 100dvh; overflow: hidden;
}

/* One screen at a time, and nothing else in the document. A television has no
   scrollbar and nobody to scroll it. */
/* The television's height, not a floor under it. min-height let a screen grow
   past the set and body{overflow:hidden} then cut the bottom off in silence,
   which is why a long question lost its last lines and a long answer had to be
   held to one nowrap line to stop the grid pushing the page. Bounded here, the
   flexible children shrink and the sizes that fit can be measured. */
.tv-screen { display: none; height: 100dvh; padding: 3.3vw 5vw; }
.tv-screen.is-on { display: flex; flex-direction: column; }

.tv-kicker {
  font-weight: 800; font-size: 1.15vw; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-dim);
}
.tv-fine { font-weight: 600; font-size: 1.15vw; color: var(--ink-dim); }

/* ─── waiting to be paired ─── */
/* Cream, not gold. Gold is the room's material on this screen and a pairing
   number that borrowed it would read as a room code from the sofa. */
#tv-join { align-items: center; justify-content: center; text-align: center; gap: 2vw; }
.tv-flow { display: flex; align-items: center; gap: 1vw; font-size: 2.6vw; color: var(--ink-dim); }
.tv-flow i {
  width: 0.5vw; height: 0.5vw; border-radius: 999px; background: var(--ink-dim);
  opacity: 0.45; animation: tv-step 1.8s var(--ease) infinite;
}
.tv-flow i:nth-child(3) { animation-delay: 0.25s; }
.tv-flow i:nth-child(4) { animation-delay: 0.5s; }
@keyframes tv-step { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }

.tv-pair { display: flex; gap: 1.4vw; }
.tv-pair-tile {
  width: 15.6vw; height: 37vh; border-radius: 2vw;
  background: var(--bg-raise); box-shadow: inset 0 0 0 0.3vw var(--line);
  display: grid; place-items: center;
  font-weight: 900; font-size: 22vh; line-height: 1; color: var(--ink);
  font-variant-numeric: tabular-nums;
  transition: box-shadow 0.4s var(--ease), transform 0.4s var(--ease);
}
.tv-pair.is-landed .tv-pair-tile {
  box-shadow: inset 0 0 0 0.3vw var(--good);
  transform: translateY(-1vh);
}
.tv-pair-say { font-weight: 700; font-size: 2vw; color: var(--ink-dim); max-width: 60ch; }
/* The landed beat carries no words. The instruction has been followed, so it
   stops being an instruction and the host and the room take its place. */
#tv-join:has(.tv-pair.is-landed) .tv-pair-say { visibility: hidden; }
.tv-landed { display: flex; align-items: center; gap: 1.4vw; font-weight: 800; font-size: 2.2vw; }
.tv-landed-code { color: var(--gold); letter-spacing: 0.1em; }

/* The way in by hand, at the foot and deliberately quiet. */
.tv-fallback {
  margin-top: auto; display: flex; align-items: center; justify-content: center;
  gap: 1.2vw; font-weight: 600; font-size: 1.15vw; color: var(--ink-dim);
}
.tv-form { display: flex; align-items: center; gap: 1.2vw; }
/* The fallback's own box, sized to sit in a footer rather than to be the
   composition. It was the composition until the screen stopped typing. */
.tv-code {
  font: inherit; font-weight: 900; font-size: 1.6vw; letter-spacing: 0.2em;
  /* Wide enough for the placeholder in the longer alphabet: CODE is four
     characters and ΚΩΔΙΚΟΣ is seven, and a box cut to the English clipped the
     Greek to ΚΩΔΙΚ. */
  width: 10ch; text-align: center; text-transform: uppercase;
  background: transparent; border: 0; border-bottom: 0.2vw dashed var(--line);
  color: var(--ink-dim); padding: 0.2vw 0.4vw; outline: none;
}
.tv-code:focus { border-color: var(--gold); }
.tv-error { font-weight: 700; font-size: 1.15vw; color: var(--bad); }

/* ─── the four answer shapes, as the promise of what is coming ─── */
.tv-shapes { display: flex; align-items: center; gap: 1vw; }
.tv-shapes i { display: block; }
.s-tri { width: 0; height: 0; border-left: 0.8vw solid transparent; border-right: 0.8vw solid transparent; border-bottom: 1.4vw solid var(--coral); }
.s-dia { width: 1.2vw; height: 1.2vw; background: var(--amber); transform: rotate(45deg); border-radius: 0.15vw; }
.s-cir { width: 1.35vw; height: 1.35vw; border-radius: 999px; background: var(--teal); }
.s-sqr { width: 1.3vw; height: 1.3vw; border-radius: 0.25vw; background: var(--blue); }

/* ─── joined a room already in play ─── */
/* No words. A television that arrives late holds here for a second, sometimes
   a whole question, and every sentence that could go here would be wrong for
   one of those two cases. */
#tv-wait { align-items: center; justify-content: center; text-align: center; gap: 3vw; }
.tv-held { position: relative; display: grid; place-items: center; padding: 3.5vh 3vw; }
.tv-held-ring {
  position: absolute; inset: 0; border-radius: 6vw;
  box-shadow: inset 0 0 0 0.2vw var(--gold);
  opacity: 0.35; animation: tv-breathe 2s var(--ease) infinite;
}
@keyframes tv-breathe { 0%, 100% { opacity: 0.18; } 50% { opacity: 0.5; } }
.tv-host { font-weight: 700; font-size: 2vw; color: var(--ink-dim); }
/* 360px at 1920. This is the single most important number on the screen: it is
   what somebody across the room copies into their phone. */
.tv-code-big {
  font-weight: 900; font-size: 18.75vw; line-height: 0.9;
  letter-spacing: 0.06em; color: var(--gold);
}

/* ─── lobby, which is also the waiting room ─── */
/* One state. The difference between an empty room and a full one is how many
   chips are in the lower band, and a screen that changed shape between those
   two would be two screens to learn instead of one.

   The code takes the top band and the QR sits beside it rather than under it,
   which is what keeps the roster its full height. */
/* The roster takes a fixed band at the foot and the code takes whatever is
   left, rather than both asking for a share and together asking for more than
   there is. Twenty chips overflowed the set until the rows were pinned. */
/* On the is-on class, not on the id alone: an id beats .tv-screen's display
   none, so a bare #tv-lobby { display: grid } leaves the lobby laid out and
   taking space behind every other screen. */
#tv-lobby.is-on { display: grid; grid-template-rows: 1fr 40vh; gap: 3vh; }
.tv-lobby-top { display: grid; grid-template-columns: 1fr 33vh; gap: 4vw; align-items: center; min-height: 0; }
.tv-lobby-code { display: flex; flex-direction: column; gap: 1vh; min-width: 0; }
.tv-lobby-at { display: flex; align-items: center; gap: 1.5vw; padding-left: 0.4vw; }
.tv-lobby-host { font-weight: 700; font-size: 2.2vw; color: var(--ink-dim); }

/* Cream, and with a quiet zone, because a code without margin is a code that
   does not scan. The path is drawn in the page's darkest ink rather than in
   black, so it sits in the room's palette without losing contrast. */
.tv-qr {
  width: 33vh; height: 33vh; border-radius: 1.7vh;
  background: var(--ink); padding: 2.5vh; box-sizing: border-box;
}
.tv-qr svg { width: 100%; height: 100%; display: block; fill: var(--bg); }

.tv-roster { display: flex; flex-direction: column; gap: 1.5vh; min-height: 0; }
.tv-roster-head { display: flex; align-items: center; gap: 1.2vw; }
/* The heartbeat: the one thing that moves while nothing is happening, so the
   room can tell a live screen from a frozen one at a glance. */
.tv-pulse {
  width: 1.1vh; height: 1.1vh; border-radius: 999px; background: var(--gold);
  animation: tv-pulse 2s var(--ease) infinite;
}
@keyframes tv-pulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
/* The code is the composition, so it gets a box and a ladder rather than a
   size. Four Latin letters in both languages, which makes this the one box on
   the television with nothing to measure twice. */
.tv-lobby-code-text {
  font-weight: 900; font-size: 18vw; line-height: 0.9;
  letter-spacing: 0.18em; color: var(--gold);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.tv-count { font-weight: 900; font-size: 6vw; line-height: 1; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
/* A fourth row of chips needs the numeral to give up a rung. */
.tv-roster.is-full .tv-count { font-size: 5vw; }
/* Twenty as five by four, relaxing to five by two under ten so a room of four
   is not four fifths empty. */
.tv-players {
  flex: 1; min-height: 0; overflow: hidden;
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 1.2vw;
}
.tv-player {
  background: var(--bg-raise); border-radius: 1.2vw; padding: 0.6vw 1vw;
  box-shadow: inset 0 0 0 0.15vw var(--line);
  display: flex; align-items: center; gap: 0.8vw; min-width: 0; min-height: 0;
  overflow: hidden;
  animation: tv-pop 0.35s var(--ease) both;
}
.tv-player.is-host { box-shadow: inset 0 0 0 0.2vw var(--gold); }
.tv-face { font-size: 2.2vw; flex: none; }
.tv-players.is-roomy .tv-face { font-size: 3.4vw; }
.tv-players.is-roomy .tv-player { border-radius: 1.5vw; padding: 1vw 1.2vw; }
/* Cut hard at the tile edge. A nickname is sixteen characters at most and the
   tile is the tile: a name that wrapped would push the grid out of true. */
.tv-player b {
  font-weight: 800; font-size: 1.4vw; min-width: 0;
  overflow: hidden; white-space: nowrap;
}
.tv-players.is-roomy .tv-player b { font-size: 2vw; }
@keyframes tv-pop { from { transform: scale(1.15); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* ─── countdown ─── */
#tv-countdown { align-items: center; justify-content: center; }
.tv-countdown { font-weight: 900; font-size: 29vw; line-height: 1; color: var(--gold); }
.tv-countdown.tick { animation: tv-tick 0.5s var(--ease); }
@keyframes tv-tick { from { transform: scale(1.5); opacity: 0.2; } to { transform: scale(1); opacity: 1; } }

/* ─── VS ─── */
#tv-vs { align-items: center; justify-content: center; gap: 1.5vw; }
.tv-side { font-weight: 900; font-size: 10.4vw; line-height: 0.9; text-transform: uppercase; letter-spacing: -0.03em; text-align: center; }
.tv-side-b { color: var(--gold); }
.tv-vs-word { font-weight: 900; font-size: 12.5vw; line-height: 0.9; color: var(--gold); }

/* ─── question, which is also the reveal ─── */
#tv-question { gap: 1.5vh; }
.tv-q-head { display: flex; align-items: center; justify-content: space-between; gap: 2vw; min-height: 9vh; }
.tv-q-right { display: flex; align-items: center; gap: 1.6vw; }
.tv-locked { font-weight: 700; font-size: 1.6vw; color: var(--ink-dim); font-variant-numeric: tabular-nums; }

/* The ring takes the timer's corner once the timer is spent: the same fact in
   the same place, something the room is waiting on. */
/* A class rather than the hidden attribute, because hidden is a property of
   HTMLElement and an SVGElement is not one: svg.hidden = false sets a plain
   JavaScript property, leaves the attribute in place, and the ring never
   appears. Nothing about that is visible from the call site. */
.tv-ready { width: 8vh; height: 8vh; flex: none; }
.tv-ready.is-off { display: none; }
.tv-ready-track { fill: none; stroke: var(--bg-sink); stroke-width: 4; }
.tv-ready-arc {
  fill: none; stroke: var(--good); stroke-width: 4; stroke-linecap: round;
  transform: rotate(-90deg); transform-origin: 50% 50%;
  transition: stroke-dashoffset 0.4s var(--ease);
}
.tv-ready-text { fill: var(--ink); font-weight: 800; font-size: 12px; }

/* ─── the fight, while it is being fought ─── */
/* Two names, two numerals and a bar. Nothing in it needs translating, and a
   room in teams could not tell from this screen that it was in teams at all. */
.tv-fight {
  display: grid; grid-template-columns: auto auto 1fr auto auto;
  align-items: center; gap: 1.2vw; flex: none;
  transition: opacity 0.4s var(--ease);
}
.tv-fight-side { font-weight: 900; font-size: 1.7vw; text-transform: uppercase; letter-spacing: -0.02em; }
.tv-fight-lock { font-weight: 900; font-size: 1.7vw; font-variant-numeric: tabular-nums; min-width: 1.2vw; text-align: center; }
.tv-fight-a { color: var(--ink); }
.tv-fight-b { color: var(--gold); }
.tv-fight-bar {
  position: relative; display: flex; height: 1.2vh; border-radius: 999px;
  overflow: hidden; background: var(--bg-sink);
  box-shadow: inset 0 0 0 0.12vw var(--line);
}
.tv-fight-fill { transition: width 0.6s var(--ease); }
.tv-fight-fill-a { background: var(--ink); }
.tv-fight-fill-b { background: var(--gold); }
.tv-fight.is-ko .tv-fight-bar { box-shadow: inset 0 0 0 0.16vw var(--bad); }
/* Paused: the fight has not ended, it is waiting, so the strip dims rather
   than the screen changing to something that would say it was over. */
.tv-fight.is-paused { opacity: 0.5; }
.tv-fight-pause {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 2vh; line-height: 1;
}

/* One column until the answer lands, two after it. */
.tv-play { flex: 1; display: grid; grid-template-columns: 1fr; gap: 3vw; min-height: 0; }
.tv-play-main { display: flex; flex-direction: column; gap: 1.5vh; min-height: 0; min-width: 0; }
#tv-question.is-revealed .tv-play { grid-template-columns: 50fr 44fr; }
.tv-progress { font-weight: 800; font-size: 2vw; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.tv-category {
  background: var(--bg-raise); border-radius: 999px; padding: 0.5vw 1.4vw;
  font-weight: 700; font-size: 1.6vw; box-shadow: inset 0 0 0 0.15vw var(--line);
  flex: none; white-space: nowrap;
}
.tv-timer { font-weight: 900; font-size: 5vw; color: var(--gold); font-variant-numeric: tabular-nums; }
.tv-track { height: 0.6vw; background: var(--bg-sink); border-radius: 999px; overflow: hidden; }
.tv-bar { height: 100%; background: var(--gold); border-radius: 999px; transition: width 1s linear; }
/* The question box is fixed so the four options never move down the screen when
   a longer question arrives. tv.js measures against this height and steps the
   type down until it fits. */
.tv-q {
  font-weight: 800; line-height: 1.2; text-wrap: balance;
  height: 30vh; display: flex; align-items: center; overflow: hidden; flex: none;
}
/* Half the width and five lines allowed, because a question that fitted three
   lines across the screen needs more of them down a column. */
#tv-question.is-revealed .tv-q { height: 22vh; }
/* Four equal cells filling whatever height is left, rather than four cells
   that grow to their own content. Auto rows were why an option had to be one
   nowrap line: let it wrap and the cell grew, the grid grew, and the screen
   overflowed. Fixed rows make the box a box, so the text can wrap inside it
   and the size that fits can be measured. */
.tv-options { flex: 1; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); gap: 1.2vw; min-height: 0; }
.tv-opt {
  border-radius: 1.5vw; padding: 1.5vw 2vw;
  display: flex; align-items: center; gap: 1.2vw;
  font-weight: 700; color: oklch(0.98 0.01 85); min-width: 0;
  /* The cell is the box the fit is measured against, so it has to be one. */
  min-height: 0; overflow: hidden;
  transition: opacity 0.3s var(--ease), outline-color 0.3s var(--ease);
  outline: 0.25vw solid transparent; outline-offset: 0.25vw;
}
.tv-shape { font-size: 0.6em; opacity: 0.9; flex: none; }
/* Wraps rather than ellipsises. An option is an answer, and half of one is
   worse than a smaller whole one on a screen a room reads together: the
   true-or-false bank runs to 77 characters and every one of them was cut. The
   grid fixes the cell's height, so wrapping moves nothing; the size that fits
   is chosen by measuring, in setQuestion. */
.tv-opt-text { flex: 1; min-width: 0; overflow: hidden; line-height: 1.15; }
/* How many in the room took this option. A fact about the room, which is the
   only kind of fact worth putting on a screen everybody is looking at. */
.tv-opt-tail { font-weight: 900; opacity: 0.75; font-variant-numeric: tabular-nums; }
.tv-opt.a-amber { color: oklch(0.22 0.05 75); }
.tv-opt.a-teal { color: oklch(0.16 0.04 200); }
.tv-opt.is-right { outline-color: var(--good); }
.tv-opt.is-out { opacity: 0.35; }
/* The question dims at the reveal so the answer is what the eye lands on. */
#tv-question.is-revealed .tv-q { opacity: 0.5; }
#tv-question.is-revealed .tv-opt { border-radius: 1vw; padding: 0.8vw 1.4vw; }
#tv-question.is-revealed .tv-options { grid-template-rows: repeat(4, minmax(0, 1fr)); grid-template-columns: 1fr; }
.tv-foot {
  font-weight: 700; font-size: 1.7vw; color: var(--ink-dim);
  min-height: 2.4vw; text-align: center;
}

/* ─── the standings, at reveal ─── */
/* The largest type on the page, because this is the moment the room wants to
   know who is winning and it used to be the smallest thing on it. */
.tv-standings {
  display: flex; flex-direction: column; gap: 1vh; min-width: 0;
  animation: tv-slide-in 0.5s var(--ease) 0.8s both;
}
.tv-rank {
  display: grid; grid-template-columns: 3vw 1fr auto auto; align-items: center;
  gap: 1.2vw; min-width: 0;
  background: var(--bg-raise); border-radius: 1vw; padding: 1vh 1.4vw;
  box-shadow: inset 0 0 0 0.15vw var(--line);
  animation: tv-swap 0.5s var(--ease) 2.4s both;
}
.tv-rank:first-child { box-shadow: inset 0 0 0 0.18vw var(--gold); }
.tv-rank-n { font-style: normal; font-weight: 900; font-size: 2.3vw; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.tv-rank-who { display: flex; align-items: center; gap: 0.9vw; min-width: 0; }
.tv-rank-face { font-size: 2.3vw; flex: none; }
/* Cut hard. The number after it never moves, whatever the name does. */
.tv-rank-who b { font-weight: 800; font-size: 2.3vw; min-width: 0; overflow: hidden; white-space: nowrap; }
.tv-rank-gain {
  font-weight: 900; font-size: 1.8vw; color: var(--good);
  font-variant-numeric: tabular-nums;
  animation: tv-float-in 0.5s var(--ease) 1.6s both;
}
.tv-rank-score { font-weight: 900; font-size: 2.8vw; font-variant-numeric: tabular-nums; }
/* A numeral rather than a sixth row: past five a television stops being
   readable from three metres, and a number needs no translation. */
.tv-rank-more {
  align-self: flex-end; font-weight: 900; font-size: 1.8vw; color: var(--ink-dim);
  background: var(--bg-raise); border-radius: 999px; padding: 0.4vh 1vw;
}
@keyframes tv-slide-in { from { opacity: 0; transform: translateX(6vw); } to { opacity: 1; transform: none; } }
@keyframes tv-float-in { from { opacity: 0; transform: translateY(1.2vh); } to { opacity: 1; transform: none; } }
@keyframes tv-swap { from { transform: translateY(var(--from, 0)); } to { transform: none; } }

/* ─── the rain ─── */
/* The bottom band and nothing else is ever drawn in it. */
.tv-rain { position: relative; height: 8vh; flex: none; overflow: hidden; }
.tv-drop {
  position: absolute; bottom: 0; display: flex; align-items: baseline; gap: 0.4vw;
  font-size: 4.5vh; line-height: 1;
  animation: tv-rise 1.5s var(--ease) forwards;
}
.tv-drop-n { font-size: 2.6vh; font-weight: 900; color: var(--ink-dim); }
/* The room changing hands is a fact about the room rather than the game, which
   is what the band is already for. It holds rather than rising, because it is
   worth reading and a reaction is not. */
.tv-drop.is-toast {
  transform: translateX(-50%);
  background: var(--bg-raise); border-radius: 999px; padding: 0.6vh 1.4vw;
  box-shadow: inset 0 0 0 0.15vw var(--gold);
  font-size: 2.4vh; font-weight: 800; white-space: nowrap;
  animation: tv-toast 3s var(--ease) forwards;
}
@keyframes tv-toast {
  from { opacity: 0; transform: translate(-50%, 1.5vh); }
  12%, 80% { opacity: 1; transform: translate(-50%, 0); }
  to { opacity: 0; transform: translate(-50%, 0); }
}
@keyframes tv-rise {
  from { transform: translateY(1vh); opacity: 0; }
  25% { opacity: 1; }
  to { transform: translateY(-6vh); opacity: 0; }
}

/* ─── the podium, and the vote beside it ─── */
/* This is what the room is facing when the game ends, and it was a list of
   five rows. Three blocks now, standing where a podium stands: second, first,
   third, left to right. They arrive in the other order, third first, so the
   winner lands last and the eye is already there. */
#tv-podium { position: relative; overflow: hidden; }
.tv-final { flex: 1; display: grid; grid-template-columns: 1fr; gap: 4vw; min-height: 0; align-items: end; }
.tv-final-podium { display: flex; flex-direction: column; gap: 2vh; min-height: 0; justify-content: flex-end; }

.tv-blocks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2vw; align-items: end; }
.tv-block {
  display: flex; flex-direction: column; align-items: center; gap: 1.5vh; min-width: 0;
  animation: tv-rise-block 0.7s var(--ease) var(--rise, 0s) both;
}
.tv-block-who { display: flex; flex-direction: column; align-items: center; gap: 0.5vh; min-width: 0; max-width: 100%; }
.tv-block-face { font-size: 10vh; line-height: 1; }
.is-p1 .tv-block-face { font-size: 12vh; }
/* The name wraps under the tile rather than pushing the column wider, and is
   cut at two lines: a block that grew with its name would move its neighbours. */
.tv-block-who b {
  font-weight: 900; font-size: 2vw; text-align: center; line-height: 1.1;
  max-width: 100%; overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.is-p1 .tv-block-who b { font-size: 2.7vw; }
.is-p2 .tv-block-who b { font-size: 2.3vw; }

.tv-block-step {
  width: 100%; border-radius: 1.5vw 1.5vw 0 0;
  background: var(--bg-raise); box-shadow: inset 0 0 0 0.15vw var(--line);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.5vh;
}
.is-p1 .tv-block-step { height: 39vh; box-shadow: inset 0 0 0 0.2vw var(--gold); }
.is-p2 .tv-block-step { height: 28vh; }
.is-p3 .tv-block-step { height: 20vh; }
.tv-block-n { font-style: normal; font-weight: 900; font-size: 5vw; color: var(--ink-dim); line-height: 1; }
.is-p1 .tv-block-n { color: var(--gold); font-size: 6vw; }
.tv-block-score { font-weight: 900; font-size: 2.2vw; font-variant-numeric: tabular-nums; }
@keyframes tv-rise-block { from { transform: translateY(45vh); opacity: 0; } to { transform: none; opacity: 1; } }

/* Fourth and fifth beneath, at the reveal's row scale. */
.tv-rest { display: flex; flex-direction: column; gap: 0.8vh; }
.tv-rest-row {
  display: grid; grid-template-columns: 2.5vw auto 1fr auto; align-items: center; gap: 1vw;
  background: var(--bg-raise); border-radius: 0.8vw; padding: 0.7vh 1.2vw;
  font-weight: 800; font-size: 1.6vw;
}
.tv-rest.is-tight .tv-rest-row { font-size: 1.3vw; padding: 0.4vh 1vw; }
.tv-rest-row i { font-style: normal; font-weight: 900; color: var(--ink-dim); }
.tv-rest-face { font-size: 2vw; }
.tv-rest-row b { min-width: 0; overflow: hidden; white-space: nowrap; }
.tv-rest-score { font-weight: 900; color: var(--gold); font-variant-numeric: tabular-nums; }

/* The vote takes the rest of the width once it opens, and the podium moves
   aside rather than being replaced: the room has only just read it. */
#tv-podium.is-voting .tv-final { grid-template-columns: 29vw 1fr; align-items: end; }
/* Sized down rather than scaled. A transform keeps the original layout box, so
   a scaled podium still asks for its full height and falls off the bottom of
   the set while looking as though it fits. */
#tv-podium.is-voting .is-p1 .tv-block-step { height: 22vh; }
#tv-podium.is-voting .is-p2 .tv-block-step { height: 16vh; }
#tv-podium.is-voting .is-p3 .tv-block-step { height: 11vh; }
#tv-podium.is-voting .tv-block-face { font-size: 5vh; }
#tv-podium.is-voting .is-p1 .tv-block-face { font-size: 6vh; }
#tv-podium.is-voting .tv-block-who b,
#tv-podium.is-voting .is-p1 .tv-block-who b,
#tv-podium.is-voting .is-p2 .tv-block-who b { font-size: 1.1vw; -webkit-line-clamp: 1; }
#tv-podium.is-voting .tv-block-n { font-size: 2.4vw; }
#tv-podium.is-voting .is-p1 .tv-block-n { font-size: 3vw; }
#tv-podium.is-voting .tv-block-score { font-size: 1.2vw; }
#tv-podium.is-voting .tv-blocks { gap: 0.8vw; }
/* Fourth and fifth do not vanish when the vote opens, they compress: a row
   becomes a chip and keeps its numeral, because the people in it are still in
   the room and about to vote. */
#tv-podium.is-voting .tv-rest { flex-direction: row; flex-wrap: wrap; gap: 0.6vw; }
#tv-podium.is-voting .tv-rest-row { font-size: 1.1vw; padding: 0.4vh 0.8vw; border-radius: 999px; grid-template-columns: auto auto auto; }
#tv-podium.is-voting .tv-rest-row b { display: none; }
#tv-podium.is-voting .tv-rest-face { font-size: 1.4vw; }
.tv-final { transition: grid-template-columns 0.6s var(--ease); }
.tv-block-step { transition: height 0.6s var(--ease); }

.tv-vote { display: flex; flex-direction: column; gap: 2vh; min-width: 0; animation: tv-slide-in 0.5s var(--ease) both; }
.tv-vote-head { display: flex; align-items: center; gap: 1.2vw; }
.tv-vote-bars { display: flex; flex-direction: column; gap: 1.4vh; }
.tv-bar-row { display: flex; flex-direction: column; gap: 0.6vh; min-width: 0; }
.tv-bar-label { font-weight: 800; font-size: 1.8vw; overflow: hidden; white-space: nowrap; }
.tv-bar-row.is-leader .tv-bar-label { color: var(--gold); }
.tv-bar-track {
  position: relative; height: 6vh; border-radius: 0.8vw; overflow: hidden;
  background: var(--bg-sink); box-shadow: inset 0 0 0 0.15vw var(--line);
  display: flex; align-items: center;
}
.tv-bar-row.is-leader .tv-bar-track { box-shadow: inset 0 0 0 0.2vw var(--gold); }
/* Read from three metres, so the fill is a surface rather than a tint, and the
   one in front takes the room's gold. */
.tv-bar-fill {
  position: absolute; inset: 0 auto 0 0; background: var(--line);
  transition: width 0.5s var(--ease);
}
.tv-bar-row.is-leader .tv-bar-fill { background: var(--gold); }
.tv-bar-row.is-leader .tv-bar-faces { color: var(--gold-ink); }
/* The faces sit on the bar, so the room can see who is arguing for what. */
.tv-bar-faces { position: relative; padding: 0 1vw; font-size: 3vh; letter-spacing: 0.2vw; }

@keyframes tv-fall { to { transform: translateY(105vh) rotate(720deg); } }
.tv-confetti { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.tv-confetti i {
  position: absolute; top: -5vh; width: 0.8vw; height: 1.4vw; border-radius: 0.2vw;
  animation: tv-fall linear forwards;
}

/* This screen is the one people stare at, so reduced motion is a real state
   rather than an afterthought: digits cut instead of popping, tiles are simply
   there, and nothing on it moves that did not have to. */
@media (prefers-reduced-motion: reduce) {
  .tv-player, .tv-countdown.tick { animation: none; }
  .tv-bar { transition: none; }
}

/* ─── the final bet ─── */
#tv-wager { align-items: center; justify-content: center; gap: 2vw; text-align: center; }
.tv-wager-title { font-weight: 900; font-size: 7vw; text-transform: uppercase; letter-spacing: -0.03em; line-height: 0.95; }
.tv-legend { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2vw; width: 100%; }
.tv-leg {
  border-radius: 1.5vw; padding: 1.8vw 1.5vw;
  display: flex; flex-direction: column; align-items: center; gap: 0.5vw;
  color: oklch(0.98 0.01 85);
}
.tv-leg.a-amber { color: oklch(0.22 0.05 75); }
.tv-leg.a-teal { color: oklch(0.16 0.04 200); }
.tv-leg .tv-shape { font-size: 2.4vw; }
.tv-leg b { font-weight: 900; font-size: 2vw; }
.tv-leg span:last-child { font-weight: 600; font-size: 1.3vw; opacity: 0.85; }

/* ─── how a fight ended ───
   Not a podium. A fight has a verdict rather than a ranking, so the screen
   leads with which kind of ending it was and lets the bar say the rest. */
#tv-victory { justify-content: center; gap: 2.5vw; text-align: center; }
.tv-vic-bar {
  height: 1.6vw; border-radius: 999px; overflow: hidden;
  background: var(--bg-sink); display: flex;
  box-shadow: inset 0 0 0 0.15vw var(--line);
}
.tv-vic-fill { transition: width 0.8s var(--ease); }
.tv-vic-fill-a { background: var(--ink); }
.tv-vic-fill-b { background: var(--gold); }
/* minmax(0, 1fr) rather than 1fr, so a long Greek side name shrinks its own
   column instead of pushing the frame past its gutters. */
.tv-vic-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center; gap: 3vw;
}
.tv-vic-side { display: flex; flex-direction: column; gap: 0.6vw; min-width: 0; }
/* One line, never broken mid word. "FOXES" at 200px is 600px of glyphs in a
   626px column, so anywhere-wrapping split it into FOXE and S. tv.js steps the
   size down until the name fits instead, which is the same answer the phone's
   side names needed. */
.tv-vic-side b {
  font-weight: 900; line-height: 0.9;
  text-transform: uppercase; letter-spacing: -0.03em;
  white-space: nowrap;
}
/* Sides keep their place and their material to the end. Left is cream and right
   is gold, as they have been since the first question, and the identity
   system's only promise is that it never flips. */
.tv-vic-a b { color: var(--ink); }
.tv-vic-b b { color: var(--gold); }
/* Outcome is carried by scale, not by fading anybody: the winner at 200px and
   the loser at 120px, both at full strength. It is the podium's own grammar,
   where the winner is bigger and the loser smaller but still whole. A dimmed
   side reads as faded, which is not what losing a fight is. */
#tv-victory .tv-vic-a b { font-size: 10.4vw; }
#tv-victory .tv-vic-b b { font-size: 6.25vw; }
#tv-victory.winner-is-b .tv-vic-a b { font-size: 6.25vw; }
#tv-victory.winner-is-b .tv-vic-b b { font-size: 10.4vw; }
.tv-vic-note { font-weight: 700; font-size: 1.6vw; color: var(--ink-dim); }
.tv-vic-crew { font-size: 3vw; letter-spacing: 0.1em; }
/* The verdict word wears the winner's material, for the same reason the names
   do: gold over a bar full of cream argues with the bar. Decision is 120px, a
   knockout 360px, because it was a louder ending. */
.tv-vic-verdict {
  font-weight: 900; font-size: 6.25vw; line-height: 0.85; color: var(--ink);
  text-transform: uppercase; letter-spacing: -0.04em;
}
.tv-vic-verdict.is-ko { font-size: 18.75vw; }
#tv-victory.winner-is-b .tv-vic-verdict { color: var(--gold); }
.tv-vic-detail { font-weight: 700; font-size: 1.9vw; color: var(--ink-dim); }

/* ─── shuffling ─── */
/* The phone's own die, and the room's topic chips beneath it, so the screen
   says what is being shuffled without a sentence to translate. */
#tv-loading { align-items: center; justify-content: center; gap: 4vh; position: relative; }
.tv-corner-code {
  position: absolute; top: 3.3vw; right: 5vw;
  font-weight: 900; font-size: 2.3vw; letter-spacing: 0.18em; color: var(--gold);
}
.tv-dice { font-size: 24vh; line-height: 1; animation: tv-rock 1.2s var(--ease) infinite; }
@keyframes tv-rock { 0%, 100% { transform: rotate(-12deg); } 50% { transform: rotate(12deg); } }
.tv-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 1vw; max-width: 80vw; }
.tv-chip {
  background: var(--bg-raise); border-radius: 999px; padding: 1.3vh 1.8vw;
  font-weight: 800; font-size: 2.1vw; box-shadow: inset 0 0 0 0.16vw var(--line);
}
.tv-steps { display: flex; gap: 1vw; }
.tv-steps i {
  width: 1.1vh; height: 1.1vh; border-radius: 999px; background: var(--gold);
  animation: tv-step 1.4s var(--ease) infinite;
}
.tv-steps i:nth-child(2) { animation-delay: 0.2s; }
.tv-steps i:nth-child(3) { animation-delay: 0.4s; }

@media (prefers-reduced-motion: reduce) {
  .tv-held-ring, .tv-pulse, .tv-dice, .tv-steps i, .tv-flow i { animation: none; }
  /* The choreography becomes an arrangement: everything is where it ended up,
     and a reaction appears in the band and fades rather than travelling. */
  .tv-standings, .tv-rank, .tv-rank-gain, .tv-block, .tv-vote { animation: none; }
  .tv-drop { animation: tv-hold 1.5s linear forwards; }
  @keyframes tv-hold { 0%, 60% { opacity: 1; } to { opacity: 0; } }
}
