/* MMToC — game-show energy wearing IMC web colors.
   Palette anchored on @imc-trading/mui-theme: darkBlue #03225f, blue #11409c,
   secondaryBlue (action) #0651e2, grayBlue #ddeaff; BUY = success green,
   SELL = error red (house trading convention). System font stacks only. */

:root {
  /* Native controls (checkboxes, file inputs, selects, scrollbars) render light-mode without this,
     so every unchecked checkbox was a white square on a navy card. */
  color-scheme: dark;
  --bg0: #011124;
  --bg1: #012649;
  --bg2: #03225f;
  --ink: #f0f6ff;
  --muted: #9db9e4;
  --gray-blue: #ddeaff;
  --blue: #11409c;
  --action: #0651e2;
  --accent: #8daff2;
  --gold: #fbbf24;
  /* Medals: silver is the coin's own mid-tone, named; bronze the one colour the palette lacked. */
  --silver: #b0aaf0;
  --bronze: #cd7f32;
  --warn: #ffa726;
  --buy: #66bb6a;
  --buy-deep: #2e7d32;
  --sell: #f44336;
  --sell-deep: #d32f2f;
  --card: rgba(221, 234, 255, 0.05);
  --card-border: rgba(221, 234, 255, 0.16);
  --line: rgba(157, 185, 228, 0.4);
  --bgap: clamp(28px, 4vw, 72px);
  --radius: 8px;
  --font-ui: Tahoma, Verdana, sans-serif;
  --font-display: "Oswald", "MarkPro", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { height: 100%; }

body {
  font-family: var(--font-ui);
  color: var(--ink);
  background: linear-gradient(160deg, var(--bg0), var(--bg1) 55%, var(--bg2));
  background-size: 200% 200%;
  animation: bgshift 30s ease-in-out infinite alternate;
  overflow: hidden;
}

/* Brand moments get the display stack (Oswald/MarkPro where installed). */
.logo, .tagline, .tname, .round-name, .vs, .question, .mkt-count,
.q-value, .a-value, .winner-banner, .splash-title, .champ-name, .champ-label,
.new-panel h2, .recap-panel h2 {
  font-family: var(--font-display);
  letter-spacing: 0.04em;
}

body::before, body::after {
  content: "";
  position: fixed;
  width: 60vmax;
  height: 60vmax;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.28;
  pointer-events: none;
  z-index: 0;
}
body::before {
  background: radial-gradient(circle, #11409c, transparent 60%);
  top: -22vmax;
  left: -16vmax;
  animation: drift1 26s ease-in-out infinite alternate;
}
body::after {
  background: radial-gradient(circle, #0082ba, transparent 60%);
  bottom: -26vmax;
  right: -16vmax;
  animation: drift2 32s ease-in-out infinite alternate;
}

@keyframes bgshift { from { background-position: 0% 0%; } to { background-position: 100% 100%; } }
@keyframes drift1 { to { transform: translate(9vmax, 7vmax) scale(1.15); } }
@keyframes drift2 { to { transform: translate(-8vmax, -6vmax) scale(1.1); } }

#app { position: relative; z-index: 1; height: 100vh; display: flex; flex-direction: column; }
#view { flex: 1; overflow: auto; padding-bottom: 3rem; }

button, input, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }
input[type="checkbox"] { accent-color: var(--action); width: 1.15rem; height: 1.15rem; cursor: pointer; }

kbd {
  display: inline-block;
  padding: 0.1em 0.5em;
  border-radius: 4px;
  background: rgba(221, 234, 255, 0.1);
  border: 1px solid rgba(221, 234, 255, 0.25);
  border-bottom-width: 2px;
  font-size: 0.82em;
  font-weight: 600;
}

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

#hints {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  display: flex;
  justify-content: center;
  gap: 1.6rem;
  padding: 0.45rem 1rem;
  font-size: clamp(0.72rem, 1.1vw, 0.95rem);
  color: var(--muted);
  background: rgba(1, 17, 36, 0.8);
  backdrop-filter: blur(8px);
  border-top: 1px solid rgba(221, 234, 255, 0.08);
}
.hint { display: inline-flex; align-items: center; gap: 0.45em; white-space: nowrap; }

#toast {
  position: fixed;
  top: 1.2rem; left: 50%;
  transform: translate(-50%, -180%);
  z-index: 70;
  max-width: min(90vw, 640px);
  padding: 0.75rem 1.4rem;
  border-radius: 6px;
  background: linear-gradient(135deg, var(--sell), var(--sell-deep));
  color: #fff;
  font-weight: 600;
  box-shadow: 0 10px 30px rgba(211, 47, 47, 0.4);
  transition: transform 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.2);
  pointer-events: none;
}
#toast.show { transform: translate(-50%, 0); }

/* Confirmations should not look like failures. */
#toast.ok {
  background: linear-gradient(135deg, var(--buy), var(--buy-deep));
  box-shadow: 0 10px 30px rgba(46, 125, 50, 0.4);
}

/* Parked off-screen it still casts its shadow down into the top bar, which put a red halo across
   the top of every page. Must stay after #toast.ok — equal specificity. */
#toast:not(.show) { box-shadow: none; }

#confetti { position: fixed; inset: 0; z-index: 80; pointer-events: none; }

.loading, .empty { color: var(--muted); text-align: center; padding: 3rem 1rem; font-size: 1.1rem; }

/* Secondary text, used in five modules. The only .dim rule used to be `.spectator-list .dim`, so
   every other one — "owned by…", the share verbs, "You have read-only access." — rendered at full
   brightness. Scope overrides to their own selector, never by narrowing this base. */
.dim { color: var(--muted); font-size: 0.84rem; }

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

.btn {
  padding: 0.55rem 1.3rem;
  border-radius: 6px;
  font-weight: 600;
  background: rgba(17, 64, 156, 0.3);
  border: 1px solid rgba(221, 234, 255, 0.22);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.btn:hover { transform: translateY(-1px); background: rgba(17, 64, 156, 0.5); }
.btn:active { transform: translateY(0); }
.btn-primary {
  background: linear-gradient(135deg, var(--action), var(--blue));
  border-color: transparent;
  color: #fff;
  box-shadow: 0 4px 18px rgba(6, 81, 226, 0.35);
}
.btn-primary:hover { background: linear-gradient(135deg, #2b6ef0, #1a4fb8); box-shadow: 0 8px 26px rgba(6, 81, 226, 0.5); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn-ghost:hover { color: var(--ink); background: rgba(221, 234, 255, 0.08); }
.btn-big { font-size: clamp(1rem, 1.8vw, 1.5rem); padding: 0.75rem 2.1rem; }
.btn-small { font-size: 0.85rem; padding: 0.3rem 0.9rem; }
.btn-icon { padding: 0.35rem 0.6rem; font-size: 1.15rem; line-height: 1; }
.btn-icon.on { color: var(--gold); }
/* Both forms: `.disabled` is the class the overlay's quote button toggles, `:disabled` the real
   attribute a button that can only ever 400 carries. They have to look the same or the second one
   reads as live. */
.btn.disabled, .btn:disabled { opacity: 0.35; pointer-events: none; }
.btn kbd { margin-left: 0.5em; }

.chip {
  padding: 0.35rem 1.05rem;
  border-radius: 6px;
  border: 1px solid rgba(221, 234, 255, 0.28);
  background: rgba(221, 234, 255, 0.06);
  font-weight: 600;
  transition: all 0.15s ease;
}
.chip:hover { border-color: var(--gold); }
.chip.picked {
  border-color: var(--gold);
  color: var(--gold);
  box-shadow: 0 0 14px rgba(251, 191, 36, 0.35);
}

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

/* The gate (sign-in and invitation) still centres a hero; the tournaments page does not. */
.home-hero { text-align: center; }

.logo {
  font-size: clamp(4rem, 11vw, 9rem);
  font-weight: 600;
  line-height: 0.95;
  /* IMC legacy brand gradient: teal -> mid blue -> deep blue */
  background: linear-gradient(100deg, #00b5b1, #0082ba 45%, #005195);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 22px rgba(0, 130, 186, 0.35));
}
.logo-accent {
  background: linear-gradient(100deg, #0082ba, #005195 70%, #11409c);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.tagline {
  margin-top: 0.6rem;
  color: var(--muted);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-size: clamp(0.7rem, 1.4vw, 1rem);
}

.tlist { display: flex; flex-direction: column; gap: 0.7rem; }

.trow {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 1.2rem;
  border-radius: var(--radius);
  background: var(--card);
  border: 1px solid var(--card-border);
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.trow:hover { transform: translateX(4px); }
.trow.selected {
  border-color: var(--accent);
  animation: pulse-glow 1.8s ease-in-out infinite;
}
.trow-main { flex: 1; min-width: 0; }
.trow-name { font-size: clamp(1.1rem, 2vw, 1.5rem); font-weight: 600; }
.trow-meta { color: var(--muted); font-size: 0.9rem; margin-top: 0.15rem; }

.badge {
  padding: 0.25rem 0.75rem;
  border-radius: 4px;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}
.badge-live { background: rgba(255, 167, 38, 0.14); color: var(--warn); border: 1px solid rgba(255, 167, 38, 0.45); }
/* Watching, not running: the normal state for most people looking at a bracket, so it states a fact
   rather than warning about one. */
.badge-watching {
  background: rgba(221, 234, 255, 0.08);
  color: var(--accent);
  border: 1px solid var(--card-border);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.7rem;
}
.badge-done { background: rgba(17, 64, 156, 0.35); color: var(--accent); border: 1px solid rgba(141, 175, 242, 0.45); }

.icon-btn {
  padding: 0.35rem 0.6rem;
  border-radius: 4px;
  color: var(--muted);
  transition: all 0.15s ease;
}
.icon-btn:hover { color: #fff; background: rgba(211, 47, 47, 0.8); }

/* ---------------------------------------------------------------- new tournament modal */

.modal {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  background: rgba(1, 17, 36, 0.75);
  backdrop-filter: blur(8px);
  animation: fade-in 0.2s ease;
}

.new-panel, .recap-panel {
  width: min(680px, 92vw);
  max-height: 88vh;
  overflow: auto;
  padding: 2rem;
  border-radius: var(--radius);
  background: linear-gradient(170deg, #0a2d66, #03225f 60%, #012649);
  border: 1px solid rgba(221, 234, 255, 0.18);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  animation: pop-in 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.15);
}
.new-panel h2, .recap-panel h2 { font-size: 1.6rem; font-weight: 600; }

.field { display: flex; flex-direction: column; gap: 0.4rem; }
.field > span { font-weight: 600; color: var(--muted); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.08em; }
.field input[type="text"], .field textarea {
  padding: 0.6rem 0.9rem;
  border-radius: 4px;
  background: rgba(1, 17, 36, 0.5);
  border: 1px solid rgba(221, 234, 255, 0.2);
  outline: none;
  resize: vertical;
  transition: border-color 0.15s ease;
}
.field input[type="text"]:focus, .field textarea:focus { border-color: var(--accent); }

.field-row { display: flex; gap: 1.2rem; align-items: flex-end; flex-wrap: wrap; }
.field.check { flex-direction: row; align-items: center; gap: 0.6rem; }
.field.check span { text-transform: none; letter-spacing: 0; font-size: 1rem; color: var(--ink); }

/* What an option actually does, on its own line under the label. */
.field.check .nt-hint { display: block; font-size: 0.8rem; color: var(--muted); }

.field-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.btn-inline {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  font-size: 0.8rem;
  color: var(--accent);
  border-bottom: 1px solid transparent;
}
.btn-inline:hover { color: var(--ink); border-bottom-color: currentColor; }

.banks { display: flex; flex-direction: column; gap: 0.4rem; max-height: 200px; overflow: auto; }
.bank {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0.9rem;
  border-radius: 4px;
  background: rgba(221, 234, 255, 0.05);
  border: 1px solid rgba(221, 234, 255, 0.12);
  cursor: pointer;
}
.bank:hover { border-color: rgba(221, 234, 255, 0.3); }
.bank-name { flex: 1; font-weight: 600; }
.bank-count { color: var(--muted); font-size: 0.85rem; }

.seg { display: inline-flex; border-radius: 6px; overflow: hidden; border: 1px solid rgba(221, 234, 255, 0.25); }
.seg-btn { padding: 0.5rem 1.3rem; font-weight: 600; background: rgba(221, 234, 255, 0.04); }
.seg-btn + .seg-btn { border-left: 1px solid rgba(221, 234, 255, 0.15); }
.seg-btn.on { background: var(--action); color: #fff; }

.panel-actions { display: flex; justify-content: flex-end; gap: 0.8rem; margin-top: 0.4rem; }

.bank-warning {
  font-size: 0.92rem;
  font-weight: 600;
  border-radius: 4px;
  padding: 0.55rem 0.9rem;
  border: 1px solid transparent;
}
.bank-warning.ok { color: var(--muted); font-weight: 400; padding-left: 0.2rem; }
.bank-warning.warn {
  color: #ffb74d;
  background: rgba(255, 167, 38, 0.08);
  border-color: rgba(255, 167, 38, 0.4);
}
.bank-warning.bad {
  color: #ef9a9a;
  background: rgba(244, 67, 54, 0.08);
  border-color: rgba(244, 67, 54, 0.4);
}

/* ---------------------------------------------------------------- tournament view */

.tview { height: 100%; display: flex; flex-direction: column; }

.tname { font-weight: 600; }
/* Live metrics strip: single line inside the existing top bar, no extra
   vertical space. Small-caps accent labels, white 600 values, dot-separated. */
.tmeta {
  display: flex;
  align-items: baseline;
  color: var(--muted);
  font-weight: 600;
  font-size: clamp(0.78rem, 1.2vw, 1rem);
  /* Wraps rather than truncating: an ellipsis mid-list leaves the separator dot orphaned. */
  min-width: 0;
  flex-wrap: wrap;
}
.tstat { display: inline-flex; align-items: baseline; gap: 0.35em; }
.tsummary { display: inline; font-weight: 400; }
.tstat + .tstat::before { content: "\00b7"; margin: 0 0.5em 0 0.85em; opacity: 0.5; }
.tstat-l {
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.72em;
  color: var(--accent);
}
.tstat b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.tstat-sub { color: var(--muted); font-weight: 400; }

/* Who is running this bracket. A full-width strip under the sub-bar: unmissable, and it never covers
   the game. Informational blue, not a warning — two hosts in a room is normal, both acting is not. */
.control-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  padding: 0.4rem clamp(0.8rem, 2vw, 1.6rem);
  background: rgba(6, 81, 226, 0.18);
  border-bottom: 1px solid rgba(141, 175, 242, 0.35);
  color: var(--gray-blue);
  font-weight: 600;
  font-size: 0.88rem;
}

/* Mine to run: a footnote, not a strip. Somebody else holding the lease is a conflict worth a band
   across the page; me holding it is the ordinary case and was reported as too loud. So it keeps no
   background, no border and no full-width presence — a faint line tucked under the right-hand end
   of the sub-bar, where the host can find it and nobody else's eye lands on it. */
.control-bar.mine {
  justify-content: flex-end;
  padding: 0.1rem clamp(0.8rem, 2vw, 1.6rem) 0;
  background: none;
  border-bottom: none;
  color: var(--muted);
  font-weight: 400;
  font-size: 0.7rem;
  opacity: 0.55;
}

/* Fullscreen is the projector, and the projector shows the game rather than who is driving it —
   the same reason the top bar and the key legend go. Keyed on the class the app sets from its own
   `fullscreenchange`, not on `:fullscreen`: the in-app F toggle is the only entry point that also
   arms Keyboard Lock, and the rest of this file already hides its chrome the same way. Taking over
   from an abandoned laptop happens on the colleague's own screen, which is not fullscreen. */
body.is-fullscreen .control-bar { display: none; }

.champion-banner {
  align-self: center;
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  margin: 0.5rem auto 0;
  padding: 0.6rem 2.4rem;
  border-radius: var(--radius);
  background: linear-gradient(135deg, var(--gold), #f59e0b);
  color: #201500;
  box-shadow: 0 0 44px rgba(251, 191, 36, 0.5);
  animation: bounce-in 0.6s cubic-bezier(0.2, 0.9, 0.3, 1.3);
}
.champion-banner .crown { font-size: clamp(1.6rem, 3.2vw, 3rem); }
.champ-name { font-size: clamp(1.6rem, 3.4vw, 3.2rem); font-weight: 600; }
.champ-label { font-size: clamp(1rem, 1.8vw, 1.5rem); font-weight: 600; text-transform: uppercase; letter-spacing: 0.12em; }
.champion-banner { cursor: pointer; }
.champ-cta { font-size: 0.85rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; opacity: 0.65; }
.champion-banner:hover .champ-cta { opacity: 1; }

/* ---------------------------------------------------------------- recap */

.recap-panel { width: min(940px, 94vw); }
.recap-tiles { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.recap-tile {
  flex: 1 1 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  padding: 0.7rem 0.5rem;
  border-radius: 6px;
  background: rgba(221, 234, 255, 0.06);
  border: 1px solid rgba(221, 234, 255, 0.14);
}
.recap-tile b { font-family: var(--font-display); font-size: clamp(1.5rem, 2.6vw, 2.4rem); color: var(--accent); }
.recap-tile span { color: var(--muted); font-size: 0.82rem; text-transform: uppercase; letter-spacing: 0.08em; text-align: center; }

.recap-section { display: flex; flex-direction: column; gap: 0.55rem; }
.recap-section h3 {
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent);
}

.road { display: flex; flex-direction: column; gap: 0.3rem; }
.road-leg {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.4rem 0.9rem;
  border-radius: 4px;
  background: rgba(221, 234, 255, 0.05);
}
.leg-round { flex: 0 0 34%; color: var(--muted); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.08em; }
.leg-beat { flex: 1; }
.leg-beat b { color: var(--gray-blue); font-weight: 600; }
.leg-bye { color: var(--muted); font-style: italic; }
.leg-score { font-weight: 600; color: var(--gold); font-variant-numeric: tabular-nums; }

.recap-badge {
  align-self: flex-start;
  padding: 0.35rem 1rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: 0.92rem;
  border: 1px solid rgba(221, 234, 255, 0.25);
  color: var(--muted);
}
.recap-badge.perfect { border-color: var(--gold); background: rgba(251, 191, 36, 0.12); color: var(--gold); }

.hl {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.6rem 0.9rem;
  border-radius: 6px;
  background: rgba(221, 234, 255, 0.05);
  border-left: 3px solid var(--accent);
}
.hl-label { color: var(--accent); font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.12em; }
.hl-line b { color: var(--gold); }
.hl-q { color: var(--muted); font-size: 0.9rem; font-style: italic; }
.hl-q b { font-style: normal; color: var(--gray-blue); }
.hl-round { text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.74rem; font-style: normal; opacity: 0.7; }

/* The audience section only exists once its request lands, and the panel is a gapped column — an
   empty wrapper would otherwise spend a 1.1rem gap on nothing. */
.recap-audience:empty { display: none; }

/* Top three spectators. Same left-accent bar as .hl, medal-coloured; a shorter list is what one or
   two spectators look like, and each row still names its place. */
.podium { list-style: none; display: flex; flex-direction: column; gap: 0.35rem; }
.podium-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.8rem;
  border-radius: 6px;
  background: rgba(221, 234, 255, 0.05);
  border-left: 3px solid var(--card-border);
}

.podium-medal {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-family: var(--font-display);
  font-weight: 600;
  /* Dark ink on a bright medal — the same panel background the recap sits on. */
  color: #04203f;
}

.podium-who {
  font-weight: bold;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.podium-hits { margin-left: auto; flex: none; color: var(--muted); font-size: 0.8rem; }
.podium-pts {
  flex: none;
  color: var(--gold);
  font-weight: bold;
  font-variant-numeric: tabular-nums;
}

.podium-row.rank-1 { border-left-color: var(--gold); }
.podium-row.rank-1 .podium-medal { background: var(--gold); }
.podium-row.rank-2 { border-left-color: var(--silver); }
.podium-row.rank-2 .podium-medal { background: var(--silver); }
.podium-row.rank-3 { border-left-color: var(--bronze); }
.podium-row.rank-3 .podium-medal { background: var(--bronze); }

/* ---------------------------------------------------------------- bracket */

.bracket {
  flex: 1;
  display: flex;
  gap: var(--bgap);
  padding: 0.6rem clamp(1rem, 3vw, 3rem) 0.8rem;
  overflow: auto;
}
.bracket-compact { --bgap: 42px; }

.round {
  display: flex;
  flex-direction: column;
  flex: 1 0 235px;
  min-width: 235px;
  /* Small brackets would otherwise stretch two cards across the whole screen.
     Auto margins center the columns when there is slack and collapse to 0 when
     the bracket overflows, so wide brackets still scroll from their first round. */
  max-width: 640px;
  /* --bh (set in JS) = column height in px: round-name + the sum of the slot
     heights computed in computeSlotHeights (identical in every column). */
  height: max(100%, var(--bh, 0px));
  transition: height 0.6s ease;
}
.round:first-child { margin-left: auto; }
.round:last-child { margin-right: auto; }
.round-name {
  flex: none; /* equal header height in every column, or slot grids drift apart */
  height: 1.7rem;
  text-align: center;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: clamp(0.7rem, 1.1vw, 0.95rem);
  color: var(--accent);
}
.slots { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.slot {
  /* Height is set inline by JS (computeSlotHeights): a slot-height binary tree
     where a parent spans exactly its two feeders, so byes and finished matches
     shrink to one line while connectors stay attached. Cards center within. */
  flex: none;
  display: flex;
  align-items: center;
  position: relative;
  padding: 0.3rem 0;
  min-height: 0;
  transition: height 0.6s ease;
}

/* Elbows may extend past their slot (--half-sib = sibling height / 2), so no
   ancestor between here and .bracket may clip overflow. */
.slot.out-top::after, .slot.out-bottom::after {
  content: "";
  position: absolute;
  right: calc(var(--bgap) * -0.5);
  width: calc(var(--bgap) * 0.5);
  pointer-events: none;
  transition: height 0.6s ease;
}
.slot.out-top::after {
  /* From this slot's center down to the parent's center: the parent spans
     exactly this slot + its sibling, so the drop is sibling_height / 2. */
  top: 50%;
  height: var(--half-sib, 50%);
  border-top: 3px solid var(--line);
  border-right: 3px solid var(--line);
  border-top-right-radius: 8px;
}
.slot.out-bottom::after {
  bottom: 50%;
  height: var(--half-sib, 50%);
  border-bottom: 3px solid var(--line);
  border-right: 3px solid var(--line);
  border-bottom-right-radius: 8px;
}
.slot.in::before {
  content: "";
  position: absolute;
  left: calc(var(--bgap) * -0.5);
  width: calc(var(--bgap) * 0.5);
  top: calc(50% - 1.5px);
  border-top: 3px solid var(--line);
  pointer-events: none;
}

/* Column headers stay in lockstep bracket-wide, or the slot grids drift apart. */
.bracket-compact .round-name { font-size: 0.72rem; height: 1.4rem; letter-spacing: 0.12em; }

/* Per-round card tiers — see ROW_HEIGHTS in app.js, which mirrors these sizes.
   Compact: crowded early rounds. Roomy: quarter-final onward, where a column
   holds few cards and a default-size one looks lost in its slot. */
.round-compact .slot { padding: 0.15rem 0; }
.round-compact .match { padding: 0.22rem 0.65rem; border-radius: 6px; border-width: 1px; }
.round-compact .pname { font-size: 0.92rem; }
.round-compact .mrow { padding: 0.05em 0; }
.round-compact .pips { font-size: 0.72rem; }

.round-roomy .match { padding: clamp(0.9rem, 2.2vh, 1.8rem) clamp(1rem, 1.6vw, 1.6rem); }
.round-roomy .pname { font-size: clamp(1.2rem, 2vw, 2.2rem); }
.round-roomy .pips { font-size: clamp(0.9rem, 1.4vw, 1.5rem); }

/* One-line cards (~20px card, ~22px slot: LINE_HEIGHT in app.js). The slot is
   flagged per match, since computeBracketLayout decides this match by match. */
.slot.slot-line { padding: 0.05rem 0; }
/* .match.match-line: must outrank the base .match padding/border further down */
.match.match-line {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.12rem 0.6rem;
  border-radius: 4px;
  border-width: 1px;
  line-height: 1.2;
}
.match-line .line-w {
  font-weight: 600;
  font-size: 0.8rem;
  color: var(--gold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.match-line .line-score { font-size: 0.62rem; font-weight: 600; color: var(--muted); flex: none; }
.match-line .line-l {
  font-size: 0.68rem;
  opacity: 0.55;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.match-line.bye { opacity: 0.35; border-style: dashed; }

/* ---------------------------------------------------------------- match cards */

.match {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: clamp(0.4rem, 1vh, 0.8rem) clamp(0.7rem, 1.1vw, 1.1rem);
  border-radius: var(--radius);
  background: var(--card);
  border: 1.5px solid var(--card-border);
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.match:hover { transform: scale(1.02); }
.match.st-pending { opacity: 0.6; cursor: default; }
.match.st-pending:hover { transform: none; }
.match.st-ready { border-color: rgba(141, 175, 242, 0.45); }
.match.st-in_progress { border-color: rgba(255, 167, 38, 0.55); }
.match.st-done { opacity: 0.85; }
.match.selected {
  border-color: var(--accent);
  animation: pulse-glow 1.6s ease-in-out infinite;
}
/* Byes always render as one-line .match-line cards (see renderLineCard). */
.match.bye { opacity: 0.35; border-style: dashed; cursor: default; }
.match.bye:hover { transform: none; }

.mrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.7rem;
  padding: 0.08em 0;
}
.pname {
  font-size: clamp(1rem, 1.6vw, 1.7rem);
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mrow.winner .pname { color: var(--gold); text-shadow: 0 0 18px rgba(251, 191, 36, 0.4); }
.mrow.loser { opacity: 0.5; }
.mrow.tbd .pname { color: var(--muted); font-weight: 400; font-style: italic; }

.pips { font-size: clamp(0.75rem, 1.1vw, 1.1rem); letter-spacing: 0.15em; color: var(--accent); white-space: nowrap; }

/* "This match is on" — a bracket card's own corner badge, and nothing else. It used to be called
   .live-dot, which collided with the spectator page's status chip: same specificity, so the chip
   inherited this absolute corner and this colour. Three separate things, three names. */
.match .live-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--warn);
  box-shadow: 0 0 12px rgba(255, 167, 38, 0.9);
  animation: live-pulse 1.2s ease-in-out infinite;
}

/* A win by default. On a full card it floats in the corner so the card keeps the height
   computeBracketLayout() gave it; on a one-line card it is the last, unshrinkable item. */
.badge-ff {
  flex: 0 0 auto;
  font-size: 0.6rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--warn);
  white-space: nowrap;
}
.match .badge-ff { position: absolute; top: 4px; right: 8px; }
.match.match-line .badge-ff { position: static; }

.reset-btn {
  position: absolute;
  bottom: 4px;
  right: 6px;
  font-size: 0.95rem;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.match:hover .reset-btn, .match.selected .reset-btn { opacity: 1; }
.reset-btn:hover { color: #ef9a9a; }

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

.overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  /* `#hints` is fixed to the bottom at z-index 60, so it paints *over* the panel: a panel that
     merely fits the viewport still had its Next button behind the key strip at 700px. Centring in
     the space above it is the fix; in fullscreen the strip is hidden and the reserve comes back. */
  padding-bottom: 2.4rem;
  background: rgba(1, 17, 36, 0.8);
  backdrop-filter: blur(10px);
  animation: fade-in 0.25s ease;

  /* One vertical budget, one scale. Every big number in here used to size itself off `vw` alone
     while the panel's own boxes were sized off `vh`, so the two fought: the trade phase overflowed
     by 96px at 1366x768 and by 157px at 1920x1080 with a three-line question, hiding the
     "it's higher / it's lower" lines under the fold. `--ovw` is that width scale capped by the
     height available — it *equals* 1vw at 16:9 and above, so a 1080p projector is untouched, and
     a shorter viewport shrinks the type instead of pushing the trade cards off the bottom.
     Use `calc(N * var(--ovw))` as the middle term of a clamp wherever `Nvw` used to be. */
  --ovw: min(1vw, 1.78vh);

  /* The question's type scale, named so that exactly one rule multiplies it by the fit factor
     `--qfit` that `fitQuestion()` measures (see `.question`). `13.5vh - 48px` is the share of the
     viewport height left once the header, the quote, the trade cards and the always-present
     `.aud-live` line have taken theirs: it lands on the 6.4rem cap at 1080p, so a projector is
     unchanged, and drops to ~64px at 768. */
  --qsize: clamp(1.5rem, min(calc(5.5 * var(--ovw)), calc(13.5vh - 48px)), 6.4rem);
}

/* The reveal keeps the question on screen as a caption above the answer, so it is sized as one. */
.overlay.phase-reveal { --qsize: clamp(1rem, calc(2.4 * var(--ovw)), 2.6rem); }

body.is-fullscreen .overlay { padding-bottom: 0; }

.ov-panel {
  width: min(1500px, 96vw);
  /* All of the room the overlay has, which is the viewport less the hints strip. The panel's own
     padding is the inset; the old 92vh cap on top of that only ever clipped. */
  max-height: 100%;
  overflow: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(0.5rem, 1.55vh, 1.5rem);
  padding: clamp(0.6rem, 1.8vh, 2rem) 1rem;
}
.ov-panel.enter { animation: pop-in 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.15); }

/* The overlay's primary action is the one button in the app that must never be the thing that
   pushes a phase past the fold, so its padding yields with the viewport. Capped at the shared
   0.75rem, so nothing changes above ~1090px of viewport height. */
.ov-panel .btn-big { padding: clamp(0.45rem, 1.1vh, 0.75rem) 2.1rem; }

.waiting { color: var(--muted); font-size: 1.4rem; font-weight: 600; }

/* ----- toss */

.vs { font-size: clamp(2rem, 5.5vw, 5rem); font-weight: 600; }
.vs-word { color: var(--gold); font-style: italic; font-size: 0.55em; margin: 0 0.3em; }

.coin-stage { perspective: 900px; padding: clamp(0.5rem, 3vh, 2rem) 0; }

.coin {
  width: clamp(130px, 22vh, 230px);
  aspect-ratio: 1;
  position: relative;
  transform-style: preserve-3d;
  transition: transform 0.2s ease;
}
/* Only a coin someone is known to be able to flip invites a click — a watcher's coin still spins and
   lands, but is not a button. `.flippable` is the *advertisement*, which renderToss withholds until
   write access is confirmed; whether the click is wired is a separate question it answers there. */
.coin.flippable { cursor: pointer; }
.coin.flippable:hover { transform: scale(1.05); }
.coin.spinning { animation: coin-loop 0.4s linear infinite; }
.coin.land-gold { animation: coin-land-gold 1.05s cubic-bezier(0.3, 0.7, 0.25, 1) forwards; }
.coin.land-silver { animation: coin-land-silver 1.05s cubic-bezier(0.3, 0.7, 0.25, 1) forwards; }

.coin .face {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  padding: 12%;
  text-align: center;
  font-size: clamp(0.95rem, 2.8vh, 1.5rem);
  font-weight: 600;
  line-height: 1.1;
  /* Never split inside a word — wrap at spaces only; JS shrinks the font for
     long single words instead (coinNameStyle in app.js). */
  word-break: normal;
  overflow-wrap: normal;
  hyphens: none;
  backface-visibility: hidden;
  border: 6px solid rgba(255, 255, 255, 0.3);
}
.coin .front {
  background: radial-gradient(circle at 35% 30%, #ffe9a3, #f6c945 45%, #b8860b);
  color: rgba(110, 70, 0, 0.8);
  box-shadow: 0 0 46px rgba(251, 191, 36, 0.5), inset 0 -8px 18px rgba(0, 0, 0, 0.25);
}
.coin .back {
  transform: rotateY(180deg);
  background: radial-gradient(circle at 35% 30%, #e8e6ff, #b0aaf0 45%, #5d4bcf);
  color: rgba(40, 25, 120, 0.8);
  box-shadow: 0 0 46px rgba(167, 139, 250, 0.5), inset 0 -8px 18px rgba(0, 0, 0, 0.25);
}

@keyframes coin-loop {
  from { transform: rotateY(0); }
  to { transform: rotateY(360deg); }
}
/* Both landings do a full theatrical flip; only the final resting face differs
   (1800deg = gold/front face, 1980deg = silver/back face). */
@keyframes coin-land-gold {
  0% { transform: rotateY(0) translateY(0); }
  45% { transform: rotateY(900deg) translateY(-9vh); }
  100% { transform: rotateY(1800deg) translateY(0); }
}
@keyframes coin-land-silver {
  0% { transform: rotateY(0) translateY(0); }
  45% { transform: rotateY(990deg) translateY(-9vh); }
  100% { transform: rotateY(1980deg) translateY(0); }
}

.coin-legend {
  display: flex;
  gap: 1.4rem;
  justify-content: center;
  margin-top: 1rem;
  color: var(--muted);
  font-weight: 600;
  font-size: clamp(0.85rem, 1.4vw, 1.15rem);
}
.legend-item { display: inline-flex; align-items: center; gap: 0.45em; word-break: normal; overflow-wrap: normal; hyphens: none; }
.legend-dot { width: 0.9em; height: 0.9em; border-radius: 50%; flex: none; }
.legend-dot.gold { background: radial-gradient(circle at 35% 30%, #ffe9a3, #f6c945 55%, #b8860b); }
.legend-dot.silver { background: radial-gradient(circle at 35% 30%, #e8e6ff, #b0aaf0 55%, #5d4bcf); }

.toss-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  min-height: 7.5rem;
}
.toss-confirm { visibility: hidden; }
.toss-confirm.show { visibility: visible; }

/* Same quiet register for "the host is doing something" and "you are waiting for the host". */
.toss-hint, .ov-waiting {
  color: var(--muted);
  font-size: clamp(0.95rem, 1.6vw, 1.4rem);
  font-weight: 600;
}
.toss-manual {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  justify-content: center;
  color: var(--muted);
  font-size: clamp(0.85rem, 1.3vw, 1.1rem);
}
.toss-result {
  font-size: clamp(1.3rem, 3vw, 2.6rem);
  font-weight: 400;
  animation: rise 0.45s ease;
}
.toss-result strong { color: var(--gold); font-weight: 600; }

/* Forfeiting is rare and irreversible: present, but never competing with the coin. */
.toss-forfeit { display: flex; gap: 1.4rem; flex-wrap: wrap; justify-content: center; }
.forfeit-link {
  font-size: 0.78rem;
  color: var(--muted);
  opacity: 0.7;
  border-bottom: 1px solid transparent;
}
.forfeit-link:hover { color: #ef9a9a; opacity: 1; border-bottom-color: currentColor; }

/* ----- market header */

.mkt-top {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 3vw, 2.6rem);
  flex-wrap: wrap;
  justify-content: center;
}
.mkt-count {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--accent);
  font-size: clamp(0.85rem, 1.5vw, 1.3rem);
}

.mdots { display: inline-flex; gap: 0.5rem; align-items: center; }
.dot {
  width: clamp(10px, 1.2vw, 16px);
  height: clamp(10px, 1.2vw, 16px);
  border-radius: 50%;
  background: rgba(221, 234, 255, 0.14);
}
.dot.cur { background: rgba(221, 234, 255, 0.3); box-shadow: 0 0 10px rgba(221, 234, 255, 0.4); animation: live-pulse 1.4s ease-in-out infinite; }
.dot.won-mm { background: var(--accent); box-shadow: 0 0 10px rgba(141, 175, 242, 0.7); }
.dot.won-tr { background: var(--gray-blue); box-shadow: 0 0 10px rgba(221, 234, 255, 0.7); }

.ov-score { display: inline-flex; gap: 1.4rem; font-size: clamp(0.9rem, 1.6vw, 1.4rem); }
.ovs .pips { margin-left: 0.35em; }

.roles { display: flex; gap: 1rem; flex-wrap: wrap; justify-content: center; }
.role {
  padding: 0.35rem 1.1rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: clamp(0.9rem, 1.7vw, 1.5rem);
}
.role-mm { background: rgba(17, 64, 156, 0.35); color: var(--accent); border: 1px solid rgba(141, 175, 242, 0.5); }
.role-tr { background: rgba(221, 234, 255, 0.08); color: var(--gray-blue); border: 1px solid rgba(221, 234, 255, 0.35); }

/* The question is the one block in the panel whose height is not bounded — it wraps to as many
   lines as the text needs — so it is the one that has to yield. `--qsize` (on `.overlay`) is the
   viewport's share of that budget; `--qfit` is how much of it this particular string may keep.
   Fit wins over size: no clamp can know how many lines a given sentence wraps to, so `fitQuestion()`
   in app.js measures the panel and shrinks the type until nothing overflows. It only ever shrinks,
   which is what keeps `--qsize` the ceiling a short question on a projector still renders at. */
.question {
  font-size: calc(var(--qfit, 1) * var(--qsize));
  font-weight: 600;
  line-height: 1.08;
  max-width: 96%;
  text-wrap: balance;
}
.phase-reveal .question { opacity: 0.75; }

/* ----- quote entry */

.quote-entry { display: flex; flex-direction: column; align-items: center; gap: 1.4rem; }
#quote-input {
  width: min(70vw, 14ch);
  text-align: center;
  font-size: clamp(2.2rem, 6vw, 5.5rem);
  font-family: var(--font-display);
  letter-spacing: 0.04em;
  font-weight: 600;
  background: transparent;
  border: none;
  border-bottom: 4px solid rgba(141, 175, 242, 0.45);
  outline: none;
  color: var(--gold);
  caret-color: var(--accent);
  transition: border-color 0.15s ease;
}
#quote-input:focus { border-bottom-color: var(--accent); }
#quote-input::placeholder { color: rgba(157, 185, 228, 0.4); font-weight: 400; }
#quote-input.shake { animation: shake 0.35s ease; }

/* ----- trade (house convention: BUY = green, SELL = red) */

.quote-shown { display: flex; flex-direction: column; align-items: center; gap: 0.2rem; }
.q-label { color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.14em; font-size: clamp(0.8rem, 1.4vw, 1.2rem); }
.q-value {
  font-size: clamp(2.2rem, calc(7 * var(--ovw)), 7rem);
  /* The display stack's default leading put 54px of empty line box around a 112px number. */
  line-height: 1.05;
  font-weight: 600;
  color: var(--gold);
  text-shadow: 0 0 40px rgba(251, 191, 36, 0.4);
}

.trade-cards {
  display: flex;
  gap: clamp(1rem, 3vw, 3rem);
  width: 100%;
  justify-content: center;
  flex-wrap: wrap;
}
/* 30vh of card for ~135px of text was 190px of slack per card at 1080p, and it was the single
   biggest claim on the phase's vertical budget. Padding, not an oversized floor, is what keeps the
   card from hugging its label — so the card stays generous where there is room and yields where
   there is not. */
.trade-card {
  flex: 0 1 480px;
  min-height: clamp(104px, 19vh, 260px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: clamp(0.6rem, 1.6vh, 1.6rem) 1rem;
  border-radius: 12px;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.trade-card:hover { transform: scale(1.04); }
.trade-card .side { font-size: clamp(2rem, calc(6 * var(--ovw)), 5.2rem); line-height: 1.1; font-weight: 600; font-family: var(--font-display); letter-spacing: 0.06em; }
.trade-card .dir { font-size: clamp(0.95rem, calc(2 * var(--ovw)), 1.7rem); font-weight: 600; opacity: 0.85; }
.trade-card.buy {
  background: linear-gradient(180deg, rgba(102, 187, 106, 0.12), rgba(46, 125, 50, 0.32));
  border: 2px solid rgba(102, 187, 106, 0.6);
  color: #a5d6a7;
}
.trade-card.sell {
  background: linear-gradient(180deg, rgba(244, 67, 54, 0.12), rgba(211, 47, 47, 0.32));
  border: 2px solid rgba(244, 67, 54, 0.6);
  color: #ef9a9a;
}
.trade-card.picked {
  transform: scale(1.06);
  box-shadow: 0 0 0 4px currentColor, 0 0 50px rgba(221, 234, 255, 0.18);
}

/* ----- reveal */

.reveal { display: flex; flex-direction: column; align-items: center; gap: clamp(0.45rem, 1.35vh, 1.3rem); width: 100%; }

.reveal-facts { display: flex; gap: 1.2rem; flex-wrap: wrap; justify-content: center; }
.fact {
  padding: 0.3rem 1.05rem;
  border-radius: 6px;
  background: rgba(221, 234, 255, 0.07);
  border: 1px solid rgba(221, 234, 255, 0.18);
  font-weight: 600;
  font-size: clamp(0.9rem, min(calc(1.7 * var(--ovw)), 2.8vh), 1.5rem);
}
.fact-buy { color: #81c784; border-color: rgba(102, 187, 106, 0.55); }
.fact-sell { color: #e57373; border-color: rgba(244, 67, 54, 0.55); }

/* Exact quote: the quote is the story, the trade cannot change the outcome. */
.fact-exact, .exact-note {
  color: var(--gold);
  border: 1px solid var(--gold);
  background: rgba(251, 191, 36, 0.12);
}
.exact-note {
  padding: 0.4rem 1.3rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: clamp(0.95rem, 1.8vw, 1.6rem);
  text-align: center;
}
.exact .fact-buy, .exact .fact-sell { opacity: 0.45; }

.answer { display: flex; flex-direction: column; align-items: center; gap: 0.1rem; }
.a-label { color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.16em; font-size: clamp(0.8rem, 1.4vw, 1.2rem); }
.a-value {
  /* 13.5vh clears the 9rem cap at 1080, so the projector's answer is untouched; below that it is
     what stops the star of the reveal from crowding out everything under it. */
  font-size: clamp(2.6rem, min(calc(8.5 * var(--ovw)), 13.5vh), 9rem);
  font-weight: 600;
  line-height: 1;
  color: var(--accent);
  text-shadow: 0 0 44px rgba(141, 175, 242, 0.45);
  font-variant-numeric: tabular-nums;
}
/* Gold while counting up, and kept gold when the quote turned out to be exact. */
.counting .a-value, .exact .a-value { color: var(--gold); text-shadow: 0 0 44px rgba(251, 191, 36, 0.5); }

.reveal-after { display: flex; flex-direction: column; align-items: center; gap: clamp(0.5rem, 1.3vh, 1.1rem); }
.counting .reveal-after { visibility: hidden; }
.settled .reveal-after { animation: rise 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2); }

.winner-banner {
  font-size: clamp(1.6rem, min(calc(4 * var(--ovw)), 6.4vh), 3.8rem);
  font-weight: 600;
  /* A display line at the UA's default leading cost 30px of the reveal's budget for nothing. */
  line-height: 1.1;
  color: var(--gold);
  text-shadow: 0 0 30px rgba(251, 191, 36, 0.4);
}
.post-context {
  font-style: italic;
  color: var(--muted);
  font-size: clamp(0.85rem, min(calc(1.6 * var(--ovw)), 2.3vh), 1.4rem);
  line-height: 1.3;
  max-width: 70ch;
}

/* ----- up next ribbon */

.up-next {
  display: inline-flex;
  align-items: baseline;
  gap: 0.6em;
  padding: 0.45rem 1.2rem;
  border-radius: 6px;
  background: rgba(221, 234, 255, 0.06);
  border: 1px solid rgba(221, 234, 255, 0.2);
  white-space: nowrap;
}
.up-next-label {
  font-size: clamp(0.6rem, 0.9vw, 0.85rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
}
.up-next-names { font-weight: 600; font-size: clamp(0.95rem, 1.7vw, 1.6rem); }
.up-next-vs { color: var(--accent); font-style: italic; font-size: 0.7em; }

.up-next-slot {
  position: absolute;
  top: 1rem;
  right: 1.2rem;
  opacity: 0.85;
  animation: fade-in 0.3s ease;
}

.up-next-big {
  padding: 0.7rem 2rem;
  background: rgba(6, 81, 226, 0.14);
  border: 1px solid rgba(141, 175, 242, 0.5);
  box-shadow: 0 0 26px rgba(6, 81, 226, 0.25);
}
.up-next-big .up-next-label { font-size: clamp(0.8rem, 1.3vw, 1.2rem); color: var(--accent); }
.up-next-big .up-next-names { font-size: clamp(1.4rem, 3.2vw, 2.9rem); }
.up-next-ready {
  color: var(--accent);
  font-weight: 600;
  font-size: clamp(0.9rem, 1.8vw, 1.7rem);
}

/* ----- splash */

.splash { display: flex; flex-direction: column; align-items: center; gap: 1.2rem; cursor: pointer; }
.splash-emoji { font-size: clamp(4rem, 14vh, 9rem); animation: bounce-in 0.7s cubic-bezier(0.2, 0.9, 0.3, 1.4); }
.splash-title {
  font-size: clamp(2rem, 6vw, 6rem);
  font-weight: 600;
  line-height: 1.05;
  text-wrap: balance;
  background: linear-gradient(100deg, var(--gold), var(--warn));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 26px rgba(251, 191, 36, 0.35));
}
.splash-sub { color: var(--muted); font-weight: 600; font-size: clamp(1rem, 2vw, 1.6rem); }

/* ---------------------------------------------------------------- animations */

@keyframes pulse-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(141, 175, 242, 0.35), 0 0 18px rgba(6, 81, 226, 0.3); }
  50% { box-shadow: 0 0 0 5px rgba(141, 175, 242, 0.12), 0 0 36px rgba(6, 81, 226, 0.5); }
}
@keyframes live-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.35); opacity: 0.7; }
}
@keyframes fade-in { from { opacity: 0; } }
@keyframes pop-in { from { opacity: 0; transform: scale(0.92); } }
@keyframes rise { from { opacity: 0; transform: translateY(26px); } }
@keyframes bounce-in {
  0% { opacity: 0; transform: scale(0.4); }
  70% { transform: scale(1.12); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-12px); }
  50% { transform: translateX(10px); }
  75% { transform: translateX(-6px); }
}

/* ---------------------------------------------------------------- small screens */

@media (max-width: 760px) {
  #hints { gap: 0.8rem; flex-wrap: wrap; }
  .tbar { flex-wrap: wrap; }
  .trade-card { flex-basis: 100%; min-height: 120px; }
  .round { flex-basis: 180px; min-width: 180px; }
}

/* ---------------------------------------------------------------- sign-in & invitations */

.gate {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.6rem;
  padding: 2rem 1rem;
}

.gate-card {
  width: min(400px, 100%);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.6rem;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.gate-card h2 {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: 0.04em;
  margin-bottom: 0.4rem;
}

.gate-card label { font-size: 0.82rem; color: var(--muted); margin-top: 0.5rem; }

.gate-card input,
.new-bank input,
.new-question textarea,
.share-form input,
.share-form select,
.join-card input {
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.6);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.55rem 0.7rem;
}

.gate-card input:focus-visible,
.join-card input:focus-visible,
.new-question textarea:focus-visible {
  outline: 2px solid var(--action);
  outline-offset: 1px;
}

.gate-card button { margin-top: 1rem; }
.gate-note { font-size: 0.78rem; color: var(--muted); line-height: 1.5; margin-top: 0.6rem; }
.gate-note a { color: var(--accent); }

/* ---------------------------------------------------------------- bank manager */

.banks-page { padding: 1.4rem clamp(1rem, 4vw, 3rem); }

.banks-layout {
  display: grid;
  grid-template-columns: minmax(240px, 340px) 1fr;
  gap: 1.4rem;
  align-items: start;
}

.banks-list { display: flex; flex-direction: column; gap: 0.5rem; }

.brow {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.7rem 0.9rem;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  cursor: pointer;
}

.brow:hover, .brow.selected { border-color: var(--action); background: rgba(6, 81, 226, 0.14); }
.brow-main { flex: 1; min-width: 0; }
.brow-name { font-weight: bold; }
.brow-meta { font-size: 0.74rem; color: var(--muted); margin-top: 0.15rem; }

.new-bank, .new-question, .bank-detail {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.new-bank h3, .new-question h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
}

.bank-head { display: flex; align-items: baseline; gap: 0.7rem; }
.bank-head h2 { font-family: var(--font-display); font-weight: 500; letter-spacing: 0.03em; }

.qlist { list-style: none; display: flex; flex-direction: column; gap: 0.35rem; }

.qrow {
  display: grid;
  /* Wording, answer, the edit and the delete button share one line; any context wraps
     underneath. Without explicit rows, auto-placement drops the button onto a line of its own. */
  grid-template-columns: 1fr auto auto auto;
  gap: 0.2rem 0.6rem;
  align-items: baseline;
  padding: 0.4rem 0.6rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: rgba(1, 17, 36, 0.35);
}

.qrow > * { grid-row: 1; }
.qanswer { font-weight: bold; color: var(--gold); font-variant-numeric: tabular-nums; }

.qcontext {
  grid-row: 2;
  grid-column: 1 / -1;
  font-size: 0.76rem;
  color: var(--muted);
}

/* Advice, not a rule: a long question is always played, just at a smaller size. Amber rather than
   red for the same reason, and it sits on its own row under the wording it is about. */
.qlong {
  grid-row: 3;
  grid-column: 1 / -1;
  font-size: 0.76rem;
  color: #ffb74d;
}
/* In the editor and the add form the note is a flex item, so it needs its own full-width line. */
.qedit .qlong, .one-question .qlong { flex: 1 1 100%; }

.shares {
  font-size: 0.82rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.7rem 0.8rem;
  background: rgba(1, 17, 36, 0.35);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.shares-state {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.9rem;
  letter-spacing: 0.03em;
  color: var(--accent);
}
.share-list { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; margin: 0.5rem 0; }
.share-form { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.share-form input { flex: 1; min-width: 160px; }

.check { display: flex; align-items: center; gap: 0.45rem; font-size: 0.84rem; color: var(--muted); }

/* ---------------------------------------------------------------- audience, host side */

.audience-badge {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  background: rgba(1, 17, 36, 0.45);
}

.audience-code {
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.16em;
  color: var(--gold);
}

.audience-count { font-size: 0.74rem; color: var(--muted); }

/* ---------------------------------------------------------------- audience, spectator page */

/* The console wraps #view in #app, the column that lets `#view { flex: 1; overflow: auto }` do the
   scrolling; here body is that column. Without it #view is an auto-height block that never scrolls,
   and a market plus a leaderboard is simply clipped by `body { overflow: hidden }` — on a phone the
   footer below the board would be unreachable. */
body.audience { display: flex; flex-direction: column; align-items: stretch; }

.join-card, .play {
  width: min(520px, 100%);
  margin: 0 auto;
  padding: 1.4rem 1.1rem calc(1.4rem + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}

.join-lead { color: var(--muted); line-height: 1.5; }
.join-card form { display: flex; flex-direction: column; gap: 0.4rem; }
.join-card label { font-size: 0.82rem; color: var(--muted); margin-top: 0.6rem; }
.join-card #code { text-transform: uppercase; letter-spacing: 0.28em; font-size: 1.5rem; text-align: center; }
.join-card button { margin-top: 1.2rem; }

.play-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--card-border);
}

.play-name { font-family: var(--font-display); font-size: 1.2rem; letter-spacing: 0.04em; }
.play-score { font-size: 0.86rem; color: var(--gold); }

.play-market, .play-wait, .play-board {
  padding: 1rem;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.play-question { font-size: 1.15rem; line-height: 1.4; }
.play-quote { margin-top: 0.8rem; font-size: 1.5rem; font-variant-numeric: tabular-nums; }
.play-quote b { color: var(--gray-blue); }

.play-sides { display: flex; gap: 0.8rem; margin-top: 1.2rem; }

/* .side-btn, not .side: the console renders <span class="side"> inside .trade-card, and an unscoped
   .side beat .trade-card.buy's green with `color: var(--ink)` — a white BUY above a green
   "it's higher". Same collision as .live-badge/.live-chip/.live-dot; keep the names apart. */
.side-btn {
  flex: 1;
  font-family: var(--font-display);
  font-size: 1.5rem;
  letter-spacing: 0.08em;
  padding: 1.1rem 0;
  color: var(--ink);
  border: 2px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  /* Big enough to hit with a thumb from the back of the room. */
  min-height: 68px;
}

.side-btn.buy { background: var(--buy-deep); }
.side-btn.sell { background: var(--sell-deep); }
.side-btn.buy.chosen { background: var(--buy); border-color: var(--ink); }
.side-btn.sell.chosen { background: var(--sell); border-color: var(--ink); }
.side-btn:disabled { opacity: 0.55; cursor: default; }

.play-hint, .play-context { font-size: 0.82rem; color: var(--muted); margin-top: 0.8rem; line-height: 1.5; }
.play-answer { font-size: 1.6rem; margin-top: 0.8rem; }
.play-answer b { color: var(--gold); font-variant-numeric: tabular-nums; }
.play-verdict { margin-top: 0.6rem; font-family: var(--font-display); letter-spacing: 0.05em; }
.play-wait { text-align: center; color: var(--muted); }
.play-wait h2 { font-family: var(--font-display); color: var(--gold); font-size: 1.6rem; }

.play-board h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1rem;
  letter-spacing: 0.04em;
  margin-bottom: 0.6rem;
}

.play-board ol { list-style: none; display: flex; flex-direction: column; gap: 0.25rem; }

.play-board li {
  display: grid;
  grid-template-columns: 1.6rem 1fr auto;
  gap: 0.6rem;
  padding: 0.35rem 0.5rem;
  border-radius: var(--radius);
  font-size: 0.92rem;
}

.play-board li.me { background: rgba(6, 81, 226, 0.28); }
.play-board .rank { color: var(--muted); font-variant-numeric: tabular-nums; }
.play-board .pts { color: var(--gold); font-weight: bold; font-variant-numeric: tabular-nums; }

@media (max-width: 760px) {
  .banks-layout { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------- shared top bar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.55rem clamp(0.8rem, 2vw, 1.6rem);
  /* Opaque: content scrolls underneath it. */
  background: #02182f;
  border-bottom: 1px solid var(--card-border);
}

.topbar-left, .topbar-right { display: flex; align-items: center; gap: 0.8rem; }
.topbar-right { position: relative; margin-left: auto; }

.brand {
  font-family: var(--font-display);
  font-size: 1.3rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0;
}

.topnav { display: flex; gap: 0.2rem; }

.navlink {
  font-family: var(--font-ui);
  font-size: 0.86rem;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: var(--radius);
  padding: 0.4rem 0.7rem;
  cursor: pointer;
}

.navlink:hover { color: var(--ink); background: rgba(221, 234, 255, 0.08); }
.navlink.on { color: var(--ink); background: rgba(6, 81, 226, 0.28); }

.account {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.5rem 0.25rem 0.25rem;
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--ink);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 0.86rem;
}

.account:hover { border-color: var(--card-border); background: rgba(221, 234, 255, 0.06); }

.avatar {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--action);
  color: #fff;
  font-size: 0.72rem;
  font-weight: bold;
  letter-spacing: 0.03em;
}

.account-menu {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 40;
  min-width: 230px;
  display: flex;
  flex-direction: column;
  padding: 0.35rem;
  background: #04203f;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
}

.account-menu-head { padding: 0.5rem 0.6rem 0.6rem; border-bottom: 1px solid var(--card-border); }

/* The account actions read as one block; the outbound spectator link stands on its own below it. */
.account-menu-group {
  display: flex;
  flex-direction: column;
  padding-bottom: 0.35rem;
  margin-bottom: 0.35rem;
  border-bottom: 1px solid var(--card-border);
}

.account-menu-name { font-weight: bold; }
.account-menu-email, .account-menu-role { font-size: 0.76rem; color: var(--muted); }
.account-menu-role { color: var(--gold); }

.account-menu-item {
  text-align: left;
  font-family: var(--font-ui);
  font-size: 0.86rem;
  color: var(--ink);
  text-decoration: none;
  background: none;
  border: 0;
  border-radius: 6px;
  padding: 0.5rem 0.6rem;
  cursor: pointer;
}

.account-menu-item:hover { background: rgba(6, 81, 226, 0.3); }
.account-menu-item.danger { color: #ffb4ae; }
.account-menu-item.danger:hover { background: rgba(211, 47, 47, 0.35); }

.page-title {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: 0.04em;
  font-size: 1.35rem;
  padding: 1.1rem clamp(0.8rem, 2vw, 1.6rem) 0.9rem;
}

.page { min-height: 100%; }
.banks-page, .banks-layout, .invites-layout { padding-inline: clamp(0.8rem, 2vw, 1.6rem); }
.banks-layout { padding-bottom: 2rem; }

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

.icon-btn .icon {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  display: block;
}

.icon-btn.danger { color: var(--muted); }
.icon-btn.danger:hover { color: #ff8a80; background: rgba(211, 47, 47, 0.18); }

/* ---------------------------------------------------------------- people & invitations */

.invites-layout {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.2rem;
  align-items: start;
  padding-bottom: 2rem;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.1rem;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.card h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1rem;
  letter-spacing: 0.04em;
}

.card label { font-size: 0.8rem; color: var(--muted); margin-top: 0.4rem; }
.card input:not([type="checkbox"]) {
  font-family: var(--font-ui);
  font-size: 0.95rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.6);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.5rem 0.65rem;
}
.card button[type="submit"] { margin-top: 0.8rem; }

.itable { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
.itable th {
  text-align: left;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  padding: 0 0.7rem 0.4rem 0;
  white-space: nowrap;
}
.itable td { padding: 0.4rem 0.7rem 0.4rem 0; border-top: 1px solid var(--card-border); }
.itable .row-actions { text-align: right; }

.people { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.86rem; }
.people li { display: flex; align-items: center; gap: 0.5rem; }
.people .who { font-weight: bold; }

.issued {
  margin-top: 0.9rem;
  padding: 0.8rem;
  background: rgba(6, 81, 226, 0.16);
  border: 1px solid var(--action);
  border-radius: var(--radius);
  font-size: 0.84rem;
}

.issued-row { display: flex; gap: 0.4rem; margin: 0.5rem 0; }
.issued-row input {
  flex: 1;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.76rem;
}

/* ---------------------------------------------------------------- tournament sharing */

.share-badge { position: relative; }

.share-panel {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 40;
  width: min(340px, 84vw);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.9rem;
  background: #04203f;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
  text-align: left;
}

.share-panel h3 { font-family: var(--font-display); font-weight: 500; font-size: 0.95rem; }

.badge-audience { background: rgba(6, 81, 226, 0.32); color: var(--gray-blue); }
.audience-label { font-size: 0.72rem; color: var(--muted); }
.audience-label b { color: var(--gray-blue); }

/* ---------------------------------------------------------------- audience result, big screen */

/* How much of the room is in, while the market is open. Under the BUY/SELL cards and deliberately
   quiet: the players are looking at this screen too, so it must not compete with their decision or
   the question. Tabular figures keep it from twitching as the count ticks up. */
.aud-live {
  font-size: clamp(0.72rem, 1.1vw, 0.95rem);
  letter-spacing: 0.04em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* The slot is in the DOM for the whole phase; `hidden` is how it says "no audience to count". */
.aud-live[hidden] { display: none; }
.aud-live-bolt { color: var(--gold); }

/* How the room traded, on a projector, at the back of a room — this is one of the best moments in
   the game and it used to be set at web scale (0.74–0.82rem type in a fixed 26px bar) while every
   neighbour in the same panel clamped up to 20–144px. It sizes off `vh` rather than `--ovw`
   deliberately: it is the last block in the reveal's vertical stack, so it should grow into
   whatever height is left over and stand down on a short laptop rather than push the Next button
   under the fold. Green = BUY, red = SELL, as everywhere else. */
.aud-result {
  width: min(760px, 88vw);
  margin: 0.3rem auto 0.1rem;
  display: grid;
  grid-template-areas: "split" "legend" "board";
  gap: clamp(0.3rem, 0.9vh, 0.8rem);
}

.aud-split {
  grid-area: split;
  display: flex;
  height: clamp(24px, 4.4vh, 52px);
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--card-border);
}

.aud-bar {
  display: grid;
  place-items: center;
  font-size: clamp(0.78rem, 2.2vh, 1.6rem);
  font-weight: bold;
  letter-spacing: 0.04em;
  white-space: nowrap;
  transition: width 0.5s ease;
}

.aud-bar.buy { background: var(--buy-deep); }
.aud-bar.sell { background: var(--sell-deep); }
.aud-split-legend {
  grid-area: legend;
  font-size: clamp(0.74rem, 1.8vh, 1.25rem);
  line-height: 1.25;
  color: var(--muted);
  text-align: center;
}

.aud-board {
  grid-area: board;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: clamp(0.1rem, 0.35vh, 0.3rem);
  width: min(26em, 100%);
  margin: 0.15rem auto 0;
}
.aud-board li {
  display: grid;
  grid-template-columns: 1.8em 1fr auto;
  gap: 0.6em;
  font-size: clamp(0.82rem, 2.1vh, 1.45rem);
  line-height: 1.3;
  padding: 0.1rem 0.3rem;
}
.aud-board .rank { color: var(--muted); font-variant-numeric: tabular-nums; }
.aud-board .pts { color: var(--gold); font-weight: bold; font-variant-numeric: tabular-nums; }

/* Wide but short — a 1366x768 laptop, or a maximised browser on a 1080p screen — cannot stack the
   split, its legend and a five-row leaderboard under the answer and still keep the Next button
   above the fold. Side by side costs nothing and hides nothing, and a projector in fullscreen at
   1080 never reaches this breakpoint, so the stacked, centred reveal is what the room sees. */
@media (min-width: 900px) and (max-height: 950px) {
  .aud-result {
    width: min(1100px, 92vw);
    grid-template-areas: "split board" "legend board";
    grid-template-columns: minmax(0, 1fr) auto;
    /* The board spans both rows and is the taller of the two columns; `1fr` on the second row is
       what makes it absorb the slack, so the legend stays under its own bar instead of drifting
       down to the board's centre. */
    grid-template-rows: auto 1fr;
    align-items: start;
    column-gap: clamp(1rem, 3vw, 2.5rem);
  }
  .aud-board { margin: 0; align-self: center; }
}

/* ---------------------------------------------------------------- spectator additions */

.play-who, .play-tally { display: flex; align-items: baseline; gap: 0.45rem; }
.play-tally { flex-direction: column; align-items: flex-end; gap: 0.1rem; }
/* Name, rank and the live chip on a phone: the chip drops to a second line rather than pushing the
   score off the row. */
.play-who { flex-wrap: wrap; }

.play-rank {
  font-size: 0.74rem;
  color: var(--gold);
  border: 1px solid var(--gold);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
}

.play-points {
  font-family: var(--font-display);
  font-size: 1.3rem;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.bonus { margin-top: 1rem; }

.bonus-track {
  height: 6px;
  border-radius: 999px;
  background: rgba(221, 234, 255, 0.14);
  overflow: hidden;
}

.bonus-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--gold), var(--warn));
  /* Drains from whatever width the render set to nothing over the time actually left, so the bar is
     smooth between the 250ms re-renders and lands on empty even if they stop. The duration is set
     inline by renderBonusBar; without it nothing moves, which is the safe failure. */
  animation: bonus-drain linear forwards;
}

@keyframes bonus-drain { to { width: 0; } }

.bonus-label { display: block; margin-top: 0.3rem; font-size: 0.74rem; color: var(--muted); }
.bonus-label b { color: var(--gold); font-variant-numeric: tabular-nums; }

.play-verdict.right { color: var(--buy); }
.play-verdict.wrong { color: var(--sell); }

.play-earned {
  margin-left: 0.4rem;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.play-split { margin-top: 0.9rem; }

.split-bar {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 0.3rem;
}

.split-buy { background: var(--buy); }
.split-sell { background: var(--sell); }

.join-lead a { color: var(--accent); }
/* The quiet footer both spectator views end on: the way out of this screen, nowhere near a
   BUY/SELL button someone is tapping in a hurry. */
.join-foot, .play-foot {
  margin-top: 1.4rem;
  font-size: 0.78rem;
  color: var(--muted);
  text-align: center;
}
.join-foot a, .play-foot button { color: var(--accent); text-decoration: underline; }

/* ---------------------------------------------------------------- tournament sub-bar */

/* The bracket's own controls sit below the shared bar, so the chrome never changes shape. */
.tbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.5rem clamp(0.8rem, 2vw, 1.6rem);
  border-bottom: 1px solid var(--card-border);
  background: rgba(1, 17, 36, 0.3);
}

.tbar .tname { font-size: 1.15rem; white-space: nowrap; }
.tbar .tmeta { flex: 1 1 20rem; row-gap: 0.1rem; }

.tbar-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  /* Never squeezed: the join code and the controls keep their size and the metrics give way. */
  flex: 0 0 auto;
}

/* ---------------------------------------------------------------- dialogs */

.dlg {
  width: min(440px, 92vw);
  /* The `* { margin: 0 }` reset kills the UA's `margin: auto`, which is what centres a modal
     dialog inside its inset-0 box; put it back rather than positioning dialogs by hand. */
  margin: auto;
  padding: 0;
  color: var(--ink);
  background: #04203f;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}

.dlg::backdrop { background: rgba(1, 12, 26, 0.72); }
.dlg form { display: flex; flex-direction: column; gap: 0.6rem; padding: 1.3rem; }

.dlg h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.15rem;
  letter-spacing: 0.03em;
}

.dlg-body { font-size: 0.9rem; line-height: 1.5; color: var(--gray-blue); }
.dlg-fields { display: flex; flex-direction: column; gap: 0.3rem; }
.dlg-fields label { font-size: 0.8rem; color: var(--muted); margin-top: 0.5rem; }

.dlg input {
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.7);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.55rem 0.7rem;
}

.dlg input:focus-visible { outline: 2px solid var(--action); outline-offset: 1px; }
.dlg-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 0.8rem; }
.dlg-copy { display: flex; gap: 0.5rem; }
.dlg-copy input {
  flex: 1;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
}

.btn-danger { background: var(--sell-deep); border-color: var(--sell); color: #fff; }
.btn-danger:hover { background: var(--sell); }

/* ---------------------------------------------------------------- people page */

.stack {
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
  padding: 0 clamp(0.8rem, 2vw, 1.6rem) 2rem;
  max-width: 860px;
}

.card-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

.ilist, .plist { list-style: none; display: flex; flex-direction: column; gap: 0.35rem; }

.irow, .prow {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  background: rgba(1, 17, 36, 0.4);
  border-radius: var(--radius);
}

.irow-main, .prow-main { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 0.5rem; }
.irow-for, .prow-name { font-weight: bold; }
.prow-seen { font-size: 0.76rem; white-space: nowrap; }
.avatar.small { width: 24px; height: 24px; font-size: 0.62rem; }

.brow-access { font-size: 0.74rem; color: var(--accent); margin-top: 0.15rem; }

/* ---------------------------------------------------------------- page heads */

.page-head {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 1.1rem clamp(0.8rem, 2vw, 1.6rem) 0.9rem;
}

.page-head .page-title { padding: 0; }
.page-actions { display: flex; align-items: center; gap: 0.6rem; margin-left: auto; }

/* The tournament list is a card in the standard stack, like every other page's content. */
.tlist { gap: 0.5rem; }

/* ---------------------------------------------------------------- question editing */

.qrow.editing { background: rgba(6, 81, 226, 0.16); }

.qedit, .account-edit {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  width: 100%;
}

.qedit input, .account-edit input:not([type="checkbox"]), .irow-new input:not([type="checkbox"]) {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.7);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.4rem 0.55rem;
}

.qe-question { flex: 1 1 18rem; }
.qe-answer { flex: 0 0 7rem; }
.qe-context { flex: 1 1 100%; }
.qedit-actions, .prow-actions { display: flex; gap: 0.4rem; margin-left: auto; }
.btn-inline.primary { color: var(--ink); background: var(--action); padding: 0.3rem 0.7rem; }

.add-questions { display: flex; flex-direction: column; gap: 0.8rem; }

.one-question {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 1rem;
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.one-question label { font-size: 0.8rem; color: var(--muted); margin-top: 0.4rem; }
.one-question input {
  font-family: var(--font-ui);
  font-size: 0.95rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.6);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.5rem 0.65rem;
  width: 100%;
}
.one-question button { margin-top: 0.8rem; align-self: flex-start; }
.oq-row { display: flex; gap: 0.7rem; align-items: flex-end; }
.oq-row > div { display: flex; flex-direction: column; gap: 0.3rem; }
.oq-grow { flex: 1; }

.bulk summary { cursor: pointer; color: var(--accent); font-size: 0.86rem; padding: 0.3rem 0; }
.bulk .new-question, .bulk .import-json { margin-top: 0.6rem; }
.import-json label { display: block; font-size: 0.8rem; color: var(--muted); margin-bottom: 0.3rem; }
input[type="file"] { font-size: 0.82rem; color: var(--muted); }

/* ---------------------------------------------------------------- invitations, inline */

.irow { flex-direction: column; align-items: stretch; gap: 0.45rem; }
.irow-head { display: flex; align-items: center; gap: 0.6rem; }
.irow-head .irow-for { flex: 1; min-width: 0; }
.irow-link { display: flex; gap: 0.4rem; }

.invite-link {
  flex: 1;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  color: var(--gray-blue);
  background: rgba(1, 17, 36, 0.7);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.35rem 0.5rem;
}

.irow-new { background: none; border: 1px dashed var(--card-border); }

.irow-new form { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

/* The row is a wrapping flex row, so this basis is a width — never let it become a column. */
.irow-new form .email-affix { flex: 1 1 14rem; font-size: 0.9rem; }
.prow.editing { background: rgba(6, 81, 226, 0.16); }
.ae-name { flex: 1 1 12rem; }

/* ---------------------------------------------------------------- sub-bar, icon-sized */

.tbar-actions .icon-btn { padding: 0.35rem; }

.audience-badge {
  gap: 0.4rem;
  padding: 0.2rem 0.5rem;
}

.audience-count {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-variant-numeric: tabular-nums;
  /* `500/500 · 500⚡` is the widest this ever gets; wrapping it would grow the whole bar a line. */
  white-space: nowrap;
}

.audience-count .icon { width: 13px; height: 13px; }
.share-panel .shares { border: 0; background: none; padding: 0; }

/* ---------------------------------------------------------------- fullscreen */

/* On a projector the console chrome is dead weight; the sub-bar keeps the join code visible. */
body.is-fullscreen .topbar { display: none; }
body.is-fullscreen #hints { display: none; }

/* ---------------------------------------------------------------- spectator page */

.btn-join {
  /* Thumb-sized: people are holding a phone at the back of a room. */
  min-height: 64px;
  font-size: 1.15rem;
  letter-spacing: 0.04em;
}

.join-note { font-size: 0.78rem; color: var(--muted); line-height: 1.45; margin-top: 0.5rem; }
/* Sized on the wrapper, so the local part and the @imc.com suffix scale together. */
.join-card .email-affix { font-size: 1.05rem; }

/* The console's logo is sized for a projector; here it has to leave the code field above the fold
   on a phone, so the same gradient wordmark just runs smaller. */
body.audience .logo { font-size: clamp(2.4rem, 14vw, 4rem); }

/* Losing the connection is not a footnote for someone holding a phone at the back of the room. */
.conn-lost {
  padding: 0.55rem 0.8rem;
  border-radius: var(--radius);
  background: rgba(255, 167, 38, 0.14);
  border: 1px solid rgba(255, 167, 38, 0.45);
  color: var(--warn);
  font-weight: bold;
  font-size: 0.9rem;
  text-align: center;
}

/* The spectator's status chip: a dot and a word, inline beside their name in .play-who. Quiet by
   design — an actual drop raises the .conn-lost banner, so this only has to be legible. */
.live-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  white-space: nowrap;
  color: var(--muted);
}

.live-chip i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--muted);
  flex: 0 0 auto;
}

.live-chip.on { color: var(--buy); }
.live-chip.on i { background: var(--buy); animation: live-pulse 2s ease-in-out infinite; }
.live-chip.off { color: var(--warn); }
.live-chip.off i { background: var(--warn); }

/* .play-who aligns its items on the baseline, and an inline-flex box takes its baseline from the
   dot; centring the chip against the name is what puts the label on the same line. */
.play-who .live-chip { align-self: center; }

/* ---------------------------------------------------------------- spectator list, host side */

/* A column so the rows scroll and the heading with the two orderings stays put: at 500 spectators
   a popover that scrolls as a whole loses its own controls off the top. */
.spectator-list {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  width: min(320px, 84vw);
  max-height: 60vh;
  overflow: hidden;
  padding: 0.8rem;
  background: #04203f;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
  text-align: left;
}

.spectator-list h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
}

/* The count on the left, the two orderings on the right. */
.spectator-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}

/* The segmented control at popover scale. */
.seg-small .seg-btn { padding: 0.15rem 0.5rem; font-size: 0.68rem; }

/* The scrolling part. `min-height: 0` is what lets it shrink inside the capped popover. */
.spectator-list ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-height: 0;
  overflow: auto;
}

/* `flex: none`: rows keep their own height and overflow the popover instead of being squashed. */
.spectator-list li {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
}
/* Faded, but still a row somebody has to read to choose between kick and remove. 0.55 plus a grey
   dot left an absent spectator barely there at all, which is the opposite failure from the one
   before it — the fade is a supporting signal, the word in `.seen` is the one that says it. */
.spectator-list li.off { opacity: 0.72; }
.spectator-list .who { font-weight: bold; white-space: nowrap; }

/* The email gives way first — the dot, the name and the score all have to stay readable. */
.spectator-list .dim {
  margin-left: auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 0.74rem;
}

.spectator-list .pts { color: var(--gold); font-weight: bold; font-variant-numeric: tabular-nums; }

/* The roster bullet: connected or not, one per row. Self-contained — it used to borrow its round
   corners from the bracket badge back when both were called .live-dot, and the three names stay
   apart (see `.live-badge` / `.live-chip`).

   Filled-and-haloed against hollow-and-grey, not one colour against another: the row now carries
   four action icons, and 7px of hue on its own was not enough to carry "here" versus "gone" past
   them. Off was `--warn` amber, which is the attention colour the bracket uses for a *live* match —
   on this row it read as "something is happening here" rather than "nobody is there". */
.spectator-list .live-dot {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--muted);
}

.spectator-list .live-dot.on {
  background: var(--buy);
  box-shadow: 0 0 0 3px rgba(102, 187, 106, 0.22);
}
.spectator-list .live-dot.off {
  background: transparent;
  border: 1.5px solid var(--muted);
}

/* The audience badge is a popover anchor now. */
.audience-badge { position: relative; }
.audience-count { border: 0; color: var(--muted); font-size: 0.74rem; }
.audience-count:hover { color: var(--ink); }

/* The code is what the room squints at from the back — and it is useless without the address, so
   that is on screen too rather than hidden in a tooltip nobody on a projector can hover. */
.audience-code { font-size: 1.7rem; letter-spacing: 0.2em; }
body.is-fullscreen .audience-code { font-size: 2.1rem; }
.audience-where { font-size: 0.9rem; color: var(--gray-blue); letter-spacing: 0.02em; }
body.is-fullscreen .audience-where { font-size: 1.1rem; }

/* ---------------------------------------------------------------- misc console polish */

.bulk h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
  margin-bottom: 0.4rem;
}

.bank-rename { display: flex; gap: 0.4rem; align-items: center; flex: 1; }
.bank-rename .br-name {
  flex: 1;
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.7);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.4rem 0.55rem;
}

.account-edit { align-items: center; }
.account-edit .ae-name { flex: 0 1 12rem; }
.ae-email { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

.share-form .share-email {
  flex: 1;
  min-width: 0;
  font-family: var(--font-ui);
  font-size: 0.86rem;
  color: var(--ink);
  background: rgba(1, 17, 36, 0.7);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 0.35rem 0.5rem;
}

/* ---------------------------------------------------------------- email fields

   Every address in the app is typed as a local part with the domain fixed beside it (emailField in
   util.js). The frame moves from the input to the wrapper so the pair reads as one control, while
   the input keeps the padding its own page gives it — that is what keeps the field the same height
   as the ones above and below it at all three scales in play. Styled by name: there is deliberately
   no `.card form` rule, and a flex-direction added there would turn every nested basis into a
   height. This block comes last so it outranks the per-page input rules it has to undo. */

.email-affix {
  display: flex;
  align-items: center;
  min-width: 0;
  background: rgba(1, 17, 36, 0.6);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
}

.email-affix:focus-within { outline: 2px solid var(--action); outline-offset: 1px; }

.email-affix input.email-local {
  flex: 1 1 auto;
  min-width: 0;
  padding-right: 0.15em;
  font: inherit;
  background: none;
  border: 0;
  border-radius: 0;
}

/* The wrapper carries the focus ring for the whole control. */
.email-affix input.email-local:focus-visible { outline: none; }

/* Not editable, and never the part that shrinks or wraps. */
.email-domain {
  flex: 0 0 auto;
  padding-right: 0.7em;
  color: var(--muted);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- question pool + choice dialog
   Appended as one block for the question-management and undo work; nothing above was touched. */

/* Same popover geometry as .share-panel and .spectator-list — a fourth badge in the same row must
   not open in a fourth shape. The scroll cap is on the duplicate list, since that is the part that
   grows with the bracket. */
.questions-badge { position: relative; }

.questions-panel {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: min(360px, 86vw);
  max-height: 70vh;
  overflow: auto;
  padding: 0.9rem;
  background: #04203f;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
  text-align: left;
}

.questions-panel h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
}

/* The repeats, worst case a long list, so it scrolls inside the popover rather than pushing the
   two buttons off the bottom of it. */
.qdupes {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-height: 30vh;
  overflow: auto;
  font-size: 0.78rem;
}

.qdupes li { display: flex; gap: 0.5rem; justify-content: space-between; }
.qdupes .who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qdupes .pts { flex: 0 0 auto; color: var(--warn); font-variant-numeric: tabular-nums; }

/* The badge says something is wrong before the panel is opened. */
.icon-btn.warn { color: var(--warn); }

/* A dialog offering three answers rather than yes/no: stacked, each with its consequence under it,
   because the host is choosing between outcomes and a row of verbs reads as a toolbar. */
.dlg-choices { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.4rem; }

.dlg-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
  text-align: left;
  padding: 0.6rem 0.8rem;
}

.dlg-choice .choice-label { font-weight: 600; }
.dlg-choice .choice-hint { font-size: 0.76rem; font-weight: 400; opacity: 0.8; line-height: 1.35; }

/* ---------------------------------------------------------------- host controls on the roster

   The spectator list gained four verbs per row (rename / kick / block / remove) plus the last-seen
   the payload has always carried and nothing ever drew. That is what the widening is for: at the
   original 320px the email and the score were already fighting for the same slack.

   `.dim` (the address) keeps `margin-left: auto`, so it is still the column that gives way first —
   the name, the age of the last ping, the score and the buttons all have to stay readable while
   somebody is deciding whether a row is a dropped phone or a squatter. */

.spectator-list { width: min(420px, 92vw); }

/* Whether the room can still hear them, and since when. Fixed-ish width so the column of scores
   after it does not jitter as rows tick over from "left just now" to "left 2m ago", and wide enough
   for the longest of those — it is the one part of the row that has to be readable at a glance. */
.spectator-list .seen {
  flex: 0 0 auto;
  min-width: 4.9rem;
  text-align: right;
  white-space: nowrap;
  color: var(--muted);
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
}

/* The words carry the row now, so they are the one thing the fade below must not swallow. */
.spectator-list .seen.gone { color: var(--gray-blue); }

/* Row-scale icon buttons: the popover cannot afford the 0.35rem/0.6rem padding of a toolbar. */
.spectator-actions { flex: 0 0 auto; display: flex; gap: 0.1rem; margin-left: 0.15rem; }
.spectator-actions .icon-btn { padding: 0.2rem; }
.spectator-actions .icon-btn .icon { width: 14px; height: 14px; }

/* Blocked addresses live under the roster rather than in it: the host needs to see who they shut
   out in order to let them back in, and mixing them into the count would misreport the room. */
.spectator-list .banned-list li { opacity: 0.6; }
.spectator-list .banned-list .who { text-decoration: line-through; }

/* ---------------------------------------------------------------- the audience line's budget

   `⚡ 37 / 120 traded` sits under the BUY/SELL cards for the whole trade phase, but it used to
   `display: none` until the first spectator connected — so the panel *grew* mid-market, the moment
   somebody joined, and at 1366x768 with a four-line question that growth was what pushed it 23px
   past the fold. A layout that changes because a stranger opened their phone is wrong twice over:
   it hides the trade cards, and it does it in front of a room.

   So the slot always occupies its line and only its content is hidden, and the question — the one
   block in the panel whose height is unbounded — gives that line back out of its own budget. The
   `vh` coefficient is raised alongside the constant so a 1080p projector is not charged for it:
   the question is still ~98px there, against the 6.4rem cap it was already hitting.

   That raised clamp now lives in `--qsize` on `.overlay`, next to `--ovw`, because the fit factor
   has to multiply exactly one declaration — a second `.question` rule down here would have been a
   ceiling the JS could not see. */

.aud-live[hidden] { display: block; visibility: hidden; }

/* The "nobody has joined yet" placeholder is an `li.dim`, so it was picking up the `margin-left:
   auto` meant for the address column and sitting hard right — which reads as a stray label rather
   than an empty list, now that a "Blocked" section can follow it. */
.spectator-list ul > li.dim { margin-left: 0; }
