* { box-sizing: border-box; }
html, body {
  margin: 0; height: 100%; width: 100%;
  background: #0b0e14;
  color: #e8ecf4;
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow: hidden;
}
#stage {
  position: relative;
  width: 100vw; height: 100vh;
}
canvas {
  display: block;
  width: 100vw; height: 100vh;
  background: #05070c;
}
/* =====================================================================
   THE IN-MATCH HUD BAR
   =====================================================================
   It used to be `left: 50%; transform: translateX(-50%)` with
   `max-width: 96vw; overflow-x: auto`, which is two bugs in one line: a
   transform-centred element is laid out at its content width and only
   moved afterwards, so with eight players it ran past the right edge, and
   the overflow-x turned that into a scrollbar the Return to Lobby button
   sat behind. Nobody scrolls a HUD.

   The fix has two halves that do different jobs:

     STRUCTURE (this rule) makes overflow impossible: a hard max-width and
     `overflow: hidden` in place of the scrollbar, so the bar can never be
     wider than the screen and can never offer to scroll.

     SCALING (--chip-scale, below) makes the result readable. Once the
     width is fixed, a full room has to fit inside it somehow; the player
     chips shrink and their names ellipsis while the fixed furniture —
     round/map, timer, Return to Lobby — stays at full size.

   scripts/hud-fit-test.mjs measures both in real Chrome at 1366x768 and
   1440x900 with eight sixteen-character names. */
#hud {
  position: absolute;
  top: clamp(8px, 1.2vh, 20px);
  /* Centred with a transform, but capped in the same breath. `left/right`
     insets plus `width: fit-content` looked tidier and does NOT work: the
     percentage in `max-width` resolves against the containing block, not
     the inset box, so the bar sat at left:14 and ran to right:1380 on a
     1366 screen. A percentage cap on the containing block is unambiguous. */
  left: 50%; transform: translateX(-50%);
  max-width: calc(100% - clamp(12px, 2vw, 32px));
  display: flex; gap: clamp(5px, 0.7vw, 12px); align-items: center;
  background: rgba(10,13,20,0.72);
  padding: clamp(5px, 0.6vw, 10px) clamp(8px, 1.1vw, 16px);
  border-radius: 999px;
  font-size: clamp(13px, 1.5vw, 22px); font-weight: 600;
  pointer-events: none;
  backdrop-filter: blur(6px);
  white-space: nowrap;
  /* No scrollbar, ever. If anything did reach this it would be clipped —
     which is precisely what the fit test asserts never happens. */
  overflow: hidden;
}

/* Round/map + timer: fixed furniture, never shrinks. */
#hud .hud-info {
  display: flex; align-items: center; gap: inherit;
  flex: none;
}

/* The player chips are the only thing that scales. --chip-scale is written
   by hud.mjs from the player count; the max() is the readability floor, so
   a big room ellipses names rather than shrinking text to nothing. */
#hud .hud-chips {
  display: flex; align-items: center;
  /* shrink weight 1: the chips are the LAST thing asked to give ground,
     after the notice and the gladiator bar have given up theirs. */
  flex: 0 1 auto; min-width: 0;
  gap: calc(clamp(4px, 0.55vw, 10px) * var(--chip-scale, 1));
  font-size: max(11px, calc(1em * var(--chip-scale, 1)));
}

#hud .chip {
  display: flex; align-items: center; gap: 0.4em;
  padding: 0.2em 0.7em; border-radius: 999px;
  background: rgba(255,255,255,0.06);
  min-width: 0;
}
/* A chip may shrink; the things inside it that carry meaning may not. The
   dot, the score and the kick button are all flex:none, so a chip can
   never squeeze below "who, how many points, and a way to remove them" —
   and that floor is what stops the shrinking from ever hiding a control. */
#hud .player-chip { flex: 0 1 auto; }
#hud .player-chip .who {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#hud .player-chip .score {
  flex: none;
  font-variant-numeric: tabular-nums;
  opacity: 0.95;
}
#hud .hud-chips:not(.compact) .player-chip .score::before { content: "— "; opacity: 0.5; }
#hud .hud-chips.compact .player-chip { gap: 0.28em; padding: 0.2em 0.42em; }
#hud .player-chip .ratio { flex: none; opacity: 0.7; }
#hud .dot {
  width: 0.7em; height: 0.7em; border-radius: 50%;
  display: inline-block; flex-shrink: 0;
}
#hud .dead { opacity: 0.3; text-decoration: line-through; }
#hud .timer { color: #fbbf24; }
#hud .timer.low { color: #ef4444; }
/* `safe center` for the same reason the lobby uses it: a plain centred
   flex column overflows in BOTH directions once it is taller than the box,
   putting the top of a full room's final scoreboard somewhere unreachable. */
#overlay {
  position: absolute; inset: 0;
  display: flex; align-items: safe center; justify-content: safe center;
  flex-direction: column; gap: clamp(6px, 1vh, 14px);
  padding: clamp(8px, 2vh, 24px) clamp(8px, 2vw, 24px);
  pointer-events: none;
  text-align: center;
  overflow: hidden;
}
#overlay .banner {
  font-size: clamp(28px, 5.5vw, 64px);
  font-weight: 800;
  text-shadow: 0 4px 24px rgba(0,0,0,0.6);
  animation: pop 0.25s ease-out;
}
#overlay .sub {
  font-size: clamp(16px, 2.6vw, 28px);
  opacity: 0.8;
  font-weight: 500;
}
#overlay .banner.round-banner {
  font-size: clamp(20px, 3.4vw, 40px);
}
#overlay .banner.match-banner {
  font-size: clamp(32px, 6.5vw, 76px);
}
#overlay .match-scoreboard {
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 0.6vh, 8px);
  background: rgba(10,13,20,0.6);
  border-radius: 14px;
  padding: clamp(10px, 1.4vh, 18px) clamp(16px, 2vw, 26px);
  min-width: min(360px, 80vw);
  max-width: min(620px, 92vw);
}
#overlay .match-score-row {
  display: flex;
  align-items: center;
  gap: 0.6em;
  font-size: clamp(14px, 1.9vw, 22px);
  font-weight: 600;
  color: #e8ecf4;
}
#overlay .match-score-row .dot {
  width: 0.8em; height: 0.8em; border-radius: 50%; flex-shrink: 0;
}
/* Eight rows of names that may be longer than the box: the name gives way,
   the score never does. */
#overlay .match-score-row .who {
  flex: 1; min-width: 0; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#overlay .match-score-row .wins { opacity: 0.75; font-variant-numeric: tabular-nums; flex: none; }
@keyframes pop {
  from { transform: scale(0.85); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
/* The control hints had the same centring trap as the HUD, plus
   white-space: nowrap. With every ability enabled the line is ~130
   characters and simply ran off both ends at the old 26px. Inset from both
   edges, smaller, and allowed to wrap — the separators are surrounded by
   spaces, so it breaks between hints rather than mid-hint. */
#controls {
  position: absolute;
  bottom: clamp(10px, 1.6vh, 22px);
  left: 50%; transform: translateX(-50%);
  /* width: max-content is load-bearing. An absolutely positioned box with
     only `left` set shrink-to-fits into the space from that edge to the
     right of the containing block — from left: 50% that is HALF the
     screen, so the hints wrapped at 683px on a 1366 display while the
     max-width said they had 1334. max-content asks for the single-line
     width and lets max-width be the only thing that can force a wrap. */
  width: max-content;
  max-width: calc(100% - clamp(12px, 2vw, 32px));
  font-size: clamp(11px, 1.25vw, 19px); opacity: 0.7; font-weight: 600;
  background: rgba(10,13,20,0.65);
  padding: 0.4em 1em; border-radius: 14px;
  pointer-events: none;
  white-space: normal; text-align: center;
}
#hud .lobby-btn {
  pointer-events: auto;
  flex: none;
  cursor: pointer;
  border: none;
  font: inherit;
  font-weight: 700;
  color: #fff;
  background: #1e3a5f;
  padding: 0.2em 0.8em;
  border-radius: 999px;
}
#hud .lobby-btn:hover { background: #2a4d7a; }

/* =====================================================================
   LOBBY
   =====================================================================
   The lobby fits the viewport. It does not scroll, and — this is the part
   two earlier attempts got wrong — it does not contain a scroll region
   either. An inner scrollbar is what you reach for when the content is
   taller than the space; the actual fix is to stop the content being that
   tall, by spending the horizontal space the panel was already occupying.

   Three levers, in order of how much height each buys back on a 1366x768
   screen with a full eight-player room:

     1. COLUMNS (~330px). The roster used to sit above the host settings in
        one narrow centre column while most of the panel width sat empty.
        Roster + bots, match settings + maps, and abilities + room privacy
        are now three independent columns that stack side by side; the
        tallest one sets the height instead of the sum of all three.
     2. THE STEPPER GRID (~70px). Round wins / time cap / player cap were
        three full-width rows. They are three cells in one row now.
     3. RHYTHM (~60px). Heading margins, row padding and gaps are tuned per
        window height rather than fixed at desktop-comfortable values.

   Measured, not asserted: scripts/lobby-fit-test.mjs renders this in real
   Chrome at 1366x768 and 1440x900 with eight players and every control
   showing, and fails if anything clips or any scrollbar appears. */

/* `align-items: safe center` is the centring that cannot trap content.
   Plain `center` overflows in BOTH directions once the panel is taller
   than the shell, putting the top of the panel somewhere unreachable —
   that was the original bug. `safe` falls back to start-alignment in
   exactly that case, so the overflow: auto below is a genuine last resort
   for absurdly short windows rather than a scrollbar anyone normally
   meets. At every supported size the panel fits and neither engages. */
#lobby {
  display: none;
  position: absolute; inset: 0;
  align-items: safe center; justify-content: center;
  overflow: auto;
  padding: clamp(6px, 1.6vh, 22px) clamp(8px, 1.2vw, 18px);
}
#lobby.visible { display: flex; }
#lobby .panel {
  display: flex;
  flex-direction: column;
  width: min(1180px, 100%);
  /* Deliberately NO max-height. Capping the panel is what forces an inner
     scroll region, and if the inner region then refuses to scroll the
     content simply spills out of the rounded corners. The panel is its
     natural height; at every supported size that height fits, and on a
     phone-narrow window the shell above is the one thing that scrolls. */
  background: #111726;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 16px;
  padding: clamp(10px, 1.6vh, 18px) clamp(14px, 1.8vw, 26px) clamp(10px, 1.4vh, 16px);
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
  font-size: clamp(13px, 1.05vw, 15px);
}

/* ---- header: title left, room code + invite link right ---- */
#lobby .panel-head {
  flex: none;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  margin-bottom: clamp(6px, 1.2vh, 14px);
}
#lobby h1 {
  margin: 0;
  font-size: clamp(19px, 2.2vw, 30px);
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.room-info {
  display: flex; align-items: center; gap: 8px;
  min-width: 0; flex: 1 1 auto; justify-content: flex-end;
  font-size: 0.9em;
}
.room-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 800; letter-spacing: 0.12em;
  background: rgba(74,222,128,0.14); color: #86efac;
  border-radius: 8px; padding: 0.25em 0.6em;
  flex: none;
}
/* The link is the one thing allowed to lose a fight for space: it ellipses
   instead of wrapping, because a second header line costs every column a
   row of height. The Copy button makes the full text reachable anyway. */
.room-link {
  opacity: 0.6; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.copy-btn {
  flex: none; border: 1px solid rgba(255,255,255,0.18);
  background: rgba(255,255,255,0.06); color: #e8ecf4;
  border-radius: 8px; padding: 0.3em 0.8em;
  font: inherit; font-size: 0.9em; font-weight: 700; cursor: pointer;
}
.copy-btn:hover { background: rgba(255,255,255,0.14); }

/* ---- the columns ----
   No overflow property at all: this region is not a scroll container and
   must never quietly become one. */
#lobby .panel-body {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  gap: clamp(2px, 1vh, 12px) clamp(14px, 1.8vw, 26px);
}
#lobby .panel-col { min-width: 0; }
/* A guest has no host controls, so those columns are empty — remove them
   rather than leaving the roster beside two blank tracks. */
#lobby .panel-col:empty { display: none; }
@media (min-width: 760px) {
  #lobby .panel-body { grid-template-columns: minmax(220px, 1fr) minmax(220px, 1fr); }
}
@media (min-width: 1060px) {
  #lobby .panel-body {
    /* The roster column is the widest because it carries the longest
       content: a 16-character bot name, its difficulty tag, a difficulty
       select and a Remove button, all on one line. lobby-fit-test.mjs
       fails if any of those names ends up ellipsised. */
    grid-template-columns: minmax(240px, 380px) minmax(200px, 1fr) minmax(230px, 1fr);
  }
}

/* Short windows tighten the rhythm rather than reaching for a scrollbar. */
@media (max-height: 820px) {
  #lobby h2 { margin: 8px 0 4px; }
  #lobby .roster-row { padding: 0.25em 0.55em; }
  #roster { gap: 4px; }
  .map-toggles label { padding: 0.3em 0.55em; }
  .private-toggle { padding: 0.1em 0; }
}

#lobby h2 {
  font-size: clamp(10px, 0.85vw, 12px);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.55;
  margin: 12px 0 6px;
}
/* The first heading in any column has nothing above it to be spaced from. */
#lobby .panel-col > h2:first-child,
#lobby .panel-col > div:first-child > h2:first-child { margin-top: 0; }
#lobby #modeControls:empty + .roster-heading { margin-top: 0; }

#roster { display: flex; flex-direction: column; gap: 5px; }
.roster-row {
  display: flex; align-items: center; gap: 8px;
  background: rgba(255,255,255,0.05);
  border-radius: 9px;
  padding: 0.35em 0.6em;
  font-size: 1em;
}
.roster-row .dot { width: 1.05em; height: 1.05em; border-radius: 50%; flex-shrink: 0; }
.roster-row .name { flex: 1; min-width: 0; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.roster-row .tag {
  font-size: 0.72em; opacity: 0.6; text-transform: uppercase; letter-spacing: 0.02em; flex: none;
}
.roster-row select {
  background: #1c2537; color: #e8ecf4; border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px; padding: 0.15em 0.35em; font: inherit; font-size: 0.82em;
}
.roster-row button.remove {
  border: none; background: rgba(239,68,68,0.18); color: #fca5a5;
  border-radius: 6px; padding: 0.2em 0.6em; font-size: 0.82em; cursor: pointer; font-weight: 600;
}
.roster-row button.remove:hover { background: rgba(239,68,68,0.32); }

/* Your own row: an editable name and a control-scheme picker. The input is
   sized to the cap it enforces so a full-length name never scrolls inside
   its own box, and it keeps the row's height rather than adding to it. */
.roster-row.is-me { background: rgba(203,213,245,0.12); }
.roster-row .name-input {
  flex: 1 1 auto; min-width: 0;
  background: rgba(0,0,0,0.28); color: #e8ecf4;
  border: 1px solid rgba(255,255,255,0.18); border-radius: 6px;
  padding: 0.15em 0.4em;
  font: inherit; font-weight: 600; font-size: 1em;
}
.roster-row .name-input:focus {
  outline: none; border-color: #4ade80; background: rgba(0,0,0,0.4);
}
.roster-row .scheme-select {
  background: #1c2537; color: #e8ecf4; border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px; padding: 0.15em 0.3em; font: inherit; font-size: 0.8em;
  flex: none;
}
.roster-row .tag-host {
  background: rgba(74,222,128,0.16); color: #86efac;
  border-radius: 5px; padding: 0.1em 0.4em; opacity: 1;
}
.roster-row button.make-host {
  border: none; background: rgba(59,130,246,0.2); color: #93c5fd;
  border-radius: 6px; padding: 0.2em 0.55em; font-size: 0.82em;
  cursor: pointer; font-weight: 600; flex: none; white-space: nowrap;
}
.roster-row button.make-host:hover { background: rgba(59,130,246,0.36); }

/* Read-only settings for guests. Everything is SHOWN — a guest needs to
   know what they are about to play — and only the controls are dead. Dimmed
   rather than hidden, so the panel a guest sees is the panel the host sees
   and the two can talk about the same screen. */
#lobby :disabled {
  cursor: not-allowed;
  opacity: 0.55;
}
#lobby .mode-row button:disabled.active {
  /* The chosen mode must still read as chosen when you cannot change it. */
  opacity: 0.9;
}
#lobby label:has(input:disabled) { cursor: default; opacity: 0.75; }
.bot-add-row, .mode-row { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.bot-add-row button, .mode-row button {
  flex: 1 1 auto;
  border: none; border-radius: 8px; padding: 0.45em 0.6em; font: inherit; font-size: 0.88em;
  font-weight: 700; cursor: pointer; color: #0b0e14; background: #cbd5f5;
}
.bot-add-row button:hover, .mode-row button:hover { background: #e8ecf4; }
.bot-add-row button:disabled { opacity: 0.35; cursor: not-allowed; }
.mode-row button.active { background: #4ade80; }

.setting-row {
  display: flex; align-items: center; gap: 8px;
  padding: 0.25em 0; font-size: 1em;
}
.setting-row .label { flex: 1; min-width: 0; opacity: 0.85; }

/* Three steppers on one row instead of three stacked full-width rows. */
.setting-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
  gap: 6px;
}
.setting-cell {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  background: rgba(255,255,255,0.04);
  border-radius: 9px;
  padding: 0.4em 0.3em;
}
.setting-cell .label {
  font-size: 0.8em; opacity: 0.7; white-space: nowrap;
}
.setting-cell .value { min-width: 2.4em; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }
.stepper { display: flex; align-items: center; gap: 4px; }
.setting-row .value { min-width: 2.6em; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }
.stepper button {
  width: 1.7em; height: 1.7em; border-radius: 50%; border: none;
  background: rgba(255,255,255,0.1); color: #e8ecf4; font: inherit; font-weight: 700; cursor: pointer;
  display: flex; align-items: center; justify-content: center; padding: 0;
}
.stepper button:hover { background: rgba(255,255,255,0.18); }
.map-toggles { display: flex; gap: 6px; flex-wrap: wrap; }
.map-toggles label {
  display: flex; align-items: center; gap: 5px;
  background: rgba(255,255,255,0.05); padding: 0.35em 0.6em; border-radius: 8px;
  font-size: 0.86em; cursor: pointer;
}
.map-toggles input, .private-toggle input { margin: 0; flex: none; }
.private-toggle {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 0.15em 0; font-size: 0.95em; cursor: pointer; line-height: 1.3;
}

#lobbyFooter {
  flex: none;
  margin-top: clamp(6px, 1.2vh, 14px);
  padding-top: clamp(6px, 1vh, 12px);
  border-top: 1px solid rgba(255,255,255,0.08);
}
#lobbyFooter .start-btn {
  display: block; width: 100%;
  border: none; border-radius: 10px; padding: 0.6em;
  font: inherit; font-size: 1.05em; font-weight: 800; cursor: pointer;
  background: #22c55e; color: #06170c;
}
#lobbyFooter .start-btn:disabled { opacity: 0.35; cursor: not-allowed; }
#lobbyFooter .start-btn:not(:disabled):hover { background: #4ade80; }
#lobbyFooter .waiting { text-align: center; opacity: 0.7; font-size: 0.95em; padding: 0.4em 0; }

/* ---- host controls in the HUD ----
   #hud is pointer-events:none so the canvas stays clickable through it;
   anything interactive inside must opt back in explicitly. */
#hud .hud-notice {
  pointer-events: auto;
  /* Shrinks ten times as readily as a player chip: a transient warning
     giving up half its width beats eight names losing theirs. It keeps its
     full text in the title attribute either way. */
  flex: 0 10 auto; min-width: 0;
  background: #7f1d1d; color: #fecaca;
  border: 1px solid #b91c1c; border-radius: 6px;
  padding: 4px 10px; font-size: 12px; max-width: 340px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Scales with its chip rather than sitting at a fixed 12px: at eight
   players a fixed-size button is a bigger share of the chip than the name. */
#hud .kick-btn {
  pointer-events: auto;
  flex: none;
  margin-left: 0.15em; padding: 0 0.35em;
  background: transparent; color: #fca5a5;
  border: 1px solid rgba(248,113,113,0.5); border-radius: 4px;
  font: inherit; font-size: max(10px, 0.62em); line-height: 1.35; cursor: pointer;
}
#hud .kick-btn:hover { background: #7f1d1d; color: #fff; }

/* ---- removed-from-room screen ---- */
#removed {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  background: rgba(5,7,12,0.94);
}
#removed .removed-card {
  text-align: center; padding: 32px 40px;
  background: #131a2a; border: 1px solid #2a3550; border-radius: 12px;
}
#removed h1 { margin: 0 0 8px; font-size: 22px; color: #f8fafc; }
#removed p { margin: 0 0 20px; color: #94a3b8; }

/* ---- gladiator health bar in the HUD ---- */
#hud .glad-bar {
  display: flex; align-items: center; gap: 0.4em;
  /* Gives ground before the chips do — a track can lose most of its width
     and still read as a proportion. But only down to a floor: at shrink 6
     with no minimum it collapsed to a 30px green smudge in an eight-player
     gladiator round, which is the one round where this number matters most.
     The floor costs the chips about two characters of name each; that is
     the right way round. */
  /* The floor is in em of THIS bar's own 13px font (not the HUD's), and it
     is sized from the contents: ~3em of name + a 36px track + ~4em of
     "479/760" + gaps and padding, plus ~2em for the round's kill count.
     Below about 12.4em the name collapses to zero and the numbers spill out
     of the pill — measured, by adding the kill count and watching
     hud-fit-test catch exactly that at four viewport sizes.

     The kill count was paid for inside this bar rather than by raising the
     floor further: the first attempt used 14.5em, which fit the bar and
     then pushed every player chip's name below its own two-character floor.
     The gladiator's name gave up the width instead (max-width 7em -> 5.2em)
     because it is the one string in here that is only nice to have — the
     health numbers and the kill count are the reason the bar exists. */
  flex: 0 2 auto; min-width: 12.4em;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(10, 14, 24, 0.78);
  border: 1px solid rgba(248, 210, 74, 0.55);
  font-size: 13px; font-weight: 600;
}
#hud .glad-name {
  color: #f8d24a; white-space: nowrap;
  /* Never zero: an ellipsised name still says who, an absent one does not.
     1.5em is about one character plus the ellipsis — the smallest thing that
     is still an answer to "whose bar is this", and the last width available
     to pay for the kill count without taking it from the player chips. */
  min-width: 1.5em; max-width: 5.2em;
  overflow: hidden; text-overflow: ellipsis;
}
#hud .glad-track {
  display: block; flex: 0 1 auto;
  width: clamp(60px, 13vw, 200px); min-width: 36px;
  height: 10px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.14); overflow: hidden;
}
#hud .glad-fill { display: block; height: 100%; background: #4ade80; }
#hud .glad-value { color: #e2e8f0; font-variant-numeric: tabular-nums; min-width: 28px; flex: none; }

/* The gladiator seat picker sits with the mode buttons it belongs to. */
#modeControls .setting-row select {
  max-width: 100%; min-width: 0; flex: 1 1 auto;
  background: #1c2537; color: #e8ecf4; border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px; padding: 0.25em 0.4em; font: inherit; font-size: 0.88em;
}

/* Endless final standings on the match-over screen. */
#overlay .match-score-row.heading { opacity: 0.55; font-size: 0.85em; letter-spacing: 0.04em; }
#overlay .ineligible { opacity: 0.5; font-size: 0.85em; }

/* ===================================================================
   TEAMS
   Compact on purpose. The lobby has no spare vertical space at eight
   players (scripts/lobby-fit-test.mjs measures it at 1366x660), so the
   team controls are one button row and the group headers are a single
   short line each.
   =================================================================== */
.team-row button { min-width: 2.6em; }

.team-opts {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 4px 0 2px;
}
.team-opts button {
  font-size: 0.78rem;
  padding: 3px 8px;
}
.team-opts .private-toggle { margin: 0; font-size: 0.76rem; }

.team-group {
  border-left: 3px solid var(--team, #3a4a66);
  padding-left: 6px;
  margin-bottom: 4px;
}
.team-head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 1px 0 2px;
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--team, #9fb0cc);
}
.team-head .team-name { font-weight: 700; }
.team-head .team-count { color: #64748b; }
.team-head .team-count::before { content: "· "; }
.team-head .team-wins {
  margin-left: auto;
  color: #cbd5e1;
  font-variant-numeric: tabular-nums;
}
.team-head .team-wins::after { content: " W"; color: #64748b; }
.roster-row.empty-side { color: #64748b; font-style: italic; font-size: 0.8rem; }

/* The per-player side picker: one small square per side, filled when it is
   the side you are on. A select would cost a labelled row per player. */
.team-slots { display: inline-flex; gap: 3px; margin-left: 4px; }
.team-slot {
  width: 14px;
  height: 14px;
  padding: 0;
  border-radius: 4px;
  border: 2px solid var(--team, #64748b);
  background: transparent;
  cursor: pointer;
  flex: 0 0 auto;
}
.team-slot.on { background: var(--team, #64748b); }
.team-slot:disabled { cursor: default; opacity: 0.75; }
.team-slot:not(.on):not(:disabled):hover { background: color-mix(in srgb, var(--team) 40%, transparent); }

/* Team totals on the end-of-match board, with members underneath. */
.team-score {
  border-left: 3px solid var(--team, #3a4a66);
  padding: 4px 0 4px 8px;
  margin-bottom: 6px;
}
.team-score.won { background: rgba(255, 255, 255, 0.04); }
.team-score-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
}
.team-score-head .wins { margin-left: auto; font-variant-numeric: tabular-nums; }
.team-member {
  display: flex;
  gap: 8px;
  padding-left: 18px;
  font-size: 0.85rem;
  color: #9fb0cc;
}
.team-member .wins { margin-left: auto; font-variant-numeric: tabular-nums; }

/* ===================================================================
   The gladiator's kill count, beside the health bar.
   =================================================================== */
.glad-kills {
  /* Shrinks last and never wraps: it is two characters and a glyph. */
  flex: 0 0 auto;
  font-size: calc(0.78rem * var(--chip-scale, 1));
  font-weight: 700;
  color: #f8d24a;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.glad-kills.none { color: #64748b; font-weight: 500; }

/* ===================================================================
   CONTROLS PANEL (keybinds)

   An overlay above the lobby. Its own scroll region is deliberate and is
   NOT the thing the fit tests forbid: that rule is about the lobby and the
   HUD, which must fit whole. This is a modal a player opens on purpose,
   and it caps at 80vh so it can never grow past the window.
   =================================================================== */
.keybind-panel {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  background: rgba(3, 5, 10, 0.72);
  padding: 16px;
}
.keybind-panel[hidden] { display: none !important; }

.keybind-card {
  width: min(30rem, 100%);
  max-height: 80vh;
  overflow-y: auto;
  background: #121a2b;
  border: 1px solid #263349;
  border-radius: 12px;
  padding: 14px 16px 12px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}
.keybind-head { display: flex; align-items: center; gap: 8px; }
.keybind-head h2 { margin: 0; flex: 1 1 auto; }
.kb-close {
  background: transparent;
  border: 0;
  color: #9fb0cc;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.kb-close:hover { color: #fff; }

.kb-scheme {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid #1e2a3e;
}
.kb-scheme .label { color: #9fb0cc; font-size: 0.85rem; }
.kb-scheme select { margin-left: auto; }

.keybind-rows { display: flex; flex-direction: column; gap: 4px; }
.keybind-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  border-radius: 7px;
}
.keybind-row.capturing { background: rgba(59, 130, 246, 0.16); outline: 1px solid #3b82f6; }
.kb-label { flex: 0 0 10.5em; font-size: 0.85rem; color: #cbd5e1; }
.kb-keys { flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: 4px; }

.key-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  background: #1c2537;
  border: 1px solid #33415c;
  border-radius: 5px;
  padding: 1px 3px 1px 6px;
  font-size: 0.78rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: #e8ecf6;
}
.key-drop {
  background: transparent;
  border: 0;
  color: #64748b;
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1;
  padding: 0 2px;
}
.key-drop:hover { color: #ef4444; }

.kb-add {
  flex: 0 0 auto;
  font-size: 0.76rem;
  padding: 3px 8px;
  white-space: nowrap;
}

.kb-note {
  min-height: 1.2em;
  margin: 8px 0 0;
  font-size: 0.8rem;
  color: #f59e0b;
  opacity: 0;
  transition: opacity 0.12s;
}
.kb-note.show { opacity: 1; }

.kb-foot {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid #1e2a3e;
}
.kb-reset { font-size: 0.8rem; }
.kb-done { margin-left: auto; font-weight: 650; }

.controls-btn { width: 100%; text-align: left; font-size: 0.82rem; }

/* A row for an action this scheme drives with the mouse: shown, so the
   panel is a complete picture of the controls, but not offered as a key. */
.keybind-row.fixed { opacity: 0.75; }
.key-chip.mouse {
  font-family: inherit;
  border-style: dashed;
  color: #9fb0cc;
}
.kb-fixed-note {
  flex: 0 0 auto;
  font-size: 0.72rem;
  color: #64748b;
  white-space: nowrap;
}
