/* Light is the default; dark applies when the OS prefers it, unless the user picked a theme (data-theme on <html>). */
:root {
  color-scheme: light;
  --bg: #faf6f1;
  --surface: #ffffff;
  --surface-2: #f3ede6;
  --ink: #23212b;
  --ink-rgb: 35 33 43;
  --muted: #6b6676;
  --line: #e4ddd4;
  --accent: #d9622b;
  --accent-rgb: 217 98 43;
  --accent-ink: #ffffff;
  --right: #1f8a5b;
  --right-rgb: 31 138 91;
  --wrong: #c43c4f;
  --wrong-rgb: 196 60 79;
  --info: #0b6aa2;
  --sweden: #d9a400;
  --tint: 12%;
  --shadow: 0 0.4rem 1.6rem rgb(40 30 20 / 8%);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #15141a;
    --surface: #201f27;
    --surface-2: #2b2a34;
    --ink: #eeebf3;
  --ink-rgb: 238 235 243;
    --ink-rgb: 238 235 243;
    --muted: #a7a2b3;
    --line: #3a3844;
    --accent: #ff8a4c;
  --accent-rgb: 255 138 76;
    --accent-rgb: 255 138 76;
    --accent-ink: #1a1208;
    --right: #4fd1a0;
    --right-rgb: 79 209 160;
    --wrong: #ff7a8a;
    --wrong-rgb: 255 122 138;
    --info: #6cc0ff;
    --sweden: #ffd24d;
    --tint: 18%;
    --shadow: 0 0.4rem 1.6rem rgb(0 0 0 / 40%);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #15141a;
  --surface: #201f27;
  --surface-2: #2b2a34;
  --ink: #eeebf3;
  --muted: #a7a2b3;
  --line: #3a3844;
  --accent: #ff8a4c;
  --accent-ink: #1a1208;
  --right: #4fd1a0;
  --right-rgb: 79 209 160;
  --wrong: #ff7a8a;
  --wrong-rgb: 255 122 138;
  --info: #6cc0ff;
  --sweden: #ffd24d;
  --tint: 18%;
  --shadow: 0 0.4rem 1.6rem rgb(0 0 0 / 40%);
}

:root {
  /* Tints are written as rgb(triplet / alpha), not color-mix(): a Chromecast runs Chrome 90, which drops
     any declaration that uses color-mix().
     One colour per answer position, the same on the TV and the phones. None is red or green, which mean wrong and right. */
  --o1: 44 102 224;
  --o2: 179 52 140;
  --o3: 168 100 0;
  --o4: 11 127 140;
  /* The highlight the remote moves: a gap in the page colour, then a solid ring, so it reads on any button. */
  --ring: 0 0 0 0.2rem var(--bg), 0 0 0 0.5rem var(--accent);
  /* How much of each screen edge a TV may crop. */
  --overscan: 0px;
  --radius: 1.1rem;
  --gap: 1.25rem;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-display: ui-rounded, "SF Pro Rounded", "Nunito", var(--font);
  /* ~19px on a laptop, ~25px on a 1080p TV, ~32px on 4K. */
  font-size: clamp(17px, 0.85vw + 9px, 64px);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  line-height: 1.55;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, .huge, #hud, button.link {
  font-family: var(--font-display);
  letter-spacing: -0.01em;
  line-height: 1.2;
}

h1 { font-size: 2rem; margin: 0 0 1.25rem; font-weight: 800; }

h2 { font-size: 1.4rem; margin: 0 0 0.5rem; }

h3 { font-size: 1.15rem; margin: 0 0 0.4rem; }

p { margin: 0 0 0.75rem; }

a { color: var(--info); }

b { font-weight: 700; }

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

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 2.5vw;
}

.header-right { display: flex; align-items: center; gap: 1rem; }

#hud { font-weight: 800; font-size: 1.25rem; min-width: 4ch; text-align: right; }

main {
  flex: 1;
  width: min(1180px, 92vw);
  margin: 0 auto;
  padding-bottom: 6rem;
}

kbd {
  font-family: var(--font);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 0.35em;
  padding: 0.05em 0.4em;
}

/* ---------- small text ---------- */

.dutch { color: var(--muted); font-style: italic; font-weight: 500; font-size: 0.75em; }

.meta, .crumb, .note { color: var(--muted); }

.meta { font-size: 0.85rem; }

.note { font-size: 0.85rem; margin-top: 1rem; }

.crumb { font-size: 0.85rem; font-weight: 600; margin: 0.9rem 0; }

/* ---------- buttons ---------- */

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  border: none;
  border-radius: var(--radius);
  transition: transform 0.12s, background-color 0.12s, border-color 0.12s;
}

button:focus-visible { outline: 0.2rem solid var(--accent); outline-offset: 0.2rem; }

button:active:not(:disabled) { transform: scale(0.98); }

button:disabled { opacity: 0.35; cursor: default; }

button.link { background: none; font-weight: 800; font-size: 1.15rem; padding: 0.3rem 0; }

button.primary, button.secondary { padding: 0.8rem 1.8rem; font-weight: 700; }

button.primary { background: var(--accent); color: var(--accent-ink); }

button.secondary { background: var(--surface-2); }

button.icon-btn {
  background: var(--surface-2);
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  font-size: 1.1rem;
}

button.big { font-size: 1.15rem; margin-bottom: 1.75rem; }

button.link { border-radius: 0.5rem; }

/* ---------- slides ---------- */

.progress { height: 0.5rem; background: var(--surface-2); border-radius: 1rem; overflow: hidden; }

.progress div { height: 100%; background: var(--accent); border-radius: 1rem; transition: width 0.3s; }

.slide {
  background: var(--surface);
  border-radius: calc(var(--radius) * 1.4);
  padding: 2.5rem 3rem;
  box-shadow: var(--shadow);
  min-height: 60vh;
}

.slide.title, .slide.summary {
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.huge { font-size: 5.5rem; line-height: 1; margin-bottom: 1rem; }
img.mark { width: 1em; height: 1em; vertical-align: -0.15em; }

.intro { font-size: 1.2rem; max-width: 32em; color: var(--muted); }

.slide .body { max-width: 46em; font-size: 1.05rem; }

.slide .body li { margin-bottom: 0.6rem; }

.slide .body ol, .slide .body ul { padding-left: 1.4em; }

.slide table { border-collapse: collapse; width: 100%; font-size: 0.95rem; margin: 1rem 0; }

.slide th, .slide td { border: 1px solid var(--line); padding: 0.6rem 0.8rem; text-align: left; vertical-align: top; }

.slide th { background: var(--surface-2); }

/* ---------- quiz ---------- */

.slide.quiz h1 { font-size: 1.6rem; font-weight: 750; }

.options { display: grid; gap: 0.8rem; }

.option {
  background: linear-gradient(rgb(var(--o-rgb) / var(--tint)), rgb(var(--o-rgb) / var(--tint))) var(--surface);
  text-align: left;
  padding: 1rem 1.2rem;
  border: 0.18rem solid rgb(var(--o-rgb) / 45%);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  gap: 1rem;
  font-size: 1.05rem;
  line-height: 1.35;
}

.option:nth-child(4n + 1), .choice:nth-child(4n + 1) { --o-rgb: var(--o1); }

.option:nth-child(4n + 2), .choice:nth-child(4n + 2) { --o-rgb: var(--o2); }

.option:nth-child(4n + 3), .choice:nth-child(4n + 3) { --o-rgb: var(--o3); }

.option:nth-child(4n + 4), .choice:nth-child(4n + 4) { --o-rgb: var(--o4); }

.option .key, .win .key {
  flex: none;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgb(var(--o-rgb));
  color: #fff;
  font-family: var(--font);
  font-weight: 800;
  font-size: 0.9rem;
}

.option.correct { background: linear-gradient(rgb(var(--right-rgb) / var(--tint)), rgb(var(--right-rgb) / var(--tint))) var(--surface); border-color: var(--right); }

.slide.quiz.answered .option:not(.correct) { opacity: 0.5; }

/* The correct answer is the one solid colour on an answered slide; the notes around it stay neutral. */
.feedback { margin-top: 1.5rem; display: grid; gap: 0.9rem; align-content: start; }

.win {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  background: var(--right);
  color: var(--surface);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.4rem;
  line-height: 1.15;
}

.win .key { background: var(--surface); color: var(--right); }

.win .verdict { margin-left: auto; font-family: var(--font); font-size: 0.95rem; font-weight: 700; white-space: nowrap; }

.explanation { font-size: 1.1rem; }

.explanation p { margin: 0 0 0.5rem; }

.aside { padding: 0.6rem 0.9rem; border-radius: 0.7rem; background: var(--surface-2); font-size: 0.95rem; }

.aside-label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }

.aside-label::before { content: ""; width: 0.7rem; height: 0.7rem; border-radius: 50%; background: var(--dot); }

.aside.remember { --dot: var(--right); font-weight: 600; }

.aside.conflict { --dot: var(--accent); }

.aside.sweden { --dot: var(--sweden); }

.slide.quiz.roomy .explanation { font-size: 1.35rem; }

.slide.quiz.roomy .aside { font-size: 1.1rem; }

nav { display: flex; justify-content: space-between; padding-top: 1.5rem; }

/* ---------- summary ---------- */

.review { margin-top: 2rem; background: var(--surface); border-radius: var(--radius); padding: 1.5rem 2rem; }

.review li { margin-bottom: 1rem; }

.confetti { text-align: center; font-size: 2rem; animation: pop 0.6s ease-out; }

@keyframes pop {
  from { transform: scale(0.97); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- TV / wide screens ---------- */

@media (min-width: 1200px) {
  main { width: 94vw; }

  .slide.quiz.answered {
    display: grid;
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
    gap: 2.5rem;
    align-items: start;
  }

  .slide.quiz.answered .feedback { margin-top: 0; }
}

nav button.secondary { background: var(--surface); border: 0.12rem solid var(--line); }

.course-actions { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin-bottom: 1.75rem; }

.course-actions button.big { margin-bottom: 0; }


/* ---------- lost Firestore listener ---------- */

.live-down {
  position: fixed;
  left: 50%;
  bottom: 1rem;
  transform: translateX(-50%);
  z-index: 50;
  padding: 0.6rem 1.1rem;
  border-radius: 2rem;
  background: var(--ink);
  color: var(--bg);
  font-size: 0.9rem;
  font-weight: 700;
}

/* ---------- help overlay ---------- */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: rgb(0 0 0 / 55%);
}

.overlay[hidden] { display: none; }

.overlay-card {
  background: var(--surface);
  border-radius: calc(var(--radius) * 1.4);
  padding: 2.5rem 3rem;
  box-shadow: var(--shadow);
  min-width: min(24rem, 100%);
}

.overlay-card dl { display: grid; grid-template-columns: auto 1fr; gap: 0.8rem 1.5rem; margin: 1rem 0 0; }

.overlay-card dt { text-align: right; }

.overlay-card dd { margin: 0; }

/* ---------- full-screen slide mode (TV) ----------
   Sizes follow the viewport with no pixel caps, so a 4K browser fills the screen.
   A Chromecast (including Cast built into 4K TVs) renders at 1280x720 CSS px at 1.5x, i.e. 1080p, and the TV upscales. */

body.on-slide { height: 100vh; overflow: hidden; }

body.on-slide header { padding: 0.8rem 2.5vw; height: 4rem; }

body.on-slide header button.link, body.on-slide #hud { font-size: 1rem; }

body.on-slide main {
  width: 100%;
  height: calc(100vh - 4rem);
  padding: 0 2.5vw 2.5vh;
  display: flex;
  flex-direction: column;
}

body.on-slide .progress { position: fixed; top: 0; left: 0; right: 0; height: 0.35rem; border-radius: 0; }

body.on-slide .crumb {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  height: 4rem;
  margin: 0;
  display: flex;
  align-items: center;
  white-space: nowrap;
}

body.on-slide .slide {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 5vh 5vw;
  display: flex;
  flex-direction: column;
  justify-content: center;
  justify-content: safe center;
}

body.on-slide .slide.quiz.answered { justify-content: start; }

body.on-slide .slide .body { max-width: 52em; font-size: 1.15rem; }

body.on-slide .slide.info h1 { font-size: 2.4rem; }

body.on-slide .slide.fact .eyebrow,
body.on-slide .slide.quiz .eyebrow { font-size: 1.1rem; margin-bottom: 0.5rem; }

body.on-slide .slide.fact .body { font-size: 1.45rem; }

body.on-slide .slide.quiz h1 { font-size: 1.9rem; max-width: 40em; }

body.on-slide .huge { font-size: 8rem; }

body.on-slide .slide.title h1 { font-size: 3rem; }

body.idle { cursor: none; }

body.idle nav.slide-nav { opacity: 0; }

@media (min-width: 1200px) {
  /* Scale with both width and height so a slide fills a 16:9 TV without scrolling. */
  :root.slide-scale { font-size: clamp(16px, min(1.3vw, 2.35vh), 90px); }

  body.on-slide .slide.quiz:not(.answered) .options { grid-template-columns: 1fr 1fr; gap: 1.2rem; }

  body.on-slide .slide.quiz:not(.answered) .option { padding: 1.4rem 1.6rem; font-size: 1.2rem; }

  body.on-slide .slide.quiz.answered {
    display: grid;
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
    gap: 3vw;
    align-content: center;
    align-content: safe center;
  }

  body.on-slide .slide.quiz.answered h1 { font-size: 1.15rem; margin-bottom: 0.9rem; }

  body.on-slide .slide.quiz.answered .options { gap: 0.5rem; }

  body.on-slide .slide.quiz.answered .option { padding: 0.5rem 0.8rem; gap: 0.7rem; font-size: 0.95rem; }
}

.overlay-card { max-width: min(60rem, 100%); max-height: 100%; overflow-y: auto; }

button.icon-btn[hidden] { display: none; }

.eyebrow { color: var(--accent); font-weight: 700; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em; }

/* ---------- lobby (TV) ----------
   A game's first screen: how to join, what the course holds and where the run stands, with no scrolling. */

body.on-lobby { height: 100vh; overflow: hidden; }

body.on-lobby main { width: 100%; min-height: 0; padding: 0 2.5vw 2.5vh; display: flex; flex-direction: column; }

:root[data-display="on"] body.on-lobby main { padding-left: 0; padding-right: 0; }

.lobby { flex: 1; min-height: 0; overflow: hidden; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 3vw; }

.lobby-join { display: flex; flex-direction: column; gap: 0.5rem; }

/* The QR code takes the height the text below it leaves, so it scans from across a room. */
.lobby-join img { width: min(52vh, 30vw); height: min(52vh, 30vw); background: #fff; border-radius: 1.2rem; }

.lobby-lead { font-family: var(--font-display); font-size: 1.5rem; font-weight: 800; line-height: 1.1; margin-top: 0.4rem; }

.lobby-host { color: var(--muted); font-size: 0.95rem; font-weight: 600; max-width: min(52vh, 30vw); overflow-wrap: anywhere; }

.lobby-code { font-family: var(--font-display); font-size: 2.8rem; font-weight: 800; letter-spacing: 0.08em; line-height: 1; }

.lobby-course { display: flex; flex-direction: column; gap: 0.8rem; min-width: 0; }

.lobby-course h1 { margin: 0; }

.lobby-course h1 .dutch { display: block; }

.lobby-summary { margin: 0; color: var(--muted); font-size: 0.95rem; line-height: 1.45; }

.lobby-stats { margin: 0; font-size: 0.95rem; font-weight: 700; }

.path { list-style: none; margin: 0; padding: 0.4rem 0 0; display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: 0.8rem 0.4rem; }

.path li { position: relative; display: flex; flex-direction: column; align-items: center; gap: 0.3rem; text-align: center; font-size: 0.8rem; font-weight: 600; line-height: 1.25; }

.path.started li { color: var(--muted); }

.path.started li.done { color: var(--ink); }

.path.started li.now { color: var(--accent); font-weight: 800; }

.path-icon { position: relative; width: 2.6rem; height: 2.6rem; border-radius: 50%; display: grid; place-items: center; font-size: 1.3rem; line-height: 1; background: var(--surface-2); border: 0.15rem solid var(--line); }

/* The line to the next module; the last module of a row has none. */
.path li:not(.row-end)::before { content: ""; position: absolute; top: 1.25rem; left: calc(50% + 1.6rem); width: calc(100% - 2.8rem); height: 0.15rem; background: var(--line); }

.path li.done::before { background: var(--right); }

.path li.done .path-icon { border-color: var(--right); }

.path li.done .path-icon::after { content: "✓"; position: absolute; top: -0.45rem; right: -0.45rem; width: 1.15rem; height: 1.15rem; border-radius: 50%; display: grid; place-items: center; background: var(--right); color: var(--bg); font-size: 0.75rem; font-weight: 800; }

.path li.now .path-icon { border-color: var(--accent); border-width: 0.25rem; }

/* Three rows of names at most: tv.js counts the ones that do not fit. */
.lobby-players { align-items: center; gap: 0.45rem; margin-top: 0.4rem; max-height: 7.6rem; overflow: hidden; }

.lobby-players .chip { font-size: 1.1rem; padding: 0.15rem 0.75rem; }

.lobby-players .chip.new { border-color: var(--accent); background: linear-gradient(rgb(var(--accent-rgb) / 22%), rgb(var(--accent-rgb) / 22%)) var(--surface-2); }

.lobby-players .chip.more { color: var(--muted); }

/* Margins centre the banner, which leaves transform to the cue that brings it in. */
.join-banner { position: fixed; top: max(1.5vh, 0.6rem); left: 0; right: 0; margin: 0 auto; width: fit-content; max-width: 70vw; z-index: 5; padding: 0.45rem 1.4rem; border-radius: 2rem; background: var(--accent); color: var(--accent-ink); font-family: var(--font-display); font-size: 1.5rem; font-weight: 800; line-height: 1.2; text-align: center; }

:root[data-cast="on"] .join-banner { top: 3vh; }

/* The host who is speaking sits in the slide's bottom corner, over its padding, and only while a clip plays. */
.speaker { position: fixed; right: 3.5vw; bottom: 3.5vh; z-index: 4; display: flex; align-items: center; gap: 0.5rem; pointer-events: none; }

.speaker[hidden] { display: none; }

.speaker img { width: 4.5rem; height: 4.5rem; border-radius: 50%; border: 0.2rem solid var(--accent); background: var(--surface-2); object-fit: cover; }

.speaker span { padding: 0.15rem 0.7rem; border-radius: 2rem; background: var(--accent); color: var(--accent-ink); font-family: var(--font-display); font-size: 0.9rem; font-weight: 800; }

:root[data-cast="on"] .speaker { right: 5vw; bottom: 5vh; }

.lobby-action > div { min-width: 0; }

.lobby-action { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; background: var(--surface); border-radius: calc(var(--radius) * 1.4); padding: 1rem 1.5rem; }

.lobby-action button.primary { flex: none; padding: 0.9rem 2.4rem; font-size: 1.15rem; }

/* The TV page in a narrow window scrolls instead. */
@media (max-width: 800px) {
  body.on-lobby { height: auto; overflow: visible; }

  .lobby { grid-template-columns: minmax(0, 1fr); overflow: visible; }
}

/* ---------- slide navigation ----------
   Back and Next are strips that slide in from the screen edge when the remote pushes past it,
   and the slide gives up that much of its width so they never cover it and it stays on the screen. */

nav.slide-nav { position: fixed; inset: 0; padding: 0; pointer-events: none; transition: opacity 0.3s; }

nav.slide-nav button.edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: calc(var(--overscan) + 4.5rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  border-radius: 0;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.95rem;
  font-weight: 800;
  pointer-events: auto;
  visibility: hidden;
}

.edge .glyph { font-size: 2rem; line-height: 1; }

nav.slide-nav button.edge.back { left: 0; padding-left: var(--overscan); transform: translateX(-100%); }

nav.slide-nav button.edge.next { right: 0; padding-right: var(--overscan); transform: translateX(100%); }

nav.slide-nav button.edge.revealed { visibility: visible; transform: none; }

body.on-slide.strip-next .slide { margin-right: 4.5rem; }

body.on-slide.strip-back .slide { margin-left: 4.5rem; }

/* Cast mode bans motion because long animations smear in the stream. These short moves are the
   exception: a strip that appears with no travel does not read as coming from the edge. */
@media (prefers-reduced-motion: no-preference) {
  :root nav.slide-nav button.edge { transition: transform 0.2s ease-out, visibility 0s 0.2s !important; }

  :root nav.slide-nav button.edge.revealed { transition: transform 0.2s ease-out !important; }
}

/* ---------- cast mode ----------
   Chrome "Cast tab" encodes the tab as ~1080p video at a low frame rate.
   Motion, soft shadows, gradients, translucency and thin or small text are what
   the encoder smears or bands, so cast mode replaces them with flat, large, high-contrast shapes. */

button.icon-btn.active { background: var(--accent); }

:root[data-cast="on"] {
  --overscan: 4vw;
  --muted: #55505f;
  --line: #b9b1a6;
  --shadow: none;
  font-size: clamp(14px, min(1.45vw, 2.6vh), 100px);
}

@media (min-width: 1200px) {
  :root[data-cast="on"].slide-scale { font-size: clamp(14px, min(1.3vw, 2.35vh), 90px); }
}

@media (prefers-color-scheme: dark) {
  :root[data-cast="on"]:not([data-theme="light"]) { --muted: #c9c4d4; --line: #6a6676; --surface-2: #34333f; }
}

:root[data-cast="on"][data-theme="dark"] { --muted: #c9c4d4; --line: #6a6676; --surface-2: #34333f; }

:root[data-cast="on"] *,
:root[data-cast="on"] *::before,
:root[data-cast="on"] *::after {
  animation: none !important;
  transition: none !important;
  box-shadow: none !important;
  text-shadow: none !important;
}

/* ---------- cues ----------
   cues.js puts these classes on the elements a cue is about. They are the second exception to cast
   mode's ban: each is short, moves only transform and opacity, and ends on the element's resting
   style, so a redraw part-way through only cuts it short. */
@media (prefers-reduced-motion: no-preference) {
  .cue-pop, :root[data-cast="on"] .cue-pop { animation: cue-pop 0.28s ease-out calc(var(--cue-i, 0) * 70ms) backwards !important; }

  .cue-rise, :root[data-cast="on"] .cue-rise { animation: cue-rise 0.3s ease-out calc(var(--cue-i, 0) * 70ms) backwards !important; }

  .cue-bump, :root[data-cast="on"] .cue-bump { animation: cue-bump 0.3s ease-out !important; }

  .cue-fade, :root[data-cast="on"] .cue-fade { animation: cue-fade 0.2s ease-out !important; }

  .cue-shake, :root[data-cast="on"] .cue-shake { animation: cue-shake 0.3s ease-in-out !important; }
}

@keyframes cue-pop {
  from { transform: scale(0.6); opacity: 0; }
}

@keyframes cue-rise {
  from { transform: translateY(0.7rem); opacity: 0; }
}

@keyframes cue-bump {
  45% { transform: scale(1.12); }
}

@keyframes cue-fade {
  from { opacity: 0.25; }
}

@keyframes cue-shake {
  20%, 60% { transform: translateX(-0.3rem); }
  40%, 80% { transform: translateX(0.3rem); }
}

/* Safe area: some TVs crop the outer edges (overscan). */
:root[data-cast="on"] body { padding: 3vh 4vw; }

:root[data-cast="on"] body.on-slide main { height: calc(100vh - 4rem - 6vh); }

:root[data-cast="on"] body.on-slide .progress { top: 3vh; left: 4vw; right: 4vw; height: 0.5rem; border-radius: 1rem; }

:root[data-cast="on"] body.on-slide .crumb { top: 3vh; }

/* No text below ~22px on a 1080p cast. */
:root[data-cast="on"] .meta,
:root[data-cast="on"] .crumb,
:root[data-cast="on"] .note,
:root[data-cast="on"] .eyebrow { font-size: 0.95rem; }

/* Faded options become solid colours so the encoder does not blend them into the background. */
:root[data-cast="on"] .slide.quiz.answered .option:not(.correct) { opacity: 1; color: var(--muted); background: var(--surface); border-color: var(--line); }

:root[data-cast="on"] .lobby-action,
:root[data-cast="on"] .slide { border-width: 0.1rem; border-style: solid; border-color: var(--line); }

:root[data-cast="on"] .option.correct { border-width: 0.3rem; }

:root[data-cast="on"] .overlay { background: var(--bg); }

:root[data-cast="on"] body.on-slide .slide { padding: 4vh 4vw; }

a.icon-btn { display: grid; place-items: center; text-decoration: none; }

a.icon-btn[hidden] { display: none; }

/* ---------- D-pad highlight ----------
   Every button the remote can reach is highlighted the same way, on every screen. */

body.dpad :is(button, a) { transition: transform 0.18s ease, box-shadow 0.18s ease; }

body.dpad :is(button, a):focus { outline: none; }

body:focus { outline: none; }

/* !important because cast mode strips every other shadow. */
body.dpad :is(button.primary, button.secondary, button.link, .tv-toolbar button):focus {
  transform: scale(1.04);
  box-shadow: var(--ring) !important;
  position: relative;
  z-index: 1;
}

/* Back and Next can hold the highlight, so they must stay visible while the remote is in use. */
body.idle.dpad nav.slide-nav { opacity: 1; }

/* The TV is driven by the phone remote, which has these actions, so the TV shows only title and score. */
:root[data-display="on"] header .icon-btn { display: none; }

:root[data-display="on"] header button.link { pointer-events: none; }

/* No mouse on the TV, so a scrollbar is only clutter. */
/* A slide that moves aside for an edge strip must not make the page wider than the screen. */
:root[data-display="on"] { scrollbar-width: none; overflow-x: hidden; }

:root[data-display="on"] ::-webkit-scrollbar { display: none; }

:root[data-display="on"] body.on-slide main { padding-left: 0; padding-right: 0; }

/* ---------- group game (TV) ---------- */

.tv-toolbar { display: flex; gap: 0.8rem; visibility: hidden; }

.tv-toolbar.revealed { visibility: visible; }

.tv-toolbar button {
  padding: 0.5rem 1.1rem;
  border-radius: 2rem;
  background: var(--surface-2);
  font-weight: 700;
  font-size: 0.9rem;
}

.option-text { display: flex; flex-direction: column; gap: 0.45rem; }

.chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }

.chip {
  display: inline-block;
  padding: 0.12rem 0.65rem;
  border-radius: 1rem;
  background: var(--surface);
  border: 0.1rem solid var(--line);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--ink);
}

.game-status {
  margin-top: 1.5rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}

.game-count { font-family: var(--font-display); font-size: 1.3rem; }

/* An inline box cannot be transformed, and the count bumps when it changes. */
.game-count b { display: inline-block; color: var(--accent); font-size: 1.6rem; }

/* A title or results slide centres its contents, which would shrink the bar to nothing. */
.slide.title .moving-on { width: 30rem; max-width: 100%; }

.countdown { flex: 1; min-width: 6rem; height: 0.5rem; border-radius: 1rem; background: var(--surface-2); overflow: hidden; }

.countdown div { height: 100%; border-radius: 1rem; background: var(--accent); }

.join-hint img { width: 7rem; height: 7rem; background: #fff; padding: 0.3rem; border-radius: 0.6rem; flex: none; }

.game-waiting { color: var(--muted); display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }

.scores-head { display: flex; flex-direction: column; align-items: center; }

/* tv.js sets data-head and data-rows on the slide and --columns on the list, to the roomiest arrangement that fits the screen. */
.slide[data-head="compact"] .scores-head { flex-direction: row; gap: 0.7rem; }

.slide[data-head="compact"] .scores-head .huge { font-size: 2.6rem; margin: 0; }

body.on-slide .slide[data-head="compact"] .scores-head h1 { font-size: 2rem; margin: 0; }

.slide[data-head="compact"] .leaderboard { margin-top: 1.1rem; }

.leaderboard {
  --row-gap: 0.6rem;
  list-style: none;
  padding: 0;
  margin: 1.5rem auto 0;
  width: min(100%, calc(4rem + 26rem * var(--columns, 1)));
  columns: var(--columns, 1);
  column-gap: 0.8rem;
}

.leaderboard li {
  display: grid;
  grid-template-columns: 2.5rem 1fr auto;
  column-gap: 0.6rem;
  align-items: center;
  margin-bottom: var(--row-gap);
  padding: 0.8rem 1.2rem;
  border-radius: var(--radius);
  background: var(--surface-2);
  font-size: 1.2rem;
  text-align: left;
  break-inside: avoid;
}

.leaderboard li:last-child { margin-bottom: 0; }

.slide[data-rows="m"] .leaderboard { --row-gap: 0.45rem; }

.slide[data-rows="m"] .leaderboard li { grid-template-columns: 2rem 1fr auto; padding: 0.5rem 1rem; font-size: 1.05rem; line-height: 1.3; }

.slide[data-rows="s"] .leaderboard { --row-gap: 0.35rem; }

/* 0.95rem is the smallest text cast mode allows. */
.slide[data-rows="s"] .leaderboard li { grid-template-columns: 1.8rem 1fr auto; padding: 0.35rem 0.8rem; border-radius: 0.7rem; font-size: 0.95rem; line-height: 1.3; }

.leaderboard .medal { font-size: 1.25em; }

.leaderboard .who { font-weight: 700; min-width: 0; overflow-wrap: anywhere; }

.leaderboard .score { font-family: var(--font-display); font-weight: 800; color: var(--accent); white-space: nowrap; }

.leaderboard .tie .who { display: grid; gap: 0.3rem; }

/* In the smaller rows tied names share a wrapping line, so a tie takes width before it takes height. */
.slide:is([data-rows="m"], [data-rows="s"]) .leaderboard .tie .who { display: block; }

.slide:is([data-rows="m"], [data-rows="s"]) .leaderboard .tie .who span + span::before { content: " · "; color: var(--muted); }

.leaderboard .tied { display: block; font-family: var(--font); font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); text-align: right; }

.climber { margin: 1.2rem 0 0; font-size: 1.2rem; font-weight: 700; }

/* Panels stay inside the part of the screen a TV does not crop. */
.overlay { padding: max(3vh, 1rem) max(var(--overscan), 1rem); }

/* The join panel is the QR code at the largest size that leaves room for the address beside it, so it
   scans from across a room. */
.overlay.bare .overlay-card { background: none; box-shadow: none; padding: 0; min-width: 0; width: 100%; max-width: none; max-height: none; overflow: visible; }

.join-panel { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5vw; align-items: center; }

.join-panel img { width: min(84vh, 46vw); height: min(84vh, 46vw); background: #fff; border-radius: 3vh; }

.join-side { display: grid; gap: 2vh; justify-items: start; }

.join-lead { font-family: var(--font-display); font-size: min(6vh, 3.4vw); font-weight: 800; line-height: 1.1; }

.join-host { color: var(--muted); font-size: min(3.6vh, 2vw); font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }

.join-code { font-family: var(--font-display); font-size: min(12vh, 6.8vw); font-weight: 800; letter-spacing: 0.08em; line-height: 1; }

.join-side .chips { margin-top: 1vh; gap: 1.2vh; }

.join-side .chip { font-size: min(3.2vh, 1.8vw); padding: 0.6vh 2.2vh; border-radius: 4vh; }

.player-list { list-style: none; padding: 0; margin: 1rem 0 0; display: grid; gap: 0.6rem; min-width: min(24rem, 100%); }

.player-list li { display: flex; justify-content: space-between; align-items: center; gap: 1rem; font-size: 1.15rem; font-weight: 700; }

.player-list li span { min-width: 0; overflow-wrap: anywhere; }

.player-list .ago { display: block; font-size: 0.85rem; font-weight: 500; color: var(--muted); }

.player-list button { flex: none; padding: 0.45rem 1.1rem; font-size: 0.85rem; }

/* ---------- simple pages: landing, host, offline ---------- */

body.simple {
  display: block;
  padding: max(1rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right)) max(1.5rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));
  font-size: 18px;
}

body.simple main { width: min(46rem, 100%); padding: 0; margin: 0 auto; }

body.simple header { padding: 0 0 1rem; max-width: 46rem; margin: 0 auto; }

a.brand { font-family: var(--font-display); font-weight: 800; font-size: 1.2rem; color: var(--ink); text-decoration: none; }
header a.small { font-family: var(--font-display); font-weight: 800; font-size: 0.85rem; color: var(--ink); text-decoration: none; }

button.small { font-size: 0.85rem; }

.panel { background: var(--surface); border-radius: calc(var(--radius) * 1.2); padding: 1.6rem; box-shadow: var(--shadow); }

.panel h1 { font-size: 1.6rem; }

.panel h2 { font-size: 1.1rem; margin-top: 1.8rem; }

/* ---------- dashboard ---------- */

.dashboard .panel { padding: 1.1rem; }

.dashboard h1 { margin-bottom: 0.75rem; }

.dashboard nav { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 0.5rem; padding-top: 0; margin-bottom: 1rem; }

.dashboard nav a { padding: 0.35rem 0.9rem; border-radius: var(--radius); background: var(--surface-2); color: var(--ink); font-size: 0.85rem; font-weight: 600; text-decoration: none; }

.dashboard nav a[aria-current] { background: var(--accent); color: var(--accent-ink); }

.dashboard .tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin: 1.25rem 0 2rem; }

.dashboard .tiles div { padding: 0.7rem 0.75rem; border-radius: var(--radius); background: var(--surface-2); }

.dashboard .tiles h2 { margin: 0; font-family: var(--font); font-size: 0.75rem; font-weight: 600; letter-spacing: 0; color: var(--muted); }

.dashboard .tiles p { margin: 0; font-size: 0.7rem; color: var(--muted); }

.dashboard .tiles .figure { font-family: var(--font-display); font-size: 2rem; font-weight: 800; line-height: 1.2; color: var(--ink); }

.dashboard .figure, .dashboard table { font-variant-numeric: tabular-nums; }

/* A fixed layout gives every table on the page the same columns whatever its labels are. */
.dashboard table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 0.85rem; }

.dashboard table + table { margin-top: 2rem; }

.dashboard th, .dashboard td { padding: 0; }

.dashboard th { text-align: left; font-weight: 400; }

.dashboard thead th { padding-bottom: 0.5rem; color: var(--muted); white-space: nowrap; }

.dashboard thead th:first-child { width: 26%; }

.dashboard tbody tr + tr { border-top: 1px solid var(--line); }

.dashboard tbody th, .dashboard td { padding: 0.45rem 0.75rem 0.45rem 0; }

.dashboard tbody th { overflow-wrap: anywhere; }

.dashboard time { white-space: nowrap; }

.dashboard td:last-child { padding-right: 0; }

.dashboard td div { display: flex; align-items: center; gap: 0.5rem; }

.dashboard td span { flex: none; width: 1.6rem; text-align: right; }

.dashboard td b { display: block; height: 0.55rem; border-radius: 0.2rem; background: var(--accent); }

.dashboard thead i { display: inline-block; width: 0.55rem; height: 0.55rem; margin-right: 0.3rem; border-radius: 0.2rem; background: var(--accent); }

.dashboard .second { background: var(--info); }

.dashboard .quiet { color: var(--muted); opacity: 0.55; }

.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.9rem; }

.stack.row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; align-items: end; }

@media (max-width: 40rem) { .stack.row { grid-template-columns: minmax(0, 1fr); } }

.stack label { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.35rem; font-weight: 600; font-size: 0.9rem; }

.stack input, .stack select {
  width: 100%;
  min-width: 0;
  font: inherit;
  padding: 0.8rem 0.9rem;
  border-radius: 0.7rem;
  border: 0.12rem solid var(--line);
  background: var(--surface-2);
  color: var(--ink);
}

.stack input:focus, .stack select:focus { outline: none; border-color: var(--accent); }

/* Stands in for a select with one option; the transparent border keeps its text on the neighbouring input's baseline. */
.stack .only-choice { padding: 0.8rem 0; border: 0.12rem solid transparent; font-size: 1rem; }

/* Without base-select the OS draws the open list and ignores every token here. */
@supports (appearance: base-select) {
  .stack select, .stack select::picker(select) { appearance: base-select; }

  .stack select { cursor: pointer; }

  .stack select::picker-icon { color: var(--muted); transition: rotate 0.12s; }

  .stack select:open::picker-icon { rotate: 180deg; }

  .stack select::picker(select) {
    margin-top: 0.3rem;
    padding: 0.3rem;
    border-radius: 0.7rem;
    border: 0.12rem solid var(--line);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow);
  }

  .stack option { padding: 0.6rem 0.7rem; border-radius: 0.5rem; font-weight: 500; cursor: pointer; }

  .stack option:hover, .stack option:focus-visible { background: var(--surface-2); outline: none; }

  .stack option:checked { font-weight: 700; }

  .stack option::checkmark { color: var(--accent); }
}

.status { color: var(--muted); min-height: 1.3em; font-size: 0.9rem; }

.code { font-family: ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: 0.12em; }

.game-list { list-style: none; padding: 0; margin: 1.5rem 0 0; display: grid; gap: 0.6rem; }

.game-list a {
  display: grid;
  gap: 0.2rem;
  padding: 0.9rem 1.1rem;
  border-radius: 0.9rem;
  background: var(--surface-2);
  color: var(--ink);
  text-decoration: none;
}

.game-list li { display: flex; gap: 0.6rem; align-items: center; }

.game-list li button { flex: none; padding: 0.5rem 0.9rem; font-size: 0.85rem; }

.game-list a { flex: 1; min-width: 0; }

.game-list .players { overflow-wrap: anywhere; }

.game-list a:hover { outline: 0.15rem solid var(--accent); }

.control-head { display: flex; justify-content: space-between; gap: 1.5rem; align-items: start; }

.control-head > div { min-width: 0; }

.control-head h1 { overflow-wrap: anywhere; }

.control-head .qr { width: min(8.5rem, 30vw); height: min(8.5rem, 30vw); background: #fff; padding: 0.4rem; border-radius: 0.7rem; flex: none; }

.control-head .code { font-size: 1.3rem; color: var(--accent); }

.cast-row { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; margin-top: 1.2rem; }

.cast-row .meta:empty { display: none; }

.cast-status { display: flex; align-items: flex-start; gap: 0.7rem; margin-top: 0.9rem; padding: 0.75rem 0.95rem; border: 1px solid var(--line); border-radius: 0.7rem; background: var(--surface); line-height: 1.4; }

.cast-status[hidden] { display: none; }

.cast-status p { margin: 0; min-width: 0; overflow-wrap: anywhere; }

.cast-status b { display: block; }

#cast-status-hint:empty { display: none; }

#cast-status-hint { display: block; margin-top: 0.15rem; color: var(--muted); font-size: 0.9rem; }

.cast-status-mark { flex: none; width: 1.1rem; height: 1.1rem; margin-top: 0.15rem; border-radius: 50%; border: 0.18rem solid var(--info); }

.cast-status[data-state="working"] .cast-status-mark { border-right-color: transparent; }

.cast-status[data-state="done"] { border-color: var(--right); }

.cast-status[data-state="done"] .cast-status-mark { border-color: var(--right); background: var(--right); }

.cast-status[data-state="failed"] { border-color: var(--wrong); }

.cast-status[data-state="failed"] .cast-status-mark { border-color: var(--wrong); background: var(--wrong); }

@media (prefers-reduced-motion: no-preference) {
  .cast-status[data-state="working"] .cast-status-mark { animation: cast-turn 0.9s linear infinite; }
}

@keyframes cast-turn {
  to { transform: rotate(360deg); }
}

button.cast { display: inline-flex; align-items: center; gap: 0.6rem; padding: 0.7rem 1.2rem; }

button.cast[hidden] { display: none; }

button.cast svg { width: 1.4rem; height: 1.4rem; fill: currentColor; flex: none; }

button.cast .cast-on { display: none; }

button.cast.on { background: var(--accent); color: var(--accent-ink); }

button.cast.on .cast-on { display: inline; }

.remote summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 1.8rem;
  padding: 0.9rem 1.2rem;
  border-radius: var(--radius);
  background: var(--surface-2);
  cursor: pointer;
  list-style: none;
}

.remote summary::-webkit-details-marker { display: none; }

.remote summary:focus-visible { outline: 0.2rem solid var(--accent); outline-offset: 0.2rem; }

.remote summary h2 { margin: 0; }

.remote summary::after { content: "Show ▾"; font-size: 0.9rem; font-weight: 700; color: var(--muted); }

.remote[open] summary { margin-bottom: 0.9rem; }

.remote[open] summary::after { content: "Hide ▴"; }

a.button.secondary { background: var(--surface-2); }

a.button { display: inline-block; padding: 0.7rem 1.2rem; border-radius: var(--radius); font-weight: 700; text-decoration: none; color: var(--ink); }

.host .pad { display: grid; gap: 0.7rem; margin-bottom: 0.7rem; }

.host .flow { grid-template-columns: 1fr 2.2fr; }

.host .flow button { padding: 1.7rem 0; }

.host .mini { grid-template-columns: repeat(5, 1fr); }

.host .mini button { padding: 0.7rem 0; font-size: 1.05rem; }

.pad-label { margin: 0.9rem 0 0.4rem; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }

.host .extras { grid-template-columns: repeat(2, 1fr); }

.host .wide { grid-template-columns: 1fr; }

.host .wide button { font-size: 1.05rem; }

.host .pad button { padding: 1.1rem 0; font-size: 1.3rem; font-weight: 800; background: var(--surface-2); }

.host .pad button.primary { background: var(--accent); color: var(--accent-ink); }

.host .extras button { font-size: 0.95rem; font-weight: 600; }

.danger-row { display: flex; gap: 0.8rem; flex-wrap: wrap; margin-top: 1.5rem; }

.landing .hero { text-align: center; margin-top: 8vh; }

.landing .hero .stack { max-width: 20rem; margin: 1.5rem auto; }

.landing #code { text-align: center; font-size: 1.6rem; letter-spacing: 0.25em; text-transform: uppercase; }

.waiting .intro { margin-top: 1rem; }

.welcome button { margin-top: 2rem; }

/* ---------- slide images ----------
   Beside the text an image is always whole: resized to fit and centred, never cropped. Only the backdrop crops. */

.slide-image { margin: 0 0 1.25rem; display: grid; place-items: center; }

.slide-image img, .slide-thumb {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  border: 0.1rem solid var(--line);
}

.slide-image img { max-height: 30vh; border-radius: 0.7rem; }

.slide-thumb { max-height: 5rem; margin-bottom: 0.7rem; border-radius: 0.4rem; }

.slide.backdrop { position: relative; }

/* Faint enough that text set straight on the slide stays readable over any picture. */
.slide-backdrop { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.16; pointer-events: none; }

.slide.backdrop > :not(.slide-backdrop) { position: relative; }

@media (min-width: 1200px) {
  body.on-slide .slide.with-image {
    display: grid;
    grid-template-columns: minmax(0, 0.8fr) minmax(0, 1fr);
    gap: 3vw;
    align-content: center;
    align-content: safe center;
    align-items: center;
  }

  body.on-slide .slide.with-image.image-right { grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr); }

  body.on-slide .slide.image-right .slide-image { order: 1; }

  body.on-slide .slide-image { margin: 0; }

  body.on-slide .slide-image img { max-height: 64vh; }

  body.on-slide .slide.quiz.with-image h1 { font-size: 1.7rem; }

  /* Beside an image the text column is too narrow for two options per row. */
  body.on-slide .slide.quiz.with-image:not(.answered) .options { grid-template-columns: 1fr; gap: 0.8rem; }

  body.on-slide .slide.quiz.with-image:not(.answered) .option { padding: 0.8rem 1.2rem; font-size: 1.15rem; }
}
