/* Quiztria for the web.
 *
 * The look is the phone game's: brass and teal light in a dark workshop, Cinzel for anything
 * stamped into metal (titles, numbers, buttons) and Spectral for anything meant to be read
 * (questions, explanations). Colours are the exact values from scripts/ui/Palette.gd.
 *
 * What the web does differently, on purpose:
 *   - the machinery is anchored in viewport units, so it fills a laptop instead of huddling in the
 *     top-left corner of one (the phone client pins it to 720x1280 coordinates);
 *   - the vignette is a gradient rather than an image, so it cannot stretch out of shape;
 *   - there is real hover, real pressing and a real focus ring, because there is a real pointer;
 *   - answers carry their keyboard shortcut where a keyboard exists.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  /* Ground */
  --void: #06080a;
  --ground: #0a0d10;
  --panel-hi: #17242f;
  --panel-lo: #0f1a23;

  /* Brass — structure, frames, numbers */
  --brass: #c6a24b;
  --brass-lit: #e6c878;
  --brass-dark: #9c7a34;
  --brass-head: #d8b866;

  /* Teal — energy, the live thing */
  --teal: #35c8ce;
  --teal-lit: #48dee5;
  --teal-dark: #1fb3bb;

  /* Text */
  --cream: #ece3cf;
  --coral: #e88079;  /* Palette.CORAL - error and 'not yet' text */
  --secondary: #a79f8c;
  --muted: #8f8873;

  /* Verdicts */
  --right: #2f9d63;
  --right-edge: #4fd08a;
  --wrong: #c0524c;
  --wrong-edge: #e88079;

  /* Type scale — 1.2 on a phone opening to 1.28 on a desktop */
  --step--2: clamp(0.688rem, 0.66rem + 0.14vw, 0.75rem);
  --step--1: clamp(0.813rem, 0.78rem + 0.16vw, 0.875rem);
  /* THE BASE IS THE PHONE'S BASE. project.godot sets `theme/default_font_size=23` against a 720-wide
   * viewport, so every label the app does not override - which is most of them - is 3.19vw. The web
   * was carrying 15px where the phone carries 12.5, and because this is what `body` inherits from,
   * that 21% was on almost every sentence in the client at once.
   *
   * The steps ABOVE this one are NOT rescaled with it, and that is deliberate: --step-2 already lands
   * within 7% of Style.question's 37pt, so the ladder is right at the top and was only wrong at the
   * bottom. Shrinking the whole thing to fix the base would have broken the half that was correct. */
  --step-0: clamp(11.5px, 3.19vw, 15px);
  --step-1: clamp(1.125rem, 1.06rem + 0.32vw, 1.313rem);
  --step-2: clamp(1.313rem, 1.19rem + 0.61vw, 1.625rem);
  --step-3: clamp(1.563rem, 1.35rem + 1.06vw, 2.125rem);
  --step-4: clamp(2rem, 1.6rem + 2vw, 3rem);

  --serif: Spectral, "Iowan Old Style", Georgia, "Times New Roman", serif;
  --display: Cinzel, "Trajan Pro", "Palatino Linotype", Georgia, serif;

  --edge: rgba(198, 162, 75, 0.26);
  --edge-lit: rgba(230, 200, 120, 0.5);
  --plate: linear-gradient(177deg, var(--panel-hi), var(--panel-lo));
  --lift: inset 0 1px 0 rgba(230, 200, 120, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.45),
    0 18px 40px -26px rgba(0, 0, 0, 0.95);

  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);

  color-scheme: dark;
}

/* ------------------------------------------------------------------ base -- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background: var(--ground);
  color: var(--cream);
  font: 400 var(--step-0) / 1.55 var(--serif);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  overscroll-behavior-y: none;
}

h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.15;
  margin: 0;
  color: var(--brass-head);
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--teal-lit);
  outline-offset: 3px;
  border-radius: 6px;
}

::selection {
  background: rgba(53, 200, 206, 0.3);
}

/* ------------------------------------------------------------- machinery -- */
/* The workshop behind the glass. Everything here is decoration and is hidden from assistive tech. */

.atmos {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background: var(--ground);
}

.atmos::after {
  /* Vignette. A gradient, not an image — it cannot stretch out of shape on a wide window. */
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(125% 90% at 50% 38%, transparent 34%, rgba(6, 8, 10, 0.72) 78%, var(--void) 100%);
}

.atmos-glow {
  /* A pool of light on the workbench, under the column of content. */
  position: absolute;
  left: 50%;
  top: 0;
  width: min(120vw, 70rem);
  height: min(90vh, 46rem);
  transform: translateX(-50%);
  background:
    radial-gradient(52% 44% at 50% 16%, rgba(53, 200, 206, 0.09), transparent 70%),
    radial-gradient(46% 50% at 50% 42%, rgba(198, 162, 75, 0.055), transparent 72%);
}

.cog {
  position: absolute;
  opacity: 0.085;
  filter: saturate(0.55) brightness(0.95);
  transform-origin: 50% 50%;
  will-change: transform;
}

.cog-near {
  right: -17vmin;
  bottom: -21vmin;
  width: 74vmin;
  animation: turn 240s linear infinite;
}

.cog-high {
  left: -13vmin;
  top: -9vmin;
  width: 46vmin;
  opacity: 0.07;
  animation: turn 190s linear infinite reverse;
}

.cog-far {
  right: 6%;
  top: 17%;
  width: 24vmin;
  opacity: 0.05;
  animation: turn 320s linear infinite;
}

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

/* The machine registers an answer: one short tick of the gears, then back to its slow turn. */
body.is-stamping .cog-near {
  animation: turn 240s linear infinite, tick 0.5s var(--ease);
}

@keyframes tick {
  40% {
    transform: rotate(2.2deg);
  }
}

/* --------------------------------------------------------------- scaffold -- */

.stage {
  position: relative;
  z-index: 1;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: clamp(14px, 3.5vw, 34px) max(16px, env(safe-area-inset-left, 0px)) calc(24px + env(safe-area-inset-bottom, 0px));
}

.view {
  width: 100%;
  max-width: 34rem;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.view[data-wide] {
  max-width: 46rem;
}

/* A screen fades up once when it is mounted. This is the only motion that is not a reply to
   something the player did. */
.view > * {
  animation: rise 0.32s var(--ease) both;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
}

.plate {
  background: var(--plate);
  border: 1px solid var(--edge);
  border-radius: 16px;
  box-shadow: var(--lift);
}

.spacer {
  flex: 1;
  min-height: 8px;
}

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

/* BUTTON SIZE IS MEASURED AGAINST THE PHONE, not chosen.
 *
 * The app draws to a 720-wide viewport with stretch mode `canvas_items` / aspect `expand`, so WIDTH
 * is the scale reference: the height grows with the device instead of scaling. 390/720 = 0.54 is
 * therefore the factor between an app pixel and a web one, and 64/720 = 8.89vw says the same thing
 * in a unit the browser can keep true on a wider phone. The cap is what the 34rem column would give.
 *
 * From Style.gd: gold_button 64px / 23pt, primary_button 70 (the menu asks for 74) / 24pt,
 * ghost_button 60px / 22pt. Confirmed against a real screenshot of the Pixel: the gold buttons
 * measure 96 of 2400 device px (4.0% of the screen), the teal one 111 (4.6%).
 *
 * The web was at 5.4% and 6.7%, with the teal button a WHOLE TYPE STEP larger than the brass ones.
 * That last part was the real drift: the app separates the primary button by colour, 24pt against
 * 23pt - the height barely differs and the text not at all. Making it bigger as well as brighter
 * says the same thing twice, and cost a screenful: the phone's menu fits, the web's did not. */
:root {
  --btn-h: clamp(31px, 8.89vw, 48px);
  --btn-h-primary: clamp(36px, 10.28vw, 56px);
  --btn-h-quiet: clamp(29px, 8.33vw, 45px);
  --btn-text: clamp(12px, 3.19vw, 17px);
  --btn-text-primary: clamp(12.5px, 3.33vw, 18px);
  --btn-text-quiet: clamp(11.5px, 3.06vw, 16px);
}

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  /* The minimum IS the height for a one-line label, exactly as custom_minimum_size is in the app.
   * The padding only matters when a label wraps, which the app's buttons cannot do. */
  min-height: var(--btn-h);
  padding: 6px 20px;
  border: 1px solid transparent;
  border-radius: 12px;
  font-family: var(--display);
  font-size: var(--btn-text);
  font-weight: 600;
  letter-spacing: 0.045em;
  transition: transform 0.12s var(--ease), box-shadow 0.16s var(--ease), filter 0.16s var(--ease),
    border-color 0.16s var(--ease), background-color 0.16s var(--ease);
}

.btn:active {
  transform: translateY(1px);
}

.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* The live thing on the screen: teal, lit from within. Never more than one per view. */
.btn-teal {
  background: linear-gradient(178deg, var(--teal-lit), var(--teal-dark));
  border-color: rgba(120, 240, 246, 0.6);
  color: #042128;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35), 0 0 26px -6px rgba(53, 200, 206, 0.55),
    0 14px 30px -20px #000;
  min-height: var(--btn-h-primary);
  font-size: var(--btn-text-primary);
}

.btn-brass {
  background: linear-gradient(178deg, var(--brass-lit), var(--brass-dark));
  border-color: rgba(240, 214, 150, 0.5);
  color: #241a06;
  box-shadow: inset 0 1px 0 rgba(255, 245, 210, 0.4), 0 12px 26px -20px #000;
}

.btn-ghost {
  background: rgba(23, 36, 47, 0.5);
  border-color: var(--edge);
  color: var(--brass-head);
}

.btn-quiet {
  background: none;
  border-color: transparent;
  color: var(--secondary);
  font-family: var(--serif);
  letter-spacing: 0;
  font-weight: 500;
  min-height: var(--btn-h-quiet);
  padding: 6px 14px;
  font-size: var(--btn-text-quiet);
}

@media (hover: hover) and (pointer: fine) {
  .btn-teal:hover:not([disabled]) {
    filter: brightness(1.07);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 34px -4px rgba(53, 200, 206, 0.7),
      0 14px 30px -20px #000;
  }
  .btn-brass:hover:not([disabled]) {
    filter: brightness(1.08);
  }
  .btn-ghost:hover:not([disabled]) {
    border-color: var(--edge-lit);
    background: rgba(29, 45, 58, 0.7);
    color: var(--brass-lit);
  }
  .btn-quiet:hover:not([disabled]) {
    color: var(--cream);
  }
}

.btn-row {
  display: flex;
  gap: 10px;
}

.btn-row > .btn {
  flex: 1;
}

/* ------------------------------------------------------------------- home -- */

.crest {
  display: block;
  /* The phone reserves a 300px block for the logo in a 720-wide viewport - 41.7% of the width.
   * Keyed to vh instead, the web's crest grew to 217px where the phone's is 162, and it was the
   * masthead, not the buttons, eating the rest of the screen. */
  height: clamp(120px, 41.67vw, 240px);
  width: auto;
  max-width: 74%;
  object-fit: contain;
  margin: clamp(2px, 1.5vh, 18px) auto 0;
  filter: drop-shadow(0 10px 26px rgba(0, 0, 0, 0.7));
}

.tagline {
  margin: 10px 0 0;
  text-align: center;
  font-family: var(--display);
  font-size: var(--step--2);
  letter-spacing: 0.24em;
  color: var(--brass);
}

.edition {
  margin: clamp(10px, 1.8vh, 18px) 0 0;
  text-align: center;
}

.edition-no {
  font-family: var(--display);
  /* 13pt of 720 on the phone. The floor is the one deliberate break with that: 1.81vw lands on 7px
   * at phone width, and 7px of web text is not the same object as 7pt of Godot text - it is where
   * browser font-boosting and a user's minimum-size setting start rewriting the page for us. */
  font-size: clamp(9px, 1.81vw, 12px);
  letter-spacing: 0.22em;
  color: var(--brass);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.edition-no::before,
.edition-no::after {
  content: "";
  height: 1px;
  width: clamp(28px, 12%, 64px);
  background: linear-gradient(90deg, transparent, var(--edge-lit));
}

.edition-no::after {
  background: linear-gradient(90deg, var(--edge-lit), transparent);
}

.edition-line {
  margin: 8px 0 0;
  font-size: clamp(10px, 2.36vw, 14px);  /* 17pt of 720 */
  color: var(--secondary);
}

.edition-line b {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(14px, 4.17vw, 22px);  /* 30pt of 720 */
  color: var(--cream);
}

/* The streak: the one number a returning player comes back for. */
.hearth {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: clamp(10px, 2.2vh, 22px) 0 0;
}

.hearth-flame {
  display: flex;
  align-items: center;
  gap: 12px;
}

.hearth-flame img {
  width: clamp(26px, 8.3vw, 42px);  /* Style.flame's emblem: 52 * 1.15 of 720 */
  filter: drop-shadow(0 0 14px rgba(53, 200, 206, 0.35));
}

/* The unlit hearth: a smaller ember at 35% and no glow, which is `ember.modulate = Color(1,1,1,0.35)`
 * in Menu.gd. A flame at full brightness over a streak of nothing is a promise the state does not
 * keep. */
.hearth-flame.is-cold img {
  width: clamp(24px, 7.22vw, 38px);
  opacity: 0.35;
  filter: none;
}

.hearth-count {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-4);
  color: var(--cream);
  line-height: 1;
  text-shadow: 0 0 28px rgba(53, 200, 206, 0.3);
}

.hearth-label {
  margin-top: 6px;
  font-size: clamp(10px, 2.5vw, 14px);  /* STREAK_UNLIT is 18pt of 720 */
  color: var(--muted);
}

.hearth-embers {
  margin-top: 4px;
  font-size: var(--step--1);
  color: var(--teal-dark);
  letter-spacing: 0.1em;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 14px;
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: rgba(23, 36, 47, 0.55);
  font-family: var(--display);
  font-size: clamp(10px, 1.94vw, 13px);  /* Style.grade_chip(..., 14) of 720 */
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--brass-head);
}

.chip-row {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.menu {
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1.94vw, 12px);  /* the phone's VBox separation: 14 of 720 */
  margin: clamp(14px, 3vh, 30px) 0 0;
}

.menu-hint {
  margin: -4px 0 2px;
  text-align: center;
  /* 15pt of 720 on the phone, held at the same 9-10px floor as the rest of the small type. These
   * lines are what tells a player what "Duell" and "Ligor" actually are, and at --step--1 they were
   * nearly the size of the buttons they explain. */
  font-size: clamp(9.5px, 2.08vw, 13px);
  color: var(--muted);
}

.home-foot {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  margin-top: 18px;
}

.home-foot .btn-quiet {
  width: auto;
  font-size: clamp(10px, 2.64vw, 14px);  /* the menu overrides ghost to 19pt of 720 */
}

/* ------------------------------------------------------------------ round -- */

.round-bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 10px;
  padding-bottom: 4px;
}

.round-bar .pos {
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  color: var(--brass);
}

.round-bar .score {
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.06em;
  color: var(--brass);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.round-bar .score b {
  color: var(--cream);
  font-weight: 700;
}

.dial {
  position: relative;
  width: 52px;
  height: 52px;
}

.dial svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.dial .track {
  fill: rgba(6, 10, 13, 0.6);
  stroke: rgba(198, 162, 75, 0.22);
  stroke-width: 2;
}

.dial .sweep {
  fill: none;
  stroke: var(--teal);
  stroke-width: 3;
  stroke-linecap: round;
  transition: stroke 0.4s linear;
}

.dial .num {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-0);
  color: var(--teal-lit);
  font-variant-numeric: tabular-nums;
}

.dial.warn .sweep {
  stroke: #e0b04c;
}
.dial.warn .num {
  color: #e0b04c;
}
.dial.crit .sweep {
  stroke: #e06b63;
}
.dial.crit .num {
  color: #e06b63;
  animation: pulse 0.9s ease-in-out infinite;
}

@keyframes pulse {
  50% {
    opacity: 0.5;
  }
}

.topic {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: clamp(6px, 2vh, 18px) 0 0;
}

.topic img {
  width: clamp(52px, 15vw, 74px);
  filter: drop-shadow(0 0 18px rgba(53, 200, 206, 0.2));
}

.topic span {
  margin-top: 4px;
  font-family: var(--display);
  font-size: var(--step--2);
  letter-spacing: 0.2em;
  color: var(--brass);
}

.question {
  margin: clamp(12px, 2.5vh, 22px) 0 clamp(14px, 2.5vh, 22px);
  font-family: var(--serif);
  font-weight: 600;
  font-size: var(--step-2);
  line-height: 1.3;
  color: var(--cream);
  text-wrap: balance;
}

.answers {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.answer {
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  padding: 14px 17px;
  text-align: left;
  border: 1px solid var(--edge);
  border-radius: 13px;
  background: var(--plate);
  box-shadow: var(--lift);
  font-family: var(--serif);
  font-weight: 500;
  /* Style.option_button: 29pt and 98px tall of 720. Pinned rather than left on --step-0, because an
   * answer is NOT body text - the phone sets it two thirds of a step above its own base, and --step-0
   * is about to become that base. */
  font-size: clamp(12px, 4.03vw, 21px);
  min-height: clamp(40px, 13.6vw, 72px);
  line-height: 1.35;
  color: var(--cream);
  transition: transform 0.13s var(--ease), border-color 0.16s var(--ease), opacity 0.3s var(--ease),
    background 0.3s var(--ease), box-shadow 0.2s var(--ease), color 0.3s var(--ease);
}

/* The keyboard shortcut. Hidden by default and switched on only where a real pointer — and so,
   in practice, a real keyboard — exists. A touch device would just be reading numbers it cannot
   press. */
.answer .key {
  display: none;
  flex: 0 0 auto;
  width: 23px;
  height: 23px;
  place-items: center;
  border: 1px solid var(--edge);
  border-radius: 5px;
  font-family: var(--display);
  font-size: var(--step--2);
  font-weight: 600;
  color: var(--brass);
}

@media (hover: hover) and (pointer: fine) {
  .answer .key {
    display: grid;
  }
}

.answer .text {
  flex: 1;
}

@media (hover: hover) and (pointer: fine) {
  .answer:not(.locked):hover {
    border-color: var(--edge-lit);
    transform: translateY(-1px);
  }
  .answer:not(.locked):hover .key {
    color: var(--brass-lit);
    border-color: var(--edge-lit);
  }
}

.answer:not(.locked):active {
  transform: translateY(1px) scale(0.995);
}

.answer.locked {
  cursor: default;
}

/* The reveal. The chosen and the correct card resolve; everything else steps back to plain text. */
.answer.is-right {
  background: linear-gradient(178deg, #35a86c, #237a4d);
  border-color: var(--right-edge);
  color: #f1fbf4;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 0 30px -8px rgba(79, 208, 138, 0.6);
  animation: strike 0.45s var(--ease);
}

.answer.is-wrong {
  background: linear-gradient(178deg, #c0524c, #8f3b36);
  border-color: var(--wrong-edge);
  color: #fdeeed;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
  animation: shake 0.4s var(--ease);
}

/* Answered, waiting on the server to reveal. Taken, but not yet judged. */
.answer.is-picked {
  border-color: var(--teal);
  box-shadow: inset 0 1px 0 rgba(230, 200, 120, 0.15), 0 0 22px -10px rgba(53, 200, 206, 0.8);
  animation: held 1.6s ease-in-out infinite;
}

@keyframes held {
  50% {
    border-color: var(--teal-lit);
  }
}

.answer.is-dim {
  background: none;
  border-color: transparent;
  box-shadow: none;
  color: var(--muted);
  opacity: 0.55;
}

.answer.is-dim .key {
  opacity: 0;
}

@keyframes strike {
  0% {
    transform: scale(0.985);
  }
  45% {
    transform: scale(1.015);
  }
}

@keyframes shake {
  25% {
    transform: translateX(-4px);
  }
  55% {
    transform: translateX(3px);
  }
  80% {
    transform: translateX(-1px);
  }
}

/* The verdict and why. Unfolds under the answers. */
.verdict {
  margin-top: clamp(14px, 2.5vh, 20px);
  overflow: hidden;
  animation: unfold 0.4s var(--ease);
}

@keyframes unfold {
  from {
    opacity: 0;
    transform: translateY(-6px);
    max-height: 0;
  }
  to {
    opacity: 1;
    transform: none;
    max-height: 40rem;
  }
}

.verdict h2 {
  font-size: var(--step-1);
  letter-spacing: 0.06em;
}

.verdict.right h2 {
  color: var(--right-edge);
}

.verdict.wrong h2 {
  color: var(--wrong-edge);
}

.verdict .gain {
  float: right;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--teal-lit);
  animation: gain 0.5s var(--ease);
}

@keyframes gain {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

.verdict p {
  margin: 8px 0 0;
  color: var(--secondary);
  font-size: var(--step-0);
  line-height: 1.6;
  max-width: 62ch;
}

.verdict .run {
  margin-top: 8px;
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  color: var(--brass);
}

.reveal-slot {
  margin-top: clamp(14px, 2.5vh, 22px);
}

.round-foot {
  margin-top: auto;
  padding-top: 20px;
  text-align: center;
}

.reveal-slot .hint {
  display: none;
  margin-top: 10px;
  text-align: center;
  font-size: var(--step--2);
  color: var(--muted);
  letter-spacing: 0.08em;
}

@media (hover: hover) and (pointer: fine) {
  .reveal-slot .hint {
    display: block;
  }
}

/* ----------------------------------------------------------------- result -- */

.result-head {
  text-align: center;
  padding: clamp(20px, 5vh, 34px) 20px;
}

.result-score {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-4);
  line-height: 1;
  color: var(--cream);
  text-shadow: 0 0 32px rgba(53, 200, 206, 0.25);
  font-variant-numeric: tabular-nums;
}

.result-of {
  margin-top: 10px;
  font-size: var(--step-1);
  color: var(--brass-head);
  font-family: var(--display);
}

.tally {
  display: flex;
  justify-content: center;
  gap: 7px;
  margin-top: 18px;
}

.tally i {
  width: 26px;
  height: 5px;
  border-radius: 3px;
  background: rgba(143, 136, 115, 0.3);
  animation: mark 0.34s var(--ease) both;
}

@keyframes mark {
  from {
    opacity: 0;
    transform: scaleX(0.2);
  }
}

.tally i.hit {
  background: var(--right-edge);
  box-shadow: 0 0 10px rgba(79, 208, 138, 0.5);
}

.tally i.miss {
  background: var(--wrong);
}

.banner {
  margin-top: 18px;
  padding: 14px 18px;
  border-radius: 12px;
  border: 1px solid rgba(53, 200, 206, 0.35);
  background: linear-gradient(178deg, rgba(53, 200, 206, 0.14), rgba(53, 200, 206, 0.05));
  text-align: center;
}

.banner h3 {
  font-size: var(--step-0);
  letter-spacing: 0.09em;
  color: var(--teal-lit);
}

.banner p {
  margin: 5px 0 0;
  font-size: var(--step--1);
  color: var(--secondary);
}

.result-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: clamp(18px, 4vh, 28px);
}

.review {
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.review-row {
  display: flex;
  gap: 12px;
  padding: 12px 15px;
  border-radius: 11px;
  border: 1px solid var(--edge);
  background: rgba(15, 26, 35, 0.6);
  align-items: flex-start;
}

.review-row .mark {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 12px;
  margin-top: 2px;
}

.review-row.hit .mark {
  background: rgba(47, 157, 99, 0.25);
  color: var(--right-edge);
}

.review-row.miss .mark {
  background: rgba(192, 82, 76, 0.25);
  color: var(--wrong-edge);
}

.review-row .q {
  font-size: var(--step--1);
  color: var(--cream);
  line-height: 1.45;
}

.review-row .a {
  margin-top: 3px;
  font-size: var(--step--1);
  color: var(--muted);
}

/* ------------------------------------------------------------------ lists -- */

.head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: clamp(14px, 3vh, 22px);
}

.head h1 {
  font-size: clamp(15px, 4.72vw, 24px);  /* Style.title's default is 33-34 of 720 on every screen */
  flex: 1;
}

.back {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  border: 1px solid var(--edge);
  background: rgba(23, 36, 47, 0.5);
  color: var(--brass-head);
  font-size: 18px;
  line-height: 1;
  transition: border-color 0.16s var(--ease), color 0.16s var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .back:hover {
    border-color: var(--edge-lit);
    color: var(--brass-lit);
  }
}

.lede {
  margin: 0 0 clamp(14px, 3vh, 20px);
  color: var(--secondary);
  font-size: var(--step-0);
  max-width: 56ch;
}

/* `hidden` HAS TO WIN. The browser's own `[hidden] { display: none }` comes from the UA sheet and
 * loses to any `display` an author sets — so every flex or grid container in this file was visible
 * while carrying the attribute. The duel screen's collapsed theme list was drawn wide open, and the
 * only reason it was noticed is that it sat beside a photograph of the phone doing it right. */
[hidden] {
  display: none !important;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: 10px;
}

.tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 16px 10px 13px;
  border: 1px solid var(--edge);
  border-radius: 14px;
  background: var(--plate);
  box-shadow: var(--lift);
  text-align: center;
  transition: transform 0.14s var(--ease), border-color 0.16s var(--ease), box-shadow 0.2s var(--ease);
}

.tile img {
  width: 54px;
  height: 54px;
  object-fit: contain;
}

.tile .name {
  font-family: var(--display);
  font-size: clamp(10px, 2.92vw, 15px);  /* 21pt of 720 */
  font-weight: 600;
  color: var(--cream);
  line-height: 1.2;
}

.tile .sub {
  font-size: clamp(9.5px, 2.64vw, 14px);  /* 19pt of 720 */
  color: var(--muted);
}

@media (hover: hover) and (pointer: fine) {
  .tile:hover {
    border-color: var(--edge-lit);
    transform: translateY(-2px);
  }
}

/* --- The Vault ---------------------------------------------------------------------------------
 *
 * Three across on a phone, like the app. The emblems ARE the reward here — they were drawn at a size
 * the old 54px tile never showed — so the grid is tuned around the art and the words hang off it.
 */

/* Three columns, always, because `_grid.columns = 3` is what the phone's Vault says - auto-fill
 * quietly became four or five on a wide window and the case stopped looking like the same case. */
.grid.vault {
  grid-template-columns: repeat(3, 1fr);
  gap: 12px 10px;
}

/* NO PLATE UNDER A RELIC. The phone puts the art straight on the dark and draws a frame only around
 * something you OWN - "owning a piece should LOOK like framing, not like the absence of a dimming",
 * as Vault.gd puts it. A card under all ten makes that frame carry no information, which is the same
 * mistake the phone already corrected once in the other direction. */
.tile.relic {
  border-color: transparent;
  background: none;
  box-shadow: none;
  padding: 6px 4px;
  /* The phone stacks art, name and price with a VBox's default separation and nothing else, so the
   * three read as one object. At the shared .tile gap of 8 they read as three. */
  gap: 3px;
}

.tile.relic {
  gap: 6px;
  padding: 12px 6px 10px;
}

/* The frame goes on the ART, not on the tile: owning a piece should read as having mounted it, and
 * the tile's own edge is still free to carry hover and focus.
 *
 * AND THE ART IS NEVER DIMMED — not when it is unaffordable, not when it is merely unowned. This is
 * the phone's ruling and it is written into `_cell` in scenes/vault/Vault.gd, which dropped a
 * modulate(0.78) on everything unowned for a reason worth repeating: at 0 owned the whole grid went
 * uniformly muddy and the dimming carried no information at all. These pieces are the reward; the
 * shelf's job is to make you want one. Affordability is carried by the price chip's colour and
 * ownership by this frame, which is enough, and neither of them costs the artwork anything. */
.relic-art {
  display: grid;
  place-items: center;
  /* The phone's relic is a 96px square in a 720-wide viewport - 46% of its cell, not the whole of
   * it. Stretched to the column the web's were more than twice the size and the case read as a shop
   * shelf rather than a display. */
  width: clamp(44px, 13.3vw, 112px);
  aspect-ratio: 1;
  border: 1px solid transparent;
  border-radius: 12px;
  transition: border-color 0.16s var(--ease), box-shadow 0.2s var(--ease);
}

.relic-art img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.relic-art[data-state="worn"] {
  border-color: rgba(53, 200, 206, 0.75);
  box-shadow: 0 0 20px -8px rgba(53, 200, 206, 0.8);
}

.relic-art[data-state="owned"] {
  border-color: rgba(198, 162, 75, 0.55);
}

/* Colour carries affordability, so at four Sparks the grid sorts itself into "reachable" and
 * "later" without a word of copy — ten identically-coloured prices said nothing. */
.relic-chip[data-state="worn"] {
  color: var(--brass-lit);
}

.relic-chip[data-state="owned"] {
  color: var(--cream);
}

.relic-chip[data-state="affordable"] {
  color: var(--teal-lit);
}

.relic-chip[data-state="locked"] {
  color: var(--muted);
}

/* 28pt and 15pt of 720. The Sparks line is the one teal thing on this screen; the earn line is the
 * small print that explains where they come from, and it was set at body size. */
.vault-sparks {
  color: var(--teal-lit);
  font-size: clamp(12px, 3.89vw, 20px);
}

.vault-earn {
  margin-top: 10px;
  font-size: clamp(9.5px, 2.08vw, 13px);
}

/* --- A row of chips where one is chosen ---------------------------------------------------------
 *
 * Style.time_picker + _style_pick_chip on the phone: a muted label, then chips 58px tall at 24pt in
 * a 720-wide viewport, dark with a thin brass edge, and the chosen one teal with a glow.
 *
 * The phone's selected chip takes a 2px border and drops its content margin by 1 so the row does not
 * reflow by two pixels every time the choice moves. Here the second pixel is an inset shadow, which
 * cannot take up space in the first place. */
.picker {
  margin: clamp(10px, 2vh, 16px) 0 0;
}

.picker-label {
  color: var(--muted);
}

.picker-sub {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: clamp(9.5px, 2.08vw, 13px);
}

.pick-row {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}

.pick-chip {
  flex: 1 1 0;
  min-height: clamp(28px, 8.06vw, 44px);
  padding: 4px 6px;
  border: 1px solid rgba(198, 162, 75, 0.4);
  border-radius: 11px;
  background: rgba(23, 36, 47, 0.6);
  color: var(--cream);
  font-family: var(--display);
  font-size: clamp(11px, 3.33vw, 17px);
  letter-spacing: 0.04em;
  transition: border-color 0.16s var(--ease), color 0.16s var(--ease), box-shadow 0.2s var(--ease);
}

.pick-chip[aria-pressed="true"] {
  border-color: var(--teal);
  background: var(--plate);
  color: var(--teal-lit);
  box-shadow: inset 0 0 0 1px var(--teal), 0 0 16px -4px rgba(53, 200, 206, 0.6);
}

/* The host's per-player easy-mode switch. Same chip vocabulary as the room settings, smaller - it
 * lives inside a roster row rather than on its own line. */
.easy-toggle {
  flex: 0 0 auto;
  padding: 4px 10px;
  border: 1px solid rgba(198, 162, 75, 0.35);
  border-radius: 999px;
  background: rgba(23, 36, 47, 0.6);
  color: var(--muted);
  font-family: var(--display);
  font-size: clamp(9px, 2.08vw, 12px);
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.easy-toggle[aria-pressed="true"] {
  border-color: var(--teal);
  background: var(--plate);
  color: var(--teal-lit);
  box-shadow: inset 0 0 0 1px var(--teal);
}

/* The name you play under, on the way into a room. Same field vocabulary as the room-code input; it
 * sits under its own small label because that is how the phone's Spela online screen opens. */
.name-input {
  width: 100%;
  margin-top: 8px;
  padding: 10px 14px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--panel-lo);
  color: var(--cream);
  font: 500 var(--step-0) var(--serif);
}

.name-input:focus-visible {
  outline: none;
  border-color: var(--teal);
  box-shadow: inset 0 0 0 1px var(--teal);
}

/* The medal over an empty league list: `Style.emblem("medal", 96)` of 720, centred, with the copy
 * under it. Same slot the empty chronicle's flame uses, one size down. */
.league-medal {
  width: clamp(38px, 13.3vw, 80px);
}

.league-lede {
  text-align: center;
  margin-inline: auto;
}

/* The relic overlay is a SURFACE, not a card: `DARK_DEEP` at 97% across the whole screen, with the
 * artwork straight on it. */
.modal-scrim.relic-scrim {
  /* Compound on purpose: `.modal-scrim` is defined further down this file, and a single class here
   * lost to it on source order alone — the sheet came out as a translucent wash over a visible grid
   * rather than the near-solid surface the phone draws. */
  background: rgba(6, 9, 12, 0.97);
  backdrop-filter: none;
}

.relic-scrim .relic-sheet {
  background: none;
  border: 0;
  box-shadow: none;
}

/* The about view. Small, plain, and never linked to — a table of three facts for somebody who came
 * here on purpose. */
.about {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: clamp(10px, 2vh, 18px) 0 0;
}

.about-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 8px 12px;
  border: 1px solid var(--edge);
  border-radius: 11px;
  background: var(--panel-lo);
}

.about-label {
  flex: 0 0 auto;
  min-width: 6rem;
  color: var(--muted);
  font-family: var(--display);
  font-size: clamp(9.5px, 2.08vw, 13px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.about-value {
  color: var(--cream);
  font-family: var(--serif);
  word-break: break-all;
}

/* The crest opens the about view on a long press, so it must not start a text or image drag on the
 * way there — a selection highlight over the logo is the tell that the gesture did not land. */
.crest {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* --- The curated themes ------------------------------------------------------------------------
 *
 * `_theme_card` in Themes.gd: a 58px-tall button in a 720-wide viewport, a 30px line-art mark, and
 * the name and count as one left-aligned line at 21pt. */
.theme-list {
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 1.39vw, 9px);
}

.theme-row {
  display: flex;
  align-items: center;
  gap: clamp(6px, 1.94vw, 12px);
  width: 100%;
  min-height: clamp(28px, 8.06vw, 44px);
  padding: 6px 14px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
  color: var(--cream);
  font-family: var(--display);
  font-size: clamp(10px, 2.92vw, 15px);
  text-align: left;
  transition: border-color 0.16s var(--ease), box-shadow 0.2s var(--ease);
}

.theme-icon {
  flex: 0 0 auto;
  width: clamp(16px, 4.17vw, 22px);
  height: auto;
}

.theme-row .sub {
  color: var(--muted);
}

/* --- The category list on the way into a room ---------------------------------------------------
 *
 * Rows, not a grid: `list.add_theme_constant_override("separation", 10)` over full-width buttons
 * 86px tall at 28pt in a 720-wide viewport, left-aligned (Online.gd). */
.cat-list {
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 1.39vw, 9px);
}

.cat-row {
  display: block;
  width: 100%;
  text-align: left;
  min-height: clamp(36px, 11.94vw, 62px);
  padding: 8px 16px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
  color: var(--cream);
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(12px, 3.89vw, 20px);
  transition: border-color 0.16s var(--ease), color 0.16s var(--ease), box-shadow 0.2s var(--ease);
}

.cat-row.picked {
  border-color: var(--teal);
  color: var(--teal-lit);
  box-shadow: inset 0 0 0 1px var(--teal), 0 0 20px -8px rgba(53, 200, 206, 0.7);
}

/* The count's colour IS the message, so it needs no adverb: you cannot open yet, you are at the
 * ceiling, or you are good to go. */
.count {
  margin: 10px 0 0;
}

.count.is-short {
  color: var(--coral);
}

.count.is-max {
  color: var(--brass);
}

.count.is-ok {
  color: var(--teal-lit);
}

/* --- Saying a question is wrong -----------------------------------------------------------------
 *
 * A list, once, at the end of a round — never on the reveal. The phone's placement and its reasoning
 * are in ReportQuestion.gd; the rows carry the question and both answers because the price of
 * reporting at the end is that nobody remembers which question it was. */
.report-sheet {
  max-height: 80vh;
  display: flex;
  flex-direction: column;
}

.report-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 12px 0;
  overflow-y: auto;
}

.report-row {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--edge);
  border-radius: 11px;
  background: var(--panel-lo);
  color: var(--cream);
  font-family: var(--serif);
  line-height: 1.35;
}

.report-row .q {
  font-weight: 600;
}

.report-row .a {
  margin-left: 14px;
  color: var(--muted);
  font-size: clamp(9.5px, 2.64vw, 14px);
}

.report-row[disabled] {
  opacity: 0.4;
  cursor: default;
}

.report-row.is-done .q::after {
  content: " \2713";
  color: var(--teal-lit);
}

/* --- A relic up close --------------------------------------------------------------------------- */

.relic-sheet {
  max-width: 21rem;
  padding: 20px 22px 18px;
}

/* Up close the art is presented the same whatever state it is in — no frame, no glow. The frame is
 * the GRID's language for "this one is yours"; repeating it here framed the worn piece and left the
 * others bare, which reads as an accident rather than as information. The chip and the button say
 * what state this is. */
.relic-sheet .relic-art {
  width: min(47.2vw, 15rem);  /* Style.avatar(id, 340) of 720 */
  margin: 0 auto 14px;
  border-width: 0;
  box-shadow: none;
}

.relic-sheet h2 {
  font-family: var(--display);
  color: var(--brass-lit);
  letter-spacing: 0.03em;
}

.relic-lore {
  margin: 6px 0 0;
  color: var(--secondary);
  font-family: var(--serif);
  font-style: italic;
  font-size: var(--step-0);
}

.relic-sheet .relic-chip {
  margin: 12px 0 0;
  font-size: var(--step--1);
  letter-spacing: 0.06em;
}

/* Owns the gap above the buttons, because the chip above it is not always there. */
.relic-sheet .btn-col {
  margin-top: 18px;
}

.btn-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sticky-foot {
  position: sticky;
  bottom: 0;
  padding: 14px 0 calc(6px + env(safe-area-inset-bottom, 0px));
  margin-top: 18px;
  background: linear-gradient(transparent, var(--ground) 38%);
}

/* ------------------------------------------------------------------ stats -- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 10px;
}

.stat {
  padding: 15px 16px;
  border: 1px solid var(--edge);
  border-radius: 13px;
  background: var(--plate);
}

.stat .n {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-2);
  color: var(--cream);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.stat .l {
  margin-top: 6px;
  font-size: var(--step--1);
  color: var(--muted);
}

.bars {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.bar {
  display: grid;
  grid-template-columns: 7.5rem 1fr auto;
  align-items: center;
  gap: 11px;
  font-size: var(--step--1);
}

.bar .l {
  color: var(--secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bar .t {
  height: 7px;
  border-radius: 4px;
  background: rgba(143, 136, 115, 0.18);
  overflow: hidden;
}

.bar .f {
  height: 100%;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--teal-dark), var(--teal-lit));
}

.bar .v {
  color: var(--brass);
  font-variant-numeric: tabular-nums;
  font-family: var(--display);
  font-size: var(--step--2);
}

.section-title {
  margin: clamp(18px, 4vh, 26px) 0 2px;
  font-size: var(--step-0);
  letter-spacing: 0.09em;
  color: var(--brass);
}

.grade-meter {
  margin-top: 6px;
}

.grade-meter .t {
  height: 9px;
  border-radius: 5px;
  background: rgba(143, 136, 115, 0.18);
  overflow: hidden;
  border: 1px solid var(--edge);
}

.grade-meter .f {
  height: 100%;
  background: linear-gradient(90deg, var(--brass-dark), var(--brass-lit));
}

.grade-meter .cap {
  margin-top: 7px;
  font-size: var(--step--1);
  color: var(--muted);
}

/* ----------------------------------------------------------------- duel -- */

.versus {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 14px;
  padding: 20px 16px;
  text-align: center;
}

.versus .side .who {
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  color: var(--brass);
  overflow: hidden;
  text-overflow: ellipsis;
}

.versus .side .pts {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-3);
  color: var(--cream);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.versus .side .of {
  font-size: var(--step--2);
  color: var(--muted);
}

.versus .side.win .pts {
  color: var(--teal-lit);
  text-shadow: 0 0 26px rgba(53, 200, 206, 0.4);
}

.versus .vs {
  font-family: var(--display);
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.1em;
}

.field {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

.field input {
  flex: 1;
  min-width: 0;
  padding: 13px 15px;
  border: 1px solid var(--edge);
  border-radius: 11px;
  background: rgba(6, 10, 13, 0.55);
  color: var(--cream);
  font: 500 var(--step-0) var(--serif);
}

.field input::placeholder {
  color: var(--muted);
}

.field input:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(53, 200, 206, 0.16);
}

.field .btn {
  width: auto;
  padding-inline: 20px;
}

/* ----------------------------------------------------------------- misc -- */

.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(3, 5, 7, 0.72);
  backdrop-filter: blur(3px);
  animation: fade 0.2s var(--ease);
}

@keyframes fade {
  from {
    opacity: 0;
  }
}

.modal {
  width: 100%;
  max-width: 24rem;
  padding: 22px;
  text-align: center;
}

.modal h2 {
  font-size: var(--step-1);
}

.modal p {
  margin: 10px 0 18px;
  color: var(--secondary);
  font-size: var(--step-0);
}

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(26px + env(safe-area-inset-bottom, 0px));
  z-index: 30;
  transform: translateX(-50%);
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid var(--edge-lit);
  background: rgba(23, 36, 47, 0.96);
  color: var(--cream);
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  box-shadow: 0 16px 40px -20px #000;
  animation: toast 0.3s var(--ease);
}

@keyframes toast {
  from {
    opacity: 0;
    transform: translate(-50%, 10px);
  }
}

.loading {
  display: grid;
  place-items: center;
  padding: 24vh 0;
  color: var(--muted);
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  font-family: var(--display);
}

.loading::before {
  content: "";
  width: 34px;
  height: 34px;
  margin-bottom: 16px;
  border-radius: 50%;
  border: 2px solid rgba(198, 162, 75, 0.22);
  border-top-color: var(--teal);
  animation: turn 0.9s linear infinite;
}

.empty {
  text-align: center;
  padding: 12vh 10px;
}

/* 132 of 720 on the phone, with the glow baked into the artwork here rather than drawn behind it. */
.empty-flame {
  display: block;
  width: clamp(54px, 18.3vw, 110px);
  margin: 0 auto clamp(10px, 2vh, 18px);
}

.empty-cta {
  display: flex;
  justify-content: center;
  margin-top: clamp(14px, 3vh, 24px);
}

.empty-cta .btn {
  width: auto;
  min-width: 14rem;
  padding-inline: 28px;
}

.empty h2 {
  font-size: clamp(12px, 3.89vw, 20px);  /* Style.heading(..., 28) of 720 */
}

.empty p {
  margin: 10px auto 0;
  color: var(--muted);
  max-width: 34ch;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------- the desk -- */
/* On a large screen the round gets room to breathe. Without this it reads as a phone layout that
   happened to be opened on a laptop — the same widths, the same gaps, a lot of empty below. The
   block stays top-aligned, because the reveal grows downwards and a centred block would shift the
   question out from under the reader's eyes every time they answered. */

@media (min-width: 820px) and (min-height: 720px) {
  .topic img {
    width: 80px;
  }

  .question {
    font-size: clamp(1.563rem, 1.3rem + 0.7vw, 1.875rem);
    margin: 22px 0;
  }

  .answers {
    gap: 11px;
  }

  .answer {
    padding: 16px 20px;
  }

  .crest {
    height: clamp(150px, 28vh, 17rem);
  }
}

/* --------------------------------------------------------------- restraint -- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .cog {
    animation: none !important;
  }
}

/* --- Rooms: the way in, and the waiting room ----------------------------------------------------
 *
 * A room is the phone's own machinery (server/src/rooms.ts): three to six categories, a six-character
 * code, and a lobby that waits for its host. The screen enforces the 3–6 rule rather than explaining
 * it — the counter says how many more are needed and the button stays dark until it is satisfied.
 */

.tile.picked {
  border-color: var(--teal);
  box-shadow: inset 0 1px 0 rgba(230, 200, 120, 0.15), 0 0 24px -8px rgba(53, 200, 206, 0.7);
}

.tile.picked .name {
  color: var(--teal-lit);
}

.code-row {
  display: flex;
  gap: 10px;
  align-items: stretch;
  margin-top: 8px;
}

.code-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 16px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--panel-lo);
  color: var(--cream);
  font-family: var(--display);
  font-size: var(--step-1);
  /* A room code is six characters and gets read aloud across a table. Wide tracking and real
   * uppercase are what make O/0 and I/1 survive that. */
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.code-input::placeholder {
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: none;
  font-size: var(--step--1);
}

.code-input:focus-visible {
  outline: none;
  border-color: var(--teal);
}

/* `flex: 0 0 auto` alone is not enough: .btn is `width: 100%`, which becomes the flex base size, so
 * the button claimed the whole row and squeezed the field down to nothing. This affected every
 * code-entry row in the client — rooms, duels, leagues — not only the newest one. */
.code-row .btn {
  flex: 0 0 auto;
  width: auto;
}

/* The code itself, in the lobby: the one thing on the screen someone has to read out. */
/* THE CODE IS THE SCREEN. It is the one thing a host reads out loud, and Lobby.gd sizes it like it:
 * a 15pt muted caption over a 46pt bold display line, centred, in GLOW — the room's teal, because
 * the code IS the room. Here it was brass, a third of the size, and wedged between its own label and
 * a Copy button on a single line. */
.room-code {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin: 4px 0 12px;
}

.room-code-label {
  color: var(--muted);
  font-size: clamp(9.5px, 2.08vw, 13px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.room-code-value {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(20px, 6.39vw, 34px);
  letter-spacing: 0.18em;
  color: var(--teal-lit);
}

/* 18pt of 720 — the phone's legend line, over the roster it counts. */
.roster-count {
  margin: 0 0 8px;
  color: var(--cream);
  font-family: var(--display);
  font-size: clamp(10px, 2.5vw, 14px);
  letter-spacing: 0.04em;
}

/* `.btn` is full-width by default — every other button on the site is a row of its own. This one
 * sits beside the code, so it has to opt out. */
.room-code-copy {
  width: auto;
  padding: 6px 14px;
  min-height: 0;
  font-size: var(--step--2);
}

.roster {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 14px 0 4px;
}

.player {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
  transition: border-color 0.16s var(--ease);
}

.player.ready {
  border-color: rgba(53, 200, 206, 0.55);
}

.player-icon {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  object-fit: contain;
}

.player-icon.blank {
  border: 1px dashed var(--edge);
  border-radius: 50%;
}

.player-who {
  flex: 1 1 auto;
  min-width: 0;
}

.player-name {
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.player-tags {
  color: var(--muted);
  font-size: var(--step--2);
  letter-spacing: 0.05em;
}

.player-ready {
  flex: 0 0 auto;
  font-size: var(--step-1);
  color: var(--muted);
}

.player.ready .player-ready {
  color: var(--teal-lit);
}

/* --- Answering something that is not multiple choice --------------------------------------------
 *
 * 416 of the bank's 6111 questions are a guess-year, a typed answer, an image choice or a map pin,
 * and nothing on the server keeps them out of a daily or a room. For a while this screen drew only
 * `options`, so those questions arrived with nothing to answer them with.
 */

/* A year on a slider, the way the phone does it: the number is the loud thing, the track is quiet,
 * and the handle starts in the middle of the range. */
.year-input {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 8px 4px 4px;
}

.year-value {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-4);
  line-height: 1;
  text-align: center;
  color: var(--brass-head);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 30px rgba(230, 200, 120, 0.35);
  transition: color 0.25s var(--ease);
}

.year-value.is-right {
  color: var(--right-edge);
}

.year-value.is-wrong {
  color: var(--wrong-edge);
}

.year-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 30px;
  background: transparent;
  cursor: pointer;
}

.year-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, rgba(198, 162, 75, 0.25), rgba(53, 200, 206, 0.3));
  border: 1px solid var(--edge);
}

.year-slider::-moz-range-track {
  height: 6px;
  border-radius: 3px;
  background: linear-gradient(90deg, rgba(198, 162, 75, 0.25), rgba(53, 200, 206, 0.3));
  border: 1px solid var(--edge);
}

.year-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  margin-top: -11px;
  border-radius: 50%;
  border: 1px solid rgba(240, 214, 150, 0.7);
  background: radial-gradient(circle at 34% 30%, var(--brass-lit), var(--brass-dark));
  box-shadow: 0 0 22px -4px rgba(230, 200, 120, 0.75), 0 6px 14px -8px #000;
}

.year-slider::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid rgba(240, 214, 150, 0.7);
  background: radial-gradient(circle at 34% 30%, var(--brass-lit), var(--brass-dark));
  box-shadow: 0 0 22px -4px rgba(230, 200, 120, 0.75), 0 6px 14px -8px #000;
}

.year-slider.is-right::-webkit-slider-thumb {
  background: radial-gradient(circle at 34% 30%, var(--right-edge), var(--right));
  border-color: var(--right-edge);
}

.year-slider.is-wrong::-webkit-slider-thumb {
  background: radial-gradient(circle at 34% 30%, var(--wrong-edge), var(--wrong));
  border-color: var(--wrong-edge);
}

.year-slider.is-right::-moz-range-thumb {
  background: radial-gradient(circle at 34% 30%, var(--right-edge), var(--right));
  border-color: var(--right-edge);
}

.year-slider.is-wrong::-moz-range-thumb {
  background: radial-gradient(circle at 34% 30%, var(--wrong-edge), var(--wrong));
  border-color: var(--wrong-edge);
}

.year-ends {
  display: flex;
  justify-content: space-between;
  color: var(--muted);
  font-family: var(--display);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
}

.year-miss {
  margin: 12px 0 0;
  text-align: center;
  color: var(--secondary);
  font-size: var(--step--1);
}

/* A typed answer. */
.typed-input {
  padding: 4px 0 2px;
}

.typed-answer {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--panel-lo);
  color: var(--cream);
  font-family: var(--serif);
  font-size: var(--step-1);
}

.typed-answer:focus-visible {
  outline: none;
  border-color: var(--teal);
}

.typed-answer::placeholder {
  color: var(--muted);
}

/* Once an answer is on its way to the match, nothing in here is live any more. */
.answers.sent .year-slider,
.answers.sent .typed-answer {
  opacity: 0.7;
  pointer-events: none;
}

/* Once the answer is in, the input's buttons have had their turn — they go rather than dim, so the
 * verdict is the brightest thing on the screen. */
.answers.sent .btn {
  display: none;
}

/* A pin on a world map. One equirectangular image cropped to a lon/lat box — the same projection
 * scripts/ui/PinMapWidget.gd uses, so a place lands in the same spot on both clients. */
.pin-hint {
  margin: 0 0 10px;
  color: var(--secondary);
  font-size: var(--step--1);
  text-align: center;
}

.pin-map {
  position: relative;
  width: 100%;
  overflow: hidden;
  border: 1px solid rgba(198, 162, 75, 0.6);
  border-radius: 10px;
  background: var(--panel-lo);
  cursor: crosshair;
  touch-action: manipulation;
}

.pin-map-img {
  position: absolute;
  max-width: none;
  image-rendering: auto;
  pointer-events: none;
  user-select: none;
}

.pin {
  position: absolute;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  pointer-events: none;
  animation: rise 0.2s var(--ease) both;
}

.pin-mine {
  border: 2px solid var(--brass-lit);
  background: rgba(230, 200, 120, 0.45);
  box-shadow: 0 0 16px -2px rgba(230, 200, 120, 0.9);
}

.pin-true {
  border: 2px solid var(--right-edge);
  background: rgba(79, 208, 138, 0.5);
  box-shadow: 0 0 18px -2px rgba(79, 208, 138, 0.95);
}

.answers.sent .pin-map {
  cursor: default;
}

/* --- Duels --------------------------------------------------------------------------------------
 *
 * The inbox sorts by whose move it is, not by when: a duel waiting on YOU is the only kind that
 * needs doing, so the colour says so before the words do.
 */

.duel-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 4px 0 10px;
}

.duel-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 12px 16px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
  text-align: left;
  transition: border-color 0.16s var(--ease), transform 0.12s var(--ease);
}

.duel-row[data-state="MY_TURN"] {
  border-color: rgba(53, 200, 206, 0.6);
  box-shadow: 0 0 22px -12px rgba(53, 200, 206, 0.9);
}

.duel-row[data-state="RESULT_NEW"] {
  border-color: rgba(198, 162, 75, 0.55);
}

.duel-row[data-state="DONE"] {
  opacity: 0.72;
}

@media (hover: hover) and (pointer: fine) {
  .duel-row:hover {
    transform: translateY(-1px);
    border-color: var(--edge-lit);
  }
}

.duel-name {
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
}

.duel-code {
  font-family: var(--display);
  font-size: var(--step--2);
  letter-spacing: 0.16em;
  color: var(--muted);
}

.duel-right {
  text-align: right;
  flex: 0 0 auto;
}

.duel-state {
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.05em;
  color: var(--secondary);
}

.duel-row[data-state="MY_TURN"] .duel-state {
  color: var(--teal-lit);
}

.duel-row[data-state="RESULT_NEW"] .duel-state {
  color: var(--brass-lit);
}

.duel-score {
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
  font-variant-numeric: tabular-nums;
}

/* One duel, up close. */
.duel-verdict {
  margin: 14px 0 4px;
  text-align: center;
  font-size: var(--step-2);
}

.duel-verdict.won {
  color: var(--right-edge);
}

.duel-verdict.lost {
  color: var(--wrong-edge);
}

.duel-board {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin: 10px 0 6px;
  padding: 18px 12px;
  border: 1px solid var(--edge);
  border-radius: 14px;
  background: var(--plate);
}

.duel-side {
  flex: 1 1 0;
  text-align: center;
  min-width: 0;
}

.duel-side-name {
  font-family: var(--display);
  font-size: var(--step--1);
  color: var(--secondary);
  letter-spacing: 0.05em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.duel-side-score {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-3);
  color: var(--brass-head);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.duel-side-sub {
  font-size: var(--step--2);
  color: var(--muted);
}

.duel-vs {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: var(--step-1);
}

/* --- Leagues ------------------------------------------------------------------------------------
 *
 * Every number on this screen is the server's. The styling's only job is to make the table readable
 * and to make your own row findable in it without hunting.
 */

.league-tabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 2px 0 8px;
  scrollbar-width: none;
}

.league-tabs::-webkit-scrollbar {
  display: none;
}

.league-tab {
  position: relative;
  flex: 0 0 auto;
  padding: 8px 16px;
  border: 1px solid var(--edge);
  border-radius: 999px;
  background: var(--panel-lo);
  color: var(--secondary);
  font-family: var(--display);
  font-size: var(--step--1);
  white-space: nowrap;
}

.league-tab.on {
  border-color: var(--teal);
  color: var(--teal-lit);
}

/* A league that wants something from you today. The server decided that, not the stylesheet. */
.league-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-left: 8px;
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 0 0 10px -1px var(--teal);
  vertical-align: middle;
}

.league-banner {
  margin: 8px 0 14px;
  padding: 14px 18px;
  border: 1px solid rgba(198, 162, 75, 0.55);
  border-radius: 14px;
  background: linear-gradient(178deg, rgba(198, 162, 75, 0.14), rgba(198, 162, 75, 0.04));
  text-align: center;
}

.league-banner-title {
  font-family: var(--display);
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  color: var(--brass-lit);
}

.league-banner-body {
  margin-top: 4px;
  color: var(--cream);
}

.league-head {
  margin: 6px 0 12px;
}

.league-name {
  font-size: var(--step-2);
}

.league-meta {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 4px;
  color: var(--muted);
  font-size: var(--step--1);
}

.league-week {
  color: var(--brass);
  font-family: var(--display);
  letter-spacing: 0.08em;
}

.table {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 14px 0 8px;
}

.table-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
}

/* Your own line, findable at a glance on a board of eight. */
.table-row.is-me {
  border-color: var(--teal);
  box-shadow: 0 0 22px -14px rgba(53, 200, 206, 0.9);
}

.rank {
  flex: 0 0 1.6rem;
  font-family: var(--display);
  font-size: var(--step-1);
  color: var(--brass-head);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Competition ranking means two people can both be 1st. That is not a bug and is not tie-broken. */
.table-row:first-child .rank {
  color: var(--brass-lit);
}

.table-icon {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  object-fit: contain;
}

.table-icon.blank {
  border: 1px dashed var(--edge);
  border-radius: 50%;
}

.table-who {
  flex: 1 1 auto;
  min-width: 0;
}

.table-name {
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.table-sub {
  color: var(--muted);
  font-size: var(--step--2);
}

.table-points {
  flex: 0 0 auto;
  font-family: var(--display);
  font-size: var(--step-1);
  color: var(--cream);
  font-variant-numeric: tabular-nums;
}

.league-weeks {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
}

.league-weeks .btn {
  width: auto;
  padding: 8px 14px;
  font-size: var(--step--2);
}

/* --- Friends and rivals -------------------------------------------------------------------------
 *
 * A rival is somebody you have played but not added, and that is the only way anyone is ever offered
 * as a friend. There is no search here on purpose.
 */

.people {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 6px 0 12px;
}

.person {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
}

.person-icon {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  object-fit: contain;
}

.person-icon.blank {
  border: 1px dashed var(--edge);
  border-radius: 50%;
}

.person-who {
  flex: 1 1 auto;
  min-width: 0;
}

.person-name {
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.person-sub {
  color: var(--muted);
  font-size: var(--step--2);
}

.person-actions {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

.person-btn {
  width: auto;
  padding: 7px 12px;
  min-height: 0;
  font-size: var(--step--2);
}

/* "Report this question" — findable by anyone who wants it, invisible to everyone else. A loud
 * button under every answer invites the shrug rather than the real complaint. */
.report-link {
  width: auto;
  margin: 18px auto 0;
  padding: 6px 12px;
  min-height: 0;
  font-size: var(--step--2);
  color: var(--muted);
  opacity: 0.75;
}

.report-link:hover {
  opacity: 1;
}

/* --- The closed test ----------------------------------------------------------------------------
 *
 * Not a store page. The app is in a CLOSED test, so a store link shows a stranger nothing at all —
 * Play hides it from any account that is not already on the tester list. The only thing that helps
 * is getting on that list, so that is what this screen asks for, with one field and no others.
 */

.beta-form {
  margin-top: 6px;
}

.beta-status {
  min-height: 1.3em;
  margin: 8px 0 0;
  text-align: center;
}

.beta-status.bad {
  color: var(--wrong-edge);
}

.beta-small {
  margin: 18px 0 0;
  color: var(--muted);
  font-size: var(--step--2);
  line-height: 1.5;
  text-align: center;
}

.beta-done {
  margin-top: 18px;
  padding: 22px 20px;
  border: 1px solid rgba(53, 200, 206, 0.5);
  border-radius: 16px;
  background: var(--plate);
  text-align: center;
}

.beta-done h2 {
  font-size: var(--step-1);
  color: var(--teal-lit);
}

.beta-done p {
  margin: 10px 0 0;
  color: var(--secondary);
}

/* --- The app, offered ---------------------------------------------------------------------------
 *
 * Its own band rather than a sixth button in the menu. That stack is five identical brass slabs
 * already, and "get the app" is not a game mode — in the stack it would lengthen the wall and file
 * the offer under the wrong heading. The frame is what says "different kind of thing".
 *
 * Brass, never teal. Today's round owns the one teal thing on this screen.
 */

.app-band {
  margin-top: clamp(16px, 3vh, 24px);
  padding: 18px 18px 16px;
  border: 1px solid var(--edge);
  border-radius: 16px;
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(198, 162, 75, 0.09), transparent 70%),
    var(--plate);
  box-shadow: var(--lift);
  text-align: center;
}

.app-band-title {
  font-family: var(--display);
  font-size: clamp(12px, 3.3vw, 17px);
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--brass-lit);
}

.app-band-body {
  margin: 8px 0 16px;
  color: var(--secondary);
  font-size: clamp(10px, 2.5vw, 14px);
  line-height: 1.5;
}

/* An <a> styled as a button needs the text centred and the underline gone — the .btn rules assume a
 * <button>, which has neither. */
.app-band a.btn {
  text-decoration: none;
}

/* --- Linking a phone and a browser --------------------------------------------------------------
 *
 * Two accounts, and Nakama cannot merge them — so the screen's job is to make the choice legible
 * before it is made. Two cards, real numbers, side by side.
 */

.app-band-link {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 4px;
  border: 0;
  background: none;
  color: var(--muted);
  font-family: var(--serif);
  font-size: clamp(9px, 2.08vw, 12px);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.app-band-link:hover {
  color: var(--secondary);
}

.pair-compare {
  margin-top: 4px;
}

.pair-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 10px 0 4px;
}

.pair-card {
  padding: 14px 12px;
  border: 1px solid var(--edge);
  border-radius: 14px;
  background: var(--plate);
  text-align: center;
}

/* The one that will be kept. Teal is the live thing everywhere else in this client, and this is the
 * account that stays live. */
.pair-card.keep {
  border-color: var(--teal);
  box-shadow: 0 0 24px -14px rgba(53, 200, 206, 0.9);
}

/* An account with nothing on it should not look like a loss. */
.pair-card.faded {
  opacity: 0.6;
}

.pair-card-label {
  font-family: var(--display);
  font-size: var(--step--2);
  letter-spacing: 0.08em;
  color: var(--secondary);
}

.pair-card-name {
  margin-top: 4px;
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pair-card-stats {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  color: var(--muted);
  font-size: var(--step--2);
}

.pair-card-stats b {
  color: var(--brass-lit);
  font-family: var(--display);
  font-size: var(--step-0);
}

.pair-note {
  margin: 10px 0 0;
  color: var(--secondary);
  font-size: var(--step--1);
  line-height: 1.5;
}

.pair-show {
  margin-top: 8px;
}

.pair-done {
  margin-top: 18px;
  padding: 20px;
  border: 1px solid rgba(53, 200, 206, 0.5);
  border-radius: 16px;
  background: var(--plate);
  text-align: center;
}

.pair-done h2 {
  font-size: var(--step-1);
  color: var(--teal-lit);
}

.pair-done p {
  margin: 8px 0 0;
  color: var(--secondary);
}

.menu-hint.bad {
  color: var(--wrong-edge);
}

/* --- A room ends with who won --------------------------------------------------------------------
 *
 * The phone has shown a ranked table since Phase 4; the web finished a multiplayer round with a solo
 * score and no mention that anybody else had been there. The ranks are the server's, already ordered
 * and already tie-broken — two clients disagreeing about who won is not a cosmetic bug.
 */

.standings-title {
  margin: 6px 0 14px;
  text-align: center;
  font-size: var(--step-2);
  color: var(--brass-lit);
}

.standings {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.standing {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border: 1px solid var(--edge);
  border-radius: 12px;
  background: var(--plate);
}

.standing.is-me {
  border-color: var(--teal);
  box-shadow: 0 0 22px -14px rgba(53, 200, 206, 0.9);
}

/* A badge, not a bare number — the phone draws a filled disc and the rank is what the row is FOR. */
.standing-rank {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--edge);
  background: rgba(143, 136, 115, 0.14);
  font-family: var(--display);
  font-size: var(--step--1);
  color: var(--secondary);
  font-variant-numeric: tabular-nums;
}

.standing:first-child .standing-rank {
  border-color: rgba(230, 200, 120, 0.7);
  background: linear-gradient(178deg, var(--brass-lit), var(--brass-dark));
  color: #241a06;
}

.standing-icon {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  object-fit: contain;
}

.standing-icon.blank {
  border: 1px dashed var(--edge);
  border-radius: 50%;
}

.standing-who {
  flex: 1 1 auto;
  min-width: 0;
}

.standing-name {
  font-family: var(--display);
  font-size: var(--step-0);
  color: var(--cream);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.standing-sub {
  color: var(--muted);
  font-size: var(--step--2);
}

.standing-points {
  flex: 0 0 auto;
  font-family: var(--display);
  font-size: var(--step-1);
  color: var(--teal-lit);
  font-variant-numeric: tabular-nums;
}
