:root {
  --bg: #14121b;
  --bg-glow: #241f36;
  --panel: #1e1b29;
  --panel-2: #262232;
  --line: #35304a;
  --ink: #f2eefb;
  --ink-dim: #a49cba;
  --accent: #f5a524;
  --accent-ink: #2a1c02;
  --good: #3fbf7f;
  --warn: #e06f5e;
  --radius: 14px;

  /* Page frame. The ad rails run to the edges of the window and are the
     outer margin themselves, so there is no padding outside them; --ad-gap is
     the only breathing room, between the board and the rails. */
  --ad-gap: 22px;
  --ad-rail: 160px;
  --ad-strip: 90px;

  /* The rails are the side margin, but nothing sits above the board, so it
     carries its own top gutter - deeper than the gap to the rails, because an
     edge with nothing beyond it needs more room to read as a margin. */
  --board-top: 40px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  background:
    radial-gradient(1100px 620px at 15% -10%, var(--bg-glow), transparent 65%),
    radial-gradient(900px 520px at 105% 10%, #2b1f33, transparent 60%),
    var(--bg);
  color: var(--ink);
  font: 16px/1.55 "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif;
}

/* The whole page: board in the middle, with the reserved ad space running the
   full height down both sides and the full width along the bottom. Those rails
   are the outer margin - they go to the window edges themselves, and hold the
   board off them. */
.page {
  min-height: 100vh;
  display: grid;
  gap: var(--ad-gap);
  grid-template-columns: var(--ad-rail) minmax(0, 1fr) var(--ad-rail);
  grid-template-rows: minmax(0, 1fr) auto;
}

/* Both rails run the whole height of the window, past the bottom strip, so the
   reserved space is a continuous frame around the board rather than three
   separate blocks. */
.ad-left {
  grid-area: 1 / 1 / 3 / 2;
}

#app-root {
  grid-area: 1 / 2;
  padding-top: var(--board-top);
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.ad-right {
  grid-area: 1 / 3 / 3 / 4;
}

.ad-bottom {
  grid-area: 2 / 2 / 3 / 3;
}

/* Ad rails follow the page down, so a side ad stays in view on a long screen. */
.ad-rail .ad-slot {
  position: sticky;
  top: 0;
  min-height: 600px;
}

.ad-strip .ad-slot {
  min-height: var(--ad-strip);
}

/* The slot holds its space and draws nothing. The border is transparent rather
   than absent so that dropping a real ad tag in cannot shift the layout by a
   pixel, and so a tint can be put back for a moment when you need to see where
   the slots actually are. */
.ad-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: var(--radius);
}

/* The board fills the middle column; the rails around it are the margin. */
.app {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The board stretches to the bottom of the window; the front page floats in
   the middle of it instead. */
.sheet {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.sheet > .layout {
  flex: 1;
  min-height: 0;
}

.sheet > .home {
  margin: auto;
}

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

.banner {
  margin-bottom: 14px;
  padding: 9px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  color: var(--ink-dim);
  font-size: 14px;
  text-align: center;
}

.banner-warn {
  background: rgba(224, 111, 94, 0.16);
  color: #ffcfc6;
}

.toast {
  position: fixed;
  left: 50%;
  top: 18px;
  transform: translateX(-50%);
  z-index: 40;
  max-width: min(560px, 92vw);
  padding: 12px 44px 12px 16px;
  border-radius: 12px;
  background: #3a2230;
  border: 1px solid #6c3b4c;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
  cursor: pointer;
}

.toast-ok {
  background: #1d3a2c;
  border-color: #2f6a4c;
  padding-right: 16px;
  cursor: default;
}

.toast-x {
  position: absolute;
  right: 14px;
  top: 9px;
  color: var(--ink-dim);
  font-size: 20px;
}

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

/* Stage across the full width, then four lanes that run to the bottom of the
   window: ask, feed, guess, players. */
.layout {
  display: grid;
  grid-template-columns:
    minmax(0, 1fr) minmax(0, 1.3fr) minmax(0, 1fr) minmax(230px, 0.75fr);
  grid-template-rows: auto minmax(0, 1fr);
  gap: 14px;
  min-height: 0;
}

.stage {
  grid-column: 1 / -1;
}

/* The lobby and the final scoreboard have no columns to run: one panel and
   the score sheet beside it. */
.layout-simple {
  grid-template-columns: minmax(0, 1fr) minmax(230px, 0.28fr);
  grid-template-rows: minmax(0, 1fr);
}

/* ---------- surfaces ---------- */

.panel,
.home,
.roster,
.stage,
.col {
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
}

.panel,
.home {
  padding: 22px;
}

/* Tighter than the other surfaces: this lane carries the identity strip above
   the list now, and the rows are short enough that panel padding around them
   only wasted the column. */
.roster {
  padding: 16px;
}

.home {
  max-width: 620px;
  margin: 0 auto;
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

h1 {
  margin: 0 0 8px;
  font-size: clamp(28px, 5vw, 40px);
  letter-spacing: -0.02em;
}

h2 {
  margin: 0;
  font-size: 22px;
  letter-spacing: -0.01em;
}

h3 {
  margin: 0 0 12px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-dim);
}

.sub,
.lede {
  margin: 4px 0 0;
  color: var(--ink-dim);
}

.lede {
  max-width: 52ch;
  margin-bottom: 22px;
}

.hint {
  margin: 12px 0 0;
  color: var(--ink-dim);
  font-size: 14px;
}

.hint.big {
  font-size: 17px;
  color: var(--ink);
}

/* ---------- forms ---------- */

.field {
  display: block;
  margin-bottom: 16px;
}

.field > span {
  display: block;
  margin-bottom: 6px;
  font-size: 14px;
  color: var(--ink-dim);
}

input[type="text"] {
  width: 100%;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #17141f;
  color: var(--ink);
  font: inherit;
  transition: border-color 0.15s, box-shadow 0.15s;
}

input[type="text"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(245, 165, 36, 0.18);
}

.code-input {
  max-width: 200px;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  font-size: 20px;
  font-weight: 700;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-top: 20px;
}

button {
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid transparent;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.1s, filter 0.15s, background 0.15s;
}

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

button:not(:disabled):hover {
  filter: brightness(1.08);
}

button:not(:disabled):active {
  transform: translateY(1px);
}

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

.secondary {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--line);
  color: var(--ink);
}

/* The two jobs, each in its own colour on the button that does it. Asking is
   the accent the board already uses for the thing in play; guessing is the
   green it uses for a right answer, which is what a guess is aiming at. The
   master holds both jobs and so has both buttons, one of each - which is the
   clearest thing that seat has ever looked. */
.job-ask {
  background: var(--accent);
  color: var(--accent-ink);
}

.job-guess {
  background: var(--good);
  color: #06301d;
}

.secondary.quiet {
  background: transparent;
}

.wide {
  display: block;
  width: 100%;
  margin-top: 10px;
}

.link-button {
  background: none;
  border: none;
  color: var(--ink-dim);
  text-decoration: underline;
  padding: 8px 4px;
}

/* ---------- score sheet ---------- */

.roster ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  align-content: start;
  gap: 6px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

.roster-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 8px;
  border-radius: 10px;
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.03);
}

/* Your own row. The "you" tag alone was easy to miss, so the row it sits on
   carries a rail down its leading edge and lifts off the background.
   Deliberately neutral rather than tinted: this same row can also be the
   spotlight's or the master's, and those two own the accent and the gold. The
   rail is drawn rather than coloured in for the same reason - it survives on
   top of either of them, so you can always find yourself in the list. */
.roster-row.is-you {
  position: relative;
  border-color: rgba(242, 238, 251, 0.3);
  background: rgba(255, 255, 255, 0.09);
}

.roster-row.is-you::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 50%;
  width: 3px;
  height: 64%;
  transform: translateY(-50%);
  border-radius: 0 3px 3px 0;
  background: var(--ink);
}

/* The row's own name is what the rail points at, so it reads at full strength
   rather than in the list's dimmed body colour. */
.roster-row.is-you .roster-who {
  color: var(--ink);
  font-weight: 600;
}

.roster-row.away {
  opacity: 0.5;
}

.roster-row.is-spotlight {
  border-color: rgba(245, 165, 36, 0.5);
  background: rgba(245, 165, 36, 0.1);
}

/* The name on one line and whatever the player is on the next. Side by side
   they pushed the row wider than the lane, which put a scrollbar under the
   whole list; stacked, a long name and four tags both fit. */
.roster-name {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 14px;
}

.roster-who {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.roster-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* The tags space themselves now, so the inline margin would double up. */
.roster-tags .tag {
  margin-left: 0;
}

.roster-score {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--accent);
}

.roster-foot {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.roster-foot .link-button {
  padding: 5px 2px;
  font-size: 14px;
}

.code-mini {
  padding: 5px 12px;
  border-radius: 8px;
  border: 1px dashed var(--line);
  background: rgba(245, 165, 36, 0.08);
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.18em;
}

/* ---------- stage ---------- */

.stage {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 18px 22px;
}

.stage-text {
  flex: 1;
  min-width: 0;
}

.stage-label {
  margin: 0 0 4px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-dim);
}

.question {
  font-size: clamp(20px, 2.6vw, 28px);
  line-height: 1.25;
}

.question.waiting {
  color: var(--ink-dim);
  font-weight: 500;
}

/* ---------- the answer's shape ---------- */

/* One slot per character of the answer. The server uncovers a letter every
   twenty seconds, so the row fills in as the round goes on. */
.letter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 12px;
}

.letter {
  display: grid;
  place-items: center;
  min-width: 22px;
  height: 30px;
  padding: 0 3px;
  border-bottom: 2px solid var(--ink-dim);
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  color: var(--accent);
  animation: letter-in 0.35s ease-out;
}

.letter.hidden {
  border-bottom-color: var(--line);
  animation: none;
}

/* A word break: no underline, just room. */
.letter-gap {
  width: 12px;
}

@keyframes letter-in {
  from {
    opacity: 0;
    transform: translateY(-6px) scale(1.4);
  }
}


.stage-meta {
  display: grid;
  justify-items: end;
  gap: 6px;
}

.round-pill {
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  color: var(--ink-dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.clock {
  padding: 6px 16px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.05);
  font-size: 24px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  color: var(--accent);
}

.clock.waiting {
  color: var(--ink-dim);
  font-size: 20px;
}

.clock.low {
  color: #ffd0c8;
  background: rgba(224, 111, 94, 0.2);
  border-color: var(--warn);
  animation: pulse 1s ease-in-out infinite;
}

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

/* ---------- the lanes ---------- */

/* Every lane runs the height of the board. The feed and the score sheet pin
   their heading and scroll their list; the rest just scroll whole. */
.col,
.roster,
.panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.col {
  padding: 18px;
  overflow-y: auto;
}

.panel {
  overflow-y: auto;
}

.col-feed,
.roster {
  overflow: hidden;
}

.col-live {
  border-color: rgba(245, 165, 36, 0.35);
}

.col-feed > h3,
.roster > h3 {
  flex: none;
}

.slot {
  padding: 16px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
}

.slot-mine {
  border-color: rgba(245, 165, 36, 0.45);
  background: rgba(245, 165, 36, 0.08);
}

.slot-solved {
  display: grid;
  border-color: var(--good);
  background: rgba(63, 191, 127, 0.12);
}

.slot .hint:first-child {
  margin-top: 0;
}

/* Says which job you were dealt, in the colour of the button underneath it. */
.turn-label {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-dim);
}

.turn-label.is-ask {
  color: var(--accent);
}

.turn-label.is-guess {
  color: #8ef0c0;
}


/* ---------- feed ---------- */

.feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  align-content: start;
  gap: 8px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

.feed-empty {
  text-align: center;
  padding: 12px 0;
}

/* A question put to the subject and a proposed answer both land here, so the
   row leads with who did it and which of the two it was. The tint down the
   left edge carries the same distinction at a glance. */
.feed-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border-radius: 10px;
  border-left: 3px solid transparent;
  background: rgba(255, 255, 255, 0.03);
}

.feed-row.pending {
  border: 1px dashed rgba(245, 165, 36, 0.45);
  border-left-width: 3px;
  background: rgba(245, 165, 36, 0.07);
}

.feed-body {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 1px;
}

.feed-who {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-dim);
}

.feed-kind {
  padding: 0 6px;
  border-radius: 999px;
  font-size: 10px;
  font-style: normal;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* The kind chip names the act, in that job's colour: asking is the accent,
   guessing is the green. A miss and a solve are the same act - both chips read
   "guessed" - so they share it, and how the guess went is carried by the row's
   own tint and the Yes/No chip on the end of it, not by this. */
.kind-asked {
  background: rgba(245, 165, 36, 0.2);
  color: var(--accent);
}

.kind-guessed,
.kind-solved {
  background: rgba(63, 191, 127, 0.2);
  color: #8ef0c0;
}

.feed-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The row's leading edge is the outcome. A question has none of its own until
   the subject rules on it, so it takes the asker's colour; a miss and a solve
   take the red and green they have earned. */
.feed-question {
  border-left-color: rgba(245, 165, 36, 0.55);
}

.feed-note {
  border-left-color: var(--good);
  background: rgba(63, 191, 127, 0.1);
}

.solved-text {
  color: var(--ink-dim);
  font-style: italic;
}

.feed-guess {
  border-left-color: rgba(224, 111, 94, 0.6);
  background: rgba(224, 111, 94, 0.1);
}

.chip {
  padding: 3px 12px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

.chip-yes {
  background: var(--good);
  color: #06301d;
}

.chip-no {
  background: var(--warn);
  color: #3a0d06;
}

.chip-wait {
  background: rgba(255, 255, 255, 0.12);
  color: var(--ink-dim);
}

/* One box, two things to do with it. */
.entry-actions {
  display: grid;
  gap: 10px;
  margin-top: 10px;
}

.entry-actions.two-up {
  grid-template-columns: 1fr auto;
}

/* The verdict slot holds the waiting chip and the subject's two buttons in the
   same cell, so swapping one for the other on hover moves nothing else on the
   row. Everybody sees the chip; only the subject has anything underneath it. */
.verdict-slot {
  flex: none;
  display: grid;
  justify-items: end;
  align-items: center;
}

.verdict-slot > * {
  grid-area: 1 / 1;
}

.row-verdict {
  display: flex;
  gap: 6px;
}

.row-verdict button {
  padding: 5px 14px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.row-verdict .row-yes {
  background: var(--good);
  color: #06301d;
}

.row-verdict .row-no {
  background: var(--warn);
  color: #3a0d06;
}

/* A row the subject can rule on: point at it and the chip gives way to yes and
   no. Kept on opacity rather than display so tabbing to a button reveals it
   too, and the buttons take no clicks while they are invisible. */
.feed-row.yours-to-answer {
  cursor: pointer;
  border-style: solid;
  border-color: rgba(245, 165, 36, 0.55);
  background: rgba(245, 165, 36, 0.12);
}

.yours-to-answer .row-verdict {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
}

.yours-to-answer:hover .row-verdict,
.yours-to-answer:focus-within .row-verdict {
  opacity: 1;
  pointer-events: auto;
}

.yours-to-answer:hover .chip-wait,
.yours-to-answer:focus-within .chip-wait {
  opacity: 0;
}

/* Nothing to point with: on a touch screen the buttons simply stay out. */
@media (hover: none) {
  .yours-to-answer .row-verdict {
    opacity: 1;
    pointer-events: auto;
  }

  .yours-to-answer .chip-wait {
    opacity: 0;
  }
}

/* The standing note under the question board: answering is the subject's
   choice, and nothing waits on a question they skip. */
.board-note {
  flex: none;
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
  color: var(--ink-dim);
  font-size: 13px;
  line-height: 1.5;
}

/* ---------- the master guesser ---------- */

/* The seat that holds both jobs, marked wherever its holder appears: a crown
   on their disc in the score sheet, and a band across the top of their own
   desk. Gold, and the only gold on the board that is not the accent. */
.crowned {
  position: relative;
  display: inline-flex;
  flex: none;
}

.crown {
  position: absolute;
  top: -9px;
  right: -7px;
  font-size: 15px;
  line-height: 1;
  transform: rotate(14deg);
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
  pointer-events: none;
}

/* The third job, and so tinted like the other two - in the gold that is this
   seat's own colour everywhere else on the board. Filling it in would have put
   it a shade away from the solid-accent spotlight tag. The crown on their disc
   is what makes the seat unmissable; this only has to name it. */
.tag-master {
  background: rgba(247, 215, 116, 0.2);
  color: #f7d774;
}

.roster-row.is-master {
  border-color: rgba(247, 215, 116, 0.5);
  background: rgba(247, 215, 116, 0.1);
}

/* The master's own desk says so across the top, where everybody else reads
   which single job they were given. */
.master-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid rgba(247, 215, 116, 0.45);
  background: linear-gradient(135deg, rgba(247, 215, 116, 0.22), rgba(245, 165, 36, 0.12));
  color: #f7d774;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* The crown sits inline here rather than pinned to a disc. */
.master-label .crown {
  position: static;
  transform: none;
  font-size: 16px;
}

.col-master {
  border-color: rgba(247, 215, 116, 0.4);
}

/* ---------- the subject's popup ---------- */

.veil {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(10, 8, 16, 0.72);
  backdrop-filter: blur(3px);
  animation: veil-in 0.15s ease-out;
}

.popup {
  width: min(520px, 100%);
  padding: 26px;
  border-radius: 16px;
  border: 1px solid rgba(245, 165, 36, 0.4);
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.6);
  animation: popup-in 0.18s ease-out;
}

.popup-question {
  margin: 0 0 18px;
  font-size: clamp(19px, 2.6vw, 25px);
  line-height: 1.3;
}

.popup .field:last-of-type {
  margin-bottom: 0;
}

@keyframes veil-in {
  from { opacity: 0; }
}

@keyframes popup-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
}

.yes-no {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.yes-no button {
  padding: 22px;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.yes-no .yes {
  background: var(--good);
  color: #06301d;
}

.yes-no .no {
  background: var(--warn);
  color: #3a0d06;
}

/* ---------- the celebration ---------- */

/* Icons float up the whole window and fade out. It never takes clicks, so a
   half-typed question underneath carries straight on. */
.party {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  overflow: hidden;
}

.party-icon {
  position: absolute;
  bottom: -12vh;
  line-height: 1;
  opacity: 0;
  animation-name: float-up;
  animation-timing-function: cubic-bezier(0.25, 0.6, 0.35, 1);
  animation-fill-mode: forwards;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.45));
}

@keyframes float-up {
  0% {
    opacity: 0;
    transform: translateY(0) scale(0.5) rotate(-12deg);
  }
  12% {
    opacity: 1;
  }
  70% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translateY(-118vh) scale(1.15) rotate(18deg);
  }
}

.party-banner {
  position: absolute;
  left: 50%;
  top: 15%;
  margin: 0;
  padding: 14px 34px;
  border-radius: 999px;
  background: rgba(30, 27, 41, 0.92);
  border: 1px solid rgba(63, 191, 127, 0.6);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
  color: #8ef0c0;
  font-size: clamp(20px, 3vw, 32px);
  font-weight: 800;
  letter-spacing: -0.01em;
  white-space: nowrap;
  animation: banner-pop 2.8s ease-out forwards;
}

.party-banner.mine {
  border-color: var(--accent);
  color: var(--accent);
}

@keyframes banner-pop {
  0% {
    opacity: 0;
    transform: translate(-50%, 14px) scale(0.85);
  }
  10% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1.06);
  }
  18% {
    transform: translate(-50%, 0) scale(1);
  }
  80% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -18px) scale(0.96);
  }
}

/* Anybody who has asked for less movement gets the banner and nothing else. */
@media (prefers-reduced-motion: reduce) {
  .party-icon {
    display: none;
  }

  .party-banner {
    animation-name: banner-fade;
  }

  @keyframes banner-fade {
    0%, 85% { opacity: 1; transform: translate(-50%, 0); }
    100% { opacity: 0; transform: translate(-50%, 0); }
  }
}

/* ---------- reveal & scores ---------- */

.answer {
  margin: 0;
  font-size: clamp(24px, 3.2vw, 32px);
  color: var(--accent);
  letter-spacing: -0.01em;
}

.verdict {
  margin: 12px 0 0;
  font-size: 16px;
  color: var(--ink-dim);
}

.rules {
  margin: 0;
  padding-left: 22px;
  display: grid;
  gap: 10px;
  color: var(--ink-dim);
}

.rules li {
  padding-left: 4px;
}

.code-chip {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 16px;
  border-radius: 12px;
  border: 1px dashed var(--line);
  background: rgba(245, 165, 36, 0.08);
  cursor: pointer;
}

.code-chip strong {
  font-size: 26px;
  letter-spacing: 0.22em;
  color: var(--accent);
}

.code-chip-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-dim);
}

.code-chip-copy {
  font-size: 12px;
  color: var(--ink-dim);
  text-decoration: underline;
}

.gain {
  margin-right: 6px;
  color: var(--good);
  font-style: normal;
  font-size: 13px;
}

.tag {
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: var(--ink-dim);
  font-size: 11px;
  font-style: normal;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* A job you hold this turn is tinted; something you simply are - in the
   spotlight, or first on the podium - is filled in. That rule is what keeps
   "asker" and "spotlight" apart on one accent, and "guesser" and "solved"
   apart on one green, without spending two more colours on it. */
.tag-spot,
.tag-first {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}

/* The host badge is administrative, not a game role - it says who to wait for,
   not what anybody is playing. Quiet and outlined so it reads as a different
   kind of thing from the coloured job tags, and so it can no longer be
   mistaken for the spotlight tag, which it used to match exactly. */
.tag-host {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink-dim);
}

.tag-asking {
  background: rgba(245, 165, 36, 0.2);
  color: var(--accent);
}

.tag-guessing {
  background: rgba(63, 191, 127, 0.2);
  color: #8ef0c0;
}

.tag-solved {
  background: var(--good);
  color: #06301d;
  font-weight: 700;
}

.tag-away {
  background: rgba(224, 111, 94, 0.18);
  color: #f0a99e;
}

/* ---------- avatars ---------- */

.avatar {
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  line-height: 1;
  background:
    radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.4), transparent 55%),
    linear-gradient(160deg, color-mix(in srgb, var(--tint) 78%, #ffffff 22%), var(--tint));
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--tint) 55%, transparent),
    0 6px 14px rgba(0, 0, 0, 0.35);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}

.avatar.dim {
  filter: grayscale(0.7);
}

.avatar.tiny {
  width: 26px;
  height: 26px;
  font-size: 14px;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--tint) 55%, transparent);
}

.avatar.small {
  width: 34px;
  height: 34px;
  font-size: 18px;
}

.avatar.medium {
  width: 48px;
  height: 48px;
  font-size: 26px;
}

.avatar.large {
  width: 76px;
  height: 76px;
  font-size: 42px;
  flex: 0 0 auto;
}

.podium {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

.podium-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.04);
}

.podium-row.first {
  background: linear-gradient(90deg, rgba(245, 165, 36, 0.22), rgba(245, 165, 36, 0.04));
  border: 1px solid rgba(245, 165, 36, 0.4);
}

.place {
  width: 22px;
  color: var(--ink-dim);
  font-weight: 700;
  text-align: center;
}

.podium-name {
  flex: 1;
  min-width: 0;
  font-size: 17px;
  font-weight: 600;
}

.podium-score {
  font-size: 19px;
  font-weight: 800;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* ---------- narrower screens ---------- */

/* The side rails are the first thing to go: below this the four lanes need
   every pixel more than a skyscraper does. The bottom strip stays. */
@media (max-width: 1500px) {
  /* The rails were the margin, so with them gone the board needs a gutter of
     its own rather than sitting against the window edge. */
  .page {
    grid-template-columns: minmax(0, 1fr);
    padding-inline: var(--ad-gap);
  }

  .ad-left,
  .ad-right {
    display: none;
  }

  #app-root {
    grid-area: 1 / 1;
  }

  .ad-bottom {
    grid-area: 2 / 1 / 3 / 2;
  }
}

@media (max-width: 920px) {
  :root {
    --ad-gap: 12px;
    --ad-strip: 60px;
    --board-top: 18px;
  }

  .page {
    min-height: 0;
  }
}


/* Under four comfortable lanes the feed drops to a full-width row of its own,
   with the score sheet running down beside both rows. */
@media (max-width: 1400px) and (min-width: 921px) {
  .layout {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(220px, 0.5fr);
    grid-template-rows: auto minmax(0, auto) minmax(0, 1fr);
  }

  /* In DOM order the sections are ask, feed, guess - the same in every phase. */
  .layout:not(.layout-simple) > section:nth-of-type(1) {
    grid-area: 2 / 1;
  }

  .layout:not(.layout-simple) > section:nth-of-type(2) {
    grid-area: 3 / 1 / 4 / 3;
  }

  .layout:not(.layout-simple) > section:nth-of-type(3) {
    grid-area: 2 / 2;
  }

  .layout:not(.layout-simple) > .roster {
    grid-area: 2 / 3 / 4 / 4;
  }
}

/* Narrow enough that columns stop helping: one lane, players first. */
@media (max-width: 920px) {
  .app {
    min-height: 0;
  }

  .layout,
  .layout-simple {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
  }

  .roster {
    order: -1;
  }

  .col,
  .roster,
  .panel {
    overflow: visible;
  }

  .feed,
  .roster ul {
    max-height: 55vh;
  }

  .stage {
    flex-wrap: wrap;
    gap: 14px;
  }

  .stage-meta {
    flex: 1 0 100%;
    grid-auto-flow: column;
    justify-items: center;
    align-items: center;
  }
}
