:root {
  color-scheme: dark;
  --bg: #07111d;
  --panel: rgba(7, 16, 30, 0.95);
  --line: rgba(255, 255, 255, 0.15);
  --blue: #4a9eff;
  --red: #ff5a5a;
  --gold: #ffcc2e; /* Neutral Flag's single flag, matches client.js's COLORS.neutral */
  --teal: #2dd4bf;
  --violet: #a78bfa;
  --slate: #94a3b8;
  --green: #4ade80; /* Ultimate Frisbee's mode-card accent — an open-field green, distinct from every other mode's accent */
  --orange: #fb923c; /* Soccer's mode-card accent — a soccer-ball/cleats energy, distinct from Ultimate's own field green */
  --rose: #fb7185; /* Tournaments' mode-card accent — a championship-ribbon energy, distinct from --red (already the Survivor card's own accent) */
  --text: #e8eaee;
}
* { box-sizing: border-box; }
/* .hud/.footer/etc. set `display` explicitly, which otherwise beats the
   browser's default `[hidden] { display: none }` UA-stylesheet rule (author
   CSS always wins over UA styles) — without this override, toggling the
   `hidden` attribute in JS would silently do nothing on those elements. */
[hidden] { display: none !important; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 22px;
  background: radial-gradient(circle at top, #14314f 0%, var(--bg) 55%, #050913 100%);
  color: var(--text);
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
.frame {
  width: min(1040px, 100%);
  border-radius: 22px;
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.35);
  background: var(--panel);
  position: relative; /* positioning context for #vgsMenu, which now lives at frame level so comms work on lobby screens too */
}

/* Waiting-room / draft-screen chat feed for VGS comms (the V-key menu
   works on those screens now, not just in-game). */
.roomChatFeed { margin-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.1); padding-top: 10px; }
.roomChatHint { color: var(--text-dim); font-size: 0.78rem; margin-bottom: 6px; }
.roomChatHint kbd { border: 1px solid rgba(255, 255, 255, 0.25); border-radius: 4px; padding: 0 5px; font-size: 0.72rem; }
.roomChatLines { display: flex; flex-direction: column; gap: 3px; min-height: 20px; max-height: 120px; overflow-y: auto; font-size: 0.85rem; }
.roomChatLine .chatName { font-weight: 700; }
.roomChatLine.blue .chatName { color: var(--blue); }
.roomChatLine.red .chatName { color: var(--red); }

/* Fixed to the viewport (not `.frame`) so it stays in the same spot
   regardless of which screen is showing, including the borderless
   fullscreen game view (see .frame.gameMode). */
.audioControls {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 6px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(7, 16, 30, 0.72);
  backdrop-filter: blur(6px);
}
/* Shown while a genuine network drop is auto-reconnecting mid-match (see
   public/client.js's "disconnect" handler) — the game screen stays up
   underneath so a successful reconnect snaps right back with no
   lobby-then-game flicker; this is just a heads-up that something's
   happening, not a full loading-screen takeover. */
.reconnectingOverlay {
  position: fixed;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid rgba(255, 200, 90, 0.4);
  background: rgba(30, 20, 5, 0.85);
  backdrop-filter: blur(6px);
  color: #ffcf6b;
  font-size: 0.9rem;
  font-weight: 600;
}
.audioBtn {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 0.95rem;
  cursor: pointer;
  line-height: 1;
}
.audioBtn:hover { background: rgba(255, 255, 255, 0.12); }
.audioBtn.muted { opacity: 0.4; }
.audioTrackLabel {
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.65);
  transition: max-width 0.4s ease, margin-left 0.4s ease;
}
.audioTrackLabel.show { max-width: 160px; margin-left: 4px; }
.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.045);
}
.brandMark { display: flex; align-items: center; gap: 10px; }
.brandIcon { width: 34px; height: 34px; display: block; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4)); }
.brandText { display: flex; flex-direction: column; line-height: 1.15; }
.brandTitle { font-size: 1.1rem; font-weight: 800; letter-spacing: 0.01em; }
.brandSubtitle { font-size: 0.66rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255, 255, 255, 0.45); }
.topbarRight { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.accountBar { display: flex; align-items: center; gap: 8px; }
.accountBar .secondary { width: auto; margin-top: 0; padding: 7px 12px; font-size: 0.85rem; }
.accountSignedIn { display: flex; align-items: center; gap: 8px; }
.accountAvatar { width: 26px; height: 26px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.25); }
#accountName { font-size: 0.9rem; font-weight: 600; }
/* Signed-in account dropdown: the name/avatar is the trigger; Daily Tasks,
   Shop, Settings, Sign out live in the list. */
.accountMenu { position: relative; }
.accountMenuBtn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 10px 5px 6px; border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18); background: rgba(255, 255, 255, 0.07);
  color: inherit; font: inherit; cursor: pointer;
}
.accountMenuBtn:hover, .accountMenu.open .accountMenuBtn { background: rgba(255, 255, 255, 0.14); }
.accountMenuCaret { font-size: 0.75rem; opacity: 0.7; }
.accountMenuList {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  min-width: 190px; padding: 6px;
  display: flex; flex-direction: column; gap: 2px;
  border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(10, 20, 36, 0.98); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
}
.accountMenuItem {
  display: block; width: 100%; text-align: left;
  padding: 9px 12px; border-radius: 8px; border: 0; background: transparent;
  color: rgba(255, 255, 255, 0.9); font: inherit; font-size: 0.9rem; cursor: pointer;
}
.accountMenuItem:hover { background: rgba(255, 255, 255, 0.1); }
.accountMenuSep { height: 1px; margin: 4px 6px; background: rgba(255, 255, 255, 0.12); }
/* Discord CTA — brand "blurple", the look people recognize from every
   community site, so it stands out from the neutral nav buttons. */
.discordBtn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px; border-radius: 8px;
  background: #5865F2; color: #fff; text-decoration: none;
  font-weight: 700; font-size: 0.85rem; border: 1px solid #5865F2;
  box-shadow: 0 2px 10px rgba(88, 101, 242, 0.35);
}
.discordBtn:hover { background: #4752C4; border-color: #4752C4; }
.discordLogo { width: 18px; height: 14px; display: block; }
.hud { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.pill {
  padding: 8px 10px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.07);
  font-size: 0.95rem;
}
.pill.blue strong { color: var(--blue); }
.pill.red strong { color: var(--red); }
.pill.you-blue { border-color: rgba(74, 158, 255, 0.55); color: var(--blue); }
.pill.you-red { border-color: rgba(255, 90, 90, 0.55); color: var(--red); }
.pill.you-spectate { opacity: 0.75; }
button.reset {
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  font-size: 0.88rem;
  cursor: pointer;
}
button.reset:hover { background: rgba(255, 255, 255, 0.13); }
.stage { position: relative; }
canvas { display: block; width: 100%; height: auto; background: #12151a; }

/* Fullscreen-style in-game view (see showScreen() in client.js) — canvas
   fills essentially the whole browser window, with only a thin transparent
   overlay strip top-left (leave/status pills, flipped there via
   flex-direction so the SAME .topbar/.hud markup used everywhere else
   doesn't need duplicating) and the fixed #audioControls owning the true
   top-right corner. Real TagPro's layout (exit/status top-left, mute/
   settings top-right, playing field filling everything else) is the model.
   The game's logical camera viewport stays a fixed 1360x800 (shared/camera.js/
   server fog-of-war depend on that number matching) — this only changes
   how large that fixed-resolution canvas is DRAWN on screen, scaled up via
   object-fit rather than the map itself becoming resolution-dependent. */
.frame.gameMode {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 0;
  border: none;
  box-shadow: none;
  display: flex;
  flex-direction: column;
  z-index: 10;
}
.frame.gameMode .topbar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  /* .brandMark below is display:none, leaving .hud as the only flex item —
     flex-start pins it to the LEFT edge (the whole point: leave the true
     top-right corner free for #audioControls). */
  justify-content: flex-start;
  border-bottom: none;
  background: linear-gradient(rgba(5, 9, 19, 0.75), transparent);
  padding: 10px 14px;
}
/* The brand is an <a> since frontpage v2 — the old `> div:first-of-type`
   selector (meant for it) was instead hiding .topbarRight, which killed
   the ENTIRE in-game HUD row (you-pill, ability-cooldown pill, Reset/
   Leave) in every mode. Owner noticed the missing cooldown pill in
   Hockey; explicit selectors now. */
.frame.gameMode .brandMark { display: none; }
.frame.gameMode .accountBar { display: none; }
.frame.gameMode .hud { gap: 8px; }
.frame.gameMode .footer { display: none; }
.frame.gameMode .stage {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;
}
.frame.gameMode canvas {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Touch controls (mobile) — a virtual joystick + 2 buttons overlaid on
   the canvas, shown ONLY on coarse-pointer (touch) devices. A laptop
   with a touchscreen that also has a keyboard/mouse never sees these —
   `pointer: coarse` only matches the device's PRIMARY pointer, and stays
   hidden here rather than trying to feature-detect "has touch," which
   would wrongly show it on such laptops too. See client.js's touch-input
   block for how these feed the exact same up/down/left/right/ability/
   detonate booleans the keyboard already produces. */
.touchControls {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 15;
  pointer-events: none; /* only the joystick base + buttons themselves opt back in */
  user-select: none;
  -webkit-user-select: none;
}
@media (pointer: coarse) {
  .touchControls { display: block; }
}
.touchJoystick {
  position: absolute;
  left: 22px;
  bottom: 22px;
  width: 124px;
  height: 124px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: 2px solid rgba(255, 255, 255, 0.28);
  pointer-events: auto;
  touch-action: none; /* stop the page from panning/zooming while dragging */
}
.touchJoystickThumb {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 52px;
  height: 52px;
  margin: -26px 0 0 -26px;
  border-radius: 50%;
  background: rgba(74, 158, 255, 0.55);
  border: 2px solid rgba(74, 158, 255, 0.9);
  pointer-events: none; /* the base circle owns the drag, not the thumb graphic */
}
.touchButtons {
  position: absolute;
  right: 20px;
  bottom: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  pointer-events: none;
}
.touchBtn {
  pointer-events: auto;
  touch-action: none;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
  font-size: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.touchBtn.active { background: rgba(74, 158, 255, 0.4); border-color: rgba(74, 158, 255, 0.9); }
.touchBtnAbility.onCooldown { opacity: 0.4; } /* mirrors the powerup pill's "X.Xs cooldown" text, at a glance without reading it */

/* "Rotate your device" prompt — shown ONLY while actively in a match
   (.frame.gameMode) on a touch device currently held in portrait.
   Landscape is the only supported orientation for real play: the game's
   camera viewport is a fixed 1360x800 (~16:9) logical resolution (see
   .frame.gameMode canvas above), which letterboxes down to an unplayable
   sliver in portrait — confirmed visually before adding this prompt.
   Pure CSS, no JS: rotating the phone changes which media query matches,
   so this shows/hides itself automatically with zero event-handling code. */
.rotatePrompt { display: none; }
@media (pointer: coarse) and (orientation: portrait) {
  .frame.gameMode .rotatePrompt {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 100; /* above .touchControls (15) and everything else in gameMode */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    background: rgba(5, 9, 19, 0.96);
    color: var(--text);
    text-align: center;
    padding: 32px;
  }
}
.rotateIcon { font-size: 3rem; animation: rotateHint 1.8s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .rotateIcon { animation: none; } }
@keyframes rotateHint { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(-90deg); } }
.rotatePromptTitle { font-size: 1.15rem; font-weight: 800; }
.rotatePromptCopy { font-size: 0.9rem; color: rgba(255, 255, 255, 0.65); max-width: 280px; }
kbd {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(255, 255, 255, 0.08);
  font-family: inherit;
  font-size: 0.85em;
}
/* Perf readout (FPS/ping), top-left of the playing field itself — same
   spot/style real TagPro uses (plain small text overlaying the canvas, not
   a pill), positioned just below the topbar's leave/status pill cluster in
   gameMode so the two don't overlap. */
.perfStats {
  position: absolute;
  top: 46px;
  left: 14px;
  z-index: 15;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.55);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  pointer-events: none;
}

/* --- top wide message HUD (Tribes-1-style ticker): system events, match
   countdown, and VGS chat lines, replacing the old floating speech bubbles
   drawn over the ball (they overlapped play) --- */
.hudTicker {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  width: min(94%, 640px);
  max-height: 118px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 12px;
  border-radius: 8px;
  background: rgba(7, 16, 30, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.08);
  pointer-events: none;
  font-size: 0.82rem;
  line-height: 1.35;
}
.hudTicker .tickerLine {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  animation: tickerIn 0.18s ease-out;
}
.hudTicker .tickerLine .tickerName { font-weight: 700; }
.hudTicker .tickerLine.chat.blue .tickerName { color: var(--blue); }
.hudTicker .tickerLine.chat.red .tickerName { color: var(--red); }
.hudTicker .tickerLine.system { color: #ffd24a; }
.hudTicker .tickerLine.capture { color: #7cff9e; font-weight: 600; }
.hudTicker .tickerLine.tag { color: rgba(255, 255, 255, 0.75); }
.hudTicker .tickerLine.powerup { color: #b388ff; }
.hudTicker .tickerLine.countdown { color: #fff; font-weight: 700; text-align: center; }
@keyframes tickerIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* --- bottom scoreboard/timer bar --- */
.matchBar {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  display: flex;
  align-items: stretch;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  font-weight: 700;
  user-select: none;
  transition: opacity 0.25s ease;
}
/* Ghosted while a live ball is rendered underneath (see render()'s
   hudOccluded) — the play matters more than the chrome in that moment. */
.matchBar.ghosted, .flagStatusPill.ghosted { opacity: 0.15; }
.matchPill { display: flex; align-items: center; gap: 8px; padding: 8px 16px; }
.matchPill.blue { background: linear-gradient(90deg, #2f6fd6, var(--blue)); border-radius: 999px 0 0 999px; padding-right: 20px; }
.matchPill.red { background: linear-gradient(90deg, var(--red), #c23a3a); border-radius: 0 999px 999px 0; padding-left: 20px; flex-direction: row-reverse; }
.matchTeamLabel { font-size: 0.8rem; letter-spacing: 0.06em; }
.matchScoreBox {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5em;
  padding: 2px 8px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.94);
  color: #12151a;
  font-size: 1.05rem;
}
.matchDots { display: flex; gap: 4px; }
.matchDots .dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, 0.3); }
.matchDots .dot.filled { background: rgba(255, 255, 255, 0.95); }
.matchTime {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 6px 18px;
  background: #12151a;
}
.matchTimeLabel { font-size: 0.62rem; letter-spacing: 0.12em; color: rgba(255, 255, 255, 0.55); }
.matchTimeValue { font-variant-numeric: tabular-nums; font-size: 1.15rem; color: #ffd24a; }
/* Regulation clock inside its last 30s (see updateMatchTime in client.js) —
   also reused for the whole of overtime/Clutch Time, both genuinely urgent. */
.matchTimeValue.urgent { color: var(--red); }
/* Pre-match freeze ("Match starting in N…") — deliberately NOT .urgent's
   red, which reads as "time's about to run out"; this is the opposite. */
.matchTimeValue.starting { color: #7cc4ff; }

/* Neutral Flag only ("Flag: open / held by BLUE / held by RED") — hidden
   on classic-CTF rooms, which already show flag status via the two home
   markers on the field itself. Sits centered just below the scoreboard, its
   own small pill so it doesn't compete with the score/clock for attention. */
.flagStatusPill {
  position: absolute;
  left: 50%;
  bottom: 66px;
  transform: translateX(-50%);
  transition: opacity 0.25s ease;
  padding: 4px 14px;
  border-radius: 999px;
  background: rgba(7, 16, 30, 0.85);
  border: 1px solid var(--line);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  color: var(--gold);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}
.flagStatusPill.held-blue { color: var(--blue); }
.flagStatusPill.held-red { color: var(--red); }
/* Ultimate Frisbee's own pill (#discStatusPill) reuses this exact class —
   .stalling overrides held-blue/held-red's color once a mark has started
   the stall clock, same urgency signal as .matchTimeValue.urgent. */
.flagStatusPill.stalling { color: var(--red); }

.vgsMenu {
  position: absolute;
  left: 14px;
  bottom: 14px;
  padding: 10px 12px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(7, 16, 30, 0.92);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  font-size: 0.88rem;
  min-width: 180px;
}
.vgsMenu .vgsTitle {
  font-weight: 700;
  margin-bottom: 6px;
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.vgsMenu .vgsRow { display: flex; gap: 8px; padding: 2px 0; align-items: baseline; }
.vgsMenu .vgsKey {
  display: inline-block;
  min-width: 1.4em;
  padding: 0 4px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.14);
  font-weight: 700;
  text-align: center;
}
.footer {
  padding: 12px 18px 16px;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  color: rgba(255, 255, 255, 0.74);
  font-size: 0.92rem;
  flex-wrap: wrap;
}

/* --- lobby / mode-select landing screen --- */
.lobby { padding: 28px 18px; }

/* --- hero band: eyebrow + logo + copy on the left, a real gameplay
   screenshot (see public/media/hero-bg.jpg — captured live via headless
   Chrome, not stock art) framed on the right. Modeled on the two-panel
   "headline + preview" hero pattern from tagpro-nexus-hub's homepage
   concept, using Cappit's own logo/brand colors instead of borrowing
   theirs. --- */
.heroBand {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: 0;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
  overflow: hidden;
  margin-bottom: 22px;
}
.heroBandLeft { padding: 26px 26px 22px; display: flex; flex-direction: column; gap: 14px; }
.heroEyebrow {
  align-self: flex-start;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid rgba(74, 158, 255, 0.35);
  background: rgba(74, 158, 255, 0.12);
  color: #8cbdff;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.heroLogo { width: min(100%, 300px); height: auto; display: block; filter: drop-shadow(0 8px 30px rgba(255, 90, 60, 0.15)) drop-shadow(0 8px 30px rgba(74, 158, 255, 0.15)); }
.heroCopy { font-size: 0.92rem; line-height: 1.55; color: rgba(255, 255, 255, 0.68); max-width: 42ch; }
.onlineIndicator { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; color: rgba(255, 255, 255, 0.6); margin-top: auto; }
.heroBandRight { background: rgba(0, 0, 0, 0.25); border-left: 1px solid var(--line); min-height: 220px; display: flex; }
.heroPreview { position: relative; width: 100%; aspect-ratio: 16 / 9; align-self: center; overflow: hidden; }
.heroPreview video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  background: #000; /* shows through before the first frame decodes */
}
.heroPreviewTag {
  position: absolute;
  left: 12px;
  bottom: 12px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(7, 16, 30, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(4px);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
}
/* prefers-reduced-motion: CSS alone can't stop an autoplay <video>, so the
   actual pause lives in client.js (checks matchMedia at load, right next
   to where the video element is looked up) — nothing to do here. */

/* --- live-dot: a small pulsing indicator, reused by both the online-count
   pill and the "Live games" section header. --- */
.liveDot {
  position: relative;
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #3ddc73;
  flex: none;
}
.liveDot::before {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: #3ddc73;
  opacity: 0.55;
  animation: liveDotPing 1.8s ease-out infinite;
}
@keyframes liveDotPing {
  0% { transform: scale(0.6); opacity: 0.55; }
  100% { transform: scale(2.1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .liveDot::before { animation: none; opacity: 0.25; }
}

.modeGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }

/* Per-mode rows (Ultimate / Soccer / CTF / More ways to play) — each a
   labeled section with the same four-card grid inside. */
.modeRows { display: flex; flex-direction: column; gap: 26px; }
.modeRowHeader { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 8px; }
.modeRowHeader h3 { margin: 0; font-size: 1.05rem; font-family: "Oxanium", Inter, system-ui, sans-serif; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.modeRowIcon { font-size: 1.1rem; }
.modeRowHint { color: var(--text-dim); font-size: 0.82rem; }
/* A four-up row collapses gracefully on narrow screens via the existing
   modeGrid auto-fit; nothing extra needed. */
.modeCard.disabled {
  cursor: default;
  opacity: 0.55;
  border-top-color: rgba(255, 255, 255, 0.18);
}
.modeCard.disabled:hover { background: rgba(255, 255, 255, 0.04); border-color: rgba(255, 255, 255, 0.15); transform: none; }
.modeCard {
  --accent: var(--blue);
  text-align: left;
  padding: 18px 18px 16px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-top: 2px solid var(--accent);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  cursor: pointer;
  font-family: inherit;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 96px;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.modeCard:hover { background: rgba(255, 255, 255, 0.09); border-color: rgba(255, 255, 255, 0.25); border-top-color: var(--accent); transform: translateY(-2px); }
.modeCard.accent-teal { --accent: var(--teal); }
.modeCard.accent-blue { --accent: var(--blue); }
.modeCard.accent-violet { --accent: var(--violet); }
.modeCard.accent-red { --accent: var(--red); }
.modeCard.accent-gold { --accent: var(--gold); }
.modeCard.accent-slate { --accent: var(--slate); }
.modeCard.accent-green { --accent: var(--green); }
.modeCard.accent-orange { --accent: var(--orange); }
.modeCard.accent-rose { --accent: var(--rose); }
.modeCardTop { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.modeCardIcon {
  position: relative; /* anchors a badge like #challengeBadge on the 1v1 Ladder card */
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.08); /* fallback for browsers without color-mix() */
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  font-size: 1.05rem;
}
.modeCardBadge {
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--accent);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.modeCardTitle { font-weight: 700; font-size: 1.12rem; }
.modeCardSubtitle { font-size: 0.83rem; color: rgba(255, 255, 255, 0.6); line-height: 1.35; flex: 1; }
.modeCardFooter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.modeCardMeta { font-size: 0.72rem; color: rgba(255, 255, 255, 0.4); }
.modeCardCta { font-size: 0.8rem; font-weight: 700; color: var(--accent); }

/* --- "Live games" panel: every public room across every mode, right on
   the landing page (see renderRoomList(landingRoomListEl, ...) in
   client.js) — reuses .roomList/.roomRow wholesale, same as the
   matchmaking screen's own list. --- */
.liveGamesSection { margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--line); }
.liveGamesHeader { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.liveGamesHeader h3 { margin: 0; font-size: 1.02rem; display: flex; align-items: center; gap: 8px; }
.lobbyHint { color: rgba(255, 255, 255, 0.6); font-size: 0.88rem; }
.primary, .secondary {
  padding: 10px 16px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: var(--text);
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
}
/* Class-scoped (not button-scoped) so an <a> styled as a button - the
   Discord invite link in the topbar - looks identical to a real one. */
a.primary, a.secondary { text-decoration: none; display: inline-block; box-sizing: border-box; }
.primary { background: linear-gradient(135deg, var(--blue), #2f6fd6); border-color: transparent; }
.primary:hover { filter: brightness(1.1); }
.secondary { background: rgba(255, 255, 255, 0.07); width: 100%; margin-top: 4px; }
.secondary:hover { background: rgba(255, 255, 255, 0.13); }
.lobbyColumns { display: grid; grid-template-columns: 1.3fr 1fr; gap: 20px; align-items: start; }
.lobbyList h3, .lobbyForms h3 {
  margin: 0 0 10px;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.55);
}
.roomList { display: flex; flex-direction: column; gap: 8px; }
.roomListEmpty { color: rgba(255, 255, 255, 0.45); font-size: 0.9rem; padding: 10px 0; }
.roomRow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.045);
  cursor: pointer;
}
.controlsSection { margin-bottom: 16px; }
.toggleWrap { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-weight: 600; }
.toggleWrap input { width: 18px; height: 18px; accent-color: #4a9eff; cursor: pointer; }
#setThrowRingOpacity { width: 160px; accent-color: #4a9eff; }
.controlsSection h4 { margin: 0 0 8px; }
.controlRow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
}
.controlLabel { flex: 1; color: rgba(255, 255, 255, 0.85); }
.keyChip {
  min-width: 76px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(255, 255, 255, 0.08);
  color: inherit;
  font: inherit;
  font-weight: 700;
  text-align: center;
  cursor: pointer;
}
.keyChip:hover { background: rgba(255, 255, 255, 0.15); }
.keyChip.capturing {
  border-color: rgba(59, 130, 246, 0.8);
  background: rgba(59, 130, 246, 0.2);
  animation: keychip-pulse 1s ease-in-out infinite;
}
@keyframes keychip-pulse { 50% { background: rgba(59, 130, 246, 0.38); } }
.macroPhraseSelect { flex: 1; min-width: 0; }
.macroRemoveBtn { flex-shrink: 0; }
.controlsHint { color: rgba(255, 255, 255, 0.55); font-size: 0.85rem; margin: 6px 0 0; }
.controlsNotice { color: #fca5a5; font-size: 0.9rem; margin: 8px 0; }
.controlsFooter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.shopHatPreview {
  width: 64px;
  height: 64px;
  display: block;
  margin: 0 auto;
}
.updateToast {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 10px;
  border: 1px solid rgba(59, 130, 246, 0.5);
  background: rgba(15, 30, 52, 0.95);
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.9rem;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
}
.updateToast button {
  padding: 6px 14px;
  border-radius: 8px;
  border: 1px solid rgba(59, 130, 246, 0.6);
  background: rgba(59, 130, 246, 0.25);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.updateToast button:hover { background: rgba(59, 130, 246, 0.4); }
.playtestBanner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding: 9px 14px;
  border-radius: 10px;
  border: 1px solid rgba(250, 204, 21, 0.35);
  background: rgba(250, 204, 21, 0.08);
  color: rgba(255, 255, 255, 0.85);
  font-size: 0.9rem;
}
.playtestBadge {
  flex-shrink: 0;
  padding: 2px 8px;
  border-radius: 6px;
  background: rgba(250, 204, 21, 0.2);
  border: 1px solid rgba(250, 204, 21, 0.5);
  color: #fde047;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.roomRow:hover { background: rgba(255, 255, 255, 0.09); }
.roomRow .roomName { font-weight: 600; }
.roomRow .roomMeta { color: rgba(255, 255, 255, 0.6); font-size: 0.85rem; }
/* A match in progress: the row itself isn't a click target (its buttons
   are), and it carries a LIVE badge + Spectate/Join actions instead of a
   bare code. */
.roomRow.live { cursor: default; }
.roomRow.live:hover { background: rgba(255, 255, 255, 0.045); }
.liveBadge {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 6px;
  background: rgba(239, 68, 68, 0.18);
  border: 1px solid rgba(239, 68, 68, 0.45);
  color: #fca5a5;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  vertical-align: 1px;
}
.roomRowActions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.roomBtn {
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.07);
  color: inherit;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.roomBtn:hover { background: rgba(255, 255, 255, 0.14); }
.roomBtn.primaryish {
  background: rgba(59, 130, 246, 0.22);
  border-color: rgba(59, 130, 246, 0.55);
}
.roomBtn.primaryish:hover { background: rgba(59, 130, 246, 0.34); }
.lobbyForms { display: flex; flex-direction: column; gap: 18px; }
.lobbyForms form {
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.03);
}
.lobbyForms input[type="text"], .lobbyForms input:not([type]), .lobbyForms input[type="checkbox"] + label, select.teamSizeSelect,
.lobbyForms input, .lobbyForms select {
  font-family: inherit;
  font-size: 0.92rem;
}
.lobbyForms input, .lobbyForms select {
  width: 100%;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(0, 0, 0, 0.25);
  color: var(--text);
  margin-top: 4px;
}
.lobbyForms .fieldRow { display: block; font-size: 0.85rem; color: rgba(255, 255, 255, 0.7); margin-top: 8px; }
.lobbyForms .checkRow { display: flex; align-items: center; gap: 6px; }
.lobbyForms .checkRow input { width: auto; margin: 0; }
.codeInput { text-transform: uppercase; letter-spacing: 0.12em; text-align: center; }
.lobbyError {
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 90, 90, 0.4);
  background: rgba(255, 90, 90, 0.12);
  color: #ffb3b3;
  font-size: 0.88rem;
}

/* --- matchmaking screen (Casual/Ranked "next screen", reuses .lobbyColumns/
   .lobbyList/.lobbyForms/.roomList above wholesale — just relocated out of
   the old single-screen lobby into its own dedicated screen) --- */
.matchmakingScreen { padding: 18px; }
.matchmakingQuickBtn { width: 100%; margin-bottom: 14px; }
.matchmakingGuestGate {
  text-align: center;
  padding: 32px 20px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
}
.matchmakingGuestGate .lobbyHint { display: block; margin-bottom: 14px; }
/* Two-provider sign-in row (Discord + Google), reused anywhere a guest
   gate offers both — matchmaking's ranked gate, the ladder join row,
   Academy's nudges. Buttons keep their own .primary/.secondary sizing;
   this just lays them out side by side and lets them wrap on narrow
   screens instead of overflowing. */
.authButtonRow { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.authButtonRow .primary, .authButtonRow .secondary { width: auto; margin-top: 0; }
/* --- waiting room + draft screen --- */
.waitingRoom, .draftScreen { padding: 18px; }
.waitingHeader { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.waitingTitle { font-size: 1.05rem; font-weight: 700; }
.waitingMeta { color: rgba(255, 255, 255, 0.6); font-size: 0.85rem; margin-top: 3px; }
.waitingMeta strong { color: var(--text); letter-spacing: 0.1em; }
.waitingActions { display: flex; align-items: center; gap: 12px; margin-top: 18px; flex-wrap: wrap; }
.waitingActions .secondary { width: auto; margin-top: 0; }
button.primary:disabled { opacity: 0.4; cursor: not-allowed; filter: none; }
.draftTimer {
  font-variant-numeric: tabular-nums;
  font-size: 1.6rem;
  font-weight: 800;
  min-width: 2.2ch;
  text-align: center;
  color: var(--text);
}
.draftTimer.urgent { color: var(--red); }

.rosterColumns { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.rosterTeam h3 { margin: 0 0 10px; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
.rosterTeam.blue h3 { color: var(--blue); }
.rosterTeam.red h3 { color: var(--red); }
.rosterList { display: flex; flex-direction: column; gap: 8px; }
.rosterRow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.045);
  font-size: 0.9rem;
}
.rosterRow.me { border-color: rgba(255, 255, 255, 0.35); background: rgba(255, 255, 255, 0.09); }
.rosterRow .rosterClass { color: rgba(255, 255, 255, 0.6); font-size: 0.85rem; }
.rosterRow .rosterClass.picked { color: var(--text); font-weight: 600; }
.rosterEmpty { color: rgba(255, 255, 255, 0.35); font-style: italic; }

.classGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-top: 20px; }
.classCard {
  text-align: left;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  cursor: pointer;
  font-family: inherit;
}
.classCard:hover { background: rgba(255, 255, 255, 0.09); }
.classCard.selected { border-color: var(--blue); background: rgba(74, 158, 255, 0.14); }
.classCard:disabled { opacity: 0.45; cursor: not-allowed; }
.classCard .className { font-weight: 700; margin-bottom: 4px; }
.classCard .classBlurb { font-size: 0.82rem; color: rgba(255, 255, 255, 0.65); line-height: 1.35; }

.mapVoteScreen { padding: 18px; }
.mapVoteOptions { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 20px; }
.mapVoteCard {
  text-align: center;
  padding: 24px 16px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  cursor: pointer;
  font-family: inherit;
}
.mapVoteCard:hover { background: rgba(255, 255, 255, 0.09); }
.mapVoteCard.selected { border-color: var(--blue); background: rgba(74, 158, 255, 0.14); }
.mapVoteCard .mapVoteName { font-size: 1.1rem; font-weight: 700; margin-bottom: 8px; }
.mapVoteCard .mapVoteCount { font-size: 0.85rem; color: rgba(255, 255, 255, 0.6); }
.mapVoteCard .mapVoteCount strong { color: var(--text); font-size: 1.3rem; display: block; }

/* --- coins, daily tasks, Cappit Shop --- */
.coinBalance {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 4px 9px;
  border-radius: 999px;
  background: rgba(255, 210, 74, 0.14);
  border: 1px solid rgba(255, 210, 74, 0.35);
  color: #ffd24a;
  font-size: 0.85rem;
  font-weight: 700;
}

.modalOverlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 16, 0.72);
  padding: 20px;
}
.modalCard {
  width: min(680px, 100%);
  max-height: 85vh;
  overflow-y: auto;
  border-radius: 16px;
  border: 1px solid var(--line);
  background: var(--panel);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.45);
  padding: 18px 20px 22px;
}
.modalHeader { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.modalHeader h3 { margin: 0; flex: 1; font-size: 1.1rem; }
.modalClose {
  border: none;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  font-size: 1.1rem;
  cursor: pointer;
  line-height: 1;
}
.modalClose:hover { background: rgba(255, 255, 255, 0.16); }

.taskRow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.03);
  margin-bottom: 8px;
}
.taskRow.completed { border-color: rgba(124, 255, 158, 0.4); background: rgba(124, 255, 158, 0.08); }
.taskRow .taskInfo { flex: 1; }
.taskRow .taskLabel { font-weight: 600; }
.taskRow .taskMeta { font-size: 0.8rem; color: rgba(255, 255, 255, 0.55); margin-top: 2px; }
.taskRow .taskDifficulty {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
}
.taskRow .taskReward { color: #ffd24a; font-weight: 700; white-space: nowrap; }
.streakInfo {
  margin-top: 14px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.04);
  font-size: 0.88rem;
  color: rgba(255, 255, 255, 0.75);
}

.shopSection h4 { margin: 16px 0 10px; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.55); }
.shopSection:first-child h4 { margin-top: 0; }
.shopGrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.shopItem {
  text-align: center;
  padding: 14px 10px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
  font-family: inherit;
  color: var(--text);
}
.shopItem:hover { background: rgba(255, 255, 255, 0.08); }
.shopItem.owned { border-color: rgba(124, 255, 158, 0.4); }
.shopItem.equipped { border-color: var(--blue); background: rgba(74, 158, 255, 0.14); }
.shopItem .shopItemIcon { font-size: 1.6rem; margin-bottom: 6px; }
.shopItem .shopItemSwatch { width: 28px; height: 28px; border-radius: 50%; margin: 0 auto 6px; border: 2px solid rgba(255, 255, 255, 0.3); }
.shopItem .shopItemName { font-size: 0.85rem; font-weight: 600; }
.shopItem .shopItemPrice { font-size: 0.78rem; color: #ffd24a; margin-top: 3px; }
.shopItem .shopItemStatus { font-size: 0.72rem; color: rgba(124, 255, 158, 0.85); margin-top: 3px; }
/* Earned-only items (graduation caps) before they're earned — a "how to
   get this" line instead of a price, and no buy affordance. */
.shopItem .shopItemEarned { color: rgba(255, 255, 255, 0.55); }
.shopItem.lockedEarned { cursor: default; opacity: 0.75; }
.shopItem.lockedEarned:hover { transform: none; }
.shopSectionHint { text-transform: none; letter-spacing: normal; font-weight: 400; }
/* Settings → Voice: ranked, draggable list of every voice pack */
.voiceOrder { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.voiceRow {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.03);
  position: relative;
}
.voiceRow[draggable="true"] { cursor: grab; }
.voiceRow.dragging { opacity: 0.45; }
.voiceRow.dropAbove::before, .voiceRow.dropBelow::after {
  content: ""; position: absolute; left: 6px; right: 6px; height: 2px; background: var(--blue); border-radius: 2px;
}
.voiceRow.dropAbove::before { top: -3px; }
.voiceRow.dropBelow::after { bottom: -3px; }
.voiceGrip { color: rgba(255, 255, 255, 0.35); font-size: 0.9rem; letter-spacing: -2px; user-select: none; }
.voiceRank { width: 20px; text-align: right; color: rgba(255, 255, 255, 0.5); font-variant-numeric: tabular-nums; font-weight: 700; font-size: 0.85rem; }
.voiceName { flex: 1; font-weight: 600; }
.voiceTag { display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 4px; font-size: 0.65rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; background: rgba(56, 189, 248, 0.18); color: #7dd3fc; vertical-align: 1px; }
.voicePlayBtn, .voiceMoveBtn {
  width: 26px; height: 26px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.2);
  background: rgba(255, 255, 255, 0.06); color: var(--text); cursor: pointer; font-size: 0.7rem; line-height: 1;
}
.voicePlayBtn:hover, .voiceMoveBtn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.14); }
.voiceMoveBtn:disabled { opacity: 0.3; cursor: default; }
.voiceArrows { display: inline-flex; gap: 4px; }
.segmented { display: inline-flex; border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 8px; overflow: hidden; }
.segBtn { border: 0; background: transparent; color: rgba(255, 255, 255, 0.7); padding: 6px 12px; font: inherit; font-size: 0.85rem; font-weight: 600; cursor: pointer; }
.segBtn + .segBtn { border-left: 1px solid rgba(255, 255, 255, 0.16); }
.segBtn.active { background: rgba(74, 158, 255, 0.2); color: #fff; }
.blockedNote { background: #7a1620; color: #fff; padding: 10px 16px; font-size: 0.9rem; text-align: center; }
.tickerScope, .vgsScope {
  display: inline-block; margin-right: 6px; padding: 0 5px; border-radius: 3px;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; vertical-align: 1px;
  background: rgba(255, 255, 255, 0.12); color: rgba(255, 255, 255, 0.7);
}
.vgsScope { margin-left: 6px; margin-right: 0; }
.voicePackGrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.voicePackItem {
  display: flex;
  align-items: center;
  gap: 6px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.03);
  padding: 4px;
}
.voicePackItem.equipped { border-color: var(--blue); background: rgba(74, 158, 255, 0.14); }
.voicePackSelectBtn {
  flex: 1;
  text-align: center;
  padding: 10px 6px;
  border: none;
  background: none;
  color: var(--text);
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}
.voicePackPreviewBtn {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  cursor: pointer;
  font-size: 0.7rem;
  line-height: 1;
}
.voicePackPreviewBtn:hover { background: rgba(255, 255, 255, 0.14); }

/* --- 1v1 challenge ladder — the badge itself lives on the 1v1 Ladder
   mode card's icon now (.modeCardIcon is position:relative for exactly
   this), not a dedicated topbar button. --- */
.challengeBadge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--red);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}
.ladderSection h4 { margin: 16px 0 10px; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.55); }
.ladderSection:first-child h4 { margin-top: 0; }
.challengeRow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.03);
  margin-bottom: 8px;
}
.challengeRow .challengeInfo { flex: 1; }
.challengeRow .challengeName { font-weight: 600; }
.challengeRow .challengeMeta { font-size: 0.8rem; color: rgba(255, 255, 255, 0.55); margin-top: 2px; }
.challengeRow .challengeActions { display: flex; gap: 6px; }
.ladderTable { width: 100%; border-collapse: collapse; }
.ladderTable th {
  text-align: left;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.5);
  padding: 6px 10px;
}
.ladderTable td { padding: 8px 10px; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.ladderTable tr.me { background: rgba(74, 158, 255, 0.1); }
.ladderRank { font-weight: 700; width: 40px; }
.ladderTable .ladderAvatar { width: 22px; height: 22px; border-radius: 50%; vertical-align: middle; margin-right: 8px; }
.ladderRecord { color: rgba(255, 255, 255, 0.55); font-size: 0.85rem; white-space: nowrap; }
.ladderJoinRow { text-align: center; padding: 20px; }

/* --- Tournaments — same section-header/row/table conventions as the 1v1
   ladder just above (.ladderSection h4/.challengeRow/.ladderTable), kept
   as their own class names since a tournament row/match has a genuinely
   different shape (an entrant pair + a winner, not a challenge or a
   ranked player), not because the visual language should differ. --- */
.tournamentListRow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.03);
  margin-bottom: 8px;
  cursor: pointer;
}
.tournamentListRow:hover { background: rgba(255, 255, 255, 0.08); }
.tournamentListRow .tournamentListInfo { flex: 1; }
.tournamentListRow .tournamentListName { font-weight: 600; }
.tournamentListRow .tournamentListMeta { font-size: 0.8rem; color: rgba(255, 255, 255, 0.55); margin-top: 2px; }
.tournamentStatusPill {
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.tournamentStatusPill.status-registration { background: rgba(45, 212, 191, 0.16); color: var(--teal); }
.tournamentStatusPill.status-in_progress { background: rgba(251, 113, 133, 0.16); color: var(--rose); }
.tournamentStatusPill.status-completed { background: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.6); }
.tournamentStatusPill.status-cancelled { background: rgba(255, 90, 90, 0.14); color: var(--red); }
.tournamentBackRow { margin-bottom: 12px; }
.tournamentMyMatch {
  margin: 0 0 16px;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid rgba(251, 113, 133, 0.35);
  background: rgba(251, 113, 133, 0.08);
}
.tournamentMyMatch p { margin: 0 0 8px; font-size: 0.88rem; }
.tournamentMyMatch p:last-child { margin-bottom: 0; }
.tournamentRoundGroup h4 { margin: 16px 0 10px; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: rgba(255, 255, 255, 0.55); }
.tournamentRoundGroup:first-child h4 { margin-top: 0; }
/* double_elim's three sections (Winners/Losers/Grand Final) — each one's
   own round groups nest inside, so .tournamentRoundGroup:first-child's
   margin-top:0 rule above already applies correctly per-section. */
.tournamentBracketSection { margin-top: 20px; padding-top: 16px; border-top: 1px solid rgba(255, 255, 255, 0.1); }
.tournamentBracketSection:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.tournamentBracketSection h3 { margin: 0; font-size: 0.95rem; color: var(--text); }
.tournamentMatchRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  margin-bottom: 6px;
  font-size: 0.88rem;
}
.tournamentMatchRow .tournamentSide { flex: 1; }
.tournamentMatchRow .tournamentSide.winner { font-weight: 700; color: #7adc96; }
.tournamentMatchRow .tournamentVs { color: rgba(255, 255, 255, 0.4); font-size: 0.75rem; padding: 0 4px; }
.tournamentMatchRow.mine { border-color: rgba(251, 113, 133, 0.4); background: rgba(251, 113, 133, 0.06); }
.tournamentTable { width: 100%; border-collapse: collapse; margin-bottom: 16px; }
.tournamentTable th { text-align: left; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: rgba(255, 255, 255, 0.5); padding: 6px 10px; }
.tournamentTable td { padding: 8px 10px; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.tournamentTable tr.me { background: rgba(251, 113, 133, 0.1); }
.tournamentCreateForm { border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 12px; padding: 12px 14px; background: rgba(255, 255, 255, 0.03); margin-bottom: 16px; }
.tournamentCreateForm h4 { margin: 0 0 10px; font-size: 0.85rem; }
.tournamentCreateForm .lobbyError { margin-top: 8px; }
.tournamentFormActions { margin-top: 10px; display: flex; gap: 10px; }
.tournamentDetailTitle { margin: 0 0 4px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tournamentDetailMeta { margin: 0 0 14px; }
.tournamentFormGrid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; }
.tournamentFormGrid .fieldRow { grid-column: span 1; }
.tournamentFormGrid .fieldRow.span2 { grid-column: span 2; }
@media (max-width: 520px) {
  .tournamentFormGrid { grid-template-columns: 1fr; }
  .tournamentFormGrid .fieldRow.span2 { grid-column: span 1; }
}

#chooseUsernameInput {
  width: 100%;
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(0, 0, 0, 0.25);
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
}
#chooseUsernameError { margin-top: 10px; }

/* --- end-of-match scoreboard --- */
.scoreboardFinalScore { font-weight: 700; font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.75); }
.scoreboardTable th:first-child, .scoreboardTable td:first-child { text-align: left; }
.scoreboardTable th, .scoreboardTable td { text-align: center; }
.scoreboardTeam.blue tr:first-child td { border-top: 2px solid var(--blue); }
.scoreboardTeam.red tr:first-child td { border-top: 2px solid var(--red); }

/* --- bots / practice mode --- */
.botChip {
  display: inline-block;
  margin-left: 6px;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.6);
  vertical-align: middle;
}
.practiceOptionRow { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.practiceOptionRow label { font-size: 0.8rem; color: rgba(255, 255, 255, 0.6); }
.difficultyCards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 16px; }
/* Survivor's side-picker reuses .difficultyCard's look for a 2-up choice
   (Attacker/Defender) instead of the 3 difficulty tiers — same class,
   just a 2-column grid so there's no empty third slot. */
#survivorTeamCards { grid-template-columns: repeat(2, 1fr); }
.difficultyCard {
  padding: 10px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
  text-align: center;
}
.difficultyCard.selected { border-color: var(--blue); background: rgba(74, 158, 255, 0.12); }
.difficultyCard .difficultyLabel { font-weight: 700; }
.difficultyCard .difficultyDetail { font-size: 0.72rem; color: rgba(255, 255, 255, 0.5); margin-top: 2px; }

/* --- replays --- */
.replayBar {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 8px 14px;
  border-radius: 16px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(7, 16, 30, 0.85);
  backdrop-filter: blur(6px);
  max-width: 94%;
}
.replayBarRow { display: flex; align-items: center; gap: 8px; }
.replayBar .audioBtn { flex: none; }
.replayBar .replayExitBtn { flex: none; padding: 6px 12px; font-size: 0.82rem; white-space: nowrap; }
/* Recording disables everything except whatever the JS leaves enabled
   (the status text) — a real MediaRecorder pass is happening in real time
   underneath, and letting the viewer scrub/change speed/exit mid-capture
   would corrupt it. */
.replayBar.recording .replayBarRow > *:not(.replayClipStatus) { opacity: 0.45; pointer-events: none; }

.replayScrubWrap { position: relative; width: 220px; flex: none; }
.replayScrub { position: relative; z-index: 1; width: 100%; display: block; margin: 0; }
.replayScrubOverlay { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.replayClipRangeBand {
  position: absolute;
  top: 50%;
  height: 6px;
  transform: translateY(-50%);
  border-radius: 3px;
  background: rgba(74, 158, 255, 0.3);
  border: 1px solid rgba(74, 158, 255, 0.55);
}
.replayCapTicks { position: absolute; inset: 0; }
.replayCapTick {
  position: absolute;
  top: 50%;
  width: 3px;
  height: 14px;
  transform: translate(-50%, -50%);
  border-radius: 1px;
  cursor: pointer;
  pointer-events: auto;
}
.replayCapTick.blue { background: var(--blue); box-shadow: 0 0 0 1px rgba(7, 16, 30, 0.6); }
.replayCapTick.red { background: var(--red); box-shadow: 0 0 0 1px rgba(7, 16, 30, 0.6); }
.replayCapTick:hover { filter: brightness(1.35); transform: translate(-50%, -50%) scaleY(1.15); }
.replayClipHandle {
  position: absolute;
  top: 50%;
  width: 10px;
  height: 20px;
  transform: translate(-50%, -50%);
  border-radius: 3px;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.35);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  cursor: ew-resize;
  pointer-events: auto;
}
.replayClipHandle:hover, .replayClipHandle:focus-visible { background: #ffd24a; outline: none; }
.replayClipHandle.dragging { background: #ffd24a; }

.replayTimeLabel { font-size: 0.78rem; font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, 0.75); white-space: nowrap; }
.replaySpeed {
  background: rgba(255, 255, 255, 0.08);
  color: var(--text);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  font-size: 0.78rem;
  padding: 3px 4px;
}
.replayClipRow { justify-content: center; flex-wrap: wrap; row-gap: 6px; }
.replayClipLabel { font-size: 0.78rem; color: rgba(255, 255, 255, 0.65); display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.replayClipLabel input {
  width: 44px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 5px;
  color: var(--text);
  padding: 2px 4px;
  font-size: 0.78rem;
}
.replayClipDuration { font-size: 0.72rem; color: rgba(255, 255, 255, 0.5); white-space: nowrap; }
.replayClipActionBtn { flex: none; padding: 6px 12px; font-size: 0.8rem; white-space: nowrap; }
.replayClipStatus { font-size: 0.75rem; white-space: nowrap; color: rgba(255, 255, 255, 0.7); }
.replayClipStatus.error { color: var(--red); }
.replayClipStatus.success { color: #7adc96; }

.replayRow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.045);
  cursor: pointer;
  margin-bottom: 8px;
}
.replayRow:hover { background: rgba(255, 255, 255, 0.09); }
.replayRow .replayScore { font-weight: 700; }
.replayRow .replayScore .blue { color: var(--blue); }
.replayRow .replayScore .red { color: var(--red); }
.replayRow .replayMeta { color: rgba(255, 255, 255, 0.6); font-size: 0.85rem; margin-top: 2px; }
.replayTag {
  display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 4px;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; vertical-align: 2px;
  background: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.75);
}
.replayTag + .replayTag { margin-left: 4px; }
.replayTag.sport-ultimate { background: rgba(56, 189, 248, 0.18); color: #7dd3fc; }
.replayTag.sport-soccer { background: rgba(74, 222, 128, 0.18); color: #86efac; }
.replayTag.sport-ctf, .replayTag.sport-neutralFlag { background: rgba(251, 191, 36, 0.18); color: #fcd34d; }
.replayFilters { display: flex; flex-direction: column; gap: 6px; margin: 0 0 10px; }
.replayChipRow { display: flex; flex-wrap: wrap; gap: 6px; }
.replayChip {
  border: 1px solid rgba(255, 255, 255, 0.16); background: transparent; color: rgba(255, 255, 255, 0.7);
  border-radius: 999px; padding: 3px 10px; font-size: 0.78rem; font-weight: 600; cursor: pointer;
}
.replayChip:hover { border-color: rgba(255, 255, 255, 0.35); color: #fff; }
.replayChip.active { background: rgba(255, 255, 255, 0.14); border-color: rgba(255, 255, 255, 0.4); color: #fff; }

/* --- Academy tutorial ---
   Redesigned after looking at a reference TagPro Academy site the user
   pointed at — same spirit (a squared-off "gaming HUD" display face for
   titles, a single muted category-badge color instead of a rainbow per
   tag, clearer Goal/Tips structure) reusing Cappit's OWN existing Academy
   identity color (--teal, already Academy's mode-card accent on the
   lobby — see .modeCard.accent-teal) as the through-line rather than
   inventing a new one. */
.academyScreen { padding: 18px; }
.academyHeader { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
.academyHeader h2 { margin: 0; font-size: 1.3rem; font-family: "Oxanium", Inter, system-ui, sans-serif; font-weight: 700; letter-spacing: 0.01em; flex: 1; }
.academyProgressSummary { color: rgba(255, 255, 255, 0.6); font-size: 0.85rem; }
/* Track tabs — one per academy (CTF / Ultimate / Soccer), pill-style,
   sitting between the header and the lesson grid. */
.academyTabs { display: flex; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
.academyTab {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  font-family: inherit; font-size: 0.88rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.academyTab:hover { background: rgba(255, 255, 255, 0.09); }
.academyTab.active { border-color: var(--teal); background: rgba(66, 214, 188, 0.12); }
.academyTab .academyTabCount { color: rgba(255, 255, 255, 0.55); font-size: 0.78rem; }
/* Guest-only "sign in to save this" banner atop the lesson grid — same
   quiet-box treatment as .matchmakingGuestGate, just lower-key (this one
   isn't blocking anything, so it shouldn't compete with the grid below). */
.academyGuestBanner {
  text-align: center;
  padding: 14px 16px;
  margin-bottom: 16px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.03);
}
.academyGuestBanner .lobbyHint { display: block; margin-bottom: 10px; }
.academyLessonGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; }
.lessonCard {
  position: relative;
  text-align: left;
  padding: 15px 16px 16px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  cursor: pointer;
  font-family: inherit;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: background 0.15s, border-color 0.15s, transform 0.15s;
  overflow: hidden;
}
/* A thin top accent bar ties every drill card back to Academy's own
   teal identity without tinting the whole card — visible on hover/
   completion, quiet otherwise so an untouched grid doesn't look striped. */
.lessonCard::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--teal); opacity: 0; transition: opacity 0.15s;
}
.lessonCard:hover { background: rgba(255, 255, 255, 0.09); transform: translateY(-1px); }
.lessonCard:hover::before { opacity: 0.55; }
.lessonCard.completed { border-color: rgba(120, 220, 150, 0.4); }
.lessonCard.completed::before { opacity: 0.85; background: #7adc96; }
.lessonCard .lessonCardTop { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.lessonCard .lessonNumber {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: rgba(255, 255, 255, 0.5);
  font-family: "Oxanium", Inter, system-ui, sans-serif; font-weight: 700;
}
.lessonCard .lessonTitle { font-weight: 700; font-size: 1.05rem; font-family: "Oxanium", Inter, system-ui, sans-serif; letter-spacing: 0.005em; }
.lessonCard .lessonSubtitle { font-size: 0.82rem; color: rgba(255, 255, 255, 0.6); line-height: 1.35; }
.lessonCard .lessonMeta { display: flex; gap: 8px; align-items: center; margin-top: 4px; flex-wrap: wrap; }
/* Deliberately ONE muted color for every category (MOVEMENT/OFFENSE/
   CHASE/etc.), not a different hue per tag — the reference site's own
   badges read as "this is a category label," not as competing brand
   colors fighting the teal accent above. */
.lessonBadge {
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600;
  padding: 3px 8px; border-radius: 999px;
  background: rgba(199, 168, 242, 0.16); color: #d9c2f7;
}
/* Beginner/Intermediate/Advanced — a quiet traffic-light progression
   (teal -> amber -> red) rather than another badge fighting for
   attention; text stays neutral, only the dot carries the color. */
.lessonDifficulty { font-size: 0.68rem; color: rgba(255, 255, 255, 0.55); display: inline-flex; align-items: center; gap: 5px; }
.lessonDifficulty::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.lessonDifficulty.diff-beginner { color: var(--teal); }
.lessonDifficulty.diff-intermediate { color: #ffcc66; }
.lessonDifficulty.diff-advanced { color: var(--red); }
.lessonCard .lessonDone { color: #7adc96; font-weight: 700; font-size: 0.78rem; white-space: nowrap; }
.lessonCard .lessonBestTime { font-size: 0.72rem; color: rgba(255, 255, 255, 0.5); font-variant-numeric: tabular-nums; }

.academyBar {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  /* above the game-mode .topbar (z-index 20), an invisible full-width
     strip that otherwise swallows every click on Exit/Retry up here */
  z-index: 25;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(7, 16, 30, 0.85);
  backdrop-filter: blur(6px);
  max-width: 94%;
}
.academyLessonTitle { font-weight: 700; white-space: nowrap; }
.academyProgressText { color: rgba(255, 255, 255, 0.7); font-size: 0.85rem; white-space: nowrap; }
/* Every lesson has a real time limit that fails the attempt — this used
   to only ever show as part of ONE lesson's own progressText string
   ("Hold: Xs"); a dedicated element makes it visible for all of them at
   once instead of depending on what each lesson's own check() happens to
   say. Louder than .academyProgressText (bigger, tabular so the digits
   don't jitter the layout as they change) and shifts to red once the
   attempt is genuinely running out, mirroring the bomb-timer's own
   "get louder as it matters more" treatment. */
.academyTimer { font-weight: 700; font-size: 0.95rem; font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap; }
.academyTimer.urgent { color: var(--red); }

/* The "pop up goal" callout (see announceAcademyGoal in client.js) —
   sits just under .academyBar, out of the way of the actual play area,
   and is driven entirely by JS toggling this one "show" class: hidden/
   transparent/shifted-up by default, animates down+in on "show", holds,
   then the same transition reverses when the class is removed. No
   keyframe animation needed for the reveal itself since it's just a
   two-state transition; the "pop" feel comes from the ease-out curve and
   a slight overshoot via cubic-bezier rather than a separate keyframe. */
.academyGoalPopup {
  position: absolute;
  top: 64px;
  left: 50%;
  transform: translate(-50%, -10px);
  width: min(92%, 480px);
  padding: 10px 18px;
  border-radius: 10px;
  border: 1px solid rgba(45, 212, 191, 0.45);
  background: rgba(10, 30, 28, 0.92);
  backdrop-filter: blur(6px);
  color: var(--text);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.35;
  text-align: center;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease-out, transform 0.35s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.academyGoalPopup.show { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) {
  .academyGoalPopup { transition: opacity 0.2s linear; }
  .academyGoalPopup.show { transform: translate(-50%, 0); }
}

.academyCard {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  max-width: 440px;
  padding: 22px 24px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-top: 2px solid rgba(45, 212, 191, 0.5);
  background: rgba(7, 16, 30, 0.94);
  backdrop-filter: blur(8px);
  text-align: center;
}
.academyCard h3 { margin: 0 0 10px; font-size: 1.25rem; font-family: "Oxanium", Inter, system-ui, sans-serif; font-weight: 700; }
.academyCard.success h3 { color: #7adc96; }
.academyCard.fail h3 { color: var(--red); }
.academyCard p { color: rgba(255, 255, 255, 0.7); margin: 0 0 16px; line-height: 1.4; }
.academyCard .academyCardActions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.academyCardMeta { display: flex; gap: 8px; justify-content: center; margin-bottom: 10px; }
/* The pre-lesson "what am I actually trying to do" callout — set apart
   from the plain takeaway/tip text below with its own quiet box and a
   target icon, so it reads as THE goal rather than one more line of
   body copy (this was previously just a bare `<p><strong>Goal:</strong>`
   with no visual weight of its own). */
.academyGoalLine {
  display: flex; align-items: flex-start; gap: 10px; text-align: left;
  margin: 0 0 14px; padding: 10px 12px;
  border-radius: 10px; border: 1px solid rgba(45, 212, 191, 0.3); background: rgba(45, 212, 191, 0.08);
  color: rgba(255, 255, 255, 0.85); font-size: 0.9rem; line-height: 1.4;
}
.academyGoalLine strong { color: var(--teal); }
.academyGoalIcon { font-size: 1.05rem; line-height: 1.3; flex-shrink: 0; }
.academyTipsList {
  text-align: left; margin: 0 0 16px; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 7px;
  color: rgba(255, 255, 255, 0.72); font-size: 0.85rem; line-height: 1.4;
}
.academyTipsList li { position: relative; padding-left: 20px; }
.academyTipsList li::before { content: "\2713"; position: absolute; left: 0; color: var(--teal); font-weight: 700; }
/* The periodic guest sign-in nudge on a lesson's success card (see
   ACADEMY_SIGNIN_NUDGE_LESSON_NUMBERS) — visually set apart from the
   "Cleared!" celebration text above it as its own quiet, secondary box,
   not competing with the main "Next Lesson" action below. */
.academyNudge {
  margin: 0 0 16px;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.04);
}
.academyNudge p { margin: 0 0 10px; font-size: 0.85rem; }

/* Sign-in provider picker (see #signInModal) */
.signInModalCard { max-width: 380px; }
.signInProviderRow { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }

@media (max-width: 760px) {
  .topbar, .footer { flex-direction: column; align-items: flex-start; }
  .lobbyColumns, .rosterColumns { grid-template-columns: 1fr; }
  .heroBand { grid-template-columns: 1fr; }
  .heroBandRight { border-left: none; border-top: 1px solid var(--line); min-height: 0; }
  .heroLogo { width: min(100%, 220px); }
  /* Nav buttons rendered at visibly different sizes on mobile (mixed
     .secondary buttons, .secondary anchors, and .reset buttons, each with
     its own padding/line-height) — normalize the whole accountBar to one
     compact, consistent control size and let it wrap cleanly. */
  .topbarRight { align-items: stretch; width: 100%; }
  .accountBar { flex-wrap: wrap; gap: 6px; }
  .accountBar .secondary,
  .accountBar .reset,
  .accountBar .discordBtn,
  .accountBar .accountMenuBtn,
  .accountBar a.secondary {
    padding: 6px 10px;
    font-size: 0.8rem;
    line-height: 1.2;
    margin: 0;
    display: inline-flex;
    align-items: center;
    border-radius: 8px;
  }
  .accountSignedIn { flex-wrap: wrap; gap: 6px; }
}