/* Palette, typefaces and design tokens live in theme.css — loaded before this
   file by every page, so the standalone tools share one source of truth.
   --gold-dark is kept here as an alias: only in-game HUD rules still use it. */
:root { --gold-dark: var(--gold-deep); }

/* Base */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Every component used to declare its own `.hidden { display:none }`, which meant
   a missed declaration left a modal permanently on screen. One global rule now. */
.hidden { display: none !important; }

/* Sprite icons carry no intrinsic size — without this they render at the SVG
   default 300x150 and blow up whatever contains them. */
.nav-icon, .hero-icon, .nav-chevron, .hero-chevron, .ui-icon {
  display: block;
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
}

/* Scrollbars. The default white Windows bar was the loudest thing on the
   Options panel. Scoped to the out-of-game shell so the HUD keeps its own. */
.options-body, .customize-card-scroll, .stats-shell, .browser-container,
.lobby-list, .replay-list, #leaderboard-rows, .horn-modal-body, .horn-modal-card,
  .modal-card, .options-overlay, .modal-overlay, .tut-welcome-card, .customize-stage,
#screen-stats, #screen-replays, #screen-leaderboard, .menu-shell,
.chat-dock-roster, .chat-dock-log {
  scrollbar-width: thin;
  scrollbar-color: rgba(168, 212, 232, 0.28) transparent;
}
.options-body::-webkit-scrollbar, .customize-card-scroll::-webkit-scrollbar,
.stats-shell::-webkit-scrollbar, .browser-container::-webkit-scrollbar,
.lobby-list::-webkit-scrollbar, .replay-list::-webkit-scrollbar,
#leaderboard-rows::-webkit-scrollbar, .horn-modal-body::-webkit-scrollbar,
.horn-modal-card::-webkit-scrollbar, .modal-card::-webkit-scrollbar,
.options-overlay::-webkit-scrollbar, .modal-overlay::-webkit-scrollbar,
.tut-welcome-card::-webkit-scrollbar,
#screen-stats::-webkit-scrollbar, #screen-replays::-webkit-scrollbar,
#screen-leaderboard::-webkit-scrollbar, .menu-shell::-webkit-scrollbar,
.chat-dock-roster::-webkit-scrollbar, .chat-dock-log::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
.options-body::-webkit-scrollbar-track, .customize-card-scroll::-webkit-scrollbar-track,
.stats-shell::-webkit-scrollbar-track, .browser-container::-webkit-scrollbar-track,
.lobby-list::-webkit-scrollbar-track, .replay-list::-webkit-scrollbar-track,
#leaderboard-rows::-webkit-scrollbar-track, .horn-modal-body::-webkit-scrollbar-track,
.horn-modal-card::-webkit-scrollbar-track, .modal-card::-webkit-scrollbar-track,
.options-overlay::-webkit-scrollbar-track, .modal-overlay::-webkit-scrollbar-track,
.tut-welcome-card::-webkit-scrollbar-track,
#screen-stats::-webkit-scrollbar-track, #screen-replays::-webkit-scrollbar-track,
#screen-leaderboard::-webkit-scrollbar-track, .menu-shell::-webkit-scrollbar-track,
.chat-dock-roster::-webkit-scrollbar-track, .chat-dock-log::-webkit-scrollbar-track {
  background: transparent;
}
.options-body::-webkit-scrollbar-thumb, .customize-card-scroll::-webkit-scrollbar-thumb,
.stats-shell::-webkit-scrollbar-thumb, .browser-container::-webkit-scrollbar-thumb,
.lobby-list::-webkit-scrollbar-thumb, .replay-list::-webkit-scrollbar-thumb,
#leaderboard-rows::-webkit-scrollbar-thumb, .horn-modal-body::-webkit-scrollbar-thumb,
.horn-modal-card::-webkit-scrollbar-thumb, .modal-card::-webkit-scrollbar-thumb,
.options-overlay::-webkit-scrollbar-thumb, .modal-overlay::-webkit-scrollbar-thumb,
.tut-welcome-card::-webkit-scrollbar-thumb,
#screen-stats::-webkit-scrollbar-thumb, #screen-replays::-webkit-scrollbar-thumb,
#screen-leaderboard::-webkit-scrollbar-thumb, .menu-shell::-webkit-scrollbar-thumb,
.chat-dock-roster::-webkit-scrollbar-thumb, .chat-dock-log::-webkit-scrollbar-thumb {
  background: rgba(168, 212, 232, 0.22);
  border-radius: 4px;
}
.options-body::-webkit-scrollbar-thumb:hover, .customize-card-scroll::-webkit-scrollbar-thumb:hover,
.stats-shell::-webkit-scrollbar-thumb:hover, .replay-list::-webkit-scrollbar-thumb:hover,
#leaderboard-rows::-webkit-scrollbar-thumb:hover, #screen-stats::-webkit-scrollbar-thumb:hover {
  background: rgba(168, 212, 232, 0.4);
}

/* Focus is invisible today. One cold ring, everywhere, keyboard-only. */
:focus-visible {
  outline: 2px solid var(--ice);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Screens */
.screen {
  display: none;
  position: fixed;
  inset: 0;
  align-items: center;
  justify-content: center;
}
.screen.active { display: flex; }

/* Menu depth field. Sits behind everything in the menu and never takes input.
   Content inside .menu-shell already establishes its own stacking, so only the
   canvas needs pinning to the back. */
.menu-bg-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease;
}
.menu-bg-canvas.is-on { opacity: 1; }
/* The menu backdrop: pre-recorded arena loops (game.js menuArena*). Two ids outrank the generic
   "#screen-menu > *" stacking rule below, which would otherwise lift it over the menu. */
#screen-menu > #menu-arena {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.6s ease;
  overflow: hidden;
}
#screen-menu > #menu-arena.is-on { opacity: 1; }
#menu-arena .menu-arena-video { position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  object-fit: cover; opacity: 0; transition: opacity 1.4s ease; }
#menu-arena .menu-arena-video.is-front { opacity: 1; }
/* Keeps the menu readable over a bright arena: darker at the rails where the panels sit. */
#menu-arena::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 45%, rgba(4, 8, 14, 0.1) 0%, rgba(4, 8, 14, 0.58) 100%),
    linear-gradient(90deg, rgba(4, 8, 14, 0.55) 0%, rgba(4, 8, 14, 0.08) 38%, rgba(4, 8, 14, 0.08) 62%, rgba(4, 8, 14, 0.5) 100%);
}
/* Snow belongs outdoors: once the arena is up, the blizzard goes. */
#screen-menu.menu-video-on .menu-bg-canvas { opacity: 0; }
/* Excludes .top-banner: those are position:fixed full-width top banners; without
   the exclusion this id-specificity rule forces them relative, dropping them into
   the centered flex row so they render off to the left instead of across the top. */
#screen-menu > *:not(.menu-bg-canvas):not(.top-banner) { position: relative; z-index: 1; }
/* Decoration only — motion-sensitive users lose nothing by not having it. */
@media (prefers-reduced-motion: reduce) {
  .menu-bg-canvas { display: none; }
}

/* Game: mouse is for aim/shoot — don't highlight HUD, faceoff timer, etc. Chat stays selectable. */
#screen-game.active #game-content {
  -webkit-user-select: none;
  user-select: none;
}
#screen-game.active #game-content input,
#screen-game.active #game-content textarea,
#screen-game.active #game-content [contenteditable="true"] {
  -webkit-user-select: text;
  user-select: text;
}
#screen-game.active #game-chat-messages,
#screen-game.active #game-chat-history-list {
  -webkit-user-select: text;
  user-select: text;
}

/* ===========================================================================
   ARENA BACKDROP
   ---------------------------------------------------------------------------
   The out-of-game UI is the ice sheet seen from above at arena-night lighting,
   with the interface laid over it like a broadcast overlay. Everything here is
   CSS gradients — no images, no canvas, no per-frame cost.

   Geometry, not decoration: one faceoff circle (cropped by the viewport, so it
   reads as part of a larger rink), the center red line, and a rim of cold light
   from the roof. This is the one place the design is allowed to be bold.
   =========================================================================== */
.arena-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background:
    /* roof light pooling toward the center of the sheet */
    radial-gradient(120% 90% at 50% 8%, rgba(92, 150, 190, 0.16) 0%, transparent 55%),
    /* cold bounce off the ice at the bottom */
    radial-gradient(80% 55% at 50% 108%, rgba(60, 110, 150, 0.13) 0%, transparent 60%),
    linear-gradient(178deg, #0a121d 0%, var(--ink) 58%, #04070c 100%);
}
/* Faceoff circle — red ring, cropped left so it feels like rink geometry
   continuing past the frame rather than a centered graphic. */
.arena-bg::before {
  content: '';
  position: absolute;
  left: -14vmax;
  top: 50%;
  width: 78vmax;
  height: 78vmax;
  margin-top: -39vmax;
  border-radius: 50%;
  border: 2px solid rgba(226, 69, 63, 0.09);
  box-shadow: inset 0 0 0 1px rgba(226, 69, 63, 0.035);
  animation: arenaDrift 52s ease-in-out infinite alternate;
}
/* Center red line + a blue line, both barely there. */
.arena-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, transparent 0 calc(50% - 1px), rgba(226, 69, 63, 0.09) calc(50% - 1px) calc(50% + 1px), transparent calc(50% + 1px)),
    linear-gradient(90deg, transparent 0 calc(22% - 1px), rgba(62, 134, 232, 0.055) calc(22% - 1px) calc(22% + 1px), transparent calc(22% + 1px)),
    linear-gradient(90deg, transparent 0 calc(78% - 1px), rgba(62, 134, 232, 0.055) calc(78% - 1px) calc(78% + 1px), transparent calc(78% + 1px));
}
/* Vignette, so panels always sit on the darkest part of the frame. */
.arena-vignette {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(78% 68% at 50% 45%, transparent 30%, rgba(3, 5, 9, 0.72) 100%);
}
@keyframes arenaDrift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(2.4vmax, -1.6vmax, 0) scale(1.035); }
}
@media (prefers-reduced-motion: reduce) {
  .arena-bg::before { animation: none; }
}

/* Menu screens sit above the backdrop. */
#screen-name, #screen-menu, #screen-browser, #screen-lobby,
#screen-customize, #screen-stats, #screen-leaderboard, #screen-replays {
  z-index: 1;
}

/* ===========================================================================
   WORDMARK
   Archivo Black cut tight, a hairline rule, then HOCKEY tracked wide in the
   condensed data face. The rule is the lockup — it's what makes it read as a
   mark rather than two lines of text.
   =========================================================================== */
.logo { line-height: 1; }
.logo-zealot {
  display: block;
  font-family: var(--font-menu-display);
  font-size: clamp(2.6rem, 5.4vw, 4.2rem);
  letter-spacing: -0.005em;
  line-height: 0.92;
  background: linear-gradient(168deg, #ffffff 0%, var(--ice) 46%, #7fb2cc 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 3px 18px rgba(120, 190, 230, 0.18));
}
.logo-hockey {
  display: block;
  position: relative;
  margin-top: 0.42em;
  padding-top: 0.42em;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: clamp(0.85rem, 1.5vw, 1.15rem);
  letter-spacing: 0.62em;
  text-indent: 0.62em;   /* optical: cancels the trailing letter-space */
  color: var(--ice-cool);
}
.logo-hockey::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, var(--gold) 0%, rgba(242, 193, 78, 0.15) 45%, transparent 100%);
}
/* Centered variant, used on the login card and the tutorial welcome modal. */
.logo--center { text-align: center; }
.logo--center .logo-hockey::before {
  background: linear-gradient(90deg, transparent, var(--gold) 50%, transparent);
}

.tagline {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ice-dim);
}

/* Build number beside the credit. Deliberately dimmer and non-uppercase-tracked so it reads
   as metadata, not as part of the byline. */
.menu-version {
  margin-left: 0.6em;
  color: rgba(125, 153, 173, 0.42);
  letter-spacing: 0.08em;
}
.menu-version::before {
  content: "·";
  margin-right: 0.6em;
  opacity: 0.7;
}

/* Small "switch server" link under the byline — present, but never louder than the footer. */
.menu-region-switch {
  align-self: flex-start;
  margin: 2px 0 0;
  padding: 4px 10px;
  background: transparent;
  border: 1px solid rgba(125, 153, 173, 0.28);
  border-radius: 4px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(125, 153, 173, 0.75);
  cursor: pointer;
}
.menu-region-switch:hover:not(:disabled),
.menu-region-switch:focus-visible { color: var(--ice, #e6f1f7); border-color: rgba(125, 153, 173, 0.6); }
.menu-region-switch:disabled { opacity: 0.45; cursor: default; }
.menu-region-switch[hidden] { display: none; }
.menu-region-switch .rs-here { font-weight: 700; }

.menu-footer {
  margin: 0;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(125, 153, 173, 0.55);
}

/* Small all-caps label that sits above a heading or field group. */
.eyebrow {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ice-dim);
}

/* ===========================================================================
   PANELS — rink boards
   A hard cold line along the top edge (the boards' kick plate) instead of a
   uniform outline. That single asymmetry is what stops these reading as
   generic dark cards.
   =========================================================================== */
.panel,
.center-card {
  position: relative;
  background:
    linear-gradient(180deg, rgba(168, 212, 232, 0.045) 0%, transparent 120px),
    var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  box-shadow: var(--shadow-panel);
}

.center-card {
  padding: 36px 40px;
  width: 100%;
  max-width: 420px;
  max-height: calc(100dvh - 48px);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ===========================================================================
   FORMS
   =========================================================================== */
.form-group { display: flex; flex-direction: column; gap: 6px; }
input[type="text"],
input[type="password"] {
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 12px 14px;
  width: 100%;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
input[type="text"]:focus,
input[type="password"]:focus {
  border-color: rgba(168, 212, 232, 0.5);
  background: #0a1119;
  box-shadow: 0 0 0 3px rgba(168, 212, 232, 0.09);
}
input[type="text"]::placeholder,
input[type="password"]::placeholder { color: rgba(125, 153, 173, 0.7); }

.error-msg {
  color: #ff8080;
  font-size: 0.85rem;
  min-height: 18px;
  letter-spacing: 0.01em;
}

/* ===========================================================================
   BUTTONS — three tiers
   Everything used to be one .btn, so Ranked and Options carried identical
   weight. Tiers: hero (Ranked only) > primary (filled) > nav row (quiet).
   The shared cue is a left "goal light" spine that brightens on hover.
   =========================================================================== */
.btn {
  position: relative;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  padding: 12px 20px;
  text-transform: uppercase;
  transition: background 0.16s ease, color 0.16s ease, transform 0.12s ease,
              box-shadow 0.16s ease, border-color 0.16s ease;
  width: 100%;
  /* Fallback so a .btn with no variant class renders as a panel button rather
     than the browser's default white — #btn-refresh was doing exactly that. */
  background: var(--rink-hi);
  color: var(--text);
}
.btn:hover:not(:disabled) { transform: translateY(-1px); }
.btn:active:not(:disabled) { transform: translateY(0); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-primary {
  background: linear-gradient(180deg, var(--blue) 0%, var(--blue-dark) 100%);
  color: #fff;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.16) inset, 0 4px 14px -4px rgba(62, 134, 232, 0.5);
}
.btn-primary:hover:not(:disabled) {
  background: linear-gradient(180deg, #5698f0 0%, #2e6fc4 100%);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2) inset, 0 8px 22px -6px rgba(62, 134, 232, 0.6);
}

.btn-secondary {
  background: var(--rink-hi);
  color: var(--text);
  border: 1px solid var(--edge);
}
.btn-secondary:hover:not(:disabled) {
  background: #1b2735;
  border-color: rgba(168, 212, 232, 0.28);
}

.btn-gold {
  background: linear-gradient(180deg, var(--gold) 0%, #d9a52f 100%);
  color: #191203;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3) inset, 0 4px 14px -4px var(--gold-glow);
}
.btn-gold:hover:not(:disabled) {
  background: linear-gradient(180deg, #ffd166 0%, var(--gold) 100%);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.35) inset, 0 8px 24px -6px var(--gold-glow);
}

.btn-ghost {
  background: transparent;
  color: var(--ice-dim);
  border: 1px solid var(--edge);
}
.btn-ghost:hover:not(:disabled) { color: var(--text); border-color: rgba(168, 212, 232, 0.3); }

.btn-small { font-size: 0.78rem; padding: 8px 14px; width: auto; letter-spacing: 0.08em; }
.btn-red  { background: linear-gradient(180deg, var(--red) 0%, var(--red-dark) 100%); color: #fff; }
.btn-blue { background: linear-gradient(180deg, var(--blue) 0%, var(--blue-dark) 100%); color: #fff; }
.btn-grey { background: var(--rink-hi); color: var(--ice-dim); border: 1px solid var(--edge); }

/* ── Nav row: the quiet tier. Left-aligned label, chevron on the right, gold
      spine that grows on hover. Used for the main menu's secondary actions. ── */
.btn-nav {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 16px 13px 18px;
  background: linear-gradient(90deg, rgba(20, 30, 44, 0.9) 0%, rgba(12, 19, 31, 0.55) 100%);
  border: 1px solid var(--edge);
  border-left: 3px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.12s ease, color 0.16s ease;
}
.btn-nav .nav-icon { flex: none; width: 18px; height: 18px; color: var(--ice-cool); transition: color 0.16s ease; }
.btn-nav .nav-label { flex: 1; }
/* Chevron stays last even when a row adds a trailing tag via ::after. */
.btn-nav .nav-chevron { order: 9; }
/* Live lobby / game counts on the Browse row. */
.nav-badges { display: flex; gap: 6px; }
.nav-badge {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 3px;
  white-space: nowrap;
}
.nav-badge--open { color: #8be0a4; background: rgba(63, 185, 107, 0.16); border: 1px solid rgba(63, 185, 107, 0.3); }
.nav-badge--live { color: #9ccbff; background: rgba(62, 134, 232, 0.16); border: 1px solid rgba(62, 134, 232, 0.35); }
.nav-badge--queue { color: #f4d27a; background: rgba(242, 193, 78, 0.16); border: 1px solid rgba(242, 193, 78, 0.4); }
/* Live matchmaking-queue count on the Ranked hero row. */
.hero-badges { display: flex; flex: none; gap: 6px; }
.btn-nav .nav-chevron {
  flex: none;
  width: 14px;
  height: 14px;
  color: rgba(125, 153, 173, 0.6);
  transition: transform 0.16s ease, color 0.16s ease;
}
.btn-nav:hover {
  background: linear-gradient(90deg, rgba(30, 43, 60, 0.95) 0%, rgba(16, 25, 38, 0.6) 100%);
  border-color: rgba(168, 212, 232, 0.26);
  border-left-color: var(--gold);
  transform: translateX(2px);
}
.btn-nav:hover .nav-icon { color: var(--gold); }
.btn-nav:hover .nav-chevron { color: var(--gold); transform: translateX(3px); }
.btn-nav:active { transform: translateX(0); }

/* ── Hero: Ranked only. Everything about it is bigger — height, type, the spine
      and a gold wash — so the primary action never competes with the rest. ── */
.btn-hero {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 20px 22px;
  background:
    linear-gradient(90deg, rgba(242, 193, 78, 0.14) 0%, rgba(242, 193, 78, 0.03) 42%, transparent 75%),
    linear-gradient(180deg, var(--rink-hi) 0%, #101927 100%);
  border: 1px solid rgba(242, 193, 78, 0.3);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  color: var(--text);
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.14s ease, box-shadow 0.2s ease;
}
.btn-hero .hero-icon { flex: none; width: 30px; height: 30px; color: var(--gold); }
.btn-hero .hero-text { flex: 1; min-width: 0; }
.btn-hero .hero-title {
  display: block;
  font-family: var(--font-menu-display);
  font-size: 1.5rem;
  line-height: 1.04;
  letter-spacing: 0.005em;
  color: var(--ice-bright);
}
.btn-hero .hero-sub {
  display: block;
  margin-top: 5px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.btn-hero .hero-chevron { flex: none; width: 20px; height: 20px; color: var(--gold); transition: transform 0.18s ease; }
.btn-hero:hover {
  background:
    linear-gradient(90deg, rgba(242, 193, 78, 0.22) 0%, rgba(242, 193, 78, 0.06) 42%, transparent 75%),
    linear-gradient(180deg, #1a2536 0%, #131d2c 100%);
  border-color: rgba(242, 193, 78, 0.55);
  box-shadow: 0 10px 34px -12px var(--gold-glow);
  transform: translateY(-2px);
}
.btn-hero:hover .hero-chevron { transform: translateX(4px); }
.btn-hero:active { transform: translateY(0); }

/* ===========================================================================
   LOGIN SCREEN
   =========================================================================== */
.login-card { max-width: 400px; gap: 12px; }
.login-brand { margin-bottom: 14px; text-align: center; }
.login-brand .tagline { margin-bottom: 14px; }
.login-card .logo-zealot { font-size: clamp(2.4rem, 4.5vw, 3.4rem); }

/* ===========================================================================
   MAIN MENU
   Two rails: identity on the left, actions on the right. The eight identical
   stacked buttons this replaces gave Ranked and Options the same weight.
   =========================================================================== */
.menu-shell {
  display: grid;
  grid-template-columns: minmax(280px, 360px) minmax(340px, 420px);
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
  width: 100%;
  max-width: 900px;
  padding: 24px;
  max-height: 100dvh;
}

.menu-brand {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
}

.menu-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.menu-nav { display: flex; flex-direction: column; gap: 7px; }

/* ── Identity card ──
   Name, region and whatever ranked/stats data the client already has. Rows are
   hidden rather than showing "—" when a value hasn't loaded. */
.identity-card { padding: 16px 18px; }
.identity-top { display: flex; align-items: center; gap: 12px; }
/* The profile crest that used to sit here was removed: with the tier badge on
   the rank row carrying the card's visual identity, a generic user glyph was
   just noise. The name leads on its own. */
/* Tier badge. An extracted PNG built by tierBadgeHTML() in tier-badges.js —
   the artwork carries its own colour, so there is nothing to theme here.
   width/height come from attributes; object-fit guards against a non-square
   source sneaking in and stretching. */
.tier-badge {
  display: inline-block;
  vertical-align: -0.28em;
  flex: none;
  object-fit: contain;
}
.intro-player-name .tier-badge { margin-right: 6px; }
/* Flex-centred on the awards card, so the global baseline nudge would push them off. */
.awards-mvp .tier-badge,
.awards-row .tier-badge { vertical-align: baseline; }
/* The badge is its own artwork, so the crest drops the framing it used to give
   the generic user glyph. */

.identity-who { flex: 1; min-width: 0; }
.identity-name {
  margin: 0;
  font-family: var(--font-menu-display);
  font-size: 1.15rem;
  line-height: 1.15;
  color: var(--ice-bright);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.identity-region { margin: 3px 0 0; font-size: 0.68rem; }
.identity-region:empty { display: none; }
.identity-signout {
  flex: none;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.identity-signout:hover { color: #ff9a92; border-color: rgba(226, 69, 63, 0.5); background: rgba(226, 69, 63, 0.08); }

/* ── Rank progress ──
   Tier name, XP bar, and the named next reward. Gold reads as "reward track",
   matching the Ranked hero tile. */
.identity-rank {
  margin-top: 14px;
  padding-top: 13px;
  border-top: 1px solid var(--edge);
}
.identity-rank[hidden] { display: none; }
.identity-rank-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 7px;
}
/* Badge + tier name read as one unit, so they sit tight together and the XP
   count keeps the far side. Centre-aligned against a baseline-aligned row. */
.identity-rank-badge {
  flex: none;
  align-self: center;
  display: inline-flex;
  margin-right: -2px;
}
/* Sized here as well as by the width/height attributes: the celebration repaints this element
   during a rank-up, and a CSS floor means the badge can never end up a different size than the
   one the menu drew. */
.identity-rank-badge .tier-badge { width: 46px; height: 46px; }
.identity-rank-badge:empty { display: none; }
.identity-rank-head .identity-rank-label { margin-right: auto; }
.identity-rank-label {
  font-family: var(--font-menu-display);
  font-size: 1.18rem;
  letter-spacing: 0.01em;
  /* Green, matching the bar beneath it and the +XP pop: rank IS the XP track. Gold is reserved
     for credits, so having the division name in gold read as a currency label. */
  color: var(--xp-green);
}
/* The post-match animation retypes this every frame while the bar climbs. It is a flex item in
   a space-between row, so without a stable width the badge and tier name shuffle sideways on
   every tick — read as the whole bar being jumpy. tabular-nums fixes the digit width; min-width
   absorbs a digit count changing (99 -> 100). */
.identity-rank-xp {
  font-variant-numeric: tabular-nums;
  min-width: 11ch;
  text-align: right;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  color: var(--ice-bright);
  white-space: nowrap;
}
.identity-rank-track {
  height: 6px;
  border-radius: 3px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  overflow: hidden;
}
.identity-rank-fill {
  height: 100%;
  width: 0;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--xp-green-deep), var(--xp-green));
  /* Slow enough to read as filling, not snapping — the end-of-match award
     animates this same bar (via transform; see setFill in game.js). */
  transition: width 0.9s cubic-bezier(0.22, 1, 0.36, 1);
  backface-visibility: hidden;   /* keeps the scaled edge from shimmering on subpixel steps */
}
.identity-rank-next {
  margin: 7px 0 0;
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--ice-bright);
}
.identity-rank-next:empty { display: none; }

.identity-stats {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  margin: 14px 0 0;
}
.identity-stats:not(:has(.identity-stat:not([hidden]))) { display: none; }
.identity-stat {
  padding: 9px 10px;
  background: var(--rink-deep);
  border-radius: var(--radius);
  border-top: 1px solid var(--edge);
  text-align: center;
}
.identity-stat[hidden] { display: none; }
.identity-stat dt {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.66rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ice-bright);
}
.identity-stat dd {
  margin: 3px 0 0;
  font-family: var(--font-menu-display);
  font-size: 1.2rem;
  line-height: 1;
  color: var(--ice-bright);
}
.identity-stat--gold dd { color: var(--gold); }

/* ── Menu responsive ──
   Collapses on narrow OR short viewports, because the failure mode we care
   about is a 720p-tall window, not a narrow one. */
@media (max-width: 900px), (max-height: 640px) {
  .menu-shell {
    grid-template-columns: minmax(0, 440px);
    justify-content: center;
    gap: 20px;
    align-content: center;
    overflow-y: auto;
  }
  .menu-brand { gap: 14px; text-align: center; }
  .menu-brand .logo { text-align: center; }
  .menu-brand .logo-hockey::before {
    background: linear-gradient(90deg, transparent, var(--gold) 50%, transparent);
  }
  .menu-footer { order: 3; }
  .menu-region-switch { order: 4; align-self: center; }
}
/* Short-but-wide (720p windows, Steam Deck docked): tighten instead of scroll. */
@media (max-height: 780px) and (min-width: 901px) {
  .menu-shell { gap: 40px; }
  .btn-hero { padding: 15px 20px; }
  .btn-hero .hero-title { font-size: 1.3rem; }
  .btn-nav { padding: 10px 14px 10px 16px; }
  .menu-nav { gap: 6px; }
  .identity-card { padding: 13px 15px; }
  .menu-brand { gap: 18px; }
}

/* ===========================================================================
   TOP BANNERS + BROWSER GATE
   Were fully inline-styled, so they picked up none of the theme.
   Visibility is still toggled from game.js showScreen('menu') — classes here
   only cover appearance.
   =========================================================================== */
.top-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9996;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  padding: 9px 16px;
  font-size: 0.86rem;
  line-height: 1.4;
  text-align: center;
  border-bottom: 1px solid;
  backdrop-filter: blur(6px);
}
#ios-add-to-home-banner { z-index: 9997; }
.top-banner--tip  { background: rgba(18, 40, 26, 0.94); color: #cfe9d6; border-bottom-color: rgba(63, 185, 107, 0.35); }
.top-banner--warn { background: rgba(58, 16, 15, 0.95); color: #f6d8d6; border-bottom-color: rgba(226, 69, 63, 0.5); }
.top-banner--info { background: rgba(10, 24, 40, 0.95); color: #c6dcee; border-bottom-color: rgba(62, 134, 232, 0.4); }
.top-banner strong { font-weight: 600; color: #fff; }
.top-banner-cta {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 4px 12px;
  border-radius: var(--radius);
  white-space: nowrap;
  transition: filter 0.15s;
}
.top-banner--warn .top-banner-cta { background: var(--red); color: #fff; }
.top-banner--info .top-banner-cta { background: rgba(62, 134, 232, 0.25); color: #9ccbff; border: 1px solid rgba(62, 134, 232, 0.5); }
.top-banner-cta:hover { filter: brightness(1.15); }
.top-banner-dismiss {
  background: none;
  border: 1px solid currentColor;
  border-radius: var(--radius);
  color: inherit;
  opacity: 0.7;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 10px;
  cursor: pointer;
  transition: opacity 0.15s;
}
.top-banner-dismiss:hover { opacity: 1; }

.gate-notice {
  margin-top: 4px;
  padding: 16px 18px;
  background: rgba(58, 16, 15, 0.5);
  border: 1px solid rgba(226, 69, 63, 0.4);
  border-left: 3px solid var(--red);
  border-radius: var(--radius);
  color: #f6d8d6;
}
.gate-notice-title {
  margin: 0 0 6px;
  font-family: var(--font-menu-display);
  font-size: 1rem;
  color: #ffd9d6;
}
.gate-notice-body { margin: 0 0 12px; font-size: 0.88rem; line-height: 1.5; }
.gate-notice-cta { display: block; text-align: center; text-decoration: none; padding: 10px 16px; }

/* ===========================================================================
   MENU MUSIC WIDGET
   Deliberately quiet: at rest it's a speaker glyph and the track name in muted
   text, so it doesn't compete with the menu. It opens on hover/focus, and
   announces itself for a few seconds when a track changes. Sits under any top
   banner via --top-banner-offset, which game.js keeps in sync.
   =========================================================================== */
.music-widget {
  position: fixed;
  top: calc(var(--top-banner-offset, 0px) + 14px);
  /* Sits to the LEFT of the global chat dock rather than on top of it. The offset is 0
     whenever the dock is hidden or collapsed, so this is exactly the old position then. */
  right: calc(14px + var(--chat-dock-offset, 0px));
  transition: right 0.18s ease, background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
  z-index: 9995;
  width: max-content;
  max-width: 260px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: transparent;
  transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

/* ── Resting handle ── */
.music-widget-handle {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 7px 10px;
  background: none;
  border: none;
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  cursor: pointer;
  opacity: 0.62;
  transition: opacity 0.2s ease, color 0.2s ease;
}
.music-widget-handle .ui-icon { width: 15px; height: 15px; }
.music-widget-now {
  max-width: 168px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Open: the panel carries the track name, so drop it from the handle rather than
   printing the same title twice. Fixed width while open keeps it from jittering
   as titles of different lengths come and go. */
.music-widget:hover,
.music-widget:focus-within,
.music-widget.is-open { width: 236px; }
.music-widget:hover .music-widget-now,
.music-widget:focus-within .music-widget-now,
.music-widget.is-open .music-widget-now { display: none; }

/* One speaker glyph or the other, never both. */
.music-widget-speaker-off { display: none; }
.music-widget.is-muted .music-widget-speaker { display: none; }
.music-widget.is-muted .music-widget-speaker-off { display: block; }
.music-widget.is-muted .music-widget-now { display: none; }

/* ── Open: hover, keyboard focus, or the post-track-change announce ── */
.music-widget:hover,
.music-widget:focus-within,
.music-widget.is-open {
  background: var(--rink);
  border-color: var(--edge);
  box-shadow: var(--shadow-panel);
}
.music-widget:hover .music-widget-handle,
.music-widget:focus-within .music-widget-handle,
.music-widget.is-open .music-widget-handle { opacity: 1; color: var(--text); }

.music-widget-panel {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  transition: grid-template-rows 0.24s ease, opacity 0.2s ease;
  opacity: 0;
}
.music-widget:hover .music-widget-panel,
.music-widget:focus-within .music-widget-panel,
.music-widget.is-open .music-widget-panel { grid-template-rows: 1fr; opacity: 1; }
.music-widget-panel > * { min-height: 0; }
/* Padding lives on the inner wrapper so the collapsed state is truly 0 height. */
.music-widget-panel-inner { overflow: hidden; }

.music-widget-eyebrow {
  margin: 0 12px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.62rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
}
.music-widget-title {
  margin: 4px 12px 0;
  font-family: var(--font-menu-display);
  font-size: 0.92rem;
  line-height: 1.2;
  color: var(--ice-bright);
  overflow-wrap: anywhere;
}
.music-widget-artist {
  margin: 2px 12px 0;
  font-size: 0.78rem;
  line-height: 1.3;
  color: var(--ice-dim);
  overflow-wrap: anywhere;
}
.music-widget-artist:empty { display: none; }

/* ── Volume ── */
.music-widget-vol {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 11px 12px 0;
}
.music-widget-vol input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  cursor: pointer;
}
.music-widget-vol input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--ice);
  border: 2px solid #0a1119;
  box-shadow: 0 0 0 1px var(--edge-hi);
  cursor: pointer;
  transition: background 0.15s;
}
.music-widget-vol input[type="range"]:hover::-webkit-slider-thumb { background: var(--gold); }
.music-widget-vol input[type="range"]::-moz-range-thumb {
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--ice); border: 2px solid #0a1119; cursor: pointer;
}
.music-widget-vol-val {
  min-width: 34px;
  text-align: right;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  color: var(--gold);
}

/* ── Buttons ── */
.music-widget-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
  margin: 9px 12px 11px;
}
.music-widget-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 6px 8px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.music-widget-btn .ui-icon { width: 12px; height: 12px; }
.music-widget-btn:hover { color: var(--text); border-color: rgba(168, 212, 232, 0.32); background: #1b2735; }

/* Screen-reader-only label for the volume input. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Narrow windows: the widget would crowd centered content, so drop the track
   name and leave just the speaker. */
@media (max-width: 900px) {
  .music-widget-now { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .music-widget, .music-widget-panel { transition: none; }
}

/* Menu/lobby ping readout. Was inline-styled. */
.menu-ping-display {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 9999;
  padding: 3px 8px;
  border-radius: var(--radius);
  background: rgba(6, 10, 17, 0.6);
  border: 1px solid var(--edge);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  pointer-events: none;
}

/* Browser Screen */
.browser-container {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  width: min(760px, 95vw);
  max-height: calc(100dvh - 48px);
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-panel);
  overflow-x: hidden;
  overflow-y: auto;
}
.browser-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 24px;
  border-bottom: 1px solid var(--edge);
  background: linear-gradient(180deg, rgba(168, 212, 232, 0.05), transparent);
}
.browser-heading .eyebrow { font-size: 0.66rem; margin-bottom: 3px; }
.browser-header h2 {
  font-family: var(--font-menu-display);
  font-size: 1.4rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
}
.header-actions { display: flex; gap: 8px; }

.lobby-list {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lobby-list::-webkit-scrollbar { width: 6px; }
.lobby-list::-webkit-scrollbar-track { background: transparent; }
.lobby-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

.empty-state {
  color: var(--ice-dim);
  text-align: center;
  padding: 36px 16px;
  font-size: 0.92rem;
}

.lobby-item {
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-left: 3px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 13px 16px;
  cursor: pointer;
  transition: border-color 0.16s, background 0.16s, transform 0.12s;
}
.lobby-item:hover {
  border-color: rgba(168, 212, 232, 0.3);
  border-left-color: var(--gold);
  background: #1b2735;
  transform: translateX(2px);
}
.lobby-item-name {
  font-family: var(--font-menu-display);
  font-size: 1rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
}
.lobby-item-info {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.86rem;
  letter-spacing: 0.05em;
  margin-top: 3px;
}
.lobby-item-btn { flex-shrink: 0; }

.menu-activity-hint {
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
  margin: 4px 0 0;
  min-height: 1.2em;
}

/* Players-online line, sits just above the "Created by Eric" footer. */
.menu-online-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  font-family: var(--font-data);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.menu-online-status[hidden] { display: none; }
.menu-online-status .online-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #3fb96b;
  box-shadow: 0 0 8px rgba(63, 185, 107, 0.85);
  animation: online-pulse 2.4s ease-in-out infinite;
}
@keyframes online-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* Over the video backdrop the dim footer lines vanish: give them a small dark glass chip and
   brighter text (only while a clip is showing — the snow field is dark enough without). */
#screen-menu.menu-video-on .menu-online-status,
#screen-menu.menu-video-on .menu-footer {
  width: fit-content;
  padding: 5px 11px;
  border-radius: 6px;
  background: rgba(5, 9, 16, 0.66);
  border: 1px solid rgba(125, 153, 173, 0.18);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
#screen-menu.menu-video-on .menu-online-status { color: #c9d6e2; }
#screen-menu.menu-video-on .menu-footer { margin-top: 6px; color: rgba(190, 210, 226, 0.88); }
#screen-menu.menu-video-on .menu-version { color: rgba(160, 186, 206, 0.72); }
#screen-menu.menu-video-on .menu-region-switch { background: rgba(5, 9, 16, 0.66); color: rgba(190, 210, 226, 0.88); }

/* Asset-preload bar along the very bottom: a centered "Loading sounds… 35%"
   caption above a chunky gold progress line, on a soft dark band so it clearly
   reads as loading without covering the menu. */
.asset-preload {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  pointer-events: none;
  padding-top: 14px;
  background: linear-gradient(to top, rgba(6, 12, 20, 0.9), rgba(6, 12, 20, 0));
  transition: opacity 0.4s ease;
}
.asset-preload[hidden] { display: none; }
.asset-preload.is-fading { opacity: 0; }
.asset-preload-row {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: 10px;
  padding: 0 12px 7px;
  font-family: var(--font-data);
  font-size: 1.02rem;
  letter-spacing: 0.03em;
  color: rgba(210, 232, 248, 0.95);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9);
}
.asset-preload-label { font-weight: 600; }
.asset-preload-pct {
  color: var(--gold);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.asset-preload-track {
  width: 100%;
  height: 7px;
  background: rgba(255, 255, 255, 0.12);
}
.asset-preload-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--gold), #ffe9a8);
  box-shadow: 0 0 14px rgba(242, 193, 78, 0.8);
  transition: width 0.25s ease;
}

.browser-section {
  border-bottom: 1px solid var(--border);
}
.browser-section:last-of-type {
  border-bottom: none;
}
.browser-section-title {
  font-family: var(--font-data);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--ice-cool);
  padding: 14px 16px 6px;
  margin: 0;
}
.browser-section-hint {
  font-family: var(--font-body);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ice-dim);
  font-size: 0.78rem;
}
.browser-live-note {
  font-size: 0.75rem;
  color: var(--muted);
  padding: 0 16px 12px;
  margin: 0;
  opacity: 0.9;
}

.active-game-item {
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-left: 3px solid var(--red);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
}
.active-game-item-main { min-width: 0; flex: 1; }
.active-game-score {
  flex-shrink: 0;
  font-size: 1.35rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.active-game-score .score-red { color: var(--red); }
.active-game-score .score-blue { color: var(--blue); }
.active-game-score .score-sep {
  color: var(--muted);
  margin: 0 4px;
  font-weight: 600;
}

.browser-container .lobby-list {
  flex: none;
  max-height: min(280px, 38vh);
}

/* Lobby Room Screen */
.lobby-room {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  width: min(940px, 95vw);
  max-height: calc(100dvh - 40px);
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-panel);
  overflow: visible;
}
.lobby-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
  border-bottom: 1px solid var(--border);
}
.lobby-header h2 { font-size: 1.1rem; }

.teams-container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 0;
  flex: 1;
  overflow: visible;
}
/* Team columns carry their own color at the top edge — the rink's own coding,
   so you can tell the three columns apart before reading a word. */
.team-column {
  padding: 14px 16px 16px;
  border-right: 1px solid var(--edge);
  border-top: 2px solid transparent;
  display: flex;
  flex-direction: column;
  overflow: visible;
}
.team-column:last-child { border-right: none; }
#team-red  { border-top-color: var(--red);  background: linear-gradient(180deg, rgba(226, 69, 63, 0.06), transparent 120px); }
#team-blue { border-top-color: var(--blue); background: linear-gradient(180deg, rgba(62, 134, 232, 0.06), transparent 120px); }
#team-spectator { border-top-color: rgba(168, 212, 232, 0.3); }

.team-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.86rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-bottom: 12px;
  color: var(--ice-cool);
}
.team-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}
.team-dot.red { background: var(--red); }
.team-dot.blue { background: var(--blue); }
.team-dot.grey { background: #555; }

.player-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
  flex: 1;
}

.player-chip {
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 10px 12px;
}

/* ── Team XP boost ──
   A team boost pays everyone in the match but charges only its owner, so the
   roster marks who is covering it. Cyan rather than team blue — see --boost. */
.player-chip--teamboost {
  border-color: var(--boost);
  box-shadow: 0 0 0 1px var(--boost-glow), 0 0 14px -2px var(--boost-glow);
  background:
    linear-gradient(180deg, rgba(90, 217, 240, 0.10), rgba(90, 217, 240, 0)) ,
    var(--rink-hi);
}
.player-chip-boost {
  flex: none;
  font-family: var(--font-data);
  font-weight: 700;
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  white-space: nowrap;
  color: var(--boost);
  background: var(--boost-deep);
  border: 1px solid var(--boost-glow);
  border-radius: 3px;
  padding: 2px 6px;
}
.lobby-boost-banner {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  margin-bottom: 10px;
  padding: 8px 12px;
  font-family: var(--font-body);
  font-size: 0.84rem;
  color: var(--ice);
  background: linear-gradient(90deg, rgba(90, 217, 240, 0.14), rgba(90, 217, 240, 0.03));
  border: 1px solid var(--boost-glow);
  border-left: 3px solid var(--boost);
  border-radius: var(--radius);
}
.lobby-boost-line { display: flex; align-items: center; gap: 9px; }
.lobby-boost-banner strong { color: var(--boost); font-weight: 700; }
.lobby-boost-banner-icon { color: var(--boost); font-size: 1rem; line-height: 1; }
/* Your own boost is green, matching its chip on the menu card and separating
   "what you bought" from "what the room is getting". */
.lobby-boost-line--mine strong,
.lobby-boost-line--mine .lobby-boost-banner-icon { color: var(--xp-green); }
.player-chip-boost--personal {
  color: var(--xp-green);
  background: #0a2418;
  border-color: rgba(110, 232, 154, 0.35);
}
/* Results chip: same shape as the first-match-of-day one, cyan instead of green. */
.end-progress-bonus--boost {
  color: #06222b;
  background: var(--boost);
}
/* Menu identity card: your own active boosts, counting down. */
.identity-boosts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.identity-boost {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 8px;
  border-radius: 3px;
  font-family: var(--font-data);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--boost-deep);
  border: 1px solid var(--boost-glow);
}
.identity-boost-name { font-weight: 700; color: var(--boost); }
.identity-boost-left { color: var(--ice-bright); letter-spacing: 0.04em; }
.identity-boost--xp { border-color: rgba(110, 232, 154, 0.35); background: #0a2418; }
.identity-boost--xp .identity-boost-name { color: var(--xp-green); }
/* Last match on the clock: say it in the colour that means "act now". */
.identity-boost--last { border-color: var(--gold); }
.identity-boost--last .identity-boost-left { color: var(--gold); }

/* ── Goalie bot tier picker (lobby) ──
   Built in game.js and appended to <body>, so it can't inherit the lobby panel's
   styles — it needs its own rules rather than the inline CSS it used to carry. */
.bot-goalie-dropdown {
  position: fixed;
  z-index: 9999;
  min-width: 190px;
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  box-shadow: var(--shadow-modal);
}
.bot-goalie-option {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 9px 11px;
  background: transparent;
  border: none;
  border-radius: 3px;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background 0.14s, color 0.14s;
}
.bot-goalie-option:hover { background: rgba(168, 212, 232, 0.09); }
.bot-goalie-option-name {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.bot-goalie-option-note {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ice-dim);
}
/* Easiest reads green, hardest gold — the difficulty is legible before the label. */
.bot-goalie-option:first-child .bot-goalie-option-note { color: var(--green); }
.bot-goalie-option:last-child .bot-goalie-option-note { color: var(--gold); }
.player-chip-bot {
  border-left: 3px solid rgba(255, 220, 60, 0.7);
  background: rgba(255, 220, 60, 0.05);
}
.player-chip-name {
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 6px;
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Name-only chip: nothing under it to separate from. */
.player-chip-name:last-child { margin-bottom: 0; }
.player-chip-name-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Ranked chip: the badge owns a fixed right-hand gutter and is centred on the WHOLE chip,
   so it stays put whether the chip is just a name or a name plus the host's team/kick row.
   Out of flow on purpose — in flow it centred on the name row and rode the top edge. The
   fixed gutter is also what lines the badges up down the roster, whatever the name lengths. */
.player-chip { position: relative; }
.player-chip--ranked {
  padding-right: 74px;
  min-height: 70px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.player-chip-rank {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  line-height: 0;
}
/* width/height attributes on the <img> already reserve the space; these are the render
   size and a guard for a chip squeezed narrower than its gutter. */
.player-chip-rank .tier-badge {
  vertical-align: middle;
  width: 50px;
  height: 50px;
  max-width: 100%;
}
.player-chip-name--promote-host {
  cursor: context-menu;
  border-radius: 4px;
  outline-offset: 2px;
}
.player-chip-name--promote-host:hover {
  background: rgba(255, 255, 255, 0.06);
}
.lobby-host-context-menu {
  position: fixed;
  /* Above the chat dock (9994), the music widget (9995) and the ping display (9999).
     At 2500 this menu opened UNDERNEATH the dock — the roster right-click looked like it
     did nothing at all, because the menu was rendering behind the panel it was opened
     from. A context menu belongs on top of whatever it was summoned over. */
  z-index: 10050;
  min-width: 168px;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
}
.lobby-host-context-menu-item {
  display: block;
  width: 100%;
  margin: 0;
  padding: 10px 12px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
/* Shown rather than hidden when unavailable — "Invite to Game" greyed with a reason is
   more useful than a menu whose items move around depending on where you are. */
.lobby-host-context-menu-item[disabled] {
  opacity: 0.42;
  cursor: default;
}
.lobby-host-context-menu-item[disabled]:hover { background: transparent; color: var(--text); }
.lobby-host-context-menu-item:hover {
  background: rgba(76, 143, 227, 0.22);
  color: #e6edf7;
}
.chip-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.chip-actions .btn {
  font-size: 0.7rem;
  padding: 4px 10px;
  width: auto;
  direction: ltr;
  unicode-bidi: isolate;
}

.lobby-status {
  padding: 12px 24px;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.85rem;
  text-align: center;
}

/* Game Screen */
.game-wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  width: 100vw;
  gap: 0;
  background: var(--bg);
  overflow: hidden;
}

#game-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  transform-origin: center center;
}

/* Fullscreen (desktop): bounding box is logical rink only; HUD + shot clock sit over the top of the ice
   so scale uses 16:9 and fills 16:9 displays without side letterboxing. Width/height set in game.js. */
#game-content.fullscreen-stack-as-overlay {
  display: block;
  position: relative;
  flex: none;
  align-items: stretch;
  max-width: none;
  box-sizing: border-box;
}
#game-content.fullscreen-stack-as-overlay .hud {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  max-width: none;
  z-index: 25;
  border-radius: 10px 10px 0 0;
}
#game-content.fullscreen-stack-as-overlay #shot-clock-bar {
  position: absolute;
  top: var(--fs-hud-h, 48px);
  left: 0;
  width: 100%;
  max-width: none;
  z-index: 24;
}
#game-content.fullscreen-stack-as-overlay .canvas-container {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  aspect-ratio: unset;
}

.hud {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 10px 10px 0 0;
  width: min(1173px, 100vw);
  /* More top room and extra right padding so HUD buttons don't overlap scores. */
  padding: 4px 72px 6px 24px;
  position: relative;
}

.btn-fullscreen {
  position: absolute;
  right: 10px;
  /* Place controls in the seam between HUD and rink (not over score text). */
  top: calc(100% + 4px);
  z-index: 20;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text);
  font-size: 1rem;
  cursor: pointer;
  padding: 3px 7px;
  line-height: 1;
  transition: color 0.15s, border-color 0.15s;
}
.btn-fullscreen:hover { color: var(--text); border-color: var(--blue); }
.btn-game-menu {
  position: absolute;
  right: 44px;
  /* Place controls in the seam between HUD and rink (not over score text). */
  top: calc(100% + 4px);
  z-index: 20;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--muted);
  cursor: pointer;
  font-size: 1.1rem;
  padding: 3px 8px;
  line-height: 1;
  transition: color 0.15s, border-color 0.15s;
}
.btn-game-menu:hover { color: var(--text); border-color: var(--blue); }
.game-menu-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Above .game-intro-overlay (55) AND .pause-overlay (60) — the menu (and Options from it)
     must stay reachable while the game is paused. */
  z-index: 70;
}
.game-menu-overlay.hidden { display: none; }
.game-menu-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 36px 48px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 260px;
  align-items: center;
}
.game-menu-card h2 { font-size: 1.6rem; font-family: var(--font-menu-display); letter-spacing: 3px; margin-bottom: 6px; }
.game-menu-main.hidden { display: none !important; }
.game-menu-vote-kick-panel { display: flex; flex-direction: column; gap: 10px; align-items: stretch; min-width: 280px; max-width: 360px; }
.game-menu-vote-kick-panel.hidden { display: none !important; }
.vote-kick-title { font-size: 1.15rem; letter-spacing: 1px; margin: 0; }
.vote-kick-hint { font-size: 0.8rem; color: var(--muted); margin: 0; line-height: 1.3; }
.vote-kick-player-list { display: flex; flex-direction: column; gap: 6px; max-height: 220px; overflow-y: auto; }
.vote-kick-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 10px; background: var(--panel2); border: 1px solid var(--border); border-radius: 6px;
}
.vote-kick-row-name { font-size: 0.9rem; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vote-kick-row-name.team-red { color: var(--red); }
.vote-kick-row-name.team-blue { color: var(--blue); }
.vote-kick-empty { font-size: 0.85rem; color: var(--muted, #8b949e); padding: 6px 2px; }

/* Report panel. Rows are selectable rather than instant-action: unlike vote-kick,
   reporting needs a target AND a reason before anything is sent. */
.report-row { width: 100%; text-align: left; cursor: pointer; font: inherit; color: inherit; }
.report-row:hover { border-color: var(--muted, #8b949e); }
.report-row--on { border-color: var(--red); background: rgba(248, 81, 73, 0.12); }
.report-reason {
  width: 100%;
  margin-top: 10px;
  padding: 8px 10px;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: inherit;
  font: inherit;
  font-size: 0.88rem;
  resize: vertical;
  outline: none;
}
.report-reason:focus { border-color: var(--muted, #8b949e); }
.report-foot { display: flex; align-items: center; gap: 10px; margin-top: 5px; }
.report-count { font-size: 0.72rem; color: var(--muted, #8b949e); }
.report-status { font-size: 0.78rem; margin-left: auto; text-align: right; }
.report-status--error { color: var(--red); }
.report-status--ok { color: var(--green); }
.report-actions { display: flex; gap: 8px; margin-top: 10px; }
.report-actions .btn { flex: 1; }
.vote-kick-banner {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 60;
  max-width: min(340px, calc(100% - 16px));
  background: rgba(22, 27, 34, 0.95);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.45);
}
.vote-kick-banner.hidden { display: none; }
.vote-kick-banner-text { font-size: 0.85rem; line-height: 1.35; margin-bottom: 8px; color: var(--text); }
.vote-kick-banner-actions { display: flex; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
.vote-kick-banner-actions.hidden { display: none !important; }
.vote-kick-banner-note { font-size: 0.75rem; color: var(--muted); margin-bottom: 4px; min-height: 1em; }
.vote-kick-banner-time { font-size: 0.8rem; font-weight: 600; color: var(--gold); font-variant-numeric: tabular-nums; }
.btn-danger { background: #7a1a1a; border-color: #c0392b; color: #fff; }
.btn-danger:hover { background: #a02020; border-color: #e74c3c; }
.hud-center {
  display: flex;
  align-items: center;
  gap: 0;
}
.hud-scoreboard {
  display: flex;
  align-items: center;
  gap: 8px;
}
.hud-divider {
  width: 1px;
  height: 28px;
  background: var(--border);
  margin: 0 18px;
}
.hud-clock-block {
  text-align: center;
}
.hud-period-block {
  text-align: center;
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}
.team-label {
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 3px;
}
.team-label.red { color: var(--red); }
.team-label.blue { color: var(--blue); }
.score-num {
  font-family: var(--font-display);
  font-size: 2rem;
  color: var(--text);
  min-width: 28px;
  text-align: center;
}
.score-dash {
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--muted);
  margin: 0 2px;
}
#game-clock {
  font-family: var(--font-display);
  font-size: 1.8rem;
  color: var(--gold);
  letter-spacing: 2px;
}
#game-period {
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 2px;
  color: var(--muted);
  text-transform: uppercase;
}
.hud-shot-counter {
  margin-top: 1px;
  font-size: 0.7rem;
  letter-spacing: 0.7px;
  color: #9fb0c7;
}

.canvas-container {
  position: relative;
  width: min(1173px, 100vw);
  aspect-ratio: 16 / 9;
  flex-shrink: 0;
  cursor: crosshair; /* JS swaps in the player's custom cursor; canvases inherit it */
}
#game-canvas {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0 0 10px 10px;
  cursor: inherit;
}
#game-canvas:hover { cursor: inherit; }

/* 3D renderer: WebGL fills the rink area; 2D canvas stays for overlay pixels only */
body.renderer-3d-active #game-canvas {
  opacity: 0;
  pointer-events: none;
  position: absolute;
  width: 1px;
  height: 1px;
  left: 0;
  top: 0;
  overflow: hidden;
}

#game-3d-canvas {
  display: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: 100%;
  border-radius: 0 0 10px 10px;
  cursor: inherit;
  z-index: 1;
  object-fit: fill;
}
#game-3d-canvas.active {
  display: block;
}

/* Desktop ability row in 3D (matches canvas drawCooldownBox) */
/* ── Spectator Puck Cam toggle ──
   Bottom-centre of the rink view. Low-contrast at rest so it reads as chrome
   rather than gameplay; it only firms up on hover or when it's actually on. */
/* The row owns the placement; the buttons just sit in it. They were each absolutely
   positioned at bottom-centre, so they landed on the same pixel and the later one in the
   DOM covered the other. */
.spectator-cam-bar {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 8px;
  pointer-events: none;   /* the bar is only a layout box — clicks belong to the buttons */
}
.spectator-puckcam-btn {
  position: relative;
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  background: rgba(6, 10, 17, 0.55);
  border: 1px solid rgba(160, 205, 230, 0.18);
  border-radius: 999px;
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0.68;
  backdrop-filter: blur(4px);
  transition: opacity 0.16s ease, color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
.spectator-puckcam-btn .ui-icon { width: 13px; height: 13px; }
.spectator-puckcam-btn:hover {
  opacity: 1;
  color: var(--text);
  border-color: rgba(160, 205, 230, 0.36);
  background: rgba(6, 10, 17, 0.78);
}
/* "On" used to be light gold on a 55%-opaque pill: over bright ice the yellow washed out and
   an engaged toggle read as off. Now the pill goes near-opaque dark with a saturated green rim
   and near-white text, so the state is unmistakable on ice, boards or crowd. */
.spectator-puckcam-btn.active {
  opacity: 1;
  color: #eafaf1;
  border-color: #21a35a;
  background: rgba(5, 24, 15, 0.92);
  box-shadow: 0 0 0 1px rgba(33, 163, 90, 0.45), 0 2px 12px rgba(0, 0, 0, 0.55);
}
.spectator-puckcam-btn.active:hover {
  color: #fff;
  border-color: #2bc46d;
  background: rgba(7, 32, 20, 0.95);
}

.canvas-container .desktop-ability-overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 10px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-end;
  gap: 8px 10px;
  padding: 0 8px;
  pointer-events: none;
  z-index: 6;
}
.desktop-ability-overlay.hidden {
  display: none !important;
}
.canvas-container .stick-zone-hud {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 92px;
  pointer-events: none;
  z-index: 7;
  padding: 6px 8px 4px;
  border-radius: 10px;
  background: rgba(6, 20, 10, 0.62);
  border: 1px solid rgba(70, 200, 110, 0.45);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
}
.canvas-container .stick-zone-hud canvas {
  display: block;
  width: 110px;
  height: 75px;
}
.desktop-ability-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-width: 48px;
}
.desktop-ability-box {
  position: relative;
  width: 44px;
  height: 44px;
  border-radius: 6px;
  box-sizing: border-box;
  background: rgba(10, 10, 20, 0.85);
  border: 2px solid #555;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.desktop-ability-box[data-state="ready"],
.desktop-ability-box[data-state="active"] {
  background: rgba(20, 20, 40, 0.85);
  border-color: var(--ability-color, #44ccff);
  box-shadow: 0 0 10px color-mix(in srgb, var(--ability-color, #44ccff) 45%, transparent);
}
.desktop-ability-box.unavailable {
  opacity: 0.38;
  filter: grayscale(0.35);
}
.desktop-ability-fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  border-radius: 0 0 4px 4px;
  pointer-events: none;
}
/* Progress toward an EXTRA charge while the ability is still usable (goalie boost, 1 of 2 held).
   Same translucent blue rise the 2D canvas HUD draws; sits above the normal fill. */
.desktop-ability-recharge {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0;
  background: rgba(100, 220, 255, 0.28);
  border-radius: 0 0 4px 4px;
  pointer-events: none;
}
.desktop-ability-count {
  position: absolute;
  top: 3px;
  right: 4px;
  font: 700 11px Impact, sans-serif;
  color: var(--ability-color, #44ccff);
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.9);
  z-index: 1;
}
.desktop-ability-key {
  position: relative;
  z-index: 2;
  font: bold 18px Impact, Arial Black, sans-serif;
  color: #555;
  line-height: 1;
}
.desktop-ability-box[data-state="ready"] .desktop-ability-key,
.desktop-ability-box[data-state="active"] .desktop-ability-key {
  font-size: 22px;
  font-weight: bold;
  color: var(--ability-color, #44ccff);
  text-shadow: 0 0 8px var(--ability-color, #44ccff);
}
.desktop-ability-label {
  font: 600 10px Segoe UI, system-ui, sans-serif;
  color: #0a0a0a;
  text-align: center;
  max-width: 72px;
  line-height: 1.1;
}
.desktop-ability-overlay.desktop-ability-over-black .desktop-ability-label {
  color: #f2f6fc;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}

/* Desktop shot meters in 3D (aligned with drawShotBars) */
.canvas-container .desktop-shot-overlay {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 118px;
  width: min(380px, calc(100% - 24px));
  display: flex;
  flex-direction: column;
  gap: 5px;
  pointer-events: none;
  z-index: 6;
}
.desktop-shot-overlay.hidden {
  display: none !important;
}
.desktop-shot-bar {
  box-sizing: border-box;
  border-radius: 5px;
  background: rgba(0, 0, 0, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.12);
  padding: 1px;
}
.desktop-shot-bar-power {
  height: 20px;
}
.desktop-shot-bar-distance {
  height: 13px;
}
.desktop-shot-fill {
  height: 100%;
  border-radius: 3px;
  width: 0%;
  min-width: 0;
  transition: width 0.04s linear, background 0.04s linear;
}

/* Recenter hints — shared pill */
.canvas-container .third-person-recenter-hint {
  position: absolute;
  z-index: 8;
  border-radius: 999px;
  background: rgba(12, 18, 28, 0.88);
  border: 1px solid rgba(120, 145, 178, 0.35);
  color: var(--text);
  text-align: center;
  pointer-events: none;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
/* Third-person chase: above ability row, not dead center */
.canvas-container #third-person-recenter-hint.third-person-recenter-hint {
  left: 50%;
  top: auto;
  bottom: calc(86px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  max-width: min(520px, calc(100% - 24px));
  padding: 9px 18px;
  font-size: 0.97rem;
  font-weight: 600;
  line-height: 1.35;
}
/* Regular pan/zoom: large, true viewport center of rink area */
.canvas-container #regular-view-recenter-hint.third-person-recenter-hint {
  left: 50%;
  top: 50%;
  bottom: auto;
  transform: translate(-50%, -50%);
  max-width: min(720px, calc(100% - 40px));
  padding: clamp(18px, 3.5vw, 28px) clamp(28px, 5vw, 48px);
  font-size: clamp(1.35rem, 3.5vw, 2.05rem);
  font-weight: 700;
  line-height: 1.3;
}
.canvas-container .third-person-recenter-hint.hidden {
  display: none !important;
}
.recenter-hint-inner {
  color: var(--muted);
}
#regular-view-recenter-hint .recenter-hint-inner {
  color: var(--text);
  font-size: 1em;
}
.recenter-kbd {
  display: inline-block;
  padding: 2px 8px;
  margin: 0 2px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel2);
  color: var(--text);
  font-size: 0.8em;
  font-family: ui-monospace, Consolas, monospace;
  font-weight: 600;
}
#regular-view-recenter-hint .recenter-kbd {
  padding: 0.2em 0.55em;
  font-size: 0.72em;
  vertical-align: middle;
}
#third-person-recenter-hint .recenter-kbd {
  font-size: 0.7em;
}

/* Ping / hint — lives IN the scoreboard bar, top-left, same plane as the other HUD text */
/* Left end of the HUD bar: the ping readout, then the Quick Match invite button. One
   absolutely-positioned row rather than two absolute elements, so the button always lands
   just past the ping text — "Ping: 123 ms · Germany" is far wider than the F1 hint, and a
   fixed offset would have them overlap on the longer string. Absolute (not an in-flow flex
   child) because .hud centres its only in-flow child: adding one here would shove the
   scoreboard off-centre. */
.hud-left {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  pointer-events: none;   /* layout box only — clicks belong to the button inside it */
  z-index: 2;             /* over the bar, under the absolutely-placed HUD buttons */
}
.hud-ping {
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 1px;
  color: #fff;
  white-space: nowrap;
  pointer-events: none;
}

/* Goal Banner */
.goal-banner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font-display);
  font-size: 3.85rem;
  letter-spacing: 3px;
  /* Black outline (matches player-name stroke) + colored glow */
  text-shadow:
    -3px -3px 0 #000, 3px -3px 0 #000, -3px 3px 0 #000, 3px 3px 0 #000,
    -3px 0 0 #000, 3px 0 0 #000, 0 -3px 0 #000, 0 3px 0 #000,
    0 0 30px currentColor, 0 4px 12px rgba(0,0,0,0.8);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s;
  white-space: pre-line;
  line-height: 1.05;
  text-align: center;
}
.goal-banner.show { opacity: 1; animation: goalPop 0.3s ease-out; }
.goal-banner.hidden { display: none; }
.goal-banner.goal-red { color: var(--red); }
.goal-banner.goal-blue { color: var(--blue); }
.goal-banner.goal-own { color: #f07820; }
/* Boss down. Same green as the checkpoint word (.gnt-cp-word) — the two land 900ms apart
   and both mean "that is banked", so they read as one beat rather than two systems. */
.goal-banner.gnt-green { color: #6cf59b; }

@keyframes goalPop {
  0% { transform: translate(-50%, -50%) scale(0.5); }
  60% { transform: translate(-50%, -50%) scale(1.1); }
  100% { transform: translate(-50%, -50%) scale(1); }
}

/* Goal banner must paint above 3D canvas (z-index 1) and its overlay canvas (z-index 3) */
.goal-banner { z-index: 4; }

/* Hat trick: bigger pop-in + pulsing gold glow layered over the team color */
.goal-banner.show.goal-hattrick {
  animation: goalPopBig 0.45s ease-out, hatTrickGlow 0.8s ease-in-out 0.45s infinite alternate;
}
@keyframes goalPopBig {
  0% { transform: translate(-50%, -50%) scale(0.35); }
  55% { transform: translate(-50%, -50%) scale(1.18); }
  100% { transform: translate(-50%, -50%) scale(1); }
}
@keyframes hatTrickGlow {
  from {
    text-shadow:
      -3px -3px 0 #000, 3px -3px 0 #000, -3px 3px 0 #000, 3px 3px 0 #000,
      -3px 0 0 #000, 3px 0 0 #000, 0 -3px 0 #000, 0 3px 0 #000,
      0 0 26px currentColor, 0 0 34px rgba(212, 175, 55, 0.5), 0 4px 12px rgba(0,0,0,0.8);
  }
  to {
    text-shadow:
      -3px -3px 0 #000, 3px -3px 0 #000, -3px 3px 0 #000, 3px 3px 0 #000,
      -3px 0 0 #000, 3px 0 0 #000, 0 -3px 0 #000, 0 3px 0 #000,
      0 0 34px currentColor, 0 0 64px rgba(255, 224, 138, 0.95), 0 4px 12px rgba(0,0,0,0.8);
  }
}

/* Faceoff countdown — DOM overlay used in 3D mode (2D mode draws on canvas) */
.countdown-overlay {
  position: absolute;
  pointer-events: none;
  z-index: 4;
  font: 88px/1 var(--font-display);
  color: #44ee44;
  /* Black outline (matches player-name stroke) + green glow */
  text-shadow:
    -3px -3px 0 #000, 3px -3px 0 #000, -3px 3px 0 #000, 3px 3px 0 #000,
    -3px 0 0 #000, 3px 0 0 #000, 0 -3px 0 #000, 0 3px 0 #000,
    0 0 20px #44ee44;
  text-align: center;
  transform: translateX(-50%);
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
}
.countdown-overlay.hidden { display: none; }

/* Ping (3D) / goalie opt-in toast — top-left of the rink view.
   Fullscreen: sit below HUD + shot-clock bar so it is not covered by the HUD (z-index 25). */
.game-notice-overlay {
  position: absolute;
  top: 6px;
  left: 6px;
  right: auto;
  text-align: left;
  pointer-events: none;
  z-index: 12;
  font: bold 15px/1.4 'Segoe UI', sans-serif;
  background: rgba(0,0,0,0.62);
  padding: 4px 8px;
  border-radius: 4px;
  color: #e0e0e0;
  box-shadow: 0 1px 6px rgba(0,0,0,0.45);
}
#game-content.fullscreen-stack-as-overlay .game-notice-overlay {
  top: calc(var(--fs-hud-h, 48px) + 6px + 6px);
}
.game-notice-overlay.hidden { display: none; }

/* Goalie rotation panel — same corner as .game-notice-overlay (opt-in toast); below it in z-order */
.goalie-rotation-overlay-dom {
  position: absolute;
  top: 6px;
  left: 6px;
  pointer-events: none;
  z-index: 11;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
#game-content.fullscreen-stack-as-overlay .goalie-rotation-overlay-dom {
  top: calc(var(--fs-hud-h, 48px) + 6px + 6px);
}
.goalie-rotation-overlay-dom.hidden { display: none; }
.goalie-rotation-block {
  background: rgba(0,0,0,0.62);
  border: 1.4px solid currentColor;
  padding: 6px 8px;
  font: 12px 'Segoe UI', sans-serif;
  min-width: 200px;
}
.goalie-rotation-title {
  font-weight: bold;
  margin-bottom: 4px;
}
.goalie-rotation-row { padding: 1px 0; color: #dbe7fb; white-space: nowrap; }
.goalie-rotation-row.current { background: rgba(255,255,255,0.14); color: #fff4a3; }

/* Offscreen puck arrow indicator */
.offscreen-puck-indicator {
  position: absolute;
  width: 42px;
  height: 30px;
  pointer-events: none;
  /* Must paint above the in-canvas HUD layers that ring the edges it clamps to:
     ability bar (6), stick-zone HUD (7), minimap (4), goal banner (4). The fullscreen
     scoreboard (25) stays above — the layout clamps below it instead. */
  z-index: 20;
}
.offscreen-puck-indicator.hidden { display: none; }
.offscreen-puck-indicator--third-person { width: 126px; height: 90px; }
.offscreen-puck-indicator-arrow {
  width: 100%;
  height: 100%;
  /* Arrow pointing right — rotated inline by JS to point toward puck */
  clip-path: polygon(93% 50%, 21% 17%, 32% 37%, 0% 37%, 0% 63%, 32% 63%, 21% 83%);
  background: linear-gradient(90deg, #6aa5ff, #b4eaff, #ecfbff);
  filter: drop-shadow(0 0 5px rgba(120,220,255,0.85));
}

/* Third-person minimap — bottom-right corner */
.third-person-minimap-wrap {
  position: absolute;
  bottom: 14px;
  right: 14px;
  pointer-events: none;
  z-index: 4;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.5);
}
.third-person-minimap-wrap.hidden { display: none; }

/* One-timer bars — 2D mode only; 3D draws these on WebGL overlay canvas */
.onetime-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 4;
}
.onetime-overlay.hidden { display: none; }
.onetime-player-bar {
  position: absolute;
  width: 60px;
  height: 4px;
  background: rgba(0,0,0,0.6);
  border-radius: 4px;
  overflow: hidden;
  transform: translateX(-50%);
}
.onetime-player-bar-fill {
  height: 100%;
  background: #f0c040;
  border-radius: 4px;
  transition: width 0.04s linear;
}

/* End Overlay */
.end-overlay {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: rgba(0,0,0,0.8);
  display: flex;
  align-items: center;
  justify-content: center;
}
.end-overlay.hidden { display: none; }
/* Game-end celebration handoff: the results slide up over the winners' huddle behind them */
.end-overlay.end-slide-in { animation: endSlideIn 0.6s ease-out; }
@keyframes endSlideIn {
  from { opacity: 0; transform: translateY(42px); }
  to { opacity: 1; transform: translateY(0); }
}

/* MVP banner — bottom of the screen during the winners' celebration (below the end overlay,
   which covers it when the results card slides in) */
#end-mvp-banner {
  position: fixed;
  left: 50%;
  bottom: 34px;
  transform: translateX(-50%);
  z-index: 9;
  background: linear-gradient(180deg, rgba(26, 21, 6, 0.93), rgba(12, 10, 4, 0.93));
  border: 1px solid #d4af37;
  border-radius: 12px;
  padding: 10px 30px 12px;
  text-align: center;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6), 0 0 24px rgba(212, 175, 55, 0.28);
  pointer-events: none;
}
#end-mvp-banner.hidden { display: none; }
#end-mvp-banner .mvp-crown { font-size: 26px; line-height: 1; }
#end-mvp-banner .mvp-label {
  color: #d4af37;
  font-family: var(--font-display);
  letter-spacing: 6px;
  font-weight: 800;
  font-size: 0.85rem;
  margin-top: 2px;
}
#end-mvp-banner .mvp-name { color: #fff; font-weight: 700; font-size: 1.25rem; margin-top: 2px; }
#end-mvp-banner .mvp-stats { color: #cfc6a4; font-size: 0.85rem; margin-top: 4px; }
#end-mvp-banner .mvp-stats b { color: #ffe08a; }
#end-mvp-banner.mvp-in { animation: mvpSlideUp 0.55s ease-out; }
@keyframes mvpSlideUp {
  from { opacity: 0; transform: translate(-50%, 30px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

/* ── Match awards — the honours card between the huddle and the Play of the Game ──
   Sits at z 2900, just under the POTG package (3000) that replaces it, and above all
   game chrome. The whole card is ~3.6s of screen time, so everything cascades in fast
   and nothing waits on anything else: the panel is already settling while the rows are
   still arriving. --aw-team is set from JS to the MVP's team colour. */
.awards-overlay {
  position: fixed;
  inset: 0;
  z-index: 2900;
  --aw-team: #d4af37;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  background:
    radial-gradient(80% 60% at 50% 50%, rgba(4, 6, 11, 0.72), rgba(4, 6, 11, 0.93) 70%);
  animation: awFadeIn 0.3s ease-out;
  font-family: var(--font-display);
}
.awards-overlay.hidden { display: none; }
.awards-overlay.awards-out { opacity: 0; transition: opacity 0.42s ease; }
@keyframes awFadeIn { from { opacity: 0; } to { opacity: 1; } }

/* Single light sweep across the card as it lands — the one flourish that sells it. */
.awards-sweep {
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 230, 160, 0.13) 48%, transparent 58%);
  transform: translateX(-100%);
  animation: awSweep 1.5s cubic-bezier(0.22, 1, 0.36, 1) 0.25s both;
}
@keyframes awSweep { to { transform: translateX(100%); } }

.awards-panel {
  position: relative;
  width: min(680px, 92vw);
  padding: 26px 32px 28px;
  border-radius: 18px;
  border: 1px solid rgba(212, 175, 55, 0.55);
  background: linear-gradient(180deg, rgba(20, 17, 8, 0.96), rgba(9, 9, 12, 0.97));
  box-shadow:
    0 24px 80px rgba(0, 0, 0, 0.75),
    0 0 40px rgba(212, 175, 55, 0.16),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  animation: awPanelIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes awPanelIn {
  from { opacity: 0; transform: translateY(26px) scale(0.96); }
  to { opacity: 1; transform: none; }
}

.awards-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.awards-rule {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(212, 175, 55, 0.65), transparent);
}
.awards-title {
  color: #d4af37;
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 8px;
  white-space: nowrap;
}

/* MVP hero block — deliberately the only thing on the card with a portrait-sized name. */
.awards-mvp {
  position: relative;
  border-radius: 14px;
  padding: 18px 22px 19px;
  margin-bottom: 14px;
  text-align: center;
  border: 1px solid color-mix(in srgb, var(--aw-team) 60%, rgba(212, 175, 55, 0.4));
  /* Neutral base, team colour on top. A gold-brown base swallowed the blue tint entirely and
     made a blue MVP read olive, while red just got warmer — the two teams have to look as
     different here as they do on the ice. */
  background:
    radial-gradient(120% 150% at 50% 0%, color-mix(in srgb, var(--aw-team) 34%, transparent), transparent 72%),
    linear-gradient(180deg, rgba(17, 18, 24, 0.88), rgba(9, 9, 13, 0.92));
  box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.45);
  opacity: 0;
  animation: awRowIn 0.46s cubic-bezier(0.16, 1, 0.3, 1) 0.16s both;
}
.awards-mvp:empty { display: none; }
.awards-mvp .aw-mvp-crown {
  font-size: 32px;
  line-height: 1;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.6));
}
.awards-mvp .aw-mvp-label {
  color: #d4af37;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 6px;
  margin-top: 4px;
}
.awards-mvp .aw-mvp-name > span { overflow-wrap: anywhere; }
.awards-mvp .aw-mvp-name {
  /* Flex so the rank badge sits centred beside the name rather than riding the text
     baseline — at 2.35rem an inline badge floats visibly off-centre. */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  color: #fff;
  font-size: 2.35rem;
  font-weight: 800;
  line-height: 1.15;
  margin-top: 3px;
  /* text-shadow, not drop-shadow: a filter on a gradient-clipped text node paints nothing. */
  text-shadow: 0 2px 14px color-mix(in srgb, var(--aw-team) 60%, transparent), 0 1px 2px #000;
}
.awards-mvp .aw-mvp-line {
  color: #cfc6a4;
  font-size: 1rem;
  letter-spacing: 2px;
  margin-top: 7px;
}
.awards-mvp .aw-mvp-line b { color: #ffe08a; }

.awards-list { display: flex; flex-direction: column; gap: 8px; }

.awards-row {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 11px 16px;
  border-radius: 10px;
  border-left: 4px solid var(--aw-row-team, #d4af37);
  background: linear-gradient(90deg, color-mix(in srgb, var(--aw-row-team, #d4af37) 13%, transparent), rgba(255, 255, 255, 0.035) 45%);
  opacity: 0;
  animation: awRowIn 0.42s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes awRowIn {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
.awards-row .aw-icon { font-size: 22px; line-height: 1; text-align: center; }
.awards-row .aw-text { min-width: 0; }
.awards-row .aw-label {
  color: #a49b80;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 3.5px;
}
.awards-row .aw-name {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  color: #fff;
  font-size: 1.22rem;
  font-weight: 700;
  line-height: 1.2;
}
/* Truncation moves to the text node: the badge is flex:none and must never be squeezed. */
.awards-row .aw-name > span {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.awards-row .aw-value {
  color: #ffe08a;
  font-size: 1.7rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  text-shadow: 0 0 14px rgba(255, 224, 138, 0.3);
}

@media (max-width: 700px) {
  .awards-panel { padding: 18px 18px 20px; }
  .awards-title { letter-spacing: 4px; font-size: 0.8rem; }
  .awards-mvp .aw-mvp-name { font-size: 1.6rem; }
  .awards-row .aw-name { font-size: 1rem; }
  .awards-row .aw-value { font-size: 1.25rem; }
}

/* ── Play of the Game — end-of-game broadcast package ──────────────────────────
   Full-screen stinger title card, then the captured goal clip with broadcast chrome
   (letterbox bars, lower-third, live REPLAY bug, slow-mo chip). --potg-team is set
   from JS to the scoring team's color. Sits above the results card (z 10) while it
   plays; game.js hides it before the results reveal. */
.potg-overlay {
  position: fixed;
  inset: 0;
  /* Above ALL game chrome (fullscreen-stack HUD, panels at z 2000) — the POTG package
     owns the whole frame; the results card (z 10) appears after it hides. */
  z-index: 3000;
  --potg-team: #d4af37;
  background:
    radial-gradient(120% 90% at 50% 110%, color-mix(in srgb, var(--potg-team) 16%, transparent), transparent 60%),
    radial-gradient(100% 80% at 50% -20%, rgba(212, 175, 55, 0.10), transparent 55%),
    #04060b;
  overflow: hidden;
  animation: potgFadeIn 0.35s ease-out;
  transition: opacity 0.45s ease;
  font-family: var(--font-display);
}
.potg-overlay.hidden { display: none; }
.potg-overlay.potg-out { opacity: 0; pointer-events: none; }
@keyframes potgFadeIn { from { opacity: 0; } to { opacity: 1; } }

/* Stinger title card */
.potg-stinger {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: 24px;
  transition: opacity 0.4s ease, transform 0.4s ease;
}
.potg-overlay.potg-stage-video .potg-stinger,
.potg-overlay.potg-stage-cine .potg-stinger {
  opacity: 0;
  transform: scale(1.06);
  pointer-events: none;
}

/* Cut flash between angles. Sits above everything but the skip button, opaque at
   the instant of the switch then falling away fast — long enough to hide the
   camera change, short enough to read as a cut rather than a fade. */
.potg-cut-flash {
  position: absolute;
  inset: 0;
  /* Above the video + 3D scene, deliberately BELOW the skip button — blacking out
     the only escape from the highlight, even for 0.4s, would be hostile. */
  z-index: 3;
  background: #000;
  opacity: 0;
  pointer-events: none;
}
.potg-cut-flash--on { animation: potgCutFlash 0.42s ease-out both; }
@keyframes potgCutFlash {
  0%   { opacity: 1; }
  55%  { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .potg-cut-flash--on { animation-duration: 0.2s; }
}

/* Cinematic angle: the live 3D scene shows through — overlay goes transparent and only
   the broadcast chrome (letterbox bars, badges, lower-third) stays on top. */
.potg-overlay.potg-stage-cine { background: transparent; }
.potg-overlay.potg-stage-cine .potg-video-wrap { opacity: 1; }
.potg-overlay.potg-stage-cine #potg-video { display: none; }
.potg-eyebrow {
  color: #cfc6a4;
  font-size: clamp(0.8rem, 1.6vw, 1.05rem);
  letter-spacing: 7px;
  animation: potgRise 0.5s ease-out both;
}
.potg-title {
  font-size: clamp(3rem, 9vw, 7rem);
  line-height: 0.95;
  letter-spacing: 6px;
  font-style: italic;
  background: linear-gradient(100deg, #8a6d1f 0%, #ffe9a3 22%, #d4af37 45%, #fff6d8 60%, #d4af37 78%, #8a6d1f 100%);
  background-size: 250% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 6px 24px rgba(212, 175, 55, 0.35));
  animation: potgRise 0.5s ease-out 0.12s both, potgShine 2.4s linear 0.5s infinite;
}
@keyframes potgShine {
  from { background-position: 125% 0; }
  to { background-position: -125% 0; }
}
.potg-title-bar {
  width: min(520px, 60vw);
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--potg-team), transparent);
  box-shadow: 0 0 18px var(--potg-team);
  animation: potgBarGrow 0.6s ease-out 0.3s both;
}
@keyframes potgBarGrow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.potg-stinger-scorer {
  color: #fff;
  font-size: clamp(1.6rem, 4vw, 2.8rem);
  letter-spacing: 4px;
  text-shadow: 0 0 22px color-mix(in srgb, var(--potg-team) 70%, transparent);
  animation: potgRise 0.5s ease-out 0.42s both;
}
.potg-tags {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  animation: potgRise 0.5s ease-out 0.55s both;
}
.potg-tag {
  color: #ffe08a;
  border: 1px solid #d4af37;
  border-radius: 999px;
  padding: 4px 14px 3px;
  font-size: clamp(0.8rem, 1.6vw, 1rem);
  letter-spacing: 3px;
  background: rgba(212, 175, 55, 0.10);
  box-shadow: 0 0 14px rgba(212, 175, 55, 0.25);
}
@keyframes potgRise {
  from { opacity: 0; transform: translateY(26px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Replay stage */
.potg-video-wrap {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease;
}
.potg-overlay.potg-stage-video .potg-video-wrap { opacity: 1; }
#potg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
}
.potg-letterbox {
  position: absolute;
  left: 0;
  right: 0;
  height: clamp(40px, 8vh, 84px);
  background: linear-gradient(180deg, rgba(2, 3, 6, 0.96), rgba(2, 3, 6, 0.55));
  pointer-events: none;
}
.potg-letterbox-top { top: 0; }
.potg-letterbox-bottom { bottom: 0; transform: rotate(180deg); }
/* Explicit skip control, top-left above the stage badge. Deliberately understated — it's an
   escape hatch, not a call to action; the highlight is the point of the screen. */
.potg-skip-btn {
  position: absolute;
  /* Above the cut flash — see .potg-cut-flash. */
  z-index: 6;
  top: clamp(10px, 2.2vh, 22px);
  left: clamp(14px, 2.5vw, 30px);
  padding: 10px 22px 9px;
  font-family: inherit;
  font-size: 1.15rem;
  letter-spacing: 3px;
  color: rgba(255, 224, 138, 0.72);
  background: rgba(8, 8, 12, 0.72);
  border: 1px solid rgba(212, 175, 55, 0.45);
  border-radius: 4px;
  cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.potg-skip-btn:hover,
.potg-skip-btn:focus-visible {
  color: #ffe08a;
  border-color: #d4af37;
  background: rgba(20, 18, 12, 0.9);
}
.potg-badge {
  position: absolute;
  /* Sits below the skip button, which owns the top-left corner. */
  top: clamp(64px, 9vh, 88px);
  left: clamp(14px, 2.5vw, 30px);
  color: #ffe08a;
  border: 1px solid #d4af37;
  border-left: 4px solid var(--potg-team);
  border-radius: 4px;
  padding: 5px 12px 4px;
  font-size: 0.95rem;
  letter-spacing: 3px;
  background: rgba(8, 8, 12, 0.75);
}
.potg-live {
  position: absolute;
  top: clamp(10px, 2.2vh, 22px);
  right: clamp(14px, 2.5vw, 30px);
  display: flex;
  align-items: center;
  gap: 8px;
  color: #fff;
  font-size: 0.95rem;
  letter-spacing: 3px;
  padding: 5px 12px 4px;
  border-radius: 4px;
  background: rgba(8, 8, 12, 0.75);
  border: 1px solid #444;
}
.potg-live-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #e34c4c;
  box-shadow: 0 0 10px #e34c4c;
  animation: potgLivePulse 1.1s ease-in-out infinite;
}
@keyframes potgLivePulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
.potg-slomo {
  position: absolute;
  top: clamp(48px, 8vh, 78px);
  right: clamp(14px, 2.5vw, 30px);
  color: #ffe08a;
  font-size: 0.9rem;
  letter-spacing: 4px;
  padding: 3px 10px 2px;
  border-radius: 4px;
  background: rgba(8, 8, 12, 0.8);
  border: 1px solid #d4af37;
  animation: potgLivePulse 0.9s ease-in-out infinite;
}
.potg-slomo.hidden { display: none; }
.potg-lower-third {
  position: absolute;
  left: clamp(14px, 2.5vw, 30px);
  bottom: clamp(14px, 3vh, 30px);
  display: flex;
  align-items: stretch;
  gap: 12px;
  background: linear-gradient(90deg, rgba(6, 8, 14, 0.92), rgba(6, 8, 14, 0.65) 78%, transparent);
  border-radius: 6px;
  padding: 10px 34px 10px 0;
  animation: potgLtIn 0.45s ease-out 0.25s both;
}
@keyframes potgLtIn {
  from { opacity: 0; transform: translateX(-40px); }
  to { opacity: 1; transform: translateX(0); }
}
.potg-lt-bar {
  width: 6px;
  border-radius: 3px;
  background: var(--potg-team);
  box-shadow: 0 0 14px var(--potg-team);
}
.potg-lt-main { display: flex; flex-direction: column; justify-content: center; }
.potg-lt-scorer {
  color: #fff;
  font-size: clamp(1.3rem, 2.6vw, 1.9rem);
  letter-spacing: 3px;
  line-height: 1.05;
}
.potg-lt-sub { color: #b9c2cc; font-size: clamp(0.85rem, 1.5vw, 1rem); letter-spacing: 2.5px; }
.potg-lt-chips { display: flex; align-items: center; gap: 8px; margin-left: 10px; }
.potg-chip {
  color: #ffe08a;
  border: 1px solid rgba(212, 175, 55, 0.7);
  border-radius: 999px;
  padding: 3px 11px 2px;
  font-size: 0.85rem;
  letter-spacing: 2px;
  background: rgba(212, 175, 55, 0.10);
  white-space: nowrap;
}
.potg-score-chip {
  position: absolute;
  right: clamp(14px, 2.5vw, 30px);
  bottom: clamp(14px, 3vh, 30px);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 7px;
  border-radius: 6px;
  background: rgba(6, 8, 14, 0.88);
  border: 1px solid #333a45;
  font-size: clamp(1rem, 2vw, 1.3rem);
  letter-spacing: 2.5px;
  animation: potgLtIn 0.45s ease-out 0.35s both;
}
.potg-score-team.red { color: var(--red); }
.potg-score-team.blue { color: var(--blue); }
.potg-score-dash { color: #8b949e; }
.potg-score-clock {
  color: #cfc6a4;
  font-size: 0.8em;
  border-left: 1px solid #333a45;
  padding-left: 10px;
  margin-left: 2px;
}
/* Narrow screens: the build-up chips would collide with the score chip — drop them
   (the stinger already sold the story) and shrink the skip button out of the way. */
@media (max-width: 900px) {
  .potg-lt-chips { display: none; }
  .potg-skip-btn { font-size: 0.95rem; letter-spacing: 2px; padding: 8px 16px 7px; }
}
.end-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 40px 48px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 320px;
  width: min(1560px, 96vw);
  max-width: 100%;
}
.end-card h2 { font-size: 2rem; font-family: var(--font-display); letter-spacing: 3px; }
.end-score { font-size: 1.1rem; color: var(--muted); }
.end-score strong { color: var(--text); }

/* Play Again (matchmaking results only). Deliberately louder than Return to Menu
   sitting under it — this is the action we want, so it reads as the primary and
   the quit button drops to a quiet outline beside it. */
.btn-play-again {
  background: linear-gradient(180deg, #4fd483 0%, var(--green, #3fb96b) 100%);
  color: #07200f;
  font-size: 1.05rem;
  font-weight: 700;
  padding: 15px 24px;
  letter-spacing: 0.14em;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.32) inset, 0 6px 20px -5px rgba(63, 185, 107, 0.6);
}
.btn-play-again:hover:not(:disabled) {
  background: linear-gradient(180deg, #66e199 0%, #46c877 100%);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.38) inset, 0 10px 28px -6px rgba(63, 185, 107, 0.7);
}
.btn-play-again.hidden { display: none; }
/* With Play Again present, Return to Menu is the secondary way out. */
.end-card .btn-play-again:not(.hidden) + #btn-return {
  background: transparent;
  color: var(--ice-dim);
  border: 1px solid var(--edge);
  box-shadow: none;
}
.end-card .btn-play-again:not(.hidden) + #btn-return:hover:not(:disabled) {
  background: var(--rink-hi);
  color: var(--text);
}

/* Period Break Overlay */
.period-break-overlay {
  position: fixed;
  inset: 0;
  z-index: 10;
  background: rgba(0,0,0,0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px;
}
.period-break-overlay.hidden { display: none; }
.break-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 14px;
  width: min(1560px, 99vw);
  box-shadow: 0 16px 60px rgba(0,0,0,0.6);
  display: flex;
  flex-direction: column;
  min-height: 0;
  max-height: min(94vh, 900px);
  max-height: min(94dvh, 900px);
  overflow: hidden;
}
.break-card h2 {
  font-size: 1.6rem;
  font-family: var(--font-display);
  letter-spacing: 3px;
  flex-shrink: 0;
}
.break-subtitle {
  margin-top: 2px;
  color: var(--muted);
  font-size: 0.82rem;
  flex-shrink: 0;
}
.break-teams {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 10px;
  min-width: 0;
}
.break-team {
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 6px 6px 4px;
  min-width: 0;
}
.break-team-title {
  font-family: var(--font-display);
  letter-spacing: 3px;
  font-size: 1.02rem;
  margin-bottom: 3px;
}
.break-team.break-red .break-team-title { color: var(--red); }
.break-team.break-blue .break-team-title { color: var(--blue); }

.break-player-row {
  display: flex;
  gap: 5px;
  padding: 3px 2px;
  border-top: 1px solid rgba(255,255,255,0.06);
  min-width: 0;
}
.break-player-row:first-child { border-top: none; }
.break-player-name {
  width: 82px;
  flex-shrink: 0;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.3px;
  font-size: 0.93rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.break-leader-crown {
  color: #f0c040;
  font-size: 0.88rem;
  vertical-align: baseline;
}
.break-player-stats {
  flex: 1;
  display: grid;
  /* 12 stats + wider PTS column (last) so blue-side box score doesn’t clip */
  grid-template-columns: repeat(12, minmax(26px, 1fr)) minmax(48px, 1.5fr);
  gap: 2px;
  min-width: 0;
}
.break-stat-cell {
  background: rgba(0,0,0,0.18);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 5px;
  padding: 3px 2px 2px;
  text-align: center;
  position: relative;
  cursor: help;
}
.break-stat-cell::after {
  content: attr(data-tooltip);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 6px);
  transform: translateX(-50%);
  background: rgba(10, 12, 16, 0.98);
  color: #e6edf7;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 6px;
  padding: 6px 8px;
  font-size: 0.8rem;
  line-height: 1.2;
  letter-spacing: 0.2px;
  white-space: nowrap;
  box-shadow: 0 6px 18px rgba(0,0,0,0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 5;
}
.break-stat-cell:hover::after {
  opacity: 1;
}
/* SA is the one tooltip that can't be centred. It carries the counts inline ("Save
   Percentage (Shots Saved: 9, Goals Allowed: 2)"), so it is several times wider than
   every other one, and it sits in the 12th of 13 columns — centred on its own cell, half
   of it hung off the right edge of the blue team's panel and got clipped. Anchoring the
   RIGHT edge to the cell instead makes it grow leftward, which cannot clip no matter how
   many shots the goalie faced. Deliberately only this cell: the rest are short enough
   that centring reads better. */
.break-stat-cell--savePct::after {
  left: auto;
  right: 0;
  transform: none;
}
.break-stat-label {
  font-size: 0.55rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  line-height: 1;
}
.break-stat-value {
  margin-top: 1px;
  font-family: var(--font-display);
  font-size: 0.85rem;
  line-height: 1;
  color: var(--text);
}
.break-stat-cell--points {
  border-color: rgba(240, 192, 64, 0.45);
  background: linear-gradient(180deg, rgba(240, 192, 64, 0.18), rgba(0, 0, 0, 0.22));
}
.break-stat-label--points {
  color: #ffd77a;
  font-size: 0.56rem;
  letter-spacing: 0.5px;
}
.break-stat-value--points {
  color: #ffd77a;
  font-size: 0.95rem;
  font-weight: 900;
}
.break-stat-value--best {
  font-weight: 800;
  color: #3fb950;
}
.break-stat-value--worst {
  color: #e34c4c;
}

/* Period break — tabs & shot map */
.break-tabs {
  display: flex;
  gap: 6px;
  margin: 8px 0 6px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 2px;
  flex-shrink: 0;
}
.break-tab {
  flex: 1;
  max-width: 220px;
  padding: 10px 14px;
  border: none;
  border-radius: 10px 10px 0 0;
  background: rgba(0, 0, 0, 0.2);
  color: var(--muted);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.15s, background 0.15s, box-shadow 0.15s;
}
.break-tab:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.04);
}
.break-tab-active {
  color: #e6edf7;
  background: linear-gradient(180deg, rgba(76, 143, 227, 0.22), rgba(22, 27, 34, 0.95));
  box-shadow: 0 -2px 0 0 var(--blue) inset;
}
.break-tab-panel-hidden,
.break-tab-panel[hidden] {
  display: none !important;
}
/* Box score tab: fill card below tabs, scroll if roster is tall */
#break-tab-stats:not(.break-tab-panel-hidden):not([hidden]) {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  overflow-x: hidden;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* ── Goal Replay Tab ─────────────────────────────────────────────────────── */
.replay-period-tabs {
  display: flex;
  gap: 3px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.replay-period-tab {
  padding: 1px 6px;
  border-radius: 20px;
  border: 1px solid rgba(255,255,255,0.14);
  background: transparent;
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 0.62rem;
  letter-spacing: 1px;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
  line-height: 1.6;
}
.replay-period-tab:hover {
  background: rgba(255,255,255,0.06);
  color: var(--text);
}
.replay-period-tab-active {
  background: rgba(76,143,227,0.2);
  border-color: var(--blue);
  color: #e6edf7;
}
#break-tab-replay:not(.break-tab-panel-hidden):not([hidden]) {
  flex: 1 1 auto;
  min-height: min(52vh, 460px);
  min-height: min(52dvh, 460px);
  overflow: hidden;
  display: flex;
}
.replay-layout {
  display: flex;
  gap: 10px;
  flex: 1;
  min-height: 0;
  padding: 4px 0 2px;
}
.replay-player-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.replay-video-wrap {
  position: relative;
  flex: 1;
  background: #000814;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.12), 0 4px 30px rgba(0,0,0,0.7);
  min-height: 120px;
}
/* "REPLAY" broadcast badge */
.replay-badge {
  position: absolute;
  top: 10px;
  left: 12px;
  background: #f0c040;
  color: #000;
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 3px;
  padding: 3px 9px 2px;
  border-radius: 3px;
  z-index: 10;
  animation: replayBadgePulse 1.8s ease-in-out infinite;
  pointer-events: none;
}
@keyframes replayBadgePulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}
/* No goals fallback */
.replay-no-goals {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 0.88rem;
  font-style: italic;
}
.replay-no-goals.hidden { display: none; }
/* Goal list sidebar */
.replay-list-col {
  width: 175px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: rgba(0,0,0,0.28);
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: 8px;
  overflow: hidden;
}
.replay-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  font-family: var(--font-display);
  letter-spacing: 3px;
  font-size: 0.68rem;
  color: var(--muted);
  padding: 7px 10px 6px;
  background: rgba(255,255,255,0.03);
  border-bottom: 1px solid rgba(255,255,255,0.07);
  flex-shrink: 0;
}
.replay-goal-list {
  flex: 1;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.14) transparent;
}
.replay-goal-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.05);
  cursor: pointer;
  transition: background 0.12s;
  position: relative;
  user-select: none;
}
.replay-goal-item:hover { background: rgba(255,255,255,0.05); }
.replay-goal-item.replay-active { background: rgba(76,143,227,0.13); }
.replay-goal-item.replay-active::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--blue);
  border-radius: 0 2px 2px 0;
}
.replay-goal-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}
.replay-goal-dot.red  { background: var(--red);  box-shadow: 0 0 4px var(--red); }
.replay-goal-dot.blue { background: var(--blue); box-shadow: 0 0 4px var(--blue); }
.replay-goal-details { flex: 1; min-width: 0; }
.replay-goal-scorer {
  font-size: 0.81rem;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.replay-goal-score {
  font-size: 0.7rem;
  color: #f0c040;
  letter-spacing: 0.4px;
  margin-top: 1px;
}
.replay-goal-num {
  font-family: var(--font-display);
  font-size: 0.7rem;
  color: var(--muted);
  flex-shrink: 0;
  letter-spacing: 1px;
}
.replay-goal-item.replay-active .replay-goal-num {
  color: var(--blue);
  animation: replayNumPulse 1.1s ease-in-out infinite;
}
@keyframes replayNumPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
/* Replay tab button — subtle play-icon accent */
.break-tab-replay-btn { gap: 4px; }

/* Shot map tab: column layout — toolbar strip, then chart | insights.
   break-card only has max-height, so flex-basis 0 + min-height 0 collapsed the body to 0px. */
#break-tab-shotmap:not(.break-tab-panel-hidden):not([hidden]) {
  flex: 1 1 auto;
  min-height: min(52vh, 480px);
  min-height: min(52dvh, 480px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  gap: 6px;
}
.break-shotmap-toolbar {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 12px 18px;
  align-items: flex-end;
  margin-bottom: 12px;
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
}
#break-tab-shotmap .break-shotmap-toolbar {
  flex-shrink: 0;
  gap: 8px 14px;
  margin-bottom: 0;
  padding: 6px 10px;
  border-radius: 8px;
}
/* Left: chart grows; right: insights scroll — min-height so grid gets real space without a fixed card height */
.break-shotmap-body {
  flex: 1 1 auto;
  min-height: min(44vh, 400px);
  min-height: min(44dvh, 400px);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(220px, 34%);
  gap: 10px;
  align-items: stretch;
}
.break-shotmap-chart-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  gap: 6px;
}
.break-shotmap-insights-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
.break-shotmap-insights-col .break-insights {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin-top: 0;
  overflow: hidden;
}
.break-shotmap-insights-col .break-insights-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding-right: 4px;
}
.break-shotmap-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.9px;
  text-transform: uppercase;
  color: var(--muted);
}
#break-tab-shotmap .break-shotmap-label {
  gap: 2px;
  font-size: 0.58rem;
  letter-spacing: 0.55px;
}
.break-shotmap-select {
  min-width: 140px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.88rem;
  font-family: inherit;
  cursor: pointer;
}
#break-tab-shotmap .break-shotmap-select {
  min-width: 118px;
  padding: 4px 8px;
  border-radius: 6px;
  font-size: 0.72rem;
}
/* Custom select dropdowns — replaces native <select> in shotmap toolbar so Tab key
   (held to view stats) doesn't close the popup (Chrome closes native popups on Tab natively). */
.csw { position: relative; display: inline-block; }
.csw-btn {
  display: block;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
}
.csw-panel {
  position: absolute;
  z-index: 2000;
  top: calc(100% + 3px);
  left: 0;
  min-width: 100%;
  background: #1a2430;
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 7px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.65);
  overflow: hidden;
}
.csw-option {
  display: block;
  width: 100%;
  padding: 8px 14px;
  background: none;
  border: none;
  color: #cdd8e0;
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}
.csw-option:hover { background: rgba(255,255,255,0.09); }
.csw-option.csw-active { background: rgba(255,255,255,0.13); color: #fff; }
/* Rink area grows with chart column; SVG stays letterboxed inside (max-width/max-height 100%). */
.break-shotmap-wrap {
  position: relative;
  width: 100%;
  flex: 1 1 auto;
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid rgba(76, 143, 227, 0.35);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.35),
    0 6px 22px rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  background: radial-gradient(ellipse 120% 80% at 50% 45%, #1a2332 0%, #0a0e14 55%, #06080c 100%);
}
.break-shotmap-svg {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  flex-shrink: 1;
  vertical-align: middle;
}
.break-shotmap-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 20px;
  margin-top: 10px;
  font-size: 0.78rem;
  color: var(--muted);
}
#break-tab-shotmap .break-shotmap-chart-col .break-shotmap-legend {
  flex-shrink: 0;
  gap: 6px 10px;
  margin-top: 0;
  font-size: 0.68rem;
}
.break-shotmap-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
#break-tab-shotmap .break-shotmap-legend-item {
  gap: 5px;
}
.break-shotmap-legend-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.5);
}
#break-tab-shotmap .break-shotmap-legend-dot {
  width: 8px;
  height: 8px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
}
.break-shotmap-count {
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--muted);
}
#break-tab-shotmap .break-shotmap-chart-col .break-shotmap-count {
  flex-shrink: 0;
  margin-top: 0;
  font-size: 0.72rem;
  line-height: 1.35;
}
.break-insights {
  margin-top: 16px;
  padding: 14px 16px;
  border-radius: 12px;
  background: linear-gradient(145deg, rgba(76, 143, 227, 0.08), rgba(0, 0, 0, 0.35));
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.break-shotmap-insights-col .break-insights {
  padding: 10px 12px;
  border-radius: 10px;
}
.break-insights-title {
  font-family: var(--font-display);
  letter-spacing: 2px;
  font-size: 0.95rem;
  color: #8ab4f8;
  margin-bottom: 10px;
}
.break-shotmap-insights-col .break-insights-title {
  flex-shrink: 0;
  font-size: 0.78rem;
  letter-spacing: 1.4px;
  margin-bottom: 8px;
}
.break-insights-list {
  margin: 0;
  padding-left: 1.15rem;
  color: var(--text);
  font-size: 0.86rem;
  line-height: 1.55;
}
.break-shotmap-insights-col .break-insights-list {
  padding-left: 1.05rem;
  font-size: 0.8rem;
  line-height: 1.48;
}
.break-insights-list li {
  margin-bottom: 8px;
}
.break-shotmap-insights-col .break-insights-list li {
  margin-bottom: 6px;
}
.break-insights-list li:last-child {
  margin-bottom: 0;
}

/* Period break + shot map on phones / touch (safe areas, dvh, 44px tap targets) */
@media (max-width: 640px) {
  .period-break-overlay {
    padding: max(8px, env(safe-area-inset-top, 0px)) max(10px, env(safe-area-inset-right, 0px))
      max(10px, env(safe-area-inset-bottom, 0px)) max(10px, env(safe-area-inset-left, 0px));
    align-items: flex-start;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .break-card {
    width: 100%;
    max-width: 100%;
    padding: 12px max(10px, env(safe-area-inset-right, 10px)) 12px max(10px, env(safe-area-inset-left, 10px));
    margin: auto 0;
  }
  .break-card h2 {
    font-size: 1.25rem;
    letter-spacing: 2px;
  }
  .break-tabs {
    gap: 4px;
    margin: 10px 0 8px;
  }
  .break-tab {
    flex: 1;
    max-width: none;
    min-height: 44px;
    padding: 10px 6px;
    font-size: 0.68rem;
    letter-spacing: 0.4px;
    touch-action: manipulation;
  }
  #break-tab-shotmap:not(.break-tab-panel-hidden):not([hidden]) {
    min-height: min(70vh, 560px);
    min-height: min(70dvh, 560px);
  }
  .break-shotmap-body {
    grid-template-columns: 1fr;
    grid-template-rows: auto minmax(200px, 1fr);
    min-height: min(58vh, 480px);
    min-height: min(58dvh, 480px);
    gap: 8px;
  }
  .break-shotmap-chart-col {
    min-height: 200px;
    max-height: min(46dvh, 340px);
  }
  .break-shotmap-wrap {
    flex: 1 1 auto;
    min-height: 160px;
    max-height: min(40dvh, 300px);
  }
  .break-shotmap-insights-col {
    min-height: 180px;
  }
  .break-shotmap-insights-col .break-insights {
    min-height: 160px;
  }
  #break-tab-shotmap .break-shotmap-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    padding: 10px;
  }
  #break-tab-shotmap .break-shotmap-label {
    gap: 4px;
    font-size: 0.62rem;
  }
  #break-tab-shotmap .break-shotmap-select,
  #break-tab-shotmap .csw,
  #break-tab-shotmap .csw-btn {
    min-width: 0;
    width: 100%;
    min-height: 44px;
    padding: 10px 12px;
    font-size: 16px;
    border-radius: 8px;
  }
  #break-tab-replay:not(.break-tab-panel-hidden):not([hidden]) {
    min-height: min(62vh, 480px);
    min-height: min(62dvh, 480px);
  }
  .replay-layout { flex-direction: column; gap: 8px; }
  .replay-list-col { width: 100%; flex-direction: row; max-height: 100px; border-radius: 6px; }
  .replay-goal-list { display: flex; flex-direction: row; overflow-x: auto; overflow-y: hidden; }
  .replay-goal-item { flex-shrink: 0; flex-direction: column; align-items: center; gap: 4px; padding: 6px 10px; border-bottom: none; border-right: 1px solid rgba(255,255,255,0.05); width: 80px; }
  .replay-goal-details { text-align: center; }
  .replay-goal-item.replay-active::before { top: 0; bottom: auto; left: 0; right: 0; width: auto; height: 3px; border-radius: 0 0 2px 2px; }
  .replay-list-header { padding: 6px 10px 4px; }
}

/* End-game stats section */
.end-stats {
  margin-top: 8px;
}
.end-stats.hidden { display: none; }
.end-stats-teams {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.end-team {
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 8px 8px 5px;
}
.end-team-title {
  font-family: var(--font-display);
  letter-spacing: 3px;
  font-size: 0.95rem;
  margin-bottom: 4px;
}
.end-team.end-red .end-team-title { color: var(--red); }
.end-team.end-blue .end-team-title { color: var(--blue); }

@media (max-width: 980px) {
  .break-teams,
  .end-stats-teams {
    grid-template-columns: 1fr;
  }
}

/* Options Modal */
/* ===========================================================================
   OPTIONS
   Tabbed. Every row is the same two-column shape (label left, control right),
   which is what makes ~60 settings scannable instead of a wall.
   =========================================================================== */
.options-overlay {
  position: fixed;
  inset: 0;
  background: rgba(4, 7, 12, 0.82);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 18px;
  overflow-y: auto;
}
.options-card {
  width: min(860px, 96vw);
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  box-shadow: var(--shadow-modal);
  padding: 0;
  max-height: calc(100dvh - 36px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.options-header {
  padding: 20px 26px 14px;
  flex-shrink: 0;
  background: linear-gradient(180deg, rgba(168, 212, 232, 0.05), transparent);
}
.options-card h2 {
  font-family: var(--font-menu-display);
  font-size: 1.7rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
  margin-bottom: 7px;
}
.options-help {
  color: var(--ice-dim);
  font-size: 0.88rem;
  margin-bottom: 0;
  min-height: 1.3em;
}

/* ── Tab strip ── */
.options-tabs {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
  padding: 0 26px;
  border-bottom: 1px solid var(--edge);
  overflow-x: auto;
  scrollbar-width: none;
}
.options-tabs::-webkit-scrollbar { display: none; }
.options-tab {
  position: relative;
  flex: none;
  padding: 11px 16px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.options-tab:hover { color: var(--text); }
.options-tab.active { color: var(--gold); border-bottom-color: var(--gold); }

.options-body {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  padding: 18px 26px 20px;
}
/* Only the selected tab's section is in flow. Advanced also carries an inline
   display:none for non-desktop builds, so it needs both to be true. */
.options-body .options-section { display: none; margin: 0; }
.options-body .options-section.options-section-active { display: block; }
/* The section heading is redundant once its name is the active tab. */
.options-body .options-section-title { display: none; }

/* ── Rows ──
   One shape for every setting: label (+ optional hint) left, control right. */
.options-toggle-row,
.options-slider-row,
.options-select-row,
.options-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 10px 18px;
  min-height: 44px;
  padding: 8px 14px;
  margin: 0 0 4px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: 0.93rem;
  color: var(--text);
  cursor: pointer;
  user-select: none;
  transition: background 0.14s, border-color 0.14s;
}
.options-toggle-row:hover,
.options-slider-row:hover,
.options-select-row:hover,
.options-row:hover {
  background: rgba(168, 212, 232, 0.045);
  border-color: var(--edge);
}
/* Every setting's label is 600, whatever row type it lives in. Toggle rows put
   their label in a bare text node (no element to target), so the weight goes on
   the row and the sub-hint opts back out — that mismatch is why "Cursor Size"
   was bold and "Cursor color" wasn't. */
.options-toggle-row,
.options-slider-row,
.options-select-row,
.options-row,
.options-name-row { font-weight: 600; letter-spacing: 0.005em; }
.options-slider-label,
.options-select-label { font-weight: 600; letter-spacing: 0.005em; }

/* Descriptions of a setting, not the setting itself — these stay regular. */
.options-subhint {
  display: block;
  grid-column: 1;
  font-size: 0.78rem;
  color: var(--ice-dim);
  margin-top: 2px;
  font-weight: 400;
  letter-spacing: 0;
}
.options-name-lock,
.options-help { font-weight: 400; }
/* A setting that only applies when the one above it is on. */
.options-row-nested {
  margin-left: 22px;
  border-left: 1px solid var(--edge);
  border-radius: 0 var(--radius) var(--radius) 0;
}

/* ── Toggle: a real switch. Native checkboxes read as a web form, not a game. ── */
.options-toggle-row { grid-template-columns: 1fr auto; }
.options-toggle-row input[type="checkbox"] {
  order: 2;
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: none;
  width: 40px;
  height: 22px;
  margin: 0;
  border-radius: 11px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.options-toggle-row input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--ice-dim);
  transition: transform 0.18s cubic-bezier(0.3, 1.4, 0.6, 1), background 0.18s ease;
}
.options-toggle-row input[type="checkbox"]:checked {
  background: rgba(62, 134, 232, 0.35);
  border-color: rgba(62, 134, 232, 0.7);
}
.options-toggle-row input[type="checkbox"]:checked::after {
  transform: translateX(18px);
  background: #cfe4ff;
}
.options-toggle-row input[type="checkbox"]:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Slider ── */
.options-slider-control {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 240px;
}
.options-slider-control input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  width: 180px;
  height: 4px;
  border-radius: 2px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  cursor: pointer;
}
.options-slider-control input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--ice);
  border: 2px solid #0a1119;
  box-shadow: 0 0 0 1px var(--edge-hi), 0 2px 6px rgba(0, 0, 0, 0.6);
  cursor: pointer;
  transition: background 0.15s;
}
.options-slider-control input[type="range"]:hover::-webkit-slider-thumb { background: var(--gold); }
.options-slider-control input[type="range"]::-moz-range-thumb {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--ice);
  border: 2px solid #0a1119;
  cursor: pointer;
}
.options-slider-value {
  min-width: 48px;
  text-align: right;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  color: var(--gold);
}

/* ── Select ── */
.options-select,
select.stats-select {
  appearance: none;
  -webkit-appearance: none;
  background: var(--rink-deep)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%237d99ad' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat right 11px center / 11px 7px;
  color: var(--text);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 8px 32px 8px 12px;
  font-family: var(--font-body);
  font-size: 0.9rem;
  cursor: pointer;
  transition: border-color 0.15s;
}
.options-select:hover,
select.stats-select:hover { border-color: rgba(168, 212, 232, 0.35); }

input[type="color"] {
  width: 42px;
  height: 30px;
  padding: 2px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  cursor: pointer;
}

/* ── Key rebinding ── */
.rebind-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 4px 18px;
}
.rebind-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 8px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  transition: background 0.14s, border-color 0.14s;
}
.rebind-row:hover { background: rgba(168, 212, 232, 0.045); border-color: var(--edge); }
.rebind-label { font-weight: 600; }
/* Keycap: raised, monospace, with the physical bottom edge a key has. */
.rebind-key {
  min-width: 52px;
  height: 32px;
  padding: 0 10px;
  border-radius: var(--radius);
  border: 1px solid var(--edge-hi);
  border-bottom-width: 3px;
  background: linear-gradient(180deg, var(--rink-hi) 0%, #0e1622 100%);
  color: var(--ice-bright);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, transform 0.08s;
}
.rebind-key:hover { border-color: var(--gold); color: var(--gold); }
.rebind-key:active { transform: translateY(1px); border-bottom-width: 2px; }
/* While waiting for a keypress. */
.rebind-key.listening {
  border-color: var(--gold);
  color: var(--gold);
  background: rgba(242, 193, 78, 0.12);
}
.rebind-key:disabled { cursor: not-allowed; opacity: 0.5; }
/* Right-clicked clear: the action has no hotkey at all. */
.rebind-key--unbound { color: var(--ice-dim, #7d8ba0); border-bottom-color: var(--edge-hi); opacity: 0.7; }
/* Shares a reserved button, so it can only be switched on or off — never moved. */
.pad-rebind-key--fixed { letter-spacing: 0.02em; font-size: 0.78rem; }

.options-name-row {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
}
.options-name-label { color: var(--ice-dim); font-weight: 600; }
.options-name-input {
  flex: 0 0 220px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--text);
  padding: 9px 12px;
  outline: none;
}
.options-name-lock { color: var(--ice-dim); font-size: 0.85rem; }

.options-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-shrink: 0;
  padding: 14px 26px;
  border-top: 1px solid var(--edge);
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.25));
}
.options-actions .btn { width: auto; min-width: 116px; }
.gchat-translation {
  color: #a0c8ff;
  font-style: italic;
  font-size: 0.92em;
}
.chat-translation {
  color: #a0c8ff;
  font-style: italic;
  font-size: 0.92em;
}

/* ── Lobby Name Modal ────────────────────────────────────────────────────── */
/* ===========================================================================
   MODALS
   One backdrop treatment for both overlay families (.modal-overlay and
   .options-overlay), which previously differed in blur and opacity.
   =========================================================================== */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(4, 7, 12, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  padding: 20px;
  backdrop-filter: blur(8px);
}
.modal-card {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  padding: 26px 28px;
  width: min(440px, 92vw);
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: var(--shadow-modal);
}
/* Eyebrow + title, so a modal announces its category before its name. */
.modal-head { display: flex; flex-direction: column; gap: 5px; }
.modal-title {
  font-family: var(--font-menu-display);
  letter-spacing: 0.01em;
  font-size: 1.45rem;
  line-height: 1.05;
  color: var(--ice-bright);
}
.modal-actions { display: flex; gap: 10px; }
.modal-actions .btn { flex: 1; }
.modal-card-wide { width: min(520px, 94vw); }

.lobby-game-info {
  padding: 8px 20px 4px;
  font-size: 0.88rem;
  color: var(--muted);
  text-align: center;
  line-height: 1.45;
}
.lobby-game-mode {
  display: inline-block;
  font-weight: 700;
  color: var(--gold);
  margin-right: 6px;
}
.lobby-game-rules {
  display: block;
  margin-top: 4px;
  font-size: 0.82rem;
  color: var(--text);
}
.lobby-go-mode-row {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.lobby-go-radio {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  color: var(--text);
  font-size: 0.95rem;
}
.lobby-practice-rules {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 0 4px;
  border-top: 1px solid var(--border);
  margin-top: 4px;
}
.lobby-practice-rules.hidden { display: none; }
.lobby-go-hint {
  font-size: 0.8rem;
  color: var(--muted);
  margin-bottom: 2px;
}
.lobby-rule-check {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  color: var(--text);
  font-size: 0.9rem;
}

/* ── Customize Screen ────────────────────────────────────────────────────── */
/* ===========================================================================
   CUSTOMIZE
   Two panels: the character on the left at a size you can actually judge, the
   settings on the right. Previously four 112px thumbnails sat at the bottom of
   a 520px scrolling column.
   =========================================================================== */
.customize-shell {
  display: grid;
  grid-template-columns: minmax(300px, 420px) minmax(360px, 460px);
  gap: clamp(20px, 3vw, 36px);
  align-items: center;
  width: 100%;
  max-width: 940px;
  padding: 24px;
  max-height: 100dvh;
}
.customize-shell--no-stage { grid-template-columns: minmax(0, 520px); justify-content: center; }

.customize-stage {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  max-height: calc(100dvh - 48px);
}
.customize-stage[hidden] { display: none; }
.customize-stage-viewport {
  position: relative;
  aspect-ratio: 1;
  min-height: 0;
  border-radius: var(--radius);
  overflow: hidden;
  background:
    radial-gradient(70% 60% at 50% 18%, rgba(120, 180, 220, 0.12), transparent 70%),
    linear-gradient(180deg, #0e1826 0%, #070d16 100%);
  border: 1px solid var(--edge);
}
.customize-hero-canvas,
.customize-hero-fallback {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.customize-hero-canvas[hidden], .customize-hero-fallback[hidden] { display: none; }

.customize-slot-tabs {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 5px;
  flex-shrink: 0;
}
.customize-slot-tab {
  padding: 8px 10px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.customize-slot-tab:hover { color: var(--text); border-color: rgba(168, 212, 232, 0.3); }
.customize-slot-tab.active {
  color: var(--gold);
  border-color: var(--gold);
  background: rgba(242, 193, 78, 0.08);
}

.customize-card {
  max-width: 460px;
  width: 100%;
  max-height: calc(100dvh - 48px);
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 0;
  box-sizing: border-box;
}
.customize-card-header { flex-shrink: 0; margin-bottom: 16px; }
.customize-title {
  font-family: var(--font-menu-display);
  font-size: 1.7rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
  margin-top: 4px;
}
.customize-card-scroll {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
  -webkit-overflow-scrolling: touch;
}
.customize-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.customize-section-title {
  font-family: var(--font-data);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 0.78rem;
  color: var(--ice-cool);
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.customize-section-sub {
  color: var(--ice-dim);
  font-family: var(--font-body);
  font-size: 0.78rem;
  letter-spacing: 0;
  font-weight: 400;
  text-transform: none;
}
.customize-name-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.name-lock-msg {
  color: var(--muted);
  font-size: 0.8rem;
}
.name-lock-msg.hidden { display: none; }
.player-skin-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.player-skin-card {
  background: var(--panel2);
  border: 2px solid var(--border);
  border-radius: 10px;
  padding: 10px;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.15s, background 0.15s;
}
.player-skin-card:hover { border-color: var(--blue); background: #1a2235; }
.player-skin-card.selected {
  border-color: var(--gold);
  background: rgba(240, 192, 64, 0.07);
}
.player-skin-previews {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.player-skin-preview {
  width: 56px;
  height: 56px;
  object-fit: contain;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: rgba(0, 0, 0, 0.25);
  padding: 3px;
}
.player-skin-preview-new {
  transform: scale(0.9);
  transform-origin: center;
}
.player-skin-name {
  font-weight: 700;
  font-size: 0.9rem;
}
.player-skin-sub {
  margin-top: 2px;
  font-size: 0.75rem;
  color: var(--muted);
}

/* Horn selection grid */
.horn-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.horn-card {
  background: var(--panel2);
  border: 2px solid var(--border);
  border-radius: 8px;
  padding: 11px 14px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  transition: border-color 0.15s, background 0.15s;
  user-select: none;
}
.horn-card:hover { border-color: var(--blue); background: #1a2235; }
.horn-card.selected {
  border-color: var(--gold);
  background: rgba(240, 192, 64, 0.07);
}
.horn-card-wide {
  grid-column: span 2;
}
.horn-card-name {
  font-weight: 600;
  font-size: 0.9rem;
}
.horn-upload-icon {
  width: 22px;
  height: 22px;
  color: var(--muted);
  flex-shrink: 0;
  transition: color 0.15s;
}
.horn-card:hover .horn-upload-icon { color: var(--blue); }
.horn-card.selected .horn-upload-icon { color: var(--gold); }
.horn-preview-btn {
  background: rgba(255,255,255,0.05);
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.65rem;
  height: 28px;
  width: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.horn-preview-btn:hover {
  background: var(--blue);
  color: #fff;
  border-color: var(--blue);
}
.horn-custom-status {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  padding: 2px 9px;
  border-radius: 999px;
  margin-top: 3px;
  display: inline-block;
}
.horn-custom-status:empty { display: none; }
.horn-custom-status.pending { background: rgba(158,106,3,.25); color: #f0c040; border: 1px solid #9e6a03; }
.horn-custom-status.approved { background: rgba(26,71,48,.35); color: #3fb950; border: 1px solid #238636; }
.horn-custom-status.denied { background: rgba(61,20,20,.35); color: #f85149; border: 1px solid #da3633; }

/* Custom upload section */
.custom-horn-section {
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.custom-horn-section.hidden { display: none; }
.upload-instructions { color: var(--muted); font-size: 0.82rem; }
.upload-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.upload-file-label {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 1px;
  padding: 7px 13px;
  text-transform: uppercase;
  white-space: nowrap;
  transition: border-color 0.15s;
  flex-shrink: 0;
}
.upload-file-label:hover { border-color: var(--blue); }
.upload-file-label input[type="file"] { display: none; }
.horn-file-name {
  color: var(--muted);
  font-size: 0.82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
}
.horn-upload-success { color: #3fb950; font-size: 0.85rem; }
.horn-upload-success.hidden { display: none; }

/* Upload File / From YouTube segmented toggle */
.horn-source-toggle {
  display: flex;
  gap: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 3px;
  margin-bottom: 10px;
  width: fit-content;
}
.horn-source-tab {
  background: transparent;
  border: none;
  border-radius: 5px;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.3px;
  padding: 6px 12px;
  transition: background 0.15s, color 0.15s;
}
.horn-source-tab:hover { color: var(--text); }
.horn-source-tab.active { background: var(--blue); color: #fff; }
.horn-source-pane { display: flex; flex-direction: column; gap: 10px; }
.horn-source-pane.hidden { display: none; }
.yt-url-input { width: 100%; }
.yt-times-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.yt-time-field {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
  color: var(--muted);
}
.yt-time-input { width: 64px; text-align: center; }
.yt-hint { font-size: 0.76rem; color: var(--muted); opacity: 0.8; }
.yt-preview-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.yt-preview-row.hidden { display: none; }
.yt-preview-row audio { height: 32px; max-width: 220px; }

.customize-actions {
  display: flex;
  gap: 10px;
  margin-top: 0;
  padding-top: 12px;
  padding-bottom: max(4px, env(safe-area-inset-bottom, 0px));
  margin-top: 16px;
  border-top: 1px solid var(--edge);
  flex-shrink: 0;
  background: var(--rink);
  position: relative;
  z-index: 1;
}
.customize-actions .btn { flex: 1; }

/* Password section in Customize */
.pw-section { display: flex; flex-direction: column; gap: 10px; }
.pw-show-wrap { display: flex; flex-direction: row; align-items: center; gap: 6px; }
.pw-show-wrap input { flex: 1; }
.btn-show-pw {
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
  padding: 7px 10px;
  display: flex;
  align-items: center;
  transition: color 0.15s, border-color 0.15s;
  flex-shrink: 0;
}
.btn-show-pw:hover { color: var(--text); border-color: var(--blue); }
.pw-success { color: #3fb950; font-size: 0.85rem; }
.pw-success.hidden { display: none; }

/* Auth phase (name entry screen) */
.auth-phase { display: flex; flex-direction: column; gap: 10px; }
.auth-phase.hidden { display: none; }
.auth-msg { color: var(--muted); font-size: 0.88rem; text-align: center; }
#confirm-row.hidden { display: none; }

.login-password-help {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.login-password-help.hidden { display: none; }
.login-password-help-line {
  margin: 0;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
  text-align: center;
}
.login-password-help .btn { width: 100%; margin-top: 4px; }

/* Pause Overlay */
.pause-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 60;
}
.pause-overlay.hidden { display: none; }
.pause-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 36px 44px;
  text-align: center;
  min-width: 300px;
  box-shadow: 0 16px 60px rgba(0,0,0,0.7);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.pause-title {
  font-family: var(--font-display);
  font-size: 2.2rem;
  letter-spacing: 4px;
  color: var(--text);
  margin: 0;
}
.pause-team-label {
  font-size: 0.95rem;
  color: var(--muted);
  letter-spacing: 1px;
}
.pause-unpause-countdown {
  font-family: var(--font-display);
  font-size: 2rem;
  letter-spacing: 2px;
  color: #5cdb7a;
  line-height: 1.2;
}
.pause-unpause-countdown.hidden { display: none; }
.pause-countdown {
  font-family: var(--font-display);
  font-size: 3.2rem;
  letter-spacing: 2px;
  color: var(--text);
  line-height: 1;
  min-height: 3.5rem;
}
.pause-countdown.pause-countdown--dimmed {
  opacity: 0.45;
  font-size: 2.4rem;
  min-height: 2.8rem;
}
.pause-countdown.low { color: #e34c4c; }
.pause-expired-msg {
  font-size: 0.88rem;
  color: var(--muted);
}
.pause-expired-msg.hidden { display: none; }
.pause-hint {
  font-size: 0.8rem;
  color: var(--muted);
  opacity: 0.7;
}

/* Custom horn multi-slot */
.custom-slot {
  border-top: 1px solid var(--border);
  padding-top: 12px;
  margin-top: 12px;
}
.custom-slot:first-of-type { border-top: none; margin-top: 4px; }
.custom-slot-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.custom-slot-num {
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--text);
  letter-spacing: 0.5px;
}

/* ── Horn Picker Row (customize screen) ─────────────────────────────────── */
.horn-picker-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}
/* Skin Studio is the headline customisation but read as just another "Change…"
   link, so it gets a full-width primary button with a gold rim and a slow
   breathing glow — loud enough to find, quiet enough to sit in a settings list. */
.skin-studio-row { margin-top: 6px; }
.btn.btn-skin-studio {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 13px 18px;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  border: 1px solid var(--gold);
  box-shadow: 0 0 0 0 var(--gold-glow);
  animation: skin-studio-pulse 3.4s ease-in-out infinite;
}
.btn.btn-skin-studio .ui-icon { width: 20px; height: 20px; flex: none; }
.btn.btn-skin-studio:hover { animation: none; box-shadow: 0 0 18px 2px var(--gold-glow); }
@keyframes skin-studio-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242, 193, 78, 0); }
  50%      { box-shadow: 0 0 16px 1px var(--gold-glow); }
}
@media (prefers-reduced-motion: reduce) {
  .btn.btn-skin-studio { animation: none; box-shadow: 0 0 12px 1px var(--gold-glow); }
}
.horn-current-label {
  flex: 1;
  padding: 8px 12px;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.92rem;
  color: var(--text);
}
.customize-current-preview-wrap {
  margin-top: 6px;
  display: flex;
  align-items: center;
}
.customize-boost-preview {
  width: 220px;
  height: 60px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: #080e1a;
  display: block;
}

/* Render target only — the visible preview is the hero stage. The canvases stay
   in the DOM because refreshModelSkinCustomizeThumbnails() blits into them by id
   and un-hides each one on success, so this has to win with !important. */
.model-skins-preview-row {
  display: none !important;
}
.model-skin-preview-canvas {
  display: block;
  width: 100%;
  height: 100%;
  vertical-align: top;
}
.model-skin-preview-fallback {
  position: absolute;
  inset: 0;
  border-radius: 9px;
}
/* Locked custom horn slot — the controls stay visible but inert, so it's clear
   what you'd be unlocking rather than the slot just vanishing. */
/* Token balance at the top of the upload tab — it's the resource you spend
   there, so it belongs where you spend it, not only in the Store. */
.horn-token-balance {
  margin: 0 0 12px;
  padding: 9px 12px;
  border-radius: var(--radius);
  background: rgba(143, 191, 214, 0.1);
  border: 1px solid rgba(143, 191, 214, 0.3);
  border-left: 3px solid var(--ice-cool);
  color: var(--ice-bright);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.84rem;
  letter-spacing: 0.06em;
}
.horn-token-balance:empty { display: none; }
.horn-token-balance--empty {
  background: rgba(242, 193, 78, 0.09);
  border-color: rgba(242, 193, 78, 0.35);
  border-left-color: var(--gold);
  color: #f0dfae;
}

.custom-slot { position: relative; }
.custom-slot--locked > *:not(.custom-slot-locked):not(.custom-slot-header) {
  opacity: 0.28;
  pointer-events: none;
  filter: grayscale(0.6);
}
.custom-slot-locked {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin: 8px 0 4px;
  padding: 10px 12px;
  background: rgba(242, 193, 78, 0.09);
  border: 1px solid rgba(242, 193, 78, 0.4);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius);
  color: #f0dfae;
  font-size: 0.88rem;
}
.custom-slot-locked .btn { width: auto; flex-shrink: 0; }

/* ── Goal Horn Modal ─────────────────────────────────────────────────────── */
/* Three stacked sections used to make this one long scroll with Save buried at
   the bottom. Same shape as the Options card now: fixed header + tabs, one
   scrolling body, actions pinned so Save is always reachable. */
.horn-modal-card {
  width: min(760px, 96vw);
  max-height: calc(100dvh - 40px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  box-shadow: var(--shadow-modal);
  padding: 0;
}
.horn-modal-header { padding: 22px 26px 14px; flex-shrink: 0; }
.horn-modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 18px 26px 20px;
}
.horn-pane { display: none; }
.horn-pane--active { display: block; }

/* Tab strip — same language as Options and Stats. */
.horn-tabs {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
  padding: 0 26px;
  border-bottom: 1px solid var(--edge);
  overflow-x: auto;
  scrollbar-width: none;
}
.horn-tabs::-webkit-scrollbar { display: none; }
.horn-tab {
  flex: none;
  padding: 11px 16px;
  margin-bottom: -1px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.horn-tab:hover { color: var(--text); }
.horn-tab.active { color: var(--gold); border-bottom-color: var(--gold); }

.horn-modal-header {
  margin-bottom: 16px;
}
.horn-modal-title {
  font-size: 1.55rem;
  font-family: var(--font-menu-display);
  letter-spacing: 0.01em;
  line-height: 1.05;
  color: var(--ice-bright);
  margin-bottom: 5px;
}
.horn-modal-sub {
  color: var(--ice-dim);
  font-size: 0.88rem;
}
.horn-modal-actions {
  display: flex;
  justify-content: flex-end;
  flex-shrink: 0;
  padding: 14px 26px;
  border-top: 1px solid var(--edge);
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.25));
  gap: 10px;
}
.horn-modal-actions .btn { width: auto; min-width: 116px; }
.horn-subsection-title {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin: 0 0 8px;
  font-weight: 700;
}
.horn-playlist-block {
  margin-bottom: 18px;
}
.horn-playlist-mount {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.horn-playlist-slot-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.horn-slot-num {
  font-weight: 800;
  font-size: 0.78rem;
  color: var(--gold);
  min-width: 4.2rem;
}
.horn-slot-label {
  flex: 1;
  min-width: 120px;
  font-size: 0.88rem;
  color: var(--text);
}
.horn-library-block {
  margin-bottom: 18px;
}
.horn-library-sub {
  font-family: var(--font-data);
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ice-cool);
  margin: 14px 0 7px;
}
.horn-library-sub:first-of-type {
  margin-top: 0;
}
.horn-library-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.horn-lib-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 9px 11px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: 8px;
}
.horn-lib-name {
  flex: 1;
  min-width: 140px;
  font-size: 0.86rem;
  font-weight: 600;
}
.horn-lib-empty {
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted);
  font-style: italic;
}
.horn-upload-summary {
  font-size: 0.82rem;
  color: var(--muted);
  margin-bottom: 2px;
}
.horn-upload-summary:empty {
  display: none;
}

/* ── Boost Skin Modal ────────────────────────────────────────────────────── */
.boost-modal-card {
  width: min(780px, 96vw);
}
/* The grid is the modal's scrolling region.
   It sits directly between the fixed header and the fixed action bar, both of which are
   flex-shrink:0, so it has to be the one that flexes and scrolls — otherwise the card just
   grows past its max-height and .horn-modal-card's overflow:hidden clips the bottom rows off,
   taking Save with them. That was survivable at six trails and broke at fourteen. */
.boost-skin-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 26px 18px;
  align-content: start;   /* rows pack to the top instead of stretching to fill */
}
/* Full-width divider between Yours and the rest. */
.boost-skin-heading {
  grid-column: 1 / -1;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  font-weight: 700;
  margin: 2px 0 0;
  padding-top: 6px;
}
.boost-skin-heading:not(:first-child) { border-top: 1px solid var(--edge); padding-top: 14px; margin-top: 8px; }
.boost-skin-card {
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 11px 11px 9px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  transition: border-color 0.15s, background 0.15s;
  user-select: none;
}
.boost-skin-card:hover { border-color: var(--blue); background: #1a2235; }
.boost-skin-card.selected {
  border-color: var(--gold);
  background: rgba(240, 192, 64, 0.07);
}
.boost-skin-preview {
  width: 120px;
  height: 70px;
  border-radius: 6px;
  background: #080e1a;
  display: block;
}
.boost-skin-name {
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--text);
  text-align: center;
}

/* ── Lobby Chat ─────────────────────────────────────────────────────────── */
.lobby-chat {
  border-top: 1px solid var(--border);
  margin-top: 10px;
  padding: 8px 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.lobby-chat-messages {
  /* Raised from 110px alongside the type bump so the same number of messages
     still fits. */
  max-height: 132px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 0.95rem;
  color: var(--text);
  scrollbar-width: thin;
}
.lobby-chat-messages .chat-msg {
  line-height: 1.4;
  word-break: break-word;
}
.lobby-chat-messages .chat-msg .chat-sender {
  font-weight: 700;
  margin-right: 4px;
}
.lobby-chat-messages .chat-msg .chat-sender.team-red  { color: var(--red); }
.lobby-chat-messages .chat-msg .chat-sender.team-blue { color: var(--blue); }
.lobby-chat-messages .chat-msg .chat-sender.team-spectator { color: var(--muted); }
.lobby-chat-input-row {
  display: flex;
  gap: 6px;
}
.lobby-chat-input {
  flex: 1;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  padding: 6px 10px;
  font-size: 0.95rem;
  outline: none;
}
.lobby-chat-input:focus { border-color: var(--blue); }

/* ── Global Chat Dock ────────────────────────────────────────────────────────
   Body-level and fixed, because every .screen is position:fixed;inset:0 and only
   one is ever displayed — a panel inside one could not persist across screens.

   It never overlaps the menus rather than floating above them: .screen is a
   centering flex container, so giving those screens a right padding equal to the
   dock width simply re-centres their content in what is left. That one rule
   covers every screen, including the lobby, whose own .lobby-chat is untouched
   and ends up sitting beside this rather than under it. */
:root { --chat-dock-width: 312px; }

.chat-dock {
  position: fixed;
  /* The music widget and the toast host both shift left by --chat-dock-offset, so this
     no longer has to duck underneath them and can use the full height. */
  top: calc(var(--top-banner-offset, 0px) + 14px);
  right: 0;
  bottom: 14px;
  /* One below #music-widget (9995) on purpose: the music controls stay clickable. */
  z-index: 9994;
  display: flex;
  align-items: stretch;
  pointer-events: none;          /* the shell is inert; its children opt back in */
}
.chat-dock > * { pointer-events: auto; }

.chat-dock-panel {
  width: var(--chat-dock-width);
  /* Flex, NOT grid. Two of the six children (the notice and the join CTA) are
     display:none most of the time, and grid assigns its tracks positionally — so
     every remaining child shifted up a track and the input row inherited the 1fr
     meant for the log, rendering a ~450px-tall text box. Flex sizes each child
     from its own rule, so hiding one cannot resize another. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--rink);
  border: 1px solid var(--edge);
  /* The "boards kick plate" the .panel class uses — keeps this in the family. */
  border-top: 2px solid var(--edge-hi);
  border-right: none;
  border-radius: var(--radius) 0 0 var(--radius);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}
.chat-dock--collapsed .chat-dock-panel { display: none; }

.chat-dock-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  writing-mode: vertical-rl;
  /* Roughly double the original 30px/0.72rem tab — at that size it read as a scrollbar
     artefact rather than the way back into chat, and it is the only affordance there is
     once the dock is collapsed. */
  width: 58px;
  padding: 26px 8px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-right: none;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  cursor: pointer;
  align-self: center;
  box-shadow: var(--shadow-panel);
}
.chat-dock-handle:hover { color: var(--ice); background: var(--rink-hi); }
.chat-dock:not(.chat-dock--collapsed) .chat-dock-handle { display: none; }
.chat-dock-handle:hover { color: var(--ice); border-color: var(--edge-hi); }
.chat-dock-handle .ui-icon { width: 26px; height: 26px; }
.chat-dock-pip {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--dm, #8b5cf6);
}

.chat-dock-head {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: none;
  padding: 8px 8px 8px 10px;
  border-bottom: 1px solid var(--edge);
  background: var(--rink-hi);
}
.chat-dock-title {
  font-family: var(--font-menu-display);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ice);
  /* min-width:0 or the flex title refuses to shrink and shoves the collapse
     button past the right edge, where it is clipped by the panel's overflow. */
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-dock-online {
  font-family: var(--font-data);
  font-size: 0.78rem;
  color: var(--ice-dim);
}
.chat-dock-online::before {
  content: '';
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 5px;
  border-radius: 50%;
  background: var(--green, #5fd08a);
  vertical-align: middle;
}
.chat-dock-collapse {
  background: none; border: none; cursor: pointer;
  color: var(--ice-dim); font-size: 1.1rem; line-height: 1;
  padding: 0 2px;
}
.chat-dock-collapse:hover { color: var(--ice); }

.chat-dock-notice {
  flex: none;
  padding: 5px 10px;
  font-size: 0.78rem;
  color: #ffd9a0;
  background: rgba(122, 78, 12, 0.5);
  border-bottom: 1px solid var(--edge);
}

.chat-dock-roster {
  /* Hugs its contents, capped. Fixed-height would leave a big empty block on a quiet
     server, and uncapped would push the log off screen on a busy one.

     flex-shrink is 0, and that is the load-bearing part. It used to be 1, and the log's
     flex-basis is `auto` — so once a room had a few screens of backlog the log's BASE size
     was larger than the whole dock, the flex algorithm shrank every shrinkable sibling to
     make it fit, and this collapsed to a two-name sliver. Exactly backwards: the busier the
     room, the less of the roster you could see. Nothing is lost by refusing to shrink here,
     because the log already absorbs the overflow itself (flex: 1 1 auto, min-height: 0 and
     its own scrollbar).

     The cap is whichever is bigger: a third of the dock, or ~8 rows (29px each) plus room
     for their group headers. On a normal window 34% is already 10-12 rows; the rem floor is
     what keeps a busy roster useful in a short window instead of scrolling at 4 names. */
  flex: 0 0 auto;
  max-height: max(34%, 17.5rem);
  overflow-y: auto;
  padding: 4px 0;
  border-bottom: 1px solid var(--edge);
}
.chat-dock-roster-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 3px 10px;
  cursor: context-menu;
  /* 0.88rem -> 1.14rem and the badge 18 -> 23px: both +30%. The crest was
     unreadable at 18px, which defeats the point of showing rank here. */
  font-size: 1.14rem;
  color: var(--ice);
}
.chat-dock-roster-row:hover { background: var(--rink-hi); }
.chat-dock-roster-row .tier-badge { width: 23px; height: 23px; flex: none; }
.chat-dock-roster-name {
  flex: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-dock-roster-row--self .chat-dock-roster-name { color: var(--gold); }
.chat-dock-roster-region {
  font-family: var(--font-data);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--ice-dim);
  opacity: 0.75;
}
/* People in a live match stay listed but read as unavailable — a name vanishing
   mid-conversation is worse than a greyed one you cannot invite. */
.chat-dock-roster-row--match { opacity: 0.5; }
/* Blocked: still listed (you must be able to find them to unblock) but visibly inert. */
.chat-dock-roster-row--blocked .chat-dock-roster-name {
  opacity: 0.45;
  text-decoration: line-through;
}
.chat-dock-roster-status {
  font-family: var(--font-data);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--ice-dim);
}
.chat-dock-roster-group {
  padding: 6px 10px 2px;
  font-family: var(--font-menu-display);
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ice-dim);
  opacity: 0.7;
}

.chat-dock-log {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 10px;
  display: flex;
  flex-direction: column;
  /* 5px, not 3: room-chat lines are dense text rows, but a DM thread is bubbles and they
     need the separation to read as separate turns. */
  gap: 5px;
  font-size: 0.88rem;
  line-height: 1.35;
  color: var(--ice);
}
.chat-line { word-break: break-word; }
.chat-line-sender { font-weight: 700; margin-right: 4px; color: var(--ice-bright, #dbeeff); }
.chat-line--system { color: var(--ice-dim); font-size: 0.82rem; }
.chat-line--error { color: #ff9c9c; font-size: 0.82rem; }
/* Private messages read as a conversation, not a log: yours on the right in violet,
   theirs on the left on the panel surface. Side and colour identify the speaker, so the
   bubbles carry no name — in a two-person thread that was pure repetition.
   Both fills are dark enough for their text to clear AA comfortably (white on #5b34b8
   ≈ 8:1, --ice on --rink-hi ≈ 13:1); the violet is a solid rather than a gradient because
   a gradient dips under the threshold at its light end. */
.chat-bubble {
  max-width: 78%;
  padding: 5px 9px;
  border-radius: 10px;
  word-break: break-word;
  line-height: 1.35;
}
.chat-bubble--mine {
  align-self: flex-end;
  background: var(--dm-deep);
  color: #fff;
  border-bottom-right-radius: 3px;   /* the squared corner points at its sender */
}
.chat-bubble--theirs {
  align-self: flex-start;
  background: var(--rink-hi);
  color: var(--ice);
  border: 1px solid var(--edge);
  border-bottom-left-radius: 3px;
}
/* Notices inside a thread ("… is offline") belong to neither side. */
.chat-dock-log .chat-line--system { align-self: center; text-align: center; max-width: 92%; }

/* Scope rail — the clickable twin of the Tab ring. */
.chat-dock-scopes {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  flex: none;
  padding: 6px 8px;
  border-bottom: 1px solid var(--edge);
}
.chat-scope-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  padding: 3px 7px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.chat-scope-chip:hover { color: var(--ice); }
.chat-scope-chip--active { color: var(--ink); background: var(--gold); border-color: var(--gold); }
.chat-scope-chip--dm.chat-scope-chip--active { background: var(--dm); border-color: var(--dm); color: #fff; }
.chat-scope-unread {
  min-width: 15px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--dm);
  color: #fff;
  font-size: 0.68rem;
  text-align: center;
}
.chat-scope-chip--active .chat-scope-unread { background: rgba(0,0,0,0.35); }

/* Unread private message.
   A slow, two-state pulse rather than a fast flash: fast blinking reads as an error and is
   hard to ignore in a bad way, ~1.8s reads as "something is waiting for you".
   Both halves of the cycle are legible on their own — solid violet with white text
   (#6d3ed8 on white ≈ 6.3:1) alternating with the resting dark chip carrying light violet
   text (#c4b5fd on #141e2c ≈ 9:1) — so the label never washes out mid-blink, which is what
   goes wrong when people animate opacity instead. */
@keyframes chat-unread-pulse {
  0%, 100% {
    background: var(--rink-hi);
    border-color: var(--dm);
    color: var(--dm-edge);
    box-shadow: 0 0 0 0 rgba(139, 92, 246, 0);
  }
  50% {
    background: var(--dm-deep);
    border-color: var(--dm-edge);
    color: #fff;
    box-shadow: 0 0 10px 1px rgba(139, 92, 246, 0.55);
  }
}
.chat-scope-chip--unread {
  animation: chat-unread-pulse 1.8s ease-in-out infinite;
  font-weight: 700;
}

/* Same signal for anyone who keeps the dock collapsed: the edge tab carries it. */
@keyframes chat-handle-pulse {
  0%, 100% { color: var(--dm-edge); border-color: var(--dm); }
  50%      { color: #fff; border-color: var(--dm-edge); background: var(--dm-deep); }
}
.chat-dock--unread.chat-dock--collapsed .chat-dock-handle {
  animation: chat-handle-pulse 1.8s ease-in-out infinite;
}
.chat-dock-pip { animation: chat-handle-pulse 1.8s ease-in-out infinite; }

/* And on the person's row in the roster, which is where the eye goes first. */
.chat-dock-roster-row--unread .chat-dock-roster-name {
  color: var(--dm-edge);
  font-weight: 700;
}
.chat-dock-roster-unread {
  width: 8px; height: 8px; flex: none;
  border-radius: 50%;
  background: var(--dm);
  animation: chat-dot-pulse 1.8s ease-in-out infinite;
}
@keyframes chat-dot-pulse {
  0%, 100% { opacity: 0.35; box-shadow: 0 0 0 0 rgba(139, 92, 246, 0); }
  50%      { opacity: 1;    box-shadow: 0 0 8px 2px rgba(139, 92, 246, 0.6); }
}

/* Respect a reduced-motion preference: keep the colour, drop the blinking. */
@media (prefers-reduced-motion: reduce) {
  .chat-scope-chip--unread,
  .chat-dock--unread.chat-dock--collapsed .chat-dock-handle,
  .chat-dock-roster-unread,
  .chat-dock-pip { animation: none; }
  .chat-scope-chip--unread {
    background: var(--dm-deep); border-color: var(--dm-edge); color: #fff;
  }
}

/* Scope rail — the clickable twin of the Tab ring. */
.chat-dock-scopes {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  flex: none;
  padding: 6px 8px;
  border-bottom: 1px solid var(--edge);
}
.chat-scope-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  padding: 3px 7px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.chat-scope-chip:hover { color: var(--ice); }
.chat-scope-chip--active { color: var(--ink); background: var(--gold); border-color: var(--gold); }
.chat-scope-chip--dm.chat-scope-chip--active { background: var(--dm); border-color: var(--dm); color: #fff; }
.chat-scope-unread {
  min-width: 15px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--dm);
  color: #fff;
  font-size: 0.68rem;
  text-align: center;
}
.chat-scope-chip--active .chat-scope-unread { background: rgba(0,0,0,0.35); }

/* Unread private message. A chip that just appears is easy to miss while you are looking
   at the menu, so it pulses until the conversation is opened — which is the moment the
   unread count clears and this class comes off. */
@keyframes chat-unread-pulse {
  0%, 100% { background: var(--rink-hi); border-color: var(--dm); color: var(--ice); }
  50%      { background: var(--dm); border-color: var(--dm-edge); color: #fff; }
}
.chat-scope-chip--unread { animation: chat-unread-pulse 1.15s ease-in-out infinite; }

/* Same signal for anyone who keeps the dock collapsed: the edge tab carries it. */
@keyframes chat-handle-pulse {
  0%, 100% { color: var(--ice-dim); border-color: var(--edge); }
  50%      { color: #fff; border-color: var(--dm-edge); }
}
.chat-dock--unread.chat-dock--collapsed .chat-dock-handle {
  animation: chat-handle-pulse 1.15s ease-in-out infinite;
}
.chat-dock-pip { animation: chat-handle-pulse 1.15s ease-in-out infinite; }

/* Respect a reduced-motion preference: keep the colour, drop the blinking. */
@media (prefers-reduced-motion: reduce) {
  .chat-scope-chip--unread,
  .chat-dock--unread.chat-dock--collapsed .chat-dock-handle,
  .chat-dock-pip { animation: none; }
  .chat-scope-chip--unread { background: var(--dm); border-color: var(--dm); color: #fff; }
}
.chat-scope-close { opacity: 0.6; font-size: 0.9rem; line-height: 1; }
.chat-scope-close:hover { opacity: 1; }

.chat-dock-input-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 8px 10px;
  border-top: 1px solid var(--edge);
}
.chat-dock-input {
  flex: 1;
  min-width: 0;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice);
  padding: 6px 9px;
  font-size: 0.88rem;
  outline: none;
}
.chat-dock-input:focus { border-color: var(--ice-dim); }

.chat-dock-joincta {
  flex: none;
  padding: 10px;
  text-align: center;
  border-top: 1px solid var(--edge);
}
.chat-dock-joincta p {
  margin: 0 0 8px;
  font-size: 0.82rem;
  color: var(--ice-dim);
}
/* Left the room: the roster, the log and the input all go, because you cannot see
   this chat when you are not in it. */
.chat-dock--out .chat-dock-roster,
.chat-dock--out .chat-dock-log,
.chat-dock--out .chat-dock-scopes,
.chat-dock--out .chat-dock-input-row { display: none; }

/* Content moves over rather than being covered. Excludes the three full-bleed
   screens: the game and the skin-studio iframe fill their space, and padding the
   loading screen shifts the whole transition at the moment players are watching. */
.screen:not(#screen-game):not(#screen-skinstudio):not(#screen-loading) {
  padding-right: var(--chat-dock-offset, 0px);
  transition: padding-right 0.18s ease;
}

@media (max-width: 1100px), (max-height: 700px) {
  :root { --chat-dock-width: 264px; }
  /* The narrower dock leaves a full 20-char name (the maxlength cap) 3px short at
     the larger roster type. Take it from the gutters rather than shrinking the
     text back — the point of the size bump is that it be readable. */
  .chat-dock-roster-row { padding-left: 8px; padding-right: 8px; gap: 6px; }
}

/* ── In-game Chat Overlay ────────────────────────────────────────────────── */
#game-chat-overlay {
  position: absolute;
  left: 10px;
  bottom: 52px;
  width: 340px;
  pointer-events: none;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#game-chat-messages {
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Raised from 160px to hold the same message count at the larger size. */
  max-height: 190px;
  overflow: hidden;
  justify-content: flex-end;
}
.gchat-msg {
  font-size: 0.95rem;
  line-height: 1.35;
  padding: 3px 7px;
  border-radius: 4px;
  background: rgba(0,0,0,0.52);
  color: #e8f0f8;
  word-break: break-word;
  transition: opacity 1s ease;
  max-width: 100%;
}
.gchat-msg.fading { opacity: 0; }
.gchat-msg .gchat-sender { font-weight: 700; margin-right: 4px; }
.gchat-msg .gchat-sender.team-red  { color: #f07070; }
.gchat-msg .gchat-sender.team-blue { color: #70a8f0; }
.gchat-msg .gchat-scope-all { color: rgba(255,220,60,0.75); font-size: 0.8rem; margin-right: 3px; }
.gchat-msg .gchat-pause-line { color: rgba(230, 232, 240, 0.92); font-size: 1rem; }
.gchat-msg.gchat-system { color: var(--muted); font-size: 0.9rem; line-height: 1.3; }
/* Output from a command the player typed (-help, -showmap, -hornvolume ...). Reads at
   full white; the muted grey above stays for passive status lines nobody asked for. */
.gchat-msg.gchat-system.gchat-local { color: #fff; }
/* Server-wide admin broadcast. The colour lives in the BACKGROUND rather than
   the text: chat rows already sit on a dark pill, so dark red type would be
   unreadable. A deep red slab with near-white text reads as urgent and stays
   legible over both the ice and the neighbouring chat rows. */
.gchat-msg.gchat-admin {
  font-size: 1.18rem;
  font-weight: 800;
  line-height: 1.3;
  color: #fff2f2;
  background: rgba(122, 12, 20, 0.94);
  border-left: 4px solid #ff5b5b;
  padding: 7px 11px;
  letter-spacing: 0.2px;
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
  box-shadow: 0 2px 10px rgba(0,0,0,0.45);
}
/* Private message inside a match. Same idea as .gchat-admin above: the colour lives in
   the background, because these rows sit on a dark pill over the ice and coloured type
   would be unreadable. Violet is the only accent free of meaning here — red and blue are
   TEAMS, gold is credits/primary. */
.gchat-msg.gchat-dm {
  background: var(--dm-deep);   /* solid: a gradient dipped under AA at the light end */
  color: #fff;
  border-left: 3px solid var(--dm-edge);
  padding: 4px 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.35);
}
.gchat-msg.gchat-dm .gchat-sender { color: #fff; }
.gchat-msg.gchat-dm .gchat-dm-tag {
  font-family: var(--font-data);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  background: rgba(0,0,0,0.28);
  border-radius: 3px;
  padding: 1px 5px;
  margin-right: 6px;
}
/* Scope badge when the in-match chat bar is aimed at a conversation. */
.chat-scope-dm { background: var(--dm); color: #fff; }

.gchat-msg.gchat-admin .gchat-admin-tag {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 1.2px;
  color: #ffd9d9;
  background: rgba(0,0,0,0.34);
  border-radius: 3px;
  padding: 1px 6px;
  margin-right: 7px;
  vertical-align: 2px;
}

/* Menu-side notification for the same broadcast. Fixed top-right so it shows on
   every non-game screen without each one needing its own slot.
   Sits LEFT of the music widget rather than on top of it: that widget is
   right:14px with max-width:260px (so its left edge lands ~274px in) and it
   expands on hover, so the toast clears 274px plus a gap. Shares the widget's
   --top-banner-offset so the two line up under any top banner.
   z-index stays below the widget's 9995 — if they ever do meet, the control the
   player can interact with wins. */
#admin-toast-host {
  position: fixed;
  top: calc(var(--top-banner-offset, 0px) + 14px);
  /* 288px clears the music widget; the dock offset then clears the dock as well, so
     broadcasts and invite toasts never land underneath it. */
  right: calc(288px + var(--chat-dock-offset, 0px));
  transition: right 0.18s ease;
  z-index: 4000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;   /* only the toasts themselves take clicks */
  max-width: min(380px, calc(100vw - 304px - var(--chat-dock-offset, 0px)));
}
/* Too narrow to sit beside the widget — drop below it and use the full width. */
@media (max-width: 720px) {
  #admin-toast-host {
    right: 14px;
    top: calc(var(--top-banner-offset, 0px) + 60px);
    max-width: calc(100vw - 28px);
  }
}
/* Game invite. Same host and stack as the admin toast, but it asks for a decision, so it
   gets actions and the violet accent that already means "this is aimed at you". */
.admin-toast.admin-toast--invite { border-left: 3px solid var(--dm); }

/* Short chat notices (invite sent / answered / refused).
   Same stack, position and shape as the broadcast toast — but NOT its colour. The base
   .admin-toast is a deep red alarm slab, which is right for a server-wide broadcast and
   completely wrong for "invite sent": a green stripe on a red box reads as an error with
   a decoration. These get the normal panel surface, with the accent carried by the left
   border and the title. */
.admin-toast.admin-toast--chat {
  background: linear-gradient(180deg, rgba(20, 30, 44, 0.98), rgba(12, 19, 31, 0.98));
  border: 1px solid var(--edge-hi);
  border-left: 3px solid var(--ice-cool);
  color: var(--ice);
}
.admin-toast.admin-toast--chat .admin-toast-title { color: var(--ice-dim); }
.admin-toast.admin-toast--chat .admin-toast-x { color: var(--ice-dim); }
.admin-toast--chat .admin-toast-x:hover { color: var(--ice-bright); }
.admin-toast.admin-toast--chat.admin-toast--ok   { border-left-color: var(--green); }
.admin-toast.admin-toast--chat.admin-toast--ok   .admin-toast-title { color: var(--green); }
.admin-toast.admin-toast--chat.admin-toast--warn { border-left-color: var(--gold); }
.admin-toast.admin-toast--chat.admin-toast--warn .admin-toast-title { color: var(--gold); }
.admin-toast--chat .admin-toast-title {
  font-family: var(--font-data);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.admin-toast-actions {
  display: flex;
  gap: 8px;
  padding: 0 12px 10px;
}

.admin-toast {
  pointer-events: auto;
  background: linear-gradient(180deg, rgba(138, 16, 24, 0.97), rgba(104, 10, 18, 0.97));
  border: 1px solid rgba(255, 110, 110, 0.55);
  border-left: 4px solid #ff5b5b;
  border-radius: 8px;
  padding: 10px 12px;
  color: #fff2f2;
  box-shadow: 0 8px 26px rgba(0,0,0,0.5);
  animation: admin-toast-in 0.26s ease-out;
}
.admin-toast.leaving { opacity: 0; transform: translateX(12px); transition: opacity .25s ease, transform .25s ease; }
.admin-toast-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.admin-toast-title {
  font-size: 0.72rem; font-weight: 800; letter-spacing: 1.2px;
  text-transform: uppercase; color: #ffd0d0;
}
.admin-toast-x {
  background: transparent; border: none; color: #ffc9c9;
  font-size: 1.15rem; line-height: 1; cursor: pointer; padding: 0 2px;
}
.admin-toast-x:hover { color: #fff; }
.admin-toast-body { font-size: 0.95rem; font-weight: 600; line-height: 1.35; word-break: break-word; }
@keyframes admin-toast-in {
  from { opacity: 0; transform: translateX(18px); }
  to   { opacity: 1; transform: translateX(0); }
}
#game-chat-input-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(0,0,0,0.68);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 6px;
  padding: 4px 8px;
  pointer-events: auto;
}
#game-chat-input-bar.hidden { display: none; }
#game-chat-scope-badge {
  font-size: 0.72rem;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 3px;
  white-space: nowrap;
  cursor: default;
  user-select: none;
}
#game-chat-scope-badge.chat-scope-team {
  background: rgba(76,143,227,0.25);
  color: #70a8f0;
  border: 1px solid rgba(76,143,227,0.4);
}
#game-chat-scope-badge.chat-scope-all {
  background: rgba(240,192,64,0.2);
  color: #f0c040;
  border: 1px solid rgba(240,192,64,0.4);
}
/* Aimed at a private conversation. Id-scoped to match its siblings above — a bare class
   would lose to them the moment anyone reorders this block. */
#game-chat-scope-badge.chat-scope-dm {
  background: var(--dm);
  color: #fff;
  border: 1px solid var(--dm-edge);
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
}
#game-chat-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: #e8f0f8;
  font-size: 0.95rem;
  font-family: inherit;
  caret-color: #f0c040;
}
#game-chat-close {
  flex-shrink: 0;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 4px;
  color: #c9d1d9;
  font-size: 0.8rem;
  line-height: 1;
  padding: 3px 7px;
  cursor: pointer;
  user-select: none;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
#game-chat-close:hover {
  background: rgba(229,76,76,0.85);
  border-color: rgba(229,76,76,0.9);
  color: #fff;
}
.game-chat-history-overlay {
  position: absolute;
  inset: 0;
  z-index: 75;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(6, 10, 18, 0.62);
}
.game-chat-history-overlay.hidden { display: none; }
.game-chat-history-card {
  width: min(720px, 92vw);
  max-height: min(520px, 78vh);
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border-radius: 12px;
  border: 1px solid rgba(200, 220, 255, 0.22);
  background: linear-gradient(180deg, rgba(9,16,30,0.98), rgba(6,11,20,0.96));
  box-shadow: 0 16px 40px rgba(0,0,0,0.5);
}
.game-chat-history-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.game-chat-history-header h3 {
  margin: 0;
  font-size: 1rem;
  letter-spacing: 0.04em;
}
.game-chat-history-list {
  overflow-y: auto;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 8px;
  padding: 10px;
  background: rgba(0,0,0,0.2);
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 180px;
}
.gchat-history-row {
  font-size: 0.95rem;
  line-height: 1.3;
  color: #dce9ff;
  word-break: break-word;
}
.gchat-history-row .name {
  font-weight: 700;
}
.gchat-history-row .name.team-red { color: var(--red); }
.gchat-history-row .name.team-blue { color: var(--blue); }
.gchat-history-row .scope {
  color: #9fb8df;
  font-size: 0.74rem;
  margin-right: 6px;
}

.mobile-shot-bars {
  position: absolute;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  width: min(320px, 68vw);
  display: flex;
  flex-direction: column;
  gap: 5px;
  z-index: 33;
  pointer-events: none;
}
.mobile-shot-bars.hidden { display: none; }
.mobile-shot-bar-row {
  display: grid;
  grid-template-columns: 34px 1fr;
  align-items: center;
  gap: 7px;
}
.mobile-shot-bar-label {
  font-family: var(--font-display);
  letter-spacing: 1.2px;
  font-size: 0.68rem;
  color: #d8e9ff;
  text-align: right;
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
}
.mobile-shot-bar-track {
  height: 10px;
  border-radius: 999px;
  background: rgba(6, 10, 18, 0.78);
  border: 1px solid rgba(186, 212, 245, 0.34);
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.38);
}
.mobile-shot-bar-fill {
  height: 100%;
  width: 0%;
  transition: width 0.04s linear;
}
.mobile-shot-bar-fill-power {
  background: linear-gradient(90deg, #2f9bff 0%, #77d4ff 55%, #d1f3ff 100%);
}
.mobile-shot-bar-fill-distance {
  background: linear-gradient(90deg, #4a92ff 0%, #9f7fff 60%, #dc86ff 100%);
}

/* ── Shot Clock Bar ─────────────────────────────────────────────────────── */
.shot-clock-bar {
  display: flex;
  width: min(1173px, 100vw);
  height: 6px;
  gap: 2px;
  flex-shrink: 0;
}
.sc-half {
  flex: 1;
  background: #1c2330;
  border-radius: 3px;
  overflow: hidden;
  position: relative;
}
.sc-half-red { border-radius: 3px 3px 3px 3px; }
.sc-half-blue { border-radius: 3px 3px 3px 3px; }
/* Red fill drains right→left; blue fill drains left→right */
.sc-fill-red {
  position: absolute;
  top: 0; left: 0;
  height: 100%;
  width: 100%;
  background: #4c8fe3;
  border-radius: 3px;
  transform-origin: left center;
  transition: width 0.12s linear, background-color 0.3s;
}
.sc-fill-blue {
  position: absolute;
  top: 0; right: 0;
  height: 100%;
  width: 100%;
  background: #4c8fe3;
  border-radius: 3px;
  transform-origin: right center;
  transition: width 0.12s linear, background-color 0.3s;
}
.sc-fill-red.sc-urgent, .sc-fill-blue.sc-urgent {
  background: #e34c4c;
}
.sc-fill-red.sc-inactive, .sc-fill-blue.sc-inactive {
  opacity: 0.25;
}

/* ── Loading Screen ──────────────────────────────────────────────────────── */
.loading-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 48px 32px;
  width: 90vw;
  max-width: 560px;
}
.loading-main-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: 520px;
  min-width: 380px;
  padding: 28px 32px 24px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 8px 48px rgba(0,0,0,0.6);
}
.loading-tips {
  width: 100%;
  max-width: 520px;
  padding: 14px 16px 12px;
  background: linear-gradient(165deg, rgba(30, 40, 58, 0.95) 0%, var(--panel2) 55%, rgba(20, 28, 42, 0.98) 100%);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 4px 28px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.04);
}
.loading-tips-inner {
  transition: opacity 0.32s ease, transform 0.32s ease;
}
.loading-tips-inner--out {
  opacity: 0;
  transform: translateY(5px);
}
.loading-tips-badge {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin-bottom: 10px;
  padding: 5px 14px 6px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid rgba(88, 166, 255, 0.35);
  background: linear-gradient(120deg, rgba(56, 139, 253, 0.12) 0%, rgba(240, 180, 60, 0.08) 100%);
}
.loading-tips-badge-glow {
  position: absolute;
  inset: -40% -20%;
  background: radial-gradient(ellipse at 30% 50%, rgba(88, 166, 255, 0.25), transparent 55%),
    radial-gradient(ellipse at 70% 80%, rgba(240, 180, 60, 0.12), transparent 50%);
  pointer-events: none;
  animation: loadingTipsBadgeShimmer 4.5s ease-in-out infinite;
}
@keyframes loadingTipsBadgeShimmer {
  0%, 100% { opacity: 0.85; transform: translateX(0); }
  50% { opacity: 1; transform: translateX(4%); }
}
.loading-tips-badge-label {
  position: relative;
  z-index: 1;
  font-family: var(--font-menu-display);
  font-size: 1.15rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  line-height: 1.1;
  background: linear-gradient(100deg, #b8e8ff 0%, var(--ice) 40%, var(--gold) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 10px rgba(88, 166, 255, 0.22));
}
.loading-tips-copy {
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--text);
}
.loading-tips-copy p {
  margin: 0 0 8px;
}
.loading-tips-copy p:last-child {
  margin-bottom: 0;
}
.loading-tips-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-top: 10px;
  font-size: 0.78rem;
  color: var(--muted);
}
.loading-tips-track {
  display: flex;
  justify-content: center;
  gap: 7px;
  margin-top: 12px;
  padding-top: 2px;
}
.loading-tips-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border);
  opacity: 0.45;
  transition: background 0.25s ease, opacity 0.25s ease, transform 0.25s ease;
}
.loading-tips-dot.active {
  background: var(--ice);
  opacity: 1;
  transform: scale(1.2);
  box-shadow: 0 0 8px rgba(136, 220, 255, 0.35);
}
/* Loading-screen logo: same artwork as the main menu, scaled down to sit inside the
   ~456px-wide loading panel (menu uses min(46vw,460px); this is roughly half that). */
.loading-logo-img {
  display: block;
  width: min(72%, 260px);
  height: auto;
  margin: -6px auto 2px;
  filter: drop-shadow(0 6px 22px rgba(0, 0, 0, .55));
}
.loading-logo {
  font-family: var(--font-menu-display);
  font-size: 2.4rem;
  letter-spacing: 4px;
  background: linear-gradient(135deg, #fff 0%, var(--ice) 50%, var(--gold) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.loading-logo span {
  background: linear-gradient(135deg, var(--ice) 0%, var(--blue) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.loading-subtitle {
  color: var(--muted);
  font-size: 0.85rem;
  letter-spacing: 3px;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.loading-spinner-row {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.loading-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--blue);
  animation: loadingBounce 1.2s ease-in-out infinite;
}
.loading-dot:nth-child(2) { animation-delay: 0.2s; background: var(--ice); }
.loading-dot:nth-child(3) { animation-delay: 0.4s; background: var(--gold); }
@keyframes loadingBounce {
  0%, 80%, 100% { transform: scale(0.7); opacity: 0.5; }
  40% { transform: scale(1.1); opacity: 1; }
}
.loading-players-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 8px 0 4px;
}
.loading-player-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.loading-player-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.88rem;
}
.loading-player-name {
  font-weight: 600;
  color: var(--text);
}
.loading-player-status {
  font-size: 0.78rem;
  color: var(--muted);
  font-weight: 500;
}
.loading-player-status.ready {
  color: #3fb950;
}
.loading-bar-track {
  width: 100%;
  height: 8px;
  background: var(--panel2);
  border-radius: 4px;
  overflow: hidden;
  border: 1px solid var(--border);
}
.loading-bar-fill {
  height: 100%;
  border-radius: 4px;
  width: 0%;
  transition: width 0.4s ease;
  background: linear-gradient(90deg, var(--blue-dark), var(--blue), var(--ice));
}
.loading-bar-fill.ready {
  width: 100%;
  background: linear-gradient(90deg, #2ea043, #3fb950, #7ee787);
}
.loading-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  max-width: 520px;
  padding-top: 4px;
  margin-top: 2px;
}
.loading-status-text {
  color: var(--muted);
  font-size: 0.85rem;
  text-align: center;
}

/* Loading Leave Confirm */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
}
.modal-backdrop.hidden { display: none; }
.modal-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 28px 28px 24px;
  width: 90vw;
  box-shadow: 0 8px 40px rgba(0,0,0,0.7);
}
.modal-title {
  font-size: 1.2rem;
  font-weight: 700;
  margin-bottom: 12px;
  color: var(--text);
}

/* ═══════════════════════════════════════════════════════════════════════════
   GAME INTRO OVERLAY — TV Broadcast Style
   ═══════════════════════════════════════════════════════════════════════════ */

.game-intro-overlay {
  position: absolute;
  inset: 0;
  z-index: 55;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.85s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
  font-family: 'Barlow Condensed', 'Impact', sans-serif;
}
.game-intro-overlay.hidden { display: none !important; }
.game-intro-overlay.intro-visible {
  opacity: 1;
  pointer-events: auto;
}
.game-intro-overlay.intro-fading {
  opacity: 0;
  transition: opacity 0.65s ease-in;
}

/* Matchup card played OVER the entrance camera: drop its opaque backdrop so the arena
   shot reads through it, and swap in a soft scrim that keeps the type legible. */
.game-intro-overlay.intro-over-camera .intro-bg-layer {
  background: linear-gradient(180deg, rgba(2,5,15,.72) 0%, rgba(2,5,15,.38) 42%,
                                      rgba(2,5,15,.38) 58%, rgba(2,5,15,.72) 100%);
}
.game-intro-overlay.intro-over-camera .intro-scanlines,
.game-intro-overlay.intro-over-camera .intro-ice-reflect { opacity: .35; }

/* Card pushed to the left half so the skaters coming out stay visible on the right.
   The hero shot's own roster panel lives at right:3%, so the two never collide. */
.game-intro-overlay.intro-side .intro-main-card {
  max-width: 60vw; margin-left: 2.5vw; margin-right: auto;
}
.game-intro-overlay.intro-side .intro-bg-layer {
  /* Lighter than it was: this scrim IS the "lighting change" between the opening shot and
     the matchup shot — the 3D lighting is identical across both — so it has to read as a
     panel behind the type, not as the arena dimming. */
  background: linear-gradient(90deg, rgba(2,5,15,.72) 0%, rgba(2,5,15,.54) 34%,
                                     rgba(2,5,15,.16) 58%, rgba(2,5,15,0) 74%);
}
/* And it arrives slowly. The stock 0.85s card fade is tuned for a card that owns the whole
   screen from a black cut; here it is appearing over a live camera move. */
.game-intro-overlay.intro-side { transition: opacity 1.9s ease; }
.game-intro-overlay.intro-side .intro-scanlines,
.game-intro-overlay.intro-side .intro-ice-reflect { opacity: .22; }
.game-intro-overlay.intro-side .intro-spotlight { opacity: .35; }
/* The card was designed to fill the screen; at 46vw everything in it reads tiny. Scale the
   TYPE up rather than transform-scaling the card, so the layout reflows and stays inside the
   left half instead of spilling over the skaters. */
/* Sized so a FULL 8-a-side roster still fits both blocks on screen. Everything here is in
   vh so it scales with the viewport rather than assuming a window height: two 8-row lists
   plus headers and the VS row have to live inside one screen, which is what caps the type.
   Bigger than stock (which was tiny at this width) but not the 2x that overflowed. */
.game-intro-overlay.intro-side .intro-matchup-label {
  font-size: 2.2vh; letter-spacing: 9px; margin-bottom: 1vh;
}
.game-intro-overlay.intro-side .intro-team-name-text { font-size: 4vh; letter-spacing: 7px; }
.game-intro-overlay.intro-side .intro-team-sub { font-size: 1.4vh; }
.game-intro-overlay.intro-side .intro-player-list { gap: 0.45vh; padding: 0.6vh 1.1vw 0.8vh; }
.game-intro-overlay.intro-side .intro-player-row { padding: 0.55vh 1vw; border-radius: 8px; }
.game-intro-overlay.intro-side .intro-player-name { font-size: 2.35vh; max-width: none; }
.game-intro-overlay.intro-side .intro-bot-tag { font-size: 1.3vh; }
.game-intro-overlay.intro-side .intro-goalie-badge { font-size: 1.7vh; padding: 0.2vh 0.7vw; }
.game-intro-overlay.intro-side .intro-vs-text { font-size: 2.4vh; }
.game-intro-overlay.intro-side .intro-vs-puck-icon { font-size: 2.2vh; }
.game-intro-overlay.intro-side .intro-vs-row { margin: 0.4vh 0; }
/* Rank badges render at a fixed size in markup, so they still want a nudge on top. */
.game-intro-overlay.intro-side .intro-player-name .tier-badge {
  transform: scale(1.35); transform-origin: center; margin: 0 13px 0 5px;
}
/* ELO / win% sit between the name and the goalie badge. Tabular figures so digits line up
   down the list instead of jittering with digit widths. */
.intro-player-metrics {
  display: flex; align-items: baseline; gap: 12px; margin-left: auto; padding-right: 12px;
  font-family: 'Barlow Condensed', sans-serif; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.intro-player-metrics > span { color: var(--text); font-weight: 700; letter-spacing: .5px; }
.intro-player-metrics i {
  font-style: normal; font-weight: 600; opacity: .5; margin-left: 4px; font-size: .62em;
  letter-spacing: 1px;
}
.intro-player-metrics .ipm-win { color: #8fd8b0; }

/* Side layout: a real COLUMN grid rather than a flex row, so name / ELO / win% / G form
   aligned columns down the whole list. display:contents lifts the metrics wrapper's children
   into the row's grid so they become columns in their own right. */
.game-intro-overlay.intro-side .intro-player-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 5.2em 4em 2.2em;
  align-items: center; column-gap: 1.1vw;
}
.game-intro-overlay.intro-side .intro-player-metrics { display: contents; }
.game-intro-overlay.intro-side .ipm-elo,
.game-intro-overlay.intro-side .ipm-win {
  font-size: 1.9vh; text-align: right;
  /* Hairline rules read as column dividers without drawing a full table. */
  border-left: 1px solid rgba(255,255,255,0.09);
  padding-left: .9vw; min-height: 1.4em; line-height: 1.4em;
}
.game-intro-overlay.intro-side .intro-goalie-badge { justify-self: end; }

/* Once the entrance hands the camera over, the card keeps its panel but drops the scrim:
   it would otherwise sit over the first featured player's shot and darken it (and his
   spotlight) for two seconds, making his introduction look dimmer than the second team's. */
.game-intro-overlay.intro-side.intro-noscrim .intro-bg-layer {
  background: linear-gradient(90deg, rgba(2,5,15,.34) 0%, rgba(2,5,15,.20) 34%,
                                     rgba(2,5,15,.04) 58%, rgba(2,5,15,0) 74%);
  transition: background .9s ease;
}

/* Longer, softer fade than the standard card exit — it clears while the camera is already
   following the first skater, so a quick cut would read as a glitch. */
.game-intro-overlay.intro-side.intro-fading { transition: opacity 1.1s ease-in; }

/* ── Deep atmosphere background ──────────────────────────────────────────── */
.intro-bg-layer {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 80% 50% at 50% -10%, rgba(227,76,76,0.12) 0%, transparent 60%),
    radial-gradient(ellipse 80% 50% at 50% 110%, rgba(76,143,227,0.12) 0%, transparent 60%),
    radial-gradient(ellipse 120% 80% at 50% 50%, rgba(4,8,20,0.0) 0%, rgba(2,5,15,0.97) 100%),
    linear-gradient(180deg, #020510 0%, #04091a 50%, #020510 100%);
  z-index: 0;
}

/* Subtle CRT scanlines for broadcast texture */
.intro-scanlines {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0, 0, 0, 0.07) 2px,
    rgba(0, 0, 0, 0.07) 4px
  );
  z-index: 1;
  pointer-events: none;
}

/* Ice reflection at bottom */
.intro-ice-reflect {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 35%;
  background: linear-gradient(0deg,
    rgba(180,220,255,0.06) 0%,
    rgba(140,200,255,0.03) 40%,
    transparent 100%);
  z-index: 1;
  pointer-events: none;
}

/* Dramatic angled spotlights from top corners */
.intro-spotlight {
  position: absolute;
  top: 0;
  width: 45%;
  height: 75%;
  z-index: 2;
  pointer-events: none;
}
.intro-spotlight--left {
  left: -5%;
  background: conic-gradient(from -12deg at 15% 0%,
    transparent 0deg,
    rgba(227,76,76,0.10) 14deg,
    rgba(227,76,76,0.04) 24deg,
    transparent 38deg);
  animation: intro-spot-pulse 3.5s ease-in-out infinite;
}
.intro-spotlight--right {
  right: -5%;
  background: conic-gradient(from 192deg at 85% 0%,
    transparent 0deg,
    rgba(76,143,227,0.10) 14deg,
    rgba(76,143,227,0.04) 24deg,
    transparent 38deg);
  animation: intro-spot-pulse 3.5s ease-in-out infinite 1.75s;
}
@keyframes intro-spot-pulse {
  0%, 100% { opacity: 0.65; }
  50%       { opacity: 1; }
}

/* ── Broadcast header bar ────────────────────────────────────────────────── */
.intro-network-bar {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 18px 7px;
  background: linear-gradient(90deg,
    rgba(0,0,0,0.85) 0%,
    rgba(10,16,35,0.92) 50%,
    rgba(0,0,0,0.85) 100%);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  flex-shrink: 0;
}
.intro-network-logo {
  font-family: 'Bebas Neue', 'Impact', sans-serif;
  font-size: 1.15rem;
  letter-spacing: 6px;
  background: linear-gradient(135deg, #fff 0%, var(--ice) 45%, var(--gold) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.intro-network-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
/* Fullscreen + menu in intro header (same actions as HUD; avoids canvas overlay covering seam buttons) */
.intro-top-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-right: 4px;
}
.intro-top-actions .btn-intro-bar {
  position: relative !important;
  top: auto !important;
  right: auto !important;
  flex-shrink: 0;
}
.intro-live-badge {
  font-family: 'Bebas Neue', sans-serif;
  font-size: 0.72rem;
  letter-spacing: 3px;
  color: #fff;
  background: #c01818;
  padding: 2px 8px 1px;
  border-radius: 3px;
  animation: intro-live-blink 1.0s step-start infinite;
}
@keyframes intro-live-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0.3; }
}
.intro-period-pill {
  font-family: 'Bebas Neue', sans-serif;
  font-size: 0.72rem;
  letter-spacing: 3px;
  color: var(--gold);
  border: 1px solid rgba(240,192,64,0.4);
  padding: 2px 10px 1px;
  border-radius: 4px;
}

/* ── Main roster card ────────────────────────────────────────────────────── */
.intro-main-card {
  position: relative;
  z-index: 10;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 10px 24px 6px;
  min-height: 0;
  overflow: hidden;
}

.intro-matchup-label {
  font-family: 'Bebas Neue', 'Impact', sans-serif;
  font-size: 1.25rem;
  letter-spacing: 8px;
  color: rgba(255,255,255,0.92);
  text-align: center;
  margin-bottom: 10px;
  animation: intro-label-fadein 0.6s ease both;
  animation-delay: 0.2s;
}
@keyframes intro-label-fadein {
  from { opacity: 0; letter-spacing: 14px; }
  to   { opacity: 1; letter-spacing: 8px; }
}

/* ── Team block ──────────────────────────────────────────────────────────── */
.intro-team-block {
  background: rgba(8,12,26,0.82);
  border-radius: 10px;
  overflow: hidden;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  margin-bottom: 0;
}
.intro-team-block--red {
  border: 1px solid rgba(227,76,76,0.22);
  box-shadow: 0 0 50px rgba(227,76,76,0.10), inset 0 1px 0 rgba(227,76,76,0.12);
}
.intro-team-block--blue {
  border: 1px solid rgba(76,143,227,0.22);
  box-shadow: 0 0 50px rgba(76,143,227,0.10), inset 0 1px 0 rgba(76,143,227,0.12);
}

.intro-team-header {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px 9px;
  overflow: hidden;
}
.intro-team-header--red { background: linear-gradient(90deg, rgba(227,76,76,0.18) 0%, rgba(227,76,76,0.04) 60%, transparent 100%); }
.intro-team-header--blue { background: linear-gradient(90deg, rgba(76,143,227,0.18) 0%, rgba(76,143,227,0.04) 60%, transparent 100%); }

.intro-team-accent {
  width: 4px;
  height: 42px;
  border-radius: 3px;
  flex-shrink: 0;
}
.intro-team-accent--red  { background: var(--red);  box-shadow: 0 0 14px rgba(227,76,76,0.8); }
.intro-team-accent--blue { background: var(--blue); box-shadow: 0 0 14px rgba(76,143,227,0.8); }

.intro-team-title-group {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.intro-team-name-text {
  font-family: 'Bebas Neue', 'Impact', sans-serif;
  font-size: 2.2rem;
  letter-spacing: 8px;
  line-height: 1;
}
.intro-team-sub {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.72rem;
  letter-spacing: 5px;
  color: rgba(255,255,255,0.35);
  font-weight: 600;
  text-transform: uppercase;
  padding-bottom: 3px;
}
.intro-team-glow {
  position: absolute;
  right: 0; top: 0; bottom: 0;
  width: 40%;
  pointer-events: none;
}
.intro-team-glow--red  { background: radial-gradient(ellipse at right center, rgba(227,76,76,0.08) 0%, transparent 70%); }
.intro-team-glow--blue { background: radial-gradient(ellipse at right center, rgba(76,143,227,0.08) 0%, transparent 70%); }

/* ── Player rows ─────────────────────────────────────────────────────────── */
.intro-player-list {
  padding: 4px 10px 6px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.intro-player-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 5px 10px;
  background: rgba(255,255,255,0.035);
  border: 1px solid rgba(255,255,255,0.055);
  border-radius: 7px;
  opacity: 0;
  transform: translateX(-22px);
  animation: intro-row-slide 0.4s cubic-bezier(0.22,1,0.36,1) forwards;
  transition: background 0.2s ease;
}
.intro-team-block--blue .intro-player-row {
  transform: translateX(22px);
}
@keyframes intro-row-slide {
  to { opacity: 1; transform: translateX(0); }
}

.intro-player-name {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 220px;
}
.intro-player-self {
  color: var(--gold);
}
.intro-bot-tag {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.6rem;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 2px;
  margin-left: 6px;
  vertical-align: middle;
  opacity: 0.7;
}

/* Goalie badge: dim = opted out, glowing gold = opted in */
.intro-goalie-badge {
  font-family: 'Bebas Neue', sans-serif;
  font-size: 0.75rem;
  letter-spacing: 2px;
  padding: 3px 9px 2px;
  border-radius: 5px;
  border: 1px solid rgba(255,255,255,0.1);
  color: rgba(255,255,255,0.2);
  background: rgba(255,255,255,0.04);
  transition: all 0.22s ease;
  user-select: none;
  flex-shrink: 0;
}
.intro-goalie-active {
  color: var(--gold);
  background: rgba(240,192,64,0.14);
  border-color: rgba(240,192,64,0.55);
  box-shadow: 0 0 10px rgba(240,192,64,0.35), inset 0 1px 0 rgba(240,192,64,0.2);
}

/* ── VS divider ──────────────────────────────────────────────────────────── */
.intro-vs-row {
  display: flex;
  align-items: center;
  gap: 0;
  margin: 8px 4px;
  animation: intro-vs-fadein 0.5s ease both;
  animation-delay: 0.15s;
}
@keyframes intro-vs-fadein {
  from { opacity: 0; transform: scaleX(0.85); }
  to   { opacity: 1; transform: scaleX(1); }
}
.intro-vs-line {
  flex: 1;
  height: 1px;
}
.intro-vs-line--red  { background: linear-gradient(90deg, transparent, rgba(227,76,76,0.6)); }
.intro-vs-line--blue { background: linear-gradient(90deg, rgba(76,143,227,0.6), transparent); }

.intro-vs-badge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 0 16px;
}
.intro-vs-puck-icon {
  font-size: 1.4rem;
  animation: intro-puck-spin 5s linear infinite;
  filter: drop-shadow(0 0 6px rgba(255,255,255,0.3));
}
@keyframes intro-puck-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.intro-vs-text {
  font-family: 'Bebas Neue', 'Impact', sans-serif;
  font-size: 1.1rem;
  letter-spacing: 6px;
  color: var(--gold);
  text-shadow: 0 0 18px rgba(240,192,64,0.55);
}

/* ── Bottom bar ──────────────────────────────────────────────────────────── */
.intro-bottom-bar {
  position: relative;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding: 8px 20px 12px;
  background: linear-gradient(0deg, rgba(2,5,15,0.92) 0%, transparent 100%);
  flex-shrink: 0;
}
.intro-goalie-hint {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.82rem;
  font-weight: 400;
  color: rgba(255,255,255,0.35);
  letter-spacing: 1px;
  text-align: center;
  animation: intro-hint-fadein 0.6s ease both;
  animation-delay: 1.2s;
}
@keyframes intro-hint-fadein {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.intro-kbd {
  display: inline-block;
  font-family: 'Barlow Condensed', monospace;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--gold);
  background: rgba(240,192,64,0.1);
  border: 1px solid rgba(240,192,64,0.4);
  border-radius: 4px;
  padding: 1px 7px;
  margin: 0 2px;
}

.intro-progress-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  width: min(520px, 88%);
}
.intro-progress-track {
  width: 100%;
  height: 3px;
  background: rgba(255,255,255,0.08);
  border-radius: 3px;
  overflow: hidden;
  position: relative;
}
.intro-progress-fill {
  height: 100%;
  width: 100%;
  border-radius: 3px;
  background: linear-gradient(90deg,
    var(--red) 0%,
    #e87030 25%,
    var(--gold) 50%,
    #30a0e8 75%,
    var(--blue) 100%);
  transition: width 0.28s linear;
  box-shadow: 0 0 8px rgba(240,192,64,0.4);
}
.intro-progress-shine {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%);
  animation: intro-shine-sweep 2.5s linear infinite;
}
@keyframes intro-shine-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(200%); }
}
.intro-countdown-label {
  font-family: 'Bebas Neue', 'Impact', sans-serif;
  font-size: 0.72rem;
  letter-spacing: 4px;
  color: rgba(255,255,255,0.35);
  text-align: center;
}

/* ── Reconnect overlay ─────────────────────────────────────────────── */
.reconnect-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
}
.reconnect-overlay.hidden { display: none; }
.reconnect-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 40px 52px;
  text-align: center;
  min-width: 300px;
  box-shadow: 0 16px 60px rgba(0,0,0,0.8);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
.reconnect-title {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--text);
  margin: 0;
}
.reconnect-msg {
  color: rgba(255,255,255,0.55);
  font-size: 0.9rem;
  margin: 0;
}
.reconnect-leave-btn {
  margin-top: 8px;
  padding: 10px 28px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 1px;
}
@keyframes reconnect-spin {
  to { transform: rotate(360deg); }
}
.reconnect-spinner {
  width: 44px;
  height: 44px;
  border: 4px solid rgba(255,255,255,0.15);
  border-top-color: #60a5fa;
  border-radius: 50%;
  animation: reconnect-spin 0.9s linear infinite;
}

/* ── Fight system UI ─────────────────────────────────────────────────────────── */
/* ── Fight challenge prompt ───────────────────────────────────────────────── */
/* Brawl join prompt. Bottom of the screen rather than centred like the fight
   challenge: this is an optional invitation during live play, not a decision that
   stops everything, so it must not sit over the middle of the rink. */
.brawl-prompt {
  position: fixed;
  left: 50%;
  bottom: clamp(14px, 4vh, 46px);
  transform: translateX(-50%);
  z-index: 3150;
  pointer-events: none;
}
.brawl-prompt.hidden { display: none; }
.brawl-prompt-card {
  pointer-events: all;
  background: rgba(8, 12, 20, 0.96);
  border: 2px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px;
  padding: 14px 22px 12px;
  text-align: center;
  min-width: 280px;
  box-shadow: 0 0 60px rgba(0, 0, 0, 0.85), 0 0 24px rgba(220, 40, 40, 0.16);
  animation: fight-card-in 0.16s ease-out;
}
.brawl-prompt-title {
  font-family: var(--font-display, inherit);
  font-size: 1.15rem;
  letter-spacing: 1px;
  color: #fff;
  margin-bottom: 9px;
}
.brawl-prompt-btns { display: flex; gap: 10px; justify-content: center; }
.brawl-prompt-btns .fight-btn { padding: 8px 18px; font-size: 0.92rem; }
.brawl-prompt-note {
  margin-top: 7px;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: rgba(220, 232, 245, 0.62);
}
.brawl-prompt .fight-challenge-timer-bar { margin-top: 8px; }

/* Brawl roster: one column per side, stacked name + health bar per fighter. */
.fight-hud-teams {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 14px;
  margin: 4px 0 2px;
}
.fight-hud-teams.hidden { display: none; }
.fight-team-col { display: flex; flex-direction: column; gap: 4px; min-width: 130px; }
.fight-team-red { align-items: flex-end; }
.fight-team-blue { align-items: flex-start; }
.fight-team-row { display: flex; align-items: center; gap: 7px; width: 100%; }
.fight-team-blue .fight-team-row { flex-direction: row-reverse; }
.fight-team-row .fight-healthbar { flex: 1; min-width: 54px; }
.fight-team-name {
  font-size: 0.76rem;
  max-width: 82px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: rgba(232, 240, 248, 0.9);
}
.fight-team-row.is-me .fight-team-name { color: #fff; font-weight: 700; }
/* Downed fighters stay listed — seeing who is already out is the point of a roster. */
.fight-team-row.is-down { opacity: 0.42; }
.fight-team-row.is-down .fight-team-name { text-decoration: line-through; }

.fight-challenge-overlay {
  position: fixed; inset: 0; z-index: 3200;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.fight-challenge-overlay.hidden { display: none; }
.fight-challenge-card {
  pointer-events: all;
  background: rgba(8,12,20,0.97);
  border: 2px solid rgba(255,255,255,0.14);
  border-radius: 16px;
  padding: 30px 40px 22px;
  text-align: center;
  min-width: 300px;
  box-shadow: 0 0 80px rgba(0,0,0,0.9), 0 0 30px rgba(220,40,40,0.18);
  animation: fight-card-in 0.16s ease-out;
}
@keyframes fight-card-in { from { transform: scale(0.9) translateY(10px); opacity:0; } to { transform:scale(1) translateY(0); opacity:1; } }
.fight-challenge-title {
  font-size: 1.05rem; font-weight: 700; color: #e6edf3; margin-bottom: 20px; line-height: 1.4;
}
.fight-challenge-btns { display: flex; gap: 12px; justify-content: center; margin-bottom: 16px; }
.fight-btn {
  padding: 13px 26px; border: none; border-radius: 9px;
  font-size: 1rem; font-weight: 900; letter-spacing: 2px; cursor: pointer;
  color: #fff; transition: transform 0.07s, filter 0.07s;
}
.fight-btn:hover { filter: brightness(1.18); transform: scale(1.05); }
.fight-btn-fight { background: #c0392b; box-shadow: 0 0 20px rgba(192,57,43,0.55); }
.fight-btn-slide  { background: #1a3a5c; }
.fight-challenge-timer-bar { height: 4px; background: rgba(255,255,255,0.08); border-radius: 2px; overflow: hidden; }
.fight-challenge-timer-fill { height: 100%; width: 100%; background: #60a5fa; transition: width linear; }

/* ── Fight approach instructions (fighters only, shown during approach) ──── */
.fight-instructions {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 3150; pointer-events: none;
  background: rgba(4,8,18,0.88); border: 1px solid rgba(255,255,255,0.13);
  border-radius: 16px; padding: 18px 28px 14px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  animation: fight-instr-in 0.3s ease-out;
  backdrop-filter: blur(6px);
}
.fight-instructions.hidden { display: none; }
@keyframes fight-instr-in {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 12px)); }
  to   { opacity: 1; transform: translate(-50%, -50%); }
}
.fight-instr-header {
  font-size: 0.65rem; font-weight: 900; letter-spacing: 5px;
  color: #e8221a; text-transform: uppercase;
}
.fight-instr-actions {
  display: flex; gap: 20px; align-items: center;
}
.fight-instr-action {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.fight-instr-key {
  min-width: 40px; height: 40px; padding: 0 8px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.22);
  border-radius: 8px; border-bottom: 3px solid rgba(255,255,255,0.32);
  font-size: 1.1rem; font-weight: 900; color: #fff;
}
.fight-instr-desc {
  font-size: 0.56rem; font-weight: 800; letter-spacing: 2px;
  color: rgba(255,255,255,0.45); text-transform: uppercase;
}
.fight-instr-tip {
  font-size: 0.54rem; color: rgba(255,255,255,0.28);
  letter-spacing: 0.5px; text-align: center; padding-top: 2px;
  border-top: 1px solid rgba(255,255,255,0.07); width: 100%;
  padding-top: 8px; margin-top: 2px;
}

/* ── Fight banner (shown to everyone) ────────────────────────────────────── */
.fight-hud-banner {
  position: fixed; top: 118px; left: 50%; transform: translateX(-50%);
  font-size: 2.6rem; font-weight: 900; letter-spacing: 8px; color: #e8221a;
  text-shadow: 0 0 30px rgba(220,40,40,0.95), 0 0 60px rgba(220,40,40,0.4), 0 2px 0 #000;
  z-index: 3400; pointer-events: none;
  animation: fight-banner-flash 0.3s ease-out;
}
.fight-hud-banner.hidden { display: none; }
/* Referee-intervened outcomes (period break interrupts a fight, or the 45s clock runs out
   with nobody knocked out) — dark yellow instead of the red FIGHT!/LIGHTS OUT! red. */
.fight-hud-banner.fight-hud-banner-warn {
  color: #b8860b;
  text-shadow: 0 0 30px rgba(184,134,11,0.9), 0 0 60px rgba(184,134,11,0.35), 0 2px 0 #000;
}
/* TEAM BRAWL reads bigger and hotter than a 1v1 FIGHT — it's a rarer, larger event,
   and the longer string needs the tighter tracking to stay on one line. */
.fight-hud-banner.is-brawl {
  font-size: 3.1rem;
  letter-spacing: 5px;
  color: #ff7a1a;
  text-shadow: 0 0 34px rgba(255, 122, 26, 0.95), 0 0 70px rgba(255, 60, 20, 0.45), 0 2px 0 #000;
}
@keyframes fight-banner-flash {
  from { opacity:0; transform: translateX(-50%) scale(1.4) translateY(-8px); }
  to   { opacity:1; transform: translateX(-50%) scale(1)   translateY(0);    }
}

/* ── Active fight HUD (fighters only) ────────────────────────────────────── */
.fight-hud {
  position: fixed; inset: 0; z-index: 3100; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  padding-top: 96px; padding-bottom: 0; /* clears the scoreboard bar */
}
.fight-hud.hidden { display: none; }

/* Big FIGHT! headline */
.fight-hud-title {
  font-family: var(--font-display);
  font-size: 2.6rem;
  letter-spacing: 8px;
  color: #ff5a3c;
  text-shadow:
    -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000,
    0 0 26px rgba(255, 90, 60, 0.8);
  animation: fightTitlePulse 0.9s ease-in-out infinite alternate;
  margin-bottom: 2px;
}
@keyframes fightTitlePulse {
  from { transform: scale(1); }
  to { transform: scale(1.06); }
}

/* Hit pips + names — the #1 piece of info */
.fight-vs-row {
  display: flex; align-items: center; gap: 10px; margin-bottom: 8px;
}
.fight-name {
  font-size: 0.78rem; font-weight: 700; letter-spacing: 1px;
  color: rgba(255,255,255,0.85); min-width: 72px;
  text-shadow: 0 1px 4px rgba(0,0,0,0.9);
}
.fight-name-me  { text-align: right; }
.fight-name-opp { text-align: left; }
/* Health bars: anchored at each fighter's name and draining TOWARD the VS divider as they
   take hits — so the remaining chunk always reads as "attached" to whoever it belongs to. */
.fight-healthbar {
  position: relative;
  width: 96px; height: 15px; border-radius: 8px;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.25);
  overflow: hidden;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.5);
}
.fight-healthbar-fill {
  position: absolute; top: 0; bottom: 0;
  width: 100%;
  background: #3ecf5e;
  box-shadow: 0 0 12px rgba(62,207,94,0.6), inset 0 1px 0 rgba(255,255,255,0.25);
  transition: width 0.25s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}
.fight-healthbar-me .fight-healthbar-fill { left: 0; }
.fight-healthbar-opp .fight-healthbar-fill { right: 0; }
.fight-healthbar-fill.tier-yellow {
  background: #e0c23c;
  box-shadow: 0 0 12px rgba(224,194,60,0.6), inset 0 1px 0 rgba(255,255,255,0.25);
}
.fight-healthbar-fill.tier-red {
  background: #e8221a;
  box-shadow: 0 0 14px rgba(232,34,26,0.75), inset 0 1px 0 rgba(255,255,255,0.25);
}
/* Fresh hit: a quick bright flash on the fill so a drop reads as an impact, not just a tween */
.fight-healthbar-fill.hit-flash { animation: fight-health-flash 0.35s ease-out; }
@keyframes fight-health-flash {
  0%   { filter: brightness(2.6); }
  100% { filter: brightness(1); }
}
.fight-vs-divider {
  font-size: 0.68rem; font-weight: 900; color: rgba(255,255,255,0.2);
  letter-spacing: 3px; padding: 0 6px;
}

/* Readiness bar — drains while on cooldown, disappears when ready */
.fight-ready-bar-wrap {
  width: 340px; max-width: 88vw; height: 4px;
  background: rgba(255,255,255,0.07); border-radius: 2px;
  overflow: hidden; margin-bottom: 10px;
}
.fight-ready-fill {
  height: 100%; width: 0; background: #4c8fe3;
  border-radius: 2px; transition: none;
}

/* Action buttons: key chip + label */
.fight-actions-row {
  display: flex; gap: 8px; align-items: stretch;
}
.fight-action {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  background: rgba(8, 12, 18, 0.82); border: 1px solid rgba(255,255,255,0.22);
  border-radius: 11px; padding: 10px 16px 8px;
  min-width: 86px; position: relative; overflow: hidden;
  transition: opacity 0.12s, background 0.12s, border-color 0.12s;
  box-shadow: 0 4px 18px rgba(0,0,0,0.45);
}
.fight-action.hidden { display: none; }
.fight-action.on-cooldown { opacity: 0.45; }
/* Per-ability cooldown sweep: --cd (1 → 0 remaining) fills the chip from the bottom */
.fight-action::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0;
  height: calc(var(--cd, 0) * 100%);
  background: rgba(255, 90, 60, 0.22);
  pointer-events: none;
  transition: height 0.08s linear;
}
.fight-action-key {
  font-size: 1.35rem; font-weight: 900; color: #fff;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.1); border-radius: 8px;
  letter-spacing: 0; line-height: 1;
  transition: background 0.1s, color 0.1s;
}
/* Third chip is an info-only "aim with mouse" reminder — no key, no cooldown, so it never
   picks up .on-cooldown/the --cd sweep. Same box, just a mouse glyph instead of a letter. */
.fight-action-key-icon { font-size: 1.1rem; }
.fight-action-label {
  font-size: 0.95rem; font-weight: 800; letter-spacing: 2px;
  color: rgba(255,255,255,0.92); text-transform: uppercase; white-space: nowrap;
  transition: color 0.1s;
}

/* Dodge button: pulse yellow when you can dodge an incoming punch */
.fight-action-dodge-wrap.dodge-incoming {
  background: rgba(255,200,0,0.14); border-color: rgba(255,200,0,0.65);
  animation: fight-dodge-pulse 0.28s ease infinite alternate;
}
.fight-action-dodge-wrap.dodge-incoming .fight-action-key {
  background: rgba(255,200,0,0.25); color: #ffd700;
}
.fight-action-dodge-wrap.dodge-incoming .fight-action-label { color: #ffd700; }
@keyframes fight-dodge-pulse {
  from { box-shadow: 0 0 0 0 rgba(255,200,0,0); }
  to   { box-shadow: 0 0 0 7px rgba(255,200,0,0.3); }
}

/* Draining timer bar at the bottom edge of the dodge button */
.fight-dodge-timer {
  position: absolute; bottom: 0; left: 0; right: 0; height: 4px;
  background: rgba(255,200,0,0.12);
}
.fight-dodge-timer.hidden { display: none; }
.fight-dodge-fill { height: 100%; width: 100%; background: #ffd700; }

/* Haymaker button: pulse orange when opponent is winding up */
.fight-action-haymaker.haymaker-incoming {
  background: rgba(255,80,0,0.14); border-color: rgba(255,80,0,0.65);
  animation: fight-haymaker-pulse 0.2s ease infinite alternate;
}
.fight-action-haymaker.haymaker-incoming .fight-action-key { color: #ff6600; }
.fight-action-haymaker.haymaker-incoming .fight-action-label { color: rgba(255,140,0,0.9); }
@keyframes fight-haymaker-pulse {
  from { box-shadow: 0 0 0 0 rgba(255,80,0,0); }
  to   { box-shadow: 0 0 0 9px rgba(255,80,0,0.35); }
}

/* Fight timer */
.fight-timeout {
  font-size: 0.65rem; color: rgba(255,255,255,0.28); margin-top: 8px; letter-spacing: 2px;
}

/* ── Dodge / haymaker timing prompt ─────────────────────────────────────── */
.fight-timing-prompt {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  margin-top: 14px;
}
.fight-timing-prompt.hidden { display: none; }

.fight-timing-prompt-text {
  font-size: 1.7rem; font-weight: 900; letter-spacing: 7px;
  color: #ffd700;
  text-shadow: 0 0 18px rgba(255,210,0,0.9), 0 0 36px rgba(255,180,0,0.45);
  animation: timing-pulse 0.16s ease infinite alternate;
}
.fight-timing-prompt.haymaker-warn .fight-timing-prompt-text {
  color: #ff7020;
  text-shadow: 0 0 18px rgba(255,90,0,0.9), 0 0 36px rgba(255,60,0,0.45);
}
@keyframes timing-pulse {
  from { opacity: 0.78; transform: scale(0.975); }
  to   { opacity: 1;    transform: scale(1.025); }
}

.fight-timing-bar-wrap {
  width: 200px; max-width: 52vw; height: 6px;
  background: rgba(255,210,0,0.13); border-radius: 3px; overflow: hidden;
}
.fight-timing-prompt.haymaker-warn .fight-timing-bar-wrap {
  background: rgba(255,90,0,0.13);
}
.fight-timing-bar-fill {
  height: 100%; width: 100%; border-radius: 3px;
  background: #ffd700;
}
.fight-timing-prompt.haymaker-warn .fight-timing-bar-fill {
  background: #ff6018;
}

/* ── KO overlay ───────────────────────────────────────────────────────────── */
.fight-ko-overlay {
  position: fixed; inset: 0; z-index: 3300; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.6);
}
.fight-ko-overlay.hidden { display: none; }
.fight-ko-text {
  font-size: 4.5rem; font-weight: 900; letter-spacing: 10px; color: #e8221a;
  text-shadow: 0 0 50px rgba(220,40,40,0.9), 0 3px 0 #000;
  animation: fight-ko-in 0.22s ease-out;
}
@keyframes fight-ko-in { from { transform: scale(2.8); opacity:0; } to { transform:scale(1); opacity:1; } }
.fight-ko-timer { font-size: 1.8rem; font-weight: 800; color: #ffe680; margin-bottom: 18px; letter-spacing: 1px; text-shadow: 0 2px 8px rgba(0,0,0,0.8); }

/* ── Spectator bar (non-fighters) — lives in the scoreboard row, right of Period ──────── */
.fight-spectator-bar {
  display: flex; align-items: center;
  margin-left: 18px; padding-left: 18px;
  border-left: 1px solid var(--border);
  font-size: 0.8rem; font-weight: 700; letter-spacing: 1px; color: #e6edf3;
  pointer-events: none;
}
.fight-spectator-bar.hidden { display: none; }
.fight-spec-progress { display: flex; align-items: center; gap: 8px; }
.fight-spec-progress.hidden { display: none; }
.fight-spec-label { color: #e8221a; font-size: 0.78rem; letter-spacing: 2px; }
.fight-spec-name { font-size: 0.8rem; font-weight: 700; }
.fight-spec-name.team-red { color: var(--red); }
.fight-spec-name.team-blue { color: var(--blue); }
.fight-spec-vs { color: var(--muted); font-size: 0.7rem; letter-spacing: 1px; }
.fight-spectator-bar .fight-healthbar { width: 52px; height: 10px; }
.fight-spec-winner { font-size: 0.85rem; font-weight: 700; letter-spacing: 1px; }
.fight-spec-winner.hidden { display: none; }
.fight-spec-winner.team-red { color: var(--red); }
.fight-spec-winner.team-blue { color: var(--blue); }

/* ── Spotlight vignette canvas ───────────────────────────────────────────── */
#fight-spotlight-canvas {
  position: fixed; inset: 0; z-index: 3000;
  pointer-events: none; opacity: 0; transition: opacity 0.4s ease;
}
#fight-spotlight-canvas.active { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════════════════
   TUTORIAL SYSTEM
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Welcome modal ─────────────────────────────────────────────────────────── */
#tutorial-welcome-modal {
  position: fixed; inset: 0; z-index: 900;
  background: rgba(5,8,15,0.82);
  display: flex; align-items: center; justify-content: center;
  backdrop-filter: blur(4px);
}
#tutorial-welcome-modal.hidden { display: none; }

/* ── Tutorial section picker ──
   Six cards is the densest grid in the app; keeping them uniform and letting
   only the hover/complete state carry color is what keeps it calm. */
.tut-welcome-card {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  padding: 30px 34px 26px;
  max-width: 860px; width: 94%;
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  box-shadow: var(--shadow-modal);
  text-align: center;
}
.tut-welcome-logo { margin-bottom: 12px; }
.tut-welcome-logo .logo-zealot { font-size: clamp(1.9rem, 3.4vw, 2.5rem); }
.tut-welcome-logo .logo-hockey { font-size: 0.78rem; }
.tut-welcome-desc {
  color: var(--ice-dim); font-size: 0.95rem; margin: 0 0 18px;
}
.tut-welcome-heading {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  margin: 0 0 16px;
}
/* Six sections read best as 3×2 — auto-fit gave a 4+2 with two orphans. */
.tut-section-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 20px;
}
@media (max-width: 700px) { .tut-section-cards { grid-template-columns: repeat(2, 1fr); } }
.tut-section-card {
  position: relative;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-left: 3px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  padding: 16px 14px;
  cursor: pointer;
  text-align: left;
  transition: border-color 0.18s, transform 0.14s, background 0.18s;
}
.tut-section-card:hover {
  border-color: rgba(242, 193, 78, 0.4);
  border-left-color: var(--gold);
  background: linear-gradient(180deg, rgba(242, 193, 78, 0.07), transparent);
  transform: translateY(-2px);
}
.tut-section-card.completed {
  border-color: rgba(63, 185, 107, 0.35);
  border-left-color: var(--green);
  background: linear-gradient(180deg, rgba(63, 185, 107, 0.07), transparent);
}
.tut-section-card.completed::after {
  content: 'Done';
  position: absolute; top: 12px; right: 12px;
  background: var(--green); color: #0d1c12;
  font-family: var(--font-data);
  font-size: 0.66rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 2px 7px; border-radius: 3px;
}
/* Emoji here on purpose — the tutorial picker is the one warm, welcoming screen
   in the app, and the line icons read too clinical for it. */
.tut-section-icon {
  font-size: 1.7rem;
  line-height: 1;
  margin-bottom: 10px;
}
.tut-section-name {
  font-family: var(--font-menu-display);
  font-size: 0.98rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
  margin-bottom: 8px;
}
.tut-section-skills {
  list-style: none; padding: 0; margin: 0;
  color: var(--ice-dim); font-size: 0.8rem; line-height: 1.65;
}
.tut-section-skills li::before { content: '›  '; color: var(--gold); }
.tut-close-btn { margin-top: 4px; }

/* ── In-game tutorial overlay ──────────────────────────────────────────────── */
#tutorial-overlay {
  position: absolute; inset: 0;
  pointer-events: none; z-index: 60;
}
#tutorial-overlay.hidden { display: none; }

/* Steal zone legend (persistent top-left panel during steal steps) */
#tutorial-steal-legend {
  position: absolute; top: 52px; left: 14px;
  background: rgba(13,17,23,0.88);
  border: 1px solid rgba(255,135,185,0.4);
  border-radius: 10px; padding: 12px 16px 10px;
  pointer-events: none; min-width: 190px;
}
#tutorial-steal-legend.hidden { display: none; }

/* Finishing practice: goalie difficulty picker (top-left, live) */
#tutorial-finishing-diff {
  position: absolute; top: 52px; left: 14px;
  background: rgba(13,17,23,0.88);
  border: 1px solid rgba(240,192,64,0.4);
  border-radius: 10px; padding: 10px 14px 12px;
  pointer-events: auto; min-width: 160px;
}
#tutorial-finishing-diff.hidden { display: none; }
.tut-diff-label {
  color: #f0c040; font-size: 0.62rem; font-weight: 800;
  letter-spacing: 2.5px; text-transform: uppercase; margin-bottom: 8px;
}
#tutorial-finishing-diff-select {
  width: 100%; background: #0d1117; color: #e6edf3;
  border: 1px solid #30363d; border-radius: 6px;
  font-size: 0.85rem; padding: 6px 8px; outline: none; cursor: pointer;
}
.steal-legend-title {
  color: #ff87b9; font-size: 0.65rem; font-weight: 800;
  letter-spacing: 3px; text-transform: uppercase; margin-bottom: 9px;
}
.steal-legend-item {
  display: flex; align-items: center; gap: 8px;
  color: #c9d1d9; font-size: 0.78rem; margin-bottom: 5px;
}
.steal-legend-dot {
  display: inline-block; width: 11px; height: 11px;
  border-radius: 50%; flex-shrink: 0; border: 2px solid;
}
.steal-dot-gold   { background: rgba(200,155,20,0.42); border-color: rgba(230,195,45,0.97); }
.steal-legend-cross {
  display: inline-block; width: 11px; height: 11px;
  flex-shrink: 0; position: relative;
}
.steal-legend-cross::before, .steal-legend-cross::after {
  content: ''; position: absolute;
}
.steal-cross-dark::before {
  width: 3px; height: 100%; left: 50%; top: 0;
  transform: translateX(-50%); background: #000;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.95);
}
.steal-cross-dark::after {
  width: 100%; height: 3px; top: 50%; left: 0;
  transform: translateY(-50%); background: #000;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.95);
}
.steal-dot-pink   { background: rgba(255,95,165,0.38); border-color: rgba(255,135,185,0.97); }
.steal-dot-green  { background: rgba(40,200,80,0.42);  border-color: rgba(60,230,100,0.98); }
.steal-legend-hint {
  color: #8b949e; font-size: 0.72rem; margin-top: 8px;
  padding-top: 7px; border-top: 1px solid rgba(255,255,255,0.08);
}
.steal-legend-hint b { color: #e6c840; }

/* Header bar */
#tutorial-header {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 16px;
  background: rgba(5,8,15,0.78);
  border-bottom: 1px solid rgba(240,192,64,0.25);
  pointer-events: auto;
}
#tutorial-section-label {
  color: #f0c040; font-size: 0.72rem; font-weight: 800;
  letter-spacing: 3px; text-transform: uppercase;
}
#tutorial-progress-dots {
  display: flex; gap: 7px; align-items: center;
}
.tut-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: rgba(255,255,255,0.18);
  transition: background 0.25s, transform 0.2s;
}
.tut-dot.done { background: #f0c040; }
.tut-dot.current {
  background: transparent;
  border: 2px solid #e6edf3;
  width: 10px; height: 10px;
}
.tut-exit-btn {
  background: rgba(200,50,50,0.18);
  border: 1px solid rgba(220,80,80,0.45);
  color: #e88; border-radius: 7px;
  padding: 7px 18px; font-size: 0.8rem; font-weight: 700;
  cursor: pointer; letter-spacing: 1.5px; text-transform: uppercase;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.tut-exit-btn:hover {
  color: #fdd; border-color: rgba(240,100,100,0.75);
  background: rgba(200,50,50,0.35);
}

/* Step panel — just below the header bar */
#tutorial-step-panel {
  position: absolute; top: 52px;
  left: 50%; transform: translateX(-50%);
  max-width: 728px; width: calc(100% - 48px); /* widened ~40% (was 520px) so instructions don't stack tall */
  background: rgba(13,17,23,0.88);
  border: 1px solid rgba(240,192,64,0.45);
  border-radius: 12px; padding: 18px 22px 16px;
  pointer-events: none;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  transition: opacity 0.2s, transform 0.25s;
}
/* One-timer feed step: the teammate's one-timer bar sits top-centre right under the
   panel — go wide and short (text spreads into fewer lines, tighter padding) so the
   bar is visible while the instruction stays up. */
body.tut-step-wide #tutorial-step-panel {
  max-width: 1100px;
  padding: 9px 24px 8px;
}
body.tut-step-wide #tutorial-step-panel #tutorial-step-title {
  display: inline-block;
  margin-right: 12px;
}
body.tut-step-wide #tutorial-step-panel #tutorial-step-instruction {
  display: inline;
}
body.tut-step-wide #tutorial-step-panel #tutorial-step-tip {
  margin-top: 4px;
}

/* Fight tutorial: the fight HUD (FIGHT! banner + team health bars) owns the top
   centre, so the instruction panel slides to the top-left corner and drops its
   centering transform. Narrower too, so it never reaches under the health bars. */
body.tut-section-fight #tutorial-step-panel {
  left: 18px;
  right: auto;
  transform: none;
  max-width: 360px;
  width: min(360px, calc(100% - 36px));
}
body.tut-section-fight #tutorial-step-panel.tut-step-enter {
  animation: tutStepSlideLeft 0.28s cubic-bezier(0.22,1,0.36,1) both;
}
@keyframes tutStepSlideLeft {
  from { opacity: 0; transform: translateX(-16px); }
  to   { opacity: 1; transform: translateX(0); }
}

#tutorial-step-panel.tut-step-enter {
  animation: tutStepSlideDown 0.28s cubic-bezier(0.22,1,0.36,1) both;
}
@keyframes tutStepSlideDown {
  from { opacity:0; transform: translateX(-50%) translateY(-14px); }
  to   { opacity:1; transform: translateX(-50%) translateY(0); }
}
#tutorial-step-badge {
  display: inline-block; font-size: 0.6rem; font-weight: 800;
  letter-spacing: 2.5px; text-transform: uppercase;
  padding: 2px 9px; border-radius: 4px; margin-bottom: 8px;
}
#tutorial-step-badge.badge-learn {
  background: rgba(76,143,227,0.18); border: 1px solid rgba(76,143,227,0.5);
  color: #7ab0f5;
}
#tutorial-step-badge.badge-learn::before { content: '📖  LEARN'; }
#tutorial-step-badge.badge-task {
  background: rgba(240,192,64,0.12); border: 1px solid rgba(240,192,64,0.4);
  color: #f0c040;
}
#tutorial-step-badge.badge-task::before { content: '🎯  DO IT'; }
#tutorial-step-badge:empty { display: none; }
#tutorial-step-title {
  color: #f0c040; font-size: 0.88rem; font-weight: 800;
  letter-spacing: 3px; text-transform: uppercase; margin-bottom: 7px;
}
#tutorial-step-instruction {
  color: #e6edf3; font-size: 0.92rem; line-height: 1.6;
}
#tutorial-step-tip {
  color: #8b949e; font-size: 0.78rem; margin-top: 8px; line-height: 1.5;
  padding-top: 7px; border-top: 1px solid rgba(255,255,255,0.07);
}
#tutorial-step-tip:empty { display: none; }
#tutorial-step-progress {
  color: #f0c040; font-size: 0.82rem; font-weight: 700;
  letter-spacing: 2px; margin-top: 6px;
}
#tutorial-step-progress:empty { display: none; }
/* Auto-advance timer bar */
/* Hide scoreboard HUD during tutorial */
body.in-tutorial .hud-center,
body.in-tutorial #shot-clock-bar { display: none !important; }

#tutorial-timer-bar-wrap {
  margin-top: 12px; height: 3px; border-radius: 2px;
  background: rgba(255,255,255,0.1); overflow: hidden;
}
#tutorial-timer-bar-wrap.hidden { display: none; }
#tutorial-timer-bar {
  height: 100%; width: 0%; border-radius: 2px;
  background: linear-gradient(90deg, #4c8fe3, #f0c040);
  transition: none;
}
#tutorial-timer-bar.tut-timer-running {
  animation: tutTimerFill var(--tut-timer-dur, 8s) linear forwards;
}
@keyframes tutTimerFill {
  from { width: 0%; }
  to   { width: 100%; }
}
#tutorial-step-progress:empty { display: none; }

/* Flash message — centered pop-up for steal/check feedback */
#tutorial-flash {
  position: absolute; left: 50%; top: 38%;
  transform: translate(-50%, -50%);
  background: rgba(13,17,23,0.92);
  border: 2px solid #f0c040; border-radius: 14px;
  color: #f0c040; font-size: 1.35rem; font-weight: 800;
  letter-spacing: 2px; padding: 16px 36px;
  pointer-events: none; text-align: center;
  box-shadow: 0 0 40px rgba(240,192,64,0.25);
}
#tutorial-flash.hidden { display: none; }
#tutorial-flash.tut-flash-in {
  animation: tutFlashPop 1.8s ease-out forwards;
}
@keyframes tutFlashPop {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.85); }
  12%  { opacity: 1; transform: translate(-50%, -50%) scale(1.05); }
  25%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  75%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}

/* Key badge */
.tut-key {
  display: inline-block;
  background: #1a2540; border: 1px solid #4c8fe3;
  color: #7ab0f5; border-radius: 5px;
  padding: 1px 7px; font-size: 0.78rem; font-weight: 700;
  font-family: monospace; letter-spacing: 0.5px;
  vertical-align: middle; line-height: 1.6;
  box-shadow: 0 1px 3px rgba(76,143,227,0.3);
}

/* Completion panel */
#tutorial-complete-panel {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  pointer-events: auto;
  background: rgba(5,8,15,0.72);
}
#tutorial-complete-panel.hidden { display: none; }
#tutorial-finish-complete-modal {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  pointer-events: auto;
  background: rgba(5,8,15,0.72);
}
#tutorial-finish-complete-modal.hidden { display: none; }
.tut-finish-complete-sub { color: #c9d1d9; font-size: 0.95rem; margin: 6px 0 16px; text-align: center; max-width: 420px; }
.tut-complete-inner {
  background: #161b22; border: 1px solid #30363d;
  border-radius: 16px; padding: 36px 40px 32px;
  text-align: center; max-width: 420px;
  box-shadow: 0 20px 50px rgba(0,0,0,0.7);
}
.tut-complete-icon {
  font-size: 3.2rem; color: #f0c040; margin-bottom: 8px;
  animation: tutCompleteIcon 0.5s cubic-bezier(0.22,1,0.36,1) both;
}
@keyframes tutCompleteIcon {
  from { transform: scale(0.4); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
.tut-complete-title {
  color: #e6edf3; font-size: 1.4rem; font-weight: 800;
  letter-spacing: 2px; margin-bottom: 18px;
}
.tut-complete-list {
  list-style: none; padding: 0; margin: 0 0 24px;
  text-align: left; display: inline-block;
}
.tut-complete-list li {
  color: #8b949e; font-size: 0.85rem; padding: 3px 0;
}
.tut-complete-list li::before { content: '✓  '; color: #f0c040; font-weight: 700; }
.tut-complete-actions {
  display: flex; flex-direction: column; gap: 10px;
}
.tut-action-btn { min-width: 220px; }

/* Target ring */
#tutorial-target-ring {
  position: absolute; width: 52px; height: 52px;
  border: 3px solid #f0c040; border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%);
  animation: tutRingPulse 1.2s ease-in-out infinite;
}
#tutorial-target-ring.hidden { display: none; }
@keyframes tutRingPulse {
  0%,100% { opacity:0.9; transform: translate(-50%,-50%) scale(1); }
  50%      { opacity:0.4; transform: translate(-50%,-50%) scale(1.4); }
}

/* Arrow indicator */
#tutorial-arrow-indicator {
  position: absolute;
  pointer-events: none;
  font-size: 48px;
  line-height: 1;
  color: #1e7a34;
  text-shadow: 0 0 5px rgba(0,0,0,0.6), 0 0 2px rgba(0,0,0,0.8);
  animation: tutArrowPulse 0.8s ease-in-out infinite;
}
/* Stemmed arrow glyph (points right by default; JS rotates it toward the target). */
#tutorial-arrow-indicator::before { content: '\279C'; }
#tutorial-arrow-indicator.hidden { display: none; }
@keyframes tutArrowPulse {
  0%,100% { opacity:1; }
  50%      { opacity:0.45; }
}

/* ── Personal Stats screen ─────────────────────────────────────────────────── */
/* Stats, Replays and Leaderboard share this shell. All three must opt into
   scrolling: the global `body { overflow:hidden }` + `.screen { position:fixed }`
   means a tall list would otherwise be clipped with no way to reach it. Only
   #screen-stats did this before, and the other two were being saved by inner
   max-height clamps. */
#screen-stats, #screen-replays, #screen-leaderboard, #screen-store {
  overflow-y: auto;
  align-items: flex-start;
}
.stats-shell { width: min(960px, 100vw - 32px); margin: 26px auto 48px; }
.stats-header { display: flex; align-items: center; gap: 18px; margin-bottom: 20px; }
/* .btn is width:100%; inside the flex header that made Back eat the whole row. */
.stats-back-btn { flex-shrink: 0; width: auto; padding: 10px 18px; }
.stats-title-block { flex: 1; min-width: 0; }
.stats-title {
  font-family: var(--font-menu-display);
  font-size: 2rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
  line-height: 1;
}
.stats-subtitle {
  color: var(--ice-dim);
  font-size: 0.85rem;
  margin-top: 5px;
  letter-spacing: 0.01em;
}
.stats-record {
  font-family: var(--font-menu-display);
  font-size: 1.4rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
  text-align: right;
  line-height: 1.15;
}
.stats-record .rec-sub {
  display: block;
  font-size: 0.68rem;
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-top: 3px;
}

.stats-empty { text-align: center; padding: 90px 0; }
.stats-empty-icon {
  width: 44px;
  height: 44px;
  margin: 0 auto;
  color: rgba(125, 153, 173, 0.55);
  fill: none;
}
.stats-empty-title {
  font-family: var(--font-menu-display);
  font-size: 1.4rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
  margin-top: 14px;
}
.stats-empty-sub { color: var(--ice-dim); margin-top: 8px; line-height: 1.6; }

/* Hero tiles */
.stats-hero-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin-bottom: 16px; }
.stats-hero {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid rgba(242, 193, 78, 0.5);
  border-radius: var(--radius);
  padding: 14px 16px 12px; text-align: center;
  position: relative; overflow: hidden;
}
.stats-hero-value {
  font-family: var(--font-menu-display);
  font-size: 1.85rem;
  color: var(--ice-bright);
  line-height: 1;
}
.stats-hero-label {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-top: 6px;
}

/* Tabs */
/* Same tab language as Options — one pattern for the whole out-of-game UI. */
.stats-tabs { display: flex; gap: 2px; margin-bottom: 16px; border-bottom: 1px solid var(--edge); }
.stats-tab {
  background: transparent; border: none; cursor: pointer;
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  padding: 10px 16px;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  transition: color .15s, border-color .15s;
}
.stats-tab:hover { color: var(--text); }
.stats-tab.active { color: var(--gold); border-bottom-color: var(--gold); }

/* Stat cards + rows */
.stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 720px) { .stats-grid { grid-template-columns: 1fr; } }
.stats-card {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.stats-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.stats-card-title {
  font-family: var(--font-data);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 0.8rem;
  color: var(--ice-cool);
  margin-bottom: 12px;
}
.stats-card-head .stats-card-title { margin-bottom: 0; }

.stat-row { margin: 13px 0; }
.stat-row-top { display: flex; align-items: baseline; gap: 8px; }
.stat-row-name { color: var(--text); font-size: 0.9rem; flex: 1; }
.stat-row-total { font-family: var(--font-menu-display); font-size: 1.1rem; color: var(--ice-bright); }
.stat-row-pergame { color: var(--ice-dim); font-size: 0.76rem; }
.stat-pct-track {
  height: 6px; background: var(--rink-deep); border-radius: 3px; margin-top: 7px;
  position: relative; overflow: hidden;
}
.stat-pct-fill {
  position: absolute; inset: 0 auto 0 0; border-radius: 4px;
  background: linear-gradient(90deg, var(--gold-deep), var(--gold));
  transition: width .5s cubic-bezier(0.22,1,0.36,1);
}
.stat-pct-fill.low { background: linear-gradient(90deg, #5a6472, #96a4b8); }
.stat-row-pctline { display: flex; justify-content: space-between; margin-top: 4px; }
.stat-row-pcttext { color: var(--muted); font-size: 0.7rem; letter-spacing: 0.5px; }
.stat-row-pcttext b { color: #f0c040; font-weight: 700; }
.stat-row-pcttext.na b { color: var(--muted); }

/* Shooting tab */
.stats-shooting-top { display: grid; grid-template-columns: minmax(0, 470px) 1fr; gap: 12px; margin-bottom: 12px; }
@media (max-width: 780px) { .stats-shooting-top { grid-template-columns: 1fr; } }
.stats-net-card canvas { display: block; width: 100%; height: auto; margin-top: 4px; border-radius: 8px; }
.stats-net-legend { display: flex; gap: 16px; margin-top: 8px; color: var(--muted); font-size: 0.74rem; }
.stats-net-legend b { color: var(--text); }
.stats-heat-card canvas { display: block; width: 100%; height: auto; border-radius: 8px; background: #0b0f14; }
.stats-heat-note { color: var(--muted); font-size: 0.72rem; margin-top: 7px; letter-spacing: 0.4px; }
.stats-select {
  background: #0d1117; color: var(--text); border: 1px solid #30363d;
  border-radius: 6px; font-size: 0.85rem; padding: 6px 10px; outline: none; cursor: pointer;
}
.stats-placement-row { display: flex; align-items: center; gap: 10px; margin: 12px 0; }
.stats-placement-label { color: var(--text); font-size: 0.86rem; flex: 1; }
.stats-placement-value { font-family: var(--font-display); font-size: 1.35rem; color: #fff; }
.stats-placement-hint { color: var(--muted); font-size: 0.7rem; margin-top: -6px; margin-bottom: 10px; }
.stats-split-sub { color: var(--muted); font-family: 'Segoe UI', sans-serif; font-size: 0.62rem; letter-spacing: 1px; text-transform: uppercase; margin: 0 8px 0 4px; }

/* ─── Instant goal replay (in-game) ──────────────────────────────────────────
   Small optional "REPLAY" button (3s window after a goal) + a broadcast-style
   playback box top-left under the scoreboard. The box is purely visual —
   pointer-events pass through so gameplay input is never blocked. */
.live-replay-btn {
  position: absolute;
  /* Clear the HUD bar: in fullscreen it's absolutely stacked over the ice (z-25, --fs-hud-h tall)
     with the shot-clock bar right under it — anchor below both so it's never covered. */
  top: calc(var(--fs-hud-h, 48px) + 20px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 26;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 16px 7px 12px;
  font-family: var(--font-display);
  font-size: 1.02rem;
  letter-spacing: 2.5px;
  color: #fff;
  background: linear-gradient(180deg, rgba(22, 30, 46, 0.92), rgba(10, 15, 26, 0.92));
  /* The gold outline is drawn by ::before (a conic line that erases clockwise as the click
     window runs out) — the real border only reserves the layout space. */
  border: 1px solid transparent;
  border-radius: 999px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  cursor: pointer;
  backdrop-filter: blur(4px);
  animation: lrBtnIn 0.28s cubic-bezier(0.2, 1.4, 0.4, 1) both;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.live-replay-btn:hover {
  box-shadow: 0 4px 22px rgba(240, 192, 64, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.1);
  transform: translateX(-50%) scale(1.05);
}
/* Countdown outline: the button's thin gold border line itself, erased clockwise from 12
   o'clock as the click window runs out (JS drives --lr-left 1 → 0). The swept-past part is
   fully transparent — the line literally disappears around the pill. */
.live-replay-btn::before {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 999px;
  padding: 2px; /* line thickness */
  background: conic-gradient(from -90deg,
    var(--gold) calc(var(--lr-left, 1) * 360deg),
    transparent calc(var(--lr-left, 1) * 360deg));
  /* Keep only the border-shaped line of the conic fill (center masked out) */
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.live-replay-btn .lr-btn-play {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  font-size: 0.62rem;
  color: #0d1117;
  background: var(--gold);
  border-radius: 50%;
  padding-left: 2px;
  animation: lrPulse 1.1s ease-in-out infinite;
}
@keyframes lrBtnIn {
  from { opacity: 0; transform: translateX(-50%) translateY(-14px) scale(0.85); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
@keyframes lrPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(240, 192, 64, 0.55); }
  50%      { box-shadow: 0 0 0 6px rgba(240, 192, 64, 0); }
}

.live-replay-box {
  position: absolute;
  top: calc(var(--fs-hud-h, 48px) + 20px);
  left: 14px;
  z-index: 26;
  /* A broadcast bug over the full-screen replay (it used to frame a small video) */
  width: max-content;
  min-width: 240px;
  pointer-events: none;
  border: 1px solid rgba(240, 192, 64, 0.4);
  border-radius: 10px;
  overflow: hidden;
  background: #05080f;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(0, 0, 0, 0.6);
  animation: lrBoxIn 0.32s cubic-bezier(0.2, 1.3, 0.4, 1) both;
}
.live-replay-box, .live-replay-box * { pointer-events: none !important; }
@keyframes lrBoxIn {
  from { opacity: 0; transform: translateY(-16px) scale(0.94); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.live-replay-box .lr-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 10px 4px 6px;
  background: linear-gradient(90deg, #131b2c 0%, #0c1220 60%, #131b2c 100%);
  border-bottom: 1px solid rgba(240, 192, 64, 0.35);
}
.live-replay-box .lr-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 2.5px;
  color: #fff;
  background: linear-gradient(180deg, #e04040, #a81f1f);
  padding: 2px 9px 2px 7px;
  border-radius: 3px;
  transform: skewX(-8deg);
}
.live-replay-box .lr-live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  animation: lrBlink 0.9s steps(2, start) infinite;
}
@keyframes lrBlink { to { visibility: hidden; } }
.live-replay-box .lr-brand {
  font-family: var(--font-display);
  font-size: 0.68rem;
  letter-spacing: 3px;
  color: rgba(240, 192, 64, 0.85);
}
.live-replay-box .lr-lowerthird {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 10px 5px 0;
  background: linear-gradient(90deg, #182238, #0e1524 70%, #182238);
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.live-replay-box .lr-team-stripe {
  align-self: stretch;
  width: 5px;
  background: var(--gold);
  transform: skewX(-8deg);
  margin-left: 6px;
}
.live-replay-box[data-team="red"] .lr-team-stripe  { background: linear-gradient(180deg, var(--red), var(--red-dark)); }
.live-replay-box[data-team="blue"] .lr-team-stripe { background: linear-gradient(180deg, var(--blue), var(--blue-dark)); }
.live-replay-box .lr-scorer {
  flex: 1;
  font-family: var(--font-display);
  font-size: 1.02rem;
  letter-spacing: 1.5px;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.live-replay-box .lr-score {
  font-family: var(--font-display);
  font-size: 1.02rem;
  letter-spacing: 1px;
  color: var(--gold);
}
.live-replay-btn.hidden { display: none !important; }
.live-replay-box.hidden { display: none !important; }
.live-replay-box .lr-skip {
  padding: 3px 10px 5px;
  font: 600 11px/1.2 var(--font-data, 'Barlow Condensed', system-ui, sans-serif);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(230, 235, 245, 0.6);
}
/* Watching a goal replay from the break screen: the card steps aside until it ends. */
.period-break-overlay.break-replay-watching { opacity: 0 !important; pointer-events: none !important; }

/* ─── Pre-game cinematic ─────────────────────────────────────────────────────
   Roster panel (right, vertical — featured player highlighted gold) + lower-third with the
   featured player's name and stat lines. Purely visual — pointer-events pass through. */
.pregame-lower3 {
  position: absolute;
  left: 50%;
  bottom: 7%;
  transform: translateX(-50%);
  z-index: 26;
  pointer-events: none;
  min-width: 300px;
  max-width: 480px;
  animation: pgIn 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.pregame-lower3.hidden { display: none !important; }
@keyframes pgIn {
  from { opacity: 0; transform: translateX(-50%) translateY(26px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.pregame-lower3 .pg-main {
  display: flex;
  align-items: stretch;
  gap: 14px;
  padding: 12px 26px 12px 0;
  background: linear-gradient(90deg, rgba(13, 19, 32, 0.94), rgba(22, 30, 48, 0.94) 55%, rgba(13, 19, 32, 0.86));
  border: 1px solid rgba(240, 192, 64, 0.4);
  border-radius: 8px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.07);
  backdrop-filter: blur(5px);
}
.pregame-lower3 .pg-stripe {
  align-self: stretch;
  width: 7px;
  margin-left: 12px;
  border-radius: 2px;
  transform: skewX(-6deg);
  background: var(--gold);
}
.pregame-lower3[data-team="red"] .pg-stripe  { background: linear-gradient(180deg, var(--red), var(--red-dark)); }
.pregame-lower3[data-team="blue"] .pg-stripe { background: linear-gradient(180deg, var(--blue), var(--blue-dark)); }
.pregame-lower3 .pg-body { flex: 1; min-width: 0; }
/* Badge sits against 2rem display type over live 3D, so it takes the same kind
   of shadow the text does — without it the artwork floats on bright ice. */
.pregame-lower3 .pg-name .tier-badge {
  vertical-align: -0.2em;
  margin-right: 10px;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.55));
}
.pregame-lower3 .pg-name {
  font-family: var(--font-display);
  font-size: 2rem;
  line-height: 1;
  letter-spacing: 2px;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
}
.pregame-lower3 .pg-team {
  font-family: var(--font-display);
  font-size: 0.74rem;
  letter-spacing: 3px;
  color: rgba(240, 192, 64, 0.9);
  margin: 3px 0 7px;
}
.pregame-lower3 .pg-stats { display: flex; flex-direction: column; gap: 3px; }
.pregame-lower3 .pg-line {
  font-size: 0.86rem;
  color: var(--muted);
  letter-spacing: 0.5px;
  animation: pgChipIn 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.pregame-lower3 .pg-line:nth-child(1) { animation-delay: 0.22s; }
.pregame-lower3 .pg-line:nth-child(2) { animation-delay: 0.32s; }
.pregame-lower3 .pg-line:nth-child(3) { animation-delay: 0.42s; }
.pregame-lower3 .pg-line:nth-child(4) { animation-delay: 0.52s; }
.pregame-lower3 .pg-line b {
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 400;
  letter-spacing: 1px;
  color: var(--gold);
  margin-left: 2px;
}
/* Top-100 leaderboard badge next to the ELO — a gold pill that reads as elite status. */
.pregame-lower3 .pg-rank {
  display: inline-block;
  font-family: var(--font-data);
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  margin-left: 8px;
  padding: 2px 11px 3px;
  border-radius: 999px;
  color: #1a1405;
  background: linear-gradient(135deg, #ffe9ad 0%, #f0c040 45%, #c9971f 100%);
  border: 1px solid rgba(255, 233, 173, 0.7);
  box-shadow: 0 0 10px rgba(240, 192, 64, 0.5);
  vertical-align: middle;
}
@keyframes pgChipIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Roster panel — vertical starters list, featured player highlighted gold */
.pregame-roster {
  position: absolute;
  right: 3%;
  top: 16%;
  z-index: 26;
  pointer-events: none;
  min-width: 210px;
  max-width: 280px;
  animation: pgrIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.pregame-roster.hidden { display: none !important; }
@keyframes pgrIn {
  from { opacity: 0; transform: translateX(30px); }
  to   { opacity: 1; transform: translateX(0); }
}
.pregame-roster .pgr-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 14px;
  background: linear-gradient(90deg, rgba(13, 19, 32, 0.95), rgba(22, 30, 48, 0.9));
  border: 1px solid rgba(240, 192, 64, 0.4);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
}
.pregame-roster .pgr-tag {
  font-family: var(--font-display);
  font-size: 0.68rem;
  letter-spacing: 3px;
  color: rgba(240, 192, 64, 0.9);
}
.pregame-roster .pgr-team {
  font-family: var(--font-display);
  font-size: 1.25rem;
  line-height: 1;
  letter-spacing: 2px;
  color: #fff;
}
.pregame-roster[data-team="red"] .pgr-team  { color: var(--red); }
.pregame-roster[data-team="blue"] .pgr-team { color: var(--blue); }
.pregame-roster .pgr-list {
  border: 1px solid rgba(240, 192, 64, 0.4);
  border-radius: 0 0 8px 8px;
  overflow: hidden;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.55);
}
.pregame-roster .pgr-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  background: rgba(13, 19, 32, 0.88);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  animation: pgrRowIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes pgrRowIn {
  from { opacity: 0; transform: translateX(22px); }
  to   { opacity: 1; transform: translateX(0); }
}
.pregame-roster .pgr-name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 1.5px;
  color: #e6edf3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pregame-roster .pgr-g {
  font-family: var(--font-display);
  font-size: 0.7rem;
  color: #0d1117;
  background: var(--ice);
  border-radius: 3px;
  padding: 1px 6px;
  letter-spacing: 1px;
}
/* Per-player ELO + top-100 rank on the TONIGHT'S STARTERS list. */
.pregame-roster .pgr-elo {
  font-family: var(--font-data);
  font-size: 0.96rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--gold);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}
.pregame-roster .pgr-rank {
  font-size: 0.8rem;
  font-weight: 700;
  color: #1a1405;
  background: linear-gradient(135deg, #ffe9ad 0%, #f0c040 45%, #c9971f 100%);
  border-radius: 999px;
  padding: 1px 8px 2px;
  box-shadow: 0 0 8px rgba(240, 192, 64, 0.5);
}
.pregame-roster .pgr-row.pgr-featured {
  background: linear-gradient(90deg, var(--gold), #ffdf87);
  box-shadow: 0 0 18px rgba(240, 192, 64, 0.45);
}
.pregame-roster .pgr-row.pgr-featured .pgr-name { color: #0d1117; text-shadow: none; }
.pregame-roster .pgr-row.pgr-featured .pgr-g { background: #0d1117; color: var(--gold); }
/* On the gold featured row, flip ELO/rank to dark so they stay legible. */
.pregame-roster .pgr-row.pgr-featured .pgr-elo { color: #0d1117; }
.pregame-roster .pgr-row.pgr-featured .pgr-rank { background: #0d1117; color: var(--gold); box-shadow: none; }

.pregame-cut-flash {
  position: absolute;
  inset: 0;
  z-index: 25;
  pointer-events: none;
  background: #fff;
  opacity: 0;
  animation: pgFlash 0.28s ease-out both;
}
.pregame-cut-flash.hidden { display: none !important; }

/* Grand-entrance prologue overlay. Two layers on one element: a hard-edged oval "mouth"
   (the tunnel opening we have no geometry for) and a soft outer darkening. Both ride the
   same opacity, which the client animates as the camera emerges. */
.pregame-tunnel {
  position: absolute; inset: 0; pointer-events: none; z-index: 42;
  background:
    radial-gradient(ellipse 62% 78% at 50% 54%,
      rgba(0,0,0,0) 0%, rgba(0,0,0,0) 52%, rgba(0,0,0,.72) 68%, rgba(0,0,0,.97) 82%, #000 100%),
    radial-gradient(ellipse 120% 120% at 50% 50%,
      rgba(0,0,0,0) 40%, rgba(0,0,0,.55) 100%);
  opacity: 0;
  transition: none;
}
.pregame-tunnel.hidden { display: none !important; }

/* ── Grand-entrance broadcast title sequence ────────────────────────────────────
   All CSS: letterbox bars, a lens vignette, and type that slams in per beat. Costs
   nothing on the render budget, which is the whole reason the "spectacle" lives here
   rather than in geometry. */
.pregame-entrance { position: absolute; inset: 0; pointer-events: none; z-index: 44; }
.pregame-entrance.hidden { display: none !important; }

.pe-bar {
  position: absolute; left: 0; right: 0; height: 11%;
  background: #000;
  transform: translateY(-101%);
  transition: transform .55s cubic-bezier(.16,.84,.34,1);
}
.pe-bar-bottom { bottom: 0; transform: translateY(101%); }
.pe-bar-top { top: 0; }
.pregame-entrance.pe-in .pe-bar { transform: translateY(0); }

.pe-vignette {
  position: absolute; inset: 0; opacity: 0;
  transition: opacity .6s ease;
  background: radial-gradient(ellipse 78% 82% at 50% 50%,
              rgba(0,0,0,0) 45%, rgba(0,0,0,.45) 100%);
}
.pregame-entrance.pe-in .pe-vignette { opacity: 1; }

.pe-stage {
  position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%);
  text-align: center; padding: 0 6vw;
  font-family: 'Rajdhani', system-ui, sans-serif;
  text-shadow: 0 4px 26px rgba(0,0,0,.85);
}
.pe-kicker, .pe-title { opacity: 0; }

/* Each block animates in only while its beat class is on the container, so the
   sequence is driven by one class swap rather than a timeline. */
/* Near the top and DARK: this sits over the centre-ice crest shot, which is mostly white
   ice, so the pale cyan it used to be washed straight out. Colour also carries meaning —
   blue for exhibition, red for ranked — and a white halo keeps it legible if the shot
   drifts over something darker. */
.pe-kicker {
  position: absolute; top: 11%; left: 0; right: 0; text-align: center;
  font-size: clamp(17px, 2.7vw, 38px); letter-spacing: .46em; text-indent: .46em;
  text-transform: uppercase; font-weight: 700;
  color: #10306b;
  text-shadow: 0 1px 0 rgba(255,255,255,.75), 0 0 22px rgba(255,255,255,.85),
               0 0 44px rgba(255,255,255,.5);
}
.pe-kicker.pe-kicker-ranked { color: #8f1220; }
.pe-title {
  font-size: clamp(30px, 6.5vw, 92px); font-weight: 700; line-height: 1.02;
  letter-spacing: .04em; color: #fff; text-transform: uppercase; margin-top: .1em;
}

@keyframes peRise {
  from { opacity: 0; transform: translateY(18px); filter: blur(6px); }
  to   { opacity: 1; transform: translateY(0);    filter: blur(0); }
}
}
}
@keyframes peFlare {
  0%   { opacity: 0; transform: scale(.86); letter-spacing: .3em; }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1); letter-spacing: .04em; }
}

.pregame-entrance.pe-b0 .pe-kicker { animation: peRise .7s ease forwards; }
.pregame-entrance.pe-b0 .pe-title  { animation: peFlare .9s ease forwards .12s; }
/* Stage 1 hands the screen to the matchup card. The wordmark clears immediately, but the
   kicker holds a further second — it is the label for the whole opening and reads better
   with the card already up than it does cut off the instant the card arrives. */
.pregame-entrance.pe-b1 .pe-title  { opacity: 0; transition: opacity .5s ease; }
.pregame-entrance.pe-b1 .pe-kicker { opacity: 0; transition: opacity .6s ease 1s; }
/* The kicker lives outside .pe-stage, so the hand-off has to clear it explicitly. */
.pregame-entrance.pe-out .pe-kicker { opacity: 0; transition: opacity .45s ease; }

/* Final beat: everything clears so the hero shot starts on a clean frame. */
.pregame-entrance.pe-out .pe-stage { opacity: 0; transition: opacity .45s ease; }
.pregame-entrance.pe-out .pe-bar-top { transform: translateY(-101%); }
.pregame-entrance.pe-out .pe-bar-bottom { transform: translateY(101%); }
.pregame-entrance.pe-out .pe-vignette { opacity: 0; }

/* Winter Classic flyover: a vintage newsreel title card. Script wordmark in the lower third
   (the jets own the top of the frame), cream on a warm sepia wash, film grain + flicker. */
.pe-grain { position: absolute; inset: 0; opacity: 0; display: none; }
.pregame-entrance.pe-retro .pe-grain {
  display: block;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .45 0 0 0 0 .35 0 0 0 .55 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
  animation: peGrain .24s steps(3) infinite, peFlicker 3.1s linear infinite;
}
.pregame-entrance.pe-retro.pe-in .pe-grain { opacity: .55; }
.pregame-entrance.pe-retro.pe-b1 .pe-grain,
.pregame-entrance.pe-retro.pe-out .pe-grain { opacity: 0; transition: opacity .8s ease; }
.pregame-entrance.pe-retro .pe-vignette {
  background: radial-gradient(ellipse 80% 84% at 50% 50%, rgba(60,36,10,0) 42%, rgba(40,22,6,.62) 100%);
}
.pregame-entrance.pe-retro .pe-stage { top: auto; bottom: 14%; transform: none; }
.pregame-entrance.pe-retro .pe-title {
  font-family: 'Lobster', Georgia, 'Times New Roman', serif; font-weight: 400;
  text-transform: none; letter-spacing: .01em;
  font-size: clamp(34px, 7vw, 104px);
  color: #f6e7c1;
  text-shadow: 3px 3px 0 #8e1b22, 6px 6px 0 rgba(20,41,79,.85), 0 8px 30px rgba(0,0,0,.7);
}
.pregame-entrance.pe-retro .pe-kicker {
  top: auto; bottom: calc(14% + clamp(34px, 7vw, 104px) * 1.25);
  font-family: Georgia, 'Times New Roman', serif; font-weight: 400; font-style: italic;
  letter-spacing: .32em; text-indent: .32em; text-transform: none;
  font-size: clamp(14px, 1.9vw, 28px);
  color: #f0dfb4;
  text-shadow: 0 2px 10px rgba(0,0,0,.85);
}
.pregame-entrance.pe-retro .pe-kicker::before,
.pregame-entrance.pe-retro .pe-kicker::after {
  content: ''; display: inline-block; vertical-align: middle;
  width: clamp(24px, 6vw, 90px); height: 2px; margin: 0 .9em;
  background: #c9a24a; box-shadow: 0 1px 0 rgba(0,0,0,.6);
}
/* the retro wordmark settles in like a slide in an old projector, not the LED flare */
.pregame-entrance.pe-retro.pe-b0 .pe-title { animation: peReel 1.2s ease forwards .25s; }
@keyframes peReel {
  0%   { opacity: 0; transform: translateY(10px) scale(1.04); filter: blur(4px) sepia(1); }
  30%  { opacity: .7; }
  40%  { opacity: .45; }
  55%  { opacity: 1; filter: blur(0) sepia(0); }
  100% { opacity: 1; transform: none; }
}
@keyframes peGrain {
  0% { background-position: 0 0; } 33% { background-position: -47px 31px; } 66% { background-position: 23px -61px; } 100% { background-position: 0 0; }
}
@keyframes peFlicker {
  0%, 100% { filter: brightness(1); } 8% { filter: brightness(1.35); } 9% { filter: brightness(.9); }
  47% { filter: brightness(1.1); } 48% { filter: brightness(1.45); } 50% { filter: brightness(1); }
}
@media (prefers-reduced-motion: reduce) { .pregame-entrance.pe-retro .pe-grain { animation: none; } }
@keyframes pgFlash {
  0% { opacity: 0.4; }
  100% { opacity: 0; }
}

/* Pre-game mosaic — split-screen team close-ups; DOM grid matches the renderer tile grid.
   Cells only hold the gold divider lines + name/stat bars; the 3D tiles render beneath. */
.pregame-mosaic {
  position: absolute;
  inset: 0;
  z-index: 24;
  pointer-events: none;
  display: grid;
}
.pregame-mosaic.hidden { display: none !important; }
.pregame-mosaic .pgm-cell {
  position: relative;
  /* Broadcast quad-box framing: solid dark mullion between feeds (adjacent cells combine to a
     6px separator), a crisp gold hairline just inside it, and a soft vignette for depth. */
  border: 3px solid #070b12;
  box-shadow:
    inset 0 0 0 1px rgba(240, 192, 64, 0.6),
    inset 0 0 0 2px rgba(0, 0, 0, 0.45),
    inset 0 0 56px rgba(0, 0, 0, 0.38);
  animation: pgmCellIn 0.3s ease-out both;
}
/* Outer frame around the whole mosaic matches the inner mullions. */
.pregame-mosaic {
  border: 3px solid #070b12;
  box-shadow: inset 0 0 0 1px rgba(240, 192, 64, 0.35);
}
@keyframes pgmCellIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.pregame-mosaic .pgm-bar {
  position: absolute;
  left: 6%;
  right: 6%;
  bottom: 7%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 5px 12px 6px;
  background: linear-gradient(90deg, rgba(13, 19, 32, 0.92), rgba(22, 30, 48, 0.92));
  border: 1px solid rgba(240, 192, 64, 0.4);
  border-radius: 6px;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.55);
  animation: pgChipIn 0.4s 0.12s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.pregame-mosaic .pgm-bar.pgm-featured {
  border-color: var(--gold);
  box-shadow: 0 0 18px rgba(240, 192, 64, 0.4), 0 8px 22px rgba(0, 0, 0, 0.55);
}
.pregame-mosaic .pgm-name {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-display);
  font-size: 1.15rem;
  line-height: 1;
  letter-spacing: 2px;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.pregame-mosaic .pgm-g {
  font-family: var(--font-display);
  font-size: 0.66rem;
  color: #0d1117;
  background: var(--ice);
  border-radius: 3px;
  padding: 1px 6px;
  letter-spacing: 1px;
}
.pregame-mosaic .pgm-stats {
  font-size: 0.7rem;
  color: var(--muted);
  letter-spacing: 0.4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.pregame-mosaic .pgm-stats b {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 0.86rem;
  color: var(--gold);
  letter-spacing: 1px;
}
.pregame-mosaic .pgm-rank {
  font-size: 0.66rem;
  font-weight: 700;
  color: #1a1405;
  background: linear-gradient(135deg, #ffe9ad 0%, #f0c040 45%, #c9971f 100%);
  border-radius: 999px;
  padding: 0 6px 1px;
  box-shadow: 0 0 7px rgba(240, 192, 64, 0.5);
  letter-spacing: 0;
}

/* Skip-intro vote (top-left, above the matchup card z-55) */
.intro-skip-wrap {
  position: absolute;
  top: calc(var(--fs-hud-h, 48px) + 20px);
  left: 14px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 9px;
}
.intro-skip-wrap.hidden { display: none !important; }
.intro-skip-btn {
  font-family: var(--font-display);
  font-size: 0.86rem;
  letter-spacing: 2px;
  color: #c8d6ea;
  background: linear-gradient(180deg, rgba(22, 30, 46, 0.92), rgba(10, 15, 26, 0.92));
  border: 1px solid rgba(240, 192, 64, 0.45);
  border-radius: 999px;
  padding: 6px 15px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, box-shadow 0.15s;
}
.intro-skip-btn:hover:not(:disabled) {
  color: #fff;
  border-color: var(--gold);
  box-shadow: 0 0 14px rgba(240, 192, 64, 0.3);
}
.intro-skip-btn:disabled { opacity: 0.65; cursor: default; color: var(--gold); }
.intro-skip-btn.hidden { display: none !important; }
.intro-skip-count {
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 1.5px;
  color: var(--gold);
  background: rgba(10, 15, 26, 0.85);
  border: 1px solid rgba(240, 192, 64, 0.4);
  border-radius: 999px;
  padding: 4px 11px;
}
.intro-skip-count.hidden { display: none !important; }

/* ─── Multi-region chips (blue USA / red Germany) ──────────────────────────── */
.region-chip {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 1.5px;
  line-height: 1;
  padding: 3px 8px 2px;
  border-radius: 4px;
  vertical-align: middle;
  color: #fff;
  background: #30363d;
}
.region-chip.region-usa { background: var(--blue-dark); border: 1px solid var(--blue); }
.region-chip.region-germany { background: var(--red-dark); border: 1px solid var(--red); }
.region-chip.hidden { display: none !important; }
.lobby-region-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 10px 0 2px;
}
.lobby-region-row.hidden { display: none !important; }
.lobby-region-row .lobby-region-label { color: var(--muted); font-size: 0.85rem; }

/* Stats page region toggle (per-region career records) — prominent bar above the stats */
.stats-region-bar {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin: 14px 0 4px;
}
.stats-region-bar.hidden { display: none !important; }
.stats-region-btn {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ice-dim);
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 9px 30px;
  cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.stats-region-btn:hover { color: #fff; }
.stats-region-btn.active { color: #fff; background: var(--blue-dark); border-color: var(--blue); box-shadow: 0 0 14px rgba(76, 143, 227, 0.35); }
.stats-region-btn[data-region="germany"].active { background: var(--red-dark); border-color: var(--red); box-shadow: 0 0 14px rgba(227, 76, 76, 0.35); }

/* Ranked | Unranked toggle on the stats page — same look as the region bar, gold for ranked. */
.stats-mode-bar {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin: 14px 0 4px;
}
.stats-mode-bar.hidden { display: none !important; }
.stats-mode-btn {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ice-dim);
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 9px 30px;
  cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.stats-mode-btn:hover { color: #fff; }
.stats-mode-btn.active { color: #fff; background: var(--blue-dark); border-color: var(--blue); box-shadow: 0 0 14px rgba(76, 143, 227, 0.35); }
.stats-mode-btn[data-mode="ranked"].active { color: #ffe9ad; background: #6b5410; border-color: #f0c040; box-shadow: 0 0 14px rgba(240, 192, 64, 0.35); }

/* Name screen: auto-login in flight (token or cross-region SSO ticket) — hide the form, show this */
.auth-signing-in {
  color: var(--muted);
  font-size: 0.95rem;
  letter-spacing: 0.5px;
  margin: 14px 0;
  animation: signingPulse 1.2s ease-in-out infinite;
}
.auth-signing-in.hidden { display: none !important; }
@keyframes signingPulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}

/* ── Ranked Matchmaking ─────────────────────────────────────────────────── */
/* The menu's Ranked entry is now .btn-hero (see the buttons section) — a gold
   spine and wash on a dark panel, not a solid gold slab. This class survives
   only as a hook; its old solid-gold styling would fight .btn-hero. */

.ranked-card { width: min(500px, 94vw); }
#ranked-setup { display: flex; flex-direction: column; gap: 14px; }
.ranked-my-elo { color: var(--ice-dim); font-size: 0.88rem; margin: -6px 0 4px; }
.ranked-my-elo b { color: var(--gold); font-family: var(--font-data); font-size: 1.15rem; letter-spacing: 0.04em; }
.ranked-pref-label { font-size: 0.92rem; font-weight: 600; margin-bottom: 9px; }
.ranked-pref-hint { display: block; color: var(--ice-dim); font-weight: 400; font-size: 0.8rem; margin-top: 2px; }
.ranked-pref-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }

/* Region picker — two wide cards, each showing that region's live queue size. */
.ranked-region-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 14px; }
.ranked-region-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 11px 8px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  cursor: pointer;
  transition: border-color 0.16s, background 0.16s, color 0.16s, transform 0.12s;
}
.ranked-region-name { font-weight: 600; font-size: 0.9rem; letter-spacing: 0.09em; text-transform: uppercase; }
.ranked-region-count { font-size: 0.76rem; letter-spacing: 0.04em; color: var(--ice-dim); }
.ranked-region-btn:hover { border-color: rgba(168, 212, 232, 0.35); color: var(--text); transform: translateY(-1px); }
.ranked-region-btn.active {
  border-color: var(--gold);
  background: linear-gradient(180deg, rgba(242, 193, 78, 0.14), rgba(242, 193, 78, 0.03));
  color: var(--gold);
}
.ranked-region-btn.active .ranked-region-count { color: var(--gold); }

/* Role cards. Selecting one is the only real choice in this modal, so they get
   the weight — big icon, clear selected state, not three small radio buttons. */
.ranked-pref-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  padding: 16px 8px 13px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 0.16s, background 0.16s, color 0.16s, transform 0.12s;
}
/* Emoji here, matching the tutorial picker — these two card grids are where the
   player picks a role, and the extra warmth suits them. */
.ranked-pref-icon { font-size: 1.7rem; line-height: 1; }
.ranked-pref-btn:hover {
  border-color: rgba(168, 212, 232, 0.35);
  color: var(--text);
  transform: translateY(-1px);
}
.ranked-pref-btn.active {
  border-color: var(--gold);
  background: linear-gradient(180deg, rgba(242, 193, 78, 0.14), rgba(242, 193, 78, 0.03));
  color: var(--gold);
}
.ranked-find-btn { width: 100%; font-size: 1rem; padding: 14px; }
.ranked-login-note {
  background: rgba(58, 16, 15, 0.5);
  border: 1px solid rgba(226, 69, 63, 0.4);
  border-left: 3px solid var(--red);
  color: #f6d8d6;
  border-radius: var(--radius);
  padding: 11px 13px;
  font-size: 0.86rem;
  line-height: 1.45;
}

/* Roam note under Find Match. The whole point of the dock is that the search does not
   trap them on this modal, so the modal has to say so before they commit. */
.ranked-roam-note {
  margin: 0;
  color: var(--ice-dim);
  font-size: 0.8rem;
  line-height: 1.5;
  text-align: center;
}

/* ── #mm-dock: the running search ─────────────────────────────────────────────
   Lives at body level, bottom-right, above every menu screen and below the modals.
   Tracks --chat-dock-offset the same way the music widget and the toast host do, so
   an expanded global-chat panel slides it left instead of burying it.
   States: (default) searching · .mm-dock--found · .mm-dock--offline. */
.mm-dock {
  position: fixed;
  bottom: 14px;
  right: calc(14px + var(--chat-dock-offset, 0px));
  /* Above every .screen and above the Options overlay (1000) — Options is somewhere a
     queued player is expressly meant to be able to sit, so the search and its Cancel have
     to stay in reach there. Below .modal-overlay (2000): a deliberately-opened modal
     (goal horn, boost picker, a confirm) covers it for as long as it is open rather than
     having this card land on top of the card they are reading. */
  z-index: 1900;
  width: 268px;
  max-width: calc(100vw - 28px);
  padding: 12px 14px 13px;
  background: linear-gradient(180deg, var(--rink-hi) 0%, var(--rink) 100%);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--gold);
  border-radius: var(--radius);
  box-shadow: 0 14px 40px -10px rgba(0, 0, 0, 0.85), 0 0 0 1px rgba(242, 193, 78, 0.08);
  transition: right 0.18s ease, border-color 0.2s ease, box-shadow 0.2s ease;
  animation: mmDockIn 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
.mm-dock.hidden { display: none; }
@keyframes mmDockIn {
  from { opacity: 0; transform: translateY(16px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

.mm-dock-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.mm-dock-dot {
  flex: none;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 9px var(--gold-glow);
  animation: mmDockPulse 1.5s ease-in-out infinite;
}
@keyframes mmDockPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
.mm-dock-eyebrow {
  flex: 1;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}
.mm-dock-timer {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  color: var(--ice-dim);
}

.mm-dock-body { display: flex; align-items: center; gap: 12px; }
.mm-dock-count {
  font-family: var(--font-menu-display);
  line-height: 1;
  color: var(--ice-bright);
  white-space: nowrap;
}
.mm-dock-have { font-size: 1.75rem; }
.mm-dock-of { font-size: 1rem; color: var(--ice-dim); margin-left: 1px; }

/* One pip per slot, all on one line: eight marks filling left to right reads as "the game
   is assembling" in a way a number that ticks does not. Auto-fit rather than a fixed eight,
   so it still lays out if the server ever runs a different game size. */
.mm-dock-pips {
  flex: 1;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 4px;
}
.mm-dock-pip {
  height: 6px;
  border-radius: 3px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  transition: background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.mm-dock-pip--on {
  background: var(--gold);
  border-color: var(--gold);
  box-shadow: 0 0 7px var(--gold-glow);
}

.mm-dock-meta {
  margin: 9px 0 0;
  min-height: 1.15em;
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--ice-dim);
}

/* Role + region chips: what this search is actually looking for. */
.mm-dock-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.mm-dock-tag {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 3px;
  color: var(--ice-cool);
  background: rgba(168, 212, 232, 0.08);
  border: 1px solid var(--edge);
}

.mm-dock-cancel {
  width: 100%;
  margin-top: 11px;
  padding: 8px 10px;
  background: transparent;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
.mm-dock-cancel:hover {
  color: #f6d8d6;
  border-color: rgba(226, 69, 63, 0.55);
  background: rgba(226, 69, 63, 0.12);
}

/* Match found: the dock is about to be replaced by the loading screen, so it only has to
   read for a beat — colour does that faster than copy. */
.mm-dock--found { border-top-color: var(--green); box-shadow: 0 14px 40px -10px rgba(0, 0, 0, 0.85), 0 0 0 1px rgba(63, 185, 107, 0.25); }
.mm-dock--found .mm-dock-dot { background: var(--green); box-shadow: 0 0 9px rgba(63, 185, 107, 0.6); animation: none; }
.mm-dock--found .mm-dock-eyebrow { color: var(--green); }
.mm-dock--found .mm-dock-pip { background: var(--green); border-color: var(--green); box-shadow: 0 0 7px rgba(63, 185, 107, 0.45); }
.mm-dock--found .mm-dock-cancel { visibility: hidden; }

/* Socket dropped mid-search. We hold the player's place in the UI and re-queue on
   reconnect (see mmResumeOnAuth in game.js), so this is a wait, not a failure. */
.mm-dock--offline { border-top-color: var(--ice-cool); }
.mm-dock--offline .mm-dock-dot { background: var(--ice-cool); box-shadow: none; }
.mm-dock--offline .mm-dock-eyebrow { color: var(--ice-cool); }

/* Attention flash — fired when a queued player clicks something the queue has closed
   off, so the answer ("you're searching, here's the way out") is where they're looking. */
.mm-dock--nudge { animation: mmDockNudge 0.62s ease-out 2; }
@keyframes mmDockNudge {
  0%, 100% { transform: none; box-shadow: 0 14px 40px -10px rgba(0, 0, 0, 0.85), 0 0 0 1px rgba(242, 193, 78, 0.08); }
  30%      { transform: translateY(-4px); box-shadow: 0 14px 40px -10px rgba(0, 0, 0, 0.85), 0 0 0 2px var(--gold); }
}

@media (prefers-reduced-motion: reduce) {
  .mm-dock, .mm-dock--nudge { animation: none; }
  .mm-dock-dot { animation: none; }
}

/* Phones: full-width strip along the bottom rather than a floating card. */
@media (max-width: 560px) {
  .mm-dock {
    left: 10px;
    right: 10px;
    bottom: 10px;
    width: auto;
    max-width: none;
  }
}

/* Lobby browser in read-only mode. A thin gold rule rather than a full warning panel: this
   is a state, not a problem, and the screen below it is still perfectly useful. */
.browser-mm-note {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 14px;
  padding: 9px 13px;
  border: 1px solid rgba(242, 193, 78, 0.28);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius);
  background: linear-gradient(90deg, rgba(242, 193, 78, 0.1) 0%, rgba(242, 193, 78, 0.02) 60%, transparent 100%);
  color: var(--ice-cool);
  font-size: 0.84rem;
  line-height: 1.4;
}
.browser-mm-note.hidden { display: none; }
.browser-mm-note-dot {
  flex: none;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 8px var(--gold-glow);
  animation: mmDockPulse 1.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .browser-mm-note-dot { animation: none; } }
/* Read-only rows keep their content at full strength — the listing is the whole point — but
   the buttons give up the primary-blue affordance as well as dimming, so a disabled Join
   cannot still read as the thing to press. Same for the library's Watch / Save MP4. */
#screen-browser .btn:disabled,
#screen-replays .btn:disabled { filter: grayscale(0.75); }

/* A locked clip tile: the poster is a plain div with its own click handler, so it has to be
   made inert here rather than with a disabled attribute. The artwork stays at full strength —
   the point of the grid is finding the clip you mean, which still works. */
.clip-tile--locked .clip-tile-poster { pointer-events: none; }
.clip-tile--locked .clip-tile-play { opacity: 0.35; cursor: not-allowed; }

/* Menu rows the running search closes off (Host / Tutorial). Still visible — a row that
   vanishes reads as a bug — but plainly out of reach, with the reason on the title
   attribute. Browse games is NOT one of them: that screen goes read-only instead. */
.btn-nav.mm-locked, .btn-hero.mm-locked {
  opacity: 0.42;
  cursor: not-allowed;
  filter: grayscale(0.55);
}
/* Hover does nothing but sit still. The row keeps its own colours (Tutorial's promoted
   variant has a green spine of its own, and repainting it here would read as a glitch) —
   the dimming and the desaturation are what say "not right now". */
.btn-nav.mm-locked:hover, .btn-hero.mm-locked:hover { transform: none; }
.btn-nav.mm-locked:hover .nav-icon, .btn-nav.mm-locked:hover .nav-chevron { transform: none; }

/* The Ranked row while the search runs: it becomes the way OUT of the queue, and is the
   only cancel a controller can reach (the dock sits outside every ZHFocus layer). Id-scoped
   because the newcomer layout's own #btn-ranked rules would otherwise win. */
#btn-ranked.mm-searching { border-left-color: var(--green); }
#btn-ranked.mm-searching .hero-title { color: var(--ice-bright); }
/* That layout hides the sub line; while the sub IS the instruction, it has to be readable. */
#btn-ranked.btn-hero--asnav.mm-searching .hero-sub {
  display: block;
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  color: var(--ice-dim);
}

/* Stats chooser modal */
.stats-choice-card { width: min(520px, 94vw); }
.stats-choice-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.stats-choice-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 24px 12px 20px;
  background: var(--rink-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.16s, background 0.16s, transform 0.12s;
}
.stats-choice-btn:hover {
  border-color: var(--gold);
  background: linear-gradient(180deg, rgba(242, 193, 78, 0.1), transparent);
  transform: translateY(-2px);
}
.stats-choice-icon { width: 30px; height: 30px; color: var(--gold); }
.stats-choice-name {
  font-family: var(--font-menu-display);
  font-size: 1.05rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
}
.stats-choice-sub { color: var(--muted); font-size: 0.78rem; }

/* Leaderboard */
.leaderboard-shell { max-width: 800px; }
.leaderboard-table-wrap {
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  overflow: hidden;
}
.leaderboard-row {
  display: grid;
  /* Fixed trophy gutter so every name starts on the same x, medal or not. */
  grid-template-columns: 58px 1.4em 1fr 96px 86px;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-bottom: 1px solid rgba(160, 205, 230, 0.07);
  border-left: 3px solid transparent;
  font-size: 0.95rem;
}
.leaderboard-row:last-child { border-bottom: none; }
.leaderboard-head {
  background: var(--rink-hi);
  color: var(--ice-cool);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  position: sticky; top: 0;
  z-index: 1;
}
.lb-rank {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.lb-elo {
  text-align: right;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}
.lb-games { text-align: right; color: var(--ice-dim); font-variant-numeric: tabular-nums; }
/* Podium: a metal spine, a tinted rank, and a matching cup. */
.lb-trophy { width: 1.15em; height: 1.15em; display: block; align-self: center; }
.lb-trophy svg { width: 100%; height: 100%; display: block; }
.leaderboard-row.lb-top1 .lb-trophy svg { fill: #f2c14e; filter: drop-shadow(0 0 5px rgba(242, 193, 78, 0.6)); }
.leaderboard-row.lb-top2 .lb-trophy svg { fill: #c3ccd6; filter: drop-shadow(0 0 4px rgba(195, 204, 214, 0.45)); }
.leaderboard-row.lb-top3 .lb-trophy svg { fill: #c98a52; filter: drop-shadow(0 0 4px rgba(201, 138, 82, 0.45)); }
.leaderboard-row.lb-top1 { border-left-color: #f2c14e; background: rgba(242, 193, 78, 0.05); }
.leaderboard-row.lb-top2 { border-left-color: #c3ccd6; background: rgba(195, 204, 214, 0.04); }
.leaderboard-row.lb-top3 { border-left-color: #c98a52; background: rgba(201, 138, 82, 0.04); }
.leaderboard-row.lb-top1 .lb-rank { color: #f2c14e; }
.leaderboard-row.lb-top2 .lb-rank { color: #c3ccd6; }
.leaderboard-row.lb-top3 .lb-rank { color: #c98a52; }
.leaderboard-row.lb-top1 .lb-name,
.leaderboard-row.lb-top2 .lb-name,
.leaderboard-row.lb-top3 .lb-name { font-weight: 600; color: var(--ice-bright); }
.leaderboard-row.lb-me {
  background: rgba(62, 134, 232, 0.1);
  border-left-color: var(--blue);
}
.leaderboard-row.lb-me .lb-name { color: #9ccbff; font-weight: 600; }
#leaderboard-rows { max-height: min(62vh, 720px); overflow-y: auto; }

/* Stats page: ranked section + section labels */
.stats-section-label {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ice-cool);
  margin: 20px 2px 10px;
}
.stats-section-label.hidden { display: none; }
.stats-section-label-sub { color: var(--muted); font-size: 0.8rem; font-family: inherit; letter-spacing: 1px; }
.stats-ranked-section { margin-bottom: 6px; }
.stats-ranked-section.hidden { display: none; }
.stats-ranked-card {
  background: linear-gradient(160deg, rgba(242, 193, 78, 0.07) 0%, var(--rink) 45%);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--gold);
  border-radius: var(--radius);
  padding: 14px 16px 12px;
}
.stats-ranked-head {
  display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px;
}
.stats-ranked-title { font-family: var(--font-menu-display); font-size: 1.1rem; letter-spacing: 0.01em; color: var(--gold); }
.stats-ranked-sub { color: var(--muted); font-size: 0.78rem; }
.stats-ranked-tiles { display: flex; flex-wrap: wrap; gap: 10px; }
.stats-ranked-tile {
  flex: 1 1 100px;
  min-width: 96px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 10px 8px 8px;
  text-align: center;
}
.stats-ranked-tile-value { font-family: var(--font-menu-display); font-size: 1.5rem; letter-spacing: 0.01em; line-height: 1; color: var(--ice-bright); }
.stats-ranked-tile-value.gold { color: var(--gold); }
.stats-ranked-tile-value.bad { color: var(--red); }
.stats-ranked-tile-label { color: var(--ice-dim); font-family: var(--font-data); font-weight: 600; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.16em; margin-top: 5px; }
.stats-ranked-empty { color: var(--muted); font-size: 0.88rem; padding: 4px 0 2px; }

/* ── End-of-match rank progress ──
   Sits under the ELO chips. The bar filling is the payoff for the match, so it
   animates rather than appearing already full. */
.end-progress {
  margin: 12px auto 4px;
  max-width: 420px;
  padding: 12px 16px 13px;
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid rgba(242, 193, 78, 0.55);
  border-radius: var(--radius);
  text-align: left;
}
.end-progress-gains {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 12px;
  margin-bottom: 10px;
}
.end-progress-xp,
.end-progress-credits {
  font-family: var(--font-menu-display);
  font-size: 1.15rem;
  line-height: 1;
}
.end-progress-xp { color: var(--xp-green); }
.end-progress-credits { color: var(--gold); }
.end-progress-bonus {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #0d1c12;
  background: var(--green);
  border-radius: 3px;
  padding: 2px 7px;
}
.end-progress-rank {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}
.end-progress-label {
  font-family: var(--font-menu-display);
  font-size: 0.95rem;
  color: var(--gold);
}
.end-progress-xpbar-text {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.74rem;
  color: var(--ice-dim);
}
.end-progress-track {
  height: 7px;
  border-radius: 4px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  overflow: hidden;
}
.end-progress-fill {
  height: 100%;
  width: 0;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--xp-green-deep), var(--xp-green));
  transition: width 1.1s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Promotion banner — the moment the whole ladder exists for. */
.end-progress-levelup {
  margin-top: 11px;
  padding: 9px 12px;
  border-radius: var(--radius);
  background: linear-gradient(90deg, rgba(242, 193, 78, 0.2), rgba(242, 193, 78, 0.04));
  border: 1px solid rgba(242, 193, 78, 0.45);
  text-align: center;
  animation: endPromoIn 0.5s cubic-bezier(0.2, 1.1, 0.35, 1) both;
}
.end-progress-levelup-kicker {
  display: block;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
}
.end-progress-levelup-name {
  display: block;
  margin-top: 3px;
  font-family: var(--font-menu-display);
  font-size: 1.35rem;
  line-height: 1.05;
  color: var(--ice-bright);
}
@keyframes endPromoIn {
  from { opacity: 0; transform: translateY(8px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .end-progress-fill { transition: none; }
  .end-progress-levelup { animation: none; }
}

/* End overlay: ELO deltas + abandoned note */
.end-elo-changes {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 7px;
  margin: 8px 0 4px;
}
.end-elo-changes.hidden { display: none; }
.end-elo-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 11px;
  font-size: 0.8rem;
}
.end-elo-chip .chip-name { color: var(--text); }
.end-elo-chip.team-red { border-color: rgba(227, 76, 76, 0.5); }
.end-elo-chip.team-blue { border-color: rgba(76, 143, 227, 0.5); }
.end-elo-chip .chip-delta { font-weight: 700; font-variant-numeric: tabular-nums; }
.end-elo-chip .chip-delta.up { color: #46c46a; }
.end-elo-chip .chip-delta.down { color: var(--red); }
.end-elo-chip .chip-elo { color: var(--gold); font-variant-numeric: tabular-nums; }
.end-abandon-note {
  background: rgba(240, 192, 64, 0.1);
  border: 1px solid rgba(240, 192, 64, 0.4);
  color: var(--text);
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 0.88rem;
  margin: 6px auto 2px;
  max-width: 560px;
}
.end-abandon-note.hidden { display: none; }

/* Ranked abandon vote banner (top-left; shows over loading + game screens) */
.ranked-abandon-banner {
  position: fixed;
  top: 14px; left: 14px;
  z-index: 9999;
  background: rgba(22, 27, 34, 0.96);
  border: 1px solid rgba(240, 192, 64, 0.55);
  border-radius: 10px;
  padding: 10px 14px;
  max-width: 320px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55);
  animation: rankedBannerIn 0.25s ease-out;
}
.ranked-abandon-banner.hidden { display: none; }
/* Sits below the ranked-abandon banner on the rare chance both are up at once. */
#forfeit-vote-banner { top: 128px; }
@keyframes rankedBannerIn {
  from { transform: translateY(-8px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.ranked-abandon-title { font-weight: 700; font-size: 0.9rem; color: var(--gold); }
.ranked-abandon-sub { color: var(--muted); font-size: 0.8rem; margin: 3px 0 8px; }
.ranked-abandon-actions { display: flex; align-items: center; gap: 10px; }
.ranked-abandon-vote-btn {
  background: var(--gold); color: #16130a; font-weight: 700;
}
.ranked-abandon-vote-btn:disabled { opacity: 0.55; cursor: default; }
.ranked-abandon-time { color: var(--gold); font-size: 0.82rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── Custom cursor options ──────────────────────────────────────────────── */
.opt-cursor-preview {
  display: inline-block;
  width: 60px;
  height: 60px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: linear-gradient(135deg, #eaf6ff 0%, #cfe9f7 100%); /* ice-like backdrop */
  background-repeat: no-repeat;
  background-position: center;
  margin-left: 8px;
}

/* ── Match replays ──────────────────────────────────────────────────────── */
/* ── Skin Studio (embedded) ── */
#screen-skinstudio { align-items: stretch; justify-content: stretch; }
.skinstudio-shell {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100dvh;
}
.skinstudio-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-shrink: 0;
  padding: 12px 20px;
  background: linear-gradient(180deg, rgba(168, 212, 232, 0.05), transparent), var(--rink);
  border-bottom: 1px solid var(--edge);
}
.skinstudio-title-block { flex: 1; min-width: 0; }
.skinstudio-title-block .eyebrow { font-size: 0.62rem; margin-bottom: 2px; }
.skinstudio-title {
  font-family: var(--font-menu-display);
  font-size: 1.25rem;
  line-height: 1;
  color: var(--ice-bright);
}
.skinstudio-hint {
  flex-shrink: 0;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ice-dim);
}
.skinstudio-frame {
  flex: 1;
  min-height: 0;
  width: 100%;
  border: 0;
  display: block;
  background: var(--ink);
}
@media (max-width: 760px) { .skinstudio-hint { display: none; } }

/* ── Store ── */
.store-shell { max-width: 900px; }
.store-balance #store-credits { color: var(--gold); }
/* Skins scroll horizontally in one row so the preview panel underneath is
   always visible without scrolling the page — the shop pattern most games use.
   Horns and trails stay a wrapping grid; they're short lists with no preview
   that needs protecting. */
/* ── Store browse layout ──
   The grid and the preview are laid out relative to each other PER CATEGORY, because the art
   is a different shape in each: skater/goalie previews are portrait, boost trails are a wide
   strip. One shared container with two modifiers rather than two separate screens.

   Default (featured / horns / bundles / xp) keeps the original stacked look. */
.store-browse {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  align-items: start;
}
/* Portrait art: cards on the left scrolling vertically, tall preview parked on the right.
   Replaces the horizontal rail, which showed four cards at a time and had to be dragged
   through the rest — a grid this size shows twelve-plus at once. */
/* One shared height for both columns, so the preview panel is exactly as tall as the grid it
   sits beside rather than shrink-wrapping its contents. Declared as a variable because both
   sides have to agree on it — two hardcoded numbers would drift apart the first time either
   was tuned. */
.store-browse--side {
  grid-template-columns: minmax(0, 1fr) 400px;
  --store-panel-h: min(64vh, 560px);
}
.store-browse--side .store-grid {
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  /* Bounded so the preview stays on screen while the grid scrolls under it. */
  max-height: var(--store-panel-h);
  overflow-y: auto;
  padding-right: 4px;
  scrollbar-width: thin;
  scrollbar-color: rgba(168, 212, 232, 0.28) transparent;
}
.store-browse--side .store-grid::-webkit-scrollbar { width: 8px; }
.store-browse--side .store-grid::-webkit-scrollbar-track { background: transparent; }
.store-browse--side .store-grid::-webkit-scrollbar-thumb {
  background: rgba(168, 212, 232, 0.22); border-radius: 4px;
}
.store-browse--side .store-preview {
  position: sticky;
  top: 8px;
  margin-top: 0;
  height: var(--store-panel-h);
  /* Column layout so the art can take every pixel the name/price row leaves behind. */
  display: flex;
  flex-direction: column;
}
.store-browse--side .store-preview-head { flex: none; }
.store-browse--side .store-preview-art {
  flex: 1;
  min-height: 0;          /* without this a flex child refuses to shrink below its content */
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Scaled by BOTH max dimensions with width/height auto, so the canvas shrinks to fit whichever
   axis runs out first and keeps its square aspect. Setting width:100% instead would stretch it
   whenever max-height had to clamp. */
.store-browse--side .store-preview-art--model canvas {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}
/* Wide art: the preview needs the full width, so it goes ABOVE and the strips sit under it. */
.store-browse--stack .store-preview { order: -1; margin-top: 0; }
.store-browse--stack .store-grid { grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); }

/* Both new layouts drop to a single column when there isn't width for a side panel. */
@media (max-width: 900px) {
  .store-browse--side { grid-template-columns: 1fr; }
  .store-browse--side .store-preview { position: static; height: auto; }
  .store-browse--side .store-grid { max-height: none; overflow-y: visible; }
}

.store-rail { position: relative; }
.store-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: 10px;
}
.store-grid--rail {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  padding: 2px 2px 10px;
  scrollbar-width: thin;
  scrollbar-color: rgba(168, 212, 232, 0.28) transparent;
}
.store-grid--rail::-webkit-scrollbar { height: 8px; }
.store-grid--rail::-webkit-scrollbar-track { background: transparent; }
.store-grid--rail::-webkit-scrollbar-thumb { background: rgba(168, 212, 232, 0.22); border-radius: 4px; }
.store-grid--rail .store-card {
  flex: 0 0 200px;
  scroll-snap-align: start;
}
/* Arrows sit over the ends of the rail. Hidden when there's nothing that way,
   so they never read as broken. */
.store-rail-arrow {
  position: absolute;
  top: calc(50% - 22px);
  transform: translateY(-50%);
  z-index: 3;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(6, 10, 17, 0.86);
  border: 1px solid var(--edge-hi);
  color: var(--ice);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s, background 0.15s, color 0.15s;
  backdrop-filter: blur(4px);
}
.store-rail-arrow.is-shown { opacity: 1; pointer-events: auto; }
.store-rail-arrow:hover { background: var(--rink-hi); color: var(--gold); }
.store-rail-arrow--prev { left: -6px; }
.store-rail-arrow--next { right: -6px; }
.store-rail-arrow--prev .ui-icon { transform: rotate(180deg); }
.store-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 15px 13px;
  background: var(--rink);
  border: 1px solid var(--edge);
  border-left: 3px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  transition: border-color 0.16s, background 0.16s;
}
.store-card:hover { border-color: rgba(168, 212, 232, 0.3); background: var(--rink-hi); }
/* Owned reads settled and quiet; locked keeps the gold "spend me" cue. */
.store-card--owned { border-left-color: var(--green); }
/* Consumable: repeatable, so it reads as available rather than owned/locked. */
.store-card--consumable { border-left-color: var(--ice-cool); }
.store-count-tag {
  display: inline-block;
  margin-bottom: 6px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ice-cool);
  background: rgba(143, 191, 214, 0.14);
  border-radius: 3px;
  padding: 2px 8px;
}
.store-card--locked { border-left-color: var(--gold); }
/* Card thumbnail — the reason to click, so it leads the card. */
.store-card-thumb {
  position: relative;
  aspect-ratio: 1;
  margin: -4px -5px 8px;
  border-radius: var(--radius);
  overflow: hidden;
  background:
    radial-gradient(70% 60% at 50% 20%, rgba(120, 180, 220, 0.1), transparent 70%),
    linear-gradient(180deg, #0e1826 0%, #070d16 100%);
  border: 1px solid var(--edge);
  display: grid;
  place-items: center;
}
.store-card-thumb--wide { aspect-ratio: 16 / 5; }
.store-card-thumb--glyph {
  aspect-ratio: 16 / 7;
  color: rgba(168, 212, 232, 0.35);
  flex-direction: column;
  gap: 6px;
  transition: color 0.16s, background 0.16s;
}
.store-card-playhint {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ice-dim);
}
.store-card:hover .store-card-thumb--glyph { color: var(--ice-cool); }
/* Currently auditioning — the card is the play button, so it has to show it. */
.store-card--playing { border-color: var(--gold); }
.store-card--playing .store-card-thumb--glyph {
  color: var(--gold);
  background:
    radial-gradient(70% 70% at 50% 50%, rgba(242, 193, 78, 0.14), transparent 70%),
    linear-gradient(180deg, #0e1826 0%, #070d16 100%);
}
.store-card--playing .store-card-playhint { color: var(--gold); }
.store-card-thumb--glyph .ui-icon { width: 24px; height: 24px; }
/* Extracted PNG artwork for horn + slot SKUs. Taller than the 16/7 SVG art
   because the source images are square — in a 16/7 box they'd shrink to a
   sliver. The play hint sits on its own grid row beneath the image. */
.store-card-thumb--img {
  aspect-ratio: 4 / 3;
  grid-auto-flow: row;
  align-content: center;
  gap: 3px;
  padding: 7px 7px 5px;
}
.store-thumb-img {
  max-width: 100%;
  max-height: 100%;
  min-height: 0;
  object-fit: contain;
  display: block;
}
.store-card:hover .store-thumb-img { transform: scale(1.04); }
.store-thumb-img { transition: transform 0.16s ease; }

/* Custom-horn SKU art — drawn to fill the thumb, not centered like a glyph. */
.store-card-thumb--art { aspect-ratio: 16 / 7; }
.store-sku-art { width: 100%; height: 100%; display: block; }
/* The bay this SKU unlocks: gold, lit, the reason to buy. */
.store-sku-art .sku-on { stroke: var(--gold); color: var(--gold); }
.store-sku-art .sku-on rect,
.store-sku-art .sku-on path { fill: rgba(242, 193, 78, 0.13); }
.store-sku-art .sku-on .sku-bay { fill: rgba(242, 193, 78, 0.07); }
/* Sound arcs are strokes, never solids — filling them closes the chord. */
.store-sku-art .sku-arc { fill: none !important; }
/* Slots below this one — context, so slot 3 reads as "the third one". */
.store-sku-art .sku-prior { stroke: rgba(168, 212, 232, 0.3); }
.store-sku-art .sku-prior rect { fill: rgba(168, 212, 232, 0.04); }
.store-sku-art .sku-prior path { fill: rgba(168, 212, 232, 0.08); }
/* Still locked after this purchase. */
.store-sku-art .sku-off { stroke: rgba(168, 212, 232, 0.16); }
.store-sku-art .sku-off .sku-bay-plus { stroke: rgba(168, 212, 232, 0.3); }
.store-sku-art .sku-bay-num {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 9px;
  letter-spacing: 0.1em;
  text-anchor: middle;
  stroke: none;
  fill: currentColor;
  opacity: 0.75;
}
.store-sku-art .sku-prior .sku-bay-num,
.store-sku-art .sku-off .sku-bay-num { fill: rgba(168, 212, 232, 0.4); }
.store-sku-art .sku-token-edge { stroke: rgba(242, 193, 78, 0.4); }
.store-sku-art .sku-token-face {
  stroke: var(--gold);
  fill: rgba(242, 193, 78, 0.09);
}
.store-card:hover .store-sku-art .sku-prior { stroke: rgba(168, 212, 232, 0.42); }
.store-card-thumb canvas { width: 100%; height: 100%; object-fit: contain; display: block; }
.store-card-thumb--wide canvas { object-fit: cover; }
.store-thumb-fallback {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
}
.store-thumb-fallback[hidden] { display: none; }

.store-card-name {
  font-family: var(--font-menu-display);
  font-size: 1rem;
  line-height: 1.15;
  color: var(--ice-bright);
}
.store-card-meta {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ice-dim);
}
.store-card-foot { margin-top: 9px; }
.store-card-price {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  color: var(--gold);
}
.store-owned-tag {
  display: inline-block;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #0d1c12;
  background: var(--green);
  border-radius: 3px;
  padding: 2px 8px;
}
.store-buy-btn {
  width: 100%;
  padding: 8px 10px;
  font-family: var(--font-data);
  font-size: 0.88rem;
  letter-spacing: 0.06em;
}
/* Affordable = bright gold. Short = the same gold, dimmed — reads as "this is
   the price, you just can't reach it yet" rather than as a different thing. */
.store-buy-btn--afford {
  background: linear-gradient(180deg, var(--gold) 0%, #d9a52f 100%);
  color: #191203;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3) inset;
}
.store-buy-btn--afford:hover { background: linear-gradient(180deg, #ffd166 0%, var(--gold) 100%); }
.store-buy-btn--short {
  background: rgba(242, 193, 78, 0.1);
  color: rgba(242, 193, 78, 0.5);
  border: 1px solid rgba(242, 193, 78, 0.22);
  cursor: not-allowed;
}
.store-buy-btn--short::before { content: '\1F512\00a0\00a0'; font-size: 0.8em; }
.store-card { cursor: pointer; }
.store-card--selected {
  border-color: var(--ice);
  background: var(--rink-hi);
  box-shadow: 0 0 0 1px rgba(168, 212, 232, 0.25);
}
/* Preview panel under the grid. */
.store-preview {
  margin-top: 14px;
  padding: 14px 16px 16px;
  background: var(--rink);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  animation: storePreviewIn 0.24s ease both;
}
@keyframes storePreviewIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .store-preview { animation: none; } }
.store-preview-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.store-preview-name {
  /* Body font, NOT --font-menu-display. Archivo Black is so heavy that the dot on a lowercase
     'i' merges into its own stem, making it indistinguishable from an 'l' — which matters here
     because this line is a product name people are about to spend credits on. Barlow at 700
     keeps the titular weight while leaving the dot clear of the stem. */
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.22rem;
  letter-spacing: 0.01em;
  color: var(--ice-bright);
}
.store-preview-price {
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  color: var(--gold);
}
.store-preview-art {
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
}
.store-preview-art img {
  display: block;
  width: 100%;
  max-height: 260px;
  object-fit: cover;
}
/* Sits over the model canvas; fades once you have obviously found it. */
.store-preview-hint {
  position: absolute;
  left: 50%;
  bottom: 8px;
  transform: translateX(-50%);
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(8, 14, 24, 0.72);
  border: 1px solid rgba(140, 200, 255, 0.28);
  color: #fff;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  pointer-events: none;
}
.store-preview-art--model:hover .store-preview-hint { opacity: 0.35; }
.store-preview-art--model { position: relative; }
.store-preview-art--model {
  display: grid;
  place-items: center;
  padding: 8px;
}
.store-preview-art--model canvas { width: 100%; max-width: 320px; height: auto; }
/* In the side layout the panel IS the preview, so the model gets the whole width. */
.store-browse--side .store-preview-art--model canvas { max-width: none; }
.store-preview-actions { margin-top: 12px; display: flex; justify-content: center; }
.store-preview-actions .btn { width: auto; display: inline-flex; align-items: center; gap: 7px; }
.store-preview-art--trail,
.store-preview-art--audio {
  display: grid;
  place-items: center;
  padding: 18px;
  min-height: 120px;
}
.store-preview-art--trail canvas { max-width: 100%; height: auto; }
.store-preview-art--audio .btn { display: inline-flex; align-items: center; gap: 7px; width: auto; }

.store-note {
  margin: 16px 2px 0;
  font-size: 0.82rem;
  color: var(--ice-dim);
}
.store-note--error { color: #ff9a92; }
.store-note--ok { color: #8be0a4; }
/* An empty note must take up no room — its top margin would otherwise still push the
   layout down on tabs that deliberately show nothing. */
.store-note:empty { display: none; }

/* ── Get Credits ──────────────────────────────────────────────────────────────
   Gold is already this game's reward accent and is reserved from team red/blue,
   so credit packs sit inside the existing language rather than inventing one.
   The escalation is carried by weight, not by hue: tier 1 is a plain hairline
   card and tier 5 is a full gold slab, with everything between graded. */

.stats-tab--gold {
  color: var(--gold);
  font-weight: 700;
}
.stats-tab--gold::before {
  content: '\2605';   /* ★ — marks the one tab that isn't spending, it's topping up */
  margin-right: 6px;
  font-size: 0.85em;
  opacity: 0.9;
}
.stats-tab--gold.active { border-bottom-color: var(--gold); }

.store-topup { padding: 4px 2px 8px; }

/* Hero banner. Two layers: a static gold wash, and a sheen that sweeps across
   it. The sweep is the only motion — a busy banner reads as a mobile freemium
   pop-up, which is the exact impression to avoid. */
.topup-hero {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--edge);
  border-top: 2px solid var(--gold);
  border-radius: var(--radius);
  /* The rink shot sits under a dark scrim so the type stays readable over it;
     the gold wash on top keeps it in the same family as the pack cards. */
  background:
    radial-gradient(120% 160% at 50% -40%, rgba(242, 193, 78, 0.20), transparent 62%),
    linear-gradient(180deg, rgba(12, 19, 31, 0.82), rgba(8, 13, 22, 0.94)),
    url('/assets/ui/store/topup-hero.jpg') center/cover no-repeat,
    linear-gradient(180deg, var(--rink-hi), var(--rink-deep));
  padding: 26px 24px 22px;
  margin-bottom: 18px;
  text-align: center;
}
/* Drifting gold motes, done with layered gradients rather than a canvas: the
   menu's particle worker is disabled under Electron, so a canvas here would
   only animate for half the players. */
.topup-hero::before {
  content: '';
  position: absolute;
  inset: -50%;
  background-image:
    radial-gradient(2px 2px at 20% 30%, rgba(242, 193, 78, 0.55), transparent 60%),
    radial-gradient(1.5px 1.5px at 70% 60%, rgba(242, 193, 78, 0.40), transparent 60%),
    radial-gradient(2px 2px at 45% 80%, rgba(204, 232, 245, 0.30), transparent 60%),
    radial-gradient(1px 1px at 85% 20%, rgba(242, 193, 78, 0.45), transparent 60%);
  background-size: 260px 260px, 190px 190px, 320px 320px, 150px 150px;
  animation: topupMotes 34s linear infinite;
  pointer-events: none;
}
.topup-hero::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: -60%;
  width: 45%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.13), transparent);
  transform: skewX(-18deg);
  animation: topupSheen 5.5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes topupMotes {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-260px, -260px, 0); }
}
@keyframes topupSheen {
  0%, 62% { left: -60%; }
  100%    { left: 130%; }
}
.topup-hero-title {
  position: relative;
  font-family: var(--font-menu-display);
  font-size: clamp(1.6rem, 4vw, 2.3rem);
  letter-spacing: 0.06em;
  color: var(--gold);
  text-shadow: 0 2px 18px var(--gold-glow);
}
.topup-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid rgba(242, 193, 78, 0.45);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  background: linear-gradient(90deg, rgba(242, 193, 78, 0.14), rgba(242, 193, 78, 0.02));
  padding: 11px 16px;
  margin-bottom: 16px;
  font-family: var(--font-data);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 0.84rem;
  color: var(--gold);
}
.topup-banner-sub {
  font-family: var(--font-body);
  letter-spacing: 0;
  text-transform: none;
  font-size: 0.8rem;
  color: var(--ice-dim);
}

/* The first-purchase offer. GREEN, not the gold used everywhere else on this pane: gold is the
   colour of every price and pack card here, so a gold banner sat inside the palette instead of
   announcing itself. Green also reads as "you gain" rather than "you pay".
   The sub-line is deliberately NOT dimmed — it carries the two facts that decide whether someone
   acts on the offer (it is already in the numbers, and it only happens once), which is not
   footnote material. */
.topup-banner--offer {
  border-color: rgba(63, 185, 107, 0.5);
  border-left-color: #3fb950;
  background: linear-gradient(90deg, rgba(63, 185, 107, 0.20), rgba(63, 185, 107, 0.03));
  color: #7ee89f;
  font-size: 1.14rem;
  padding: 14px 18px;
  /* Wraps to two lines on a narrow pane; centred as a block keeps it looking deliberate
     rather than like a heading that overflowed. */
  flex-wrap: wrap;
}
.topup-banner--offer .topup-banner-sub {
  font-size: 0.9rem;
  color: var(--text);
  opacity: 0.92;
}

/* Five across on anything wide enough. auto-fit gave four columns and orphaned
   the top tier onto its own row, which broke the left-to-right value ladder —
   the one thing this grid exists to communicate. */
.topup-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: stretch;
  gap: 12px;
}
@media (max-width: 1080px) {
  .topup-grid { grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); }
}

.pack-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 16px 12px 13px;
  border: 1px solid var(--edge);
  border-top: 2px solid var(--edge-hi);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--rink-hi), var(--rink));
  box-shadow: var(--shadow-panel);
  transition: transform 0.16s cubic-bezier(0.2, 1.1, 0.35, 1), border-color 0.16s;
}
.pack-card:hover { transform: translateY(-3px); border-color: var(--edge-hi); }

/* 16:9, matching the art. A square box letterboxed the wide coin-pile pieces
   and left a band of dead space above and below each one. */
.pack-art {
  width: calc(100% + 24px);      /* bleed to the card edges — the art is already
                                    on the panel's own near-black, so a margin
                                    would read as an unintended border */
  margin: -16px -12px 8px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-bottom: 1px solid var(--edge);
}
/* Until the art lands, a card without an image still needs to read as a pack
   rather than as a broken image. */
.pack-art--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.4rem;
  color: var(--gold);
  opacity: 0.65;
  text-shadow: 0 2px 14px var(--gold-glow);
  background: var(--rink-deep);
}
.pack-amount {
  font-family: var(--font-menu-display);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ice-bright);
}
.pack-amount-label {
  font-family: var(--font-data);
  font-size: 0.68rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ice-dim);
}
.pack-bonus {
  margin-top: 3px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(63, 185, 107, 0.16);
  border: 1px solid rgba(63, 185, 107, 0.4);
  font-family: var(--font-data);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #6fd48f;
}
/* Always rendered, hidden when there's no bonus, so tier 1's button lines up
   with the rest instead of floating a chip's height higher. */
.pack-bonus--empty { visibility: hidden; }
.pack-bonus { margin-bottom: 10px; }
/* auto top margin, so the price sits on the same baseline on every card no
   matter how tall the art or name made the one next to it. */
.pack-buy { width: 100%; margin-top: auto; }

/* "+ tax", tucked into the button's top-right corner.
   Taken OUT OF FLOW deliberately: in normal flow it shifts the price off-centre
   by its own width, and the price is the thing the eye should land on. Absolute
   positioning leaves the amount centred in the button exactly as it would be
   with no suffix at all. (.btn is already position:relative.)
   Quiet, but not so faint it stops being a disclosure — and it inherits the
   button's colour rather than picking its own, so it stays legible against the
   gold fill at rest and on hover. */
.pack-tax {
  position: absolute;
  top: 3px;
  right: 7px;
  font-size: 0.52rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  line-height: 1;
  opacity: 0.65;
  /* The corner is a small target and clicking it must still buy the pack;
     letting the click fall through to the button removes any doubt. */
  pointer-events: none;
}

/* Already bought today — one of each pack per rolling 24h.
   Green rather than gold because the store already reads green as "you have
   this" (.store-owned-btn) and gold as "this costs money". Matches .btn's
   12px/20px padding and takes the same auto top margin, so a grid with a mix of
   bought and available packs keeps one shared button baseline. */
.pack-bought {
  width: 100%;
  margin-top: auto;
  padding: 12px 20px;
  font-size: 0.92rem;
  letter-spacing: 0.09em;
  cursor: default;
}
/* Bought cards recede a little so the packs still available lead the eye —
   without hiding them, since the player needs to see what they already took. */
.pack-card--bought { opacity: 0.68; }
.pack-card--bought:hover { transform: none; border-color: var(--edge); }
@media (prefers-reduced-motion: reduce) {
  .pack-card--bought:hover { transform: none; }
}

.pack-ribbon {
  position: absolute;
  top: -1px; right: -1px;
  padding: 3px 9px;
  border-bottom-left-radius: var(--radius);
  background: var(--gold);
  color: #201704;
  font-family: var(--font-data);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

/* Escalation. Each tier gets visibly more expensive-looking than the last. */
.pack-card--t2 { border-color: rgba(160, 205, 230, 0.22); }
.pack-card--t3 {
  border-color: rgba(242, 193, 78, 0.42);
  border-top-color: var(--gold);
}
.pack-card--t4 {
  border-color: rgba(242, 193, 78, 0.6);
  border-top-color: var(--gold);
  background: linear-gradient(180deg, rgba(242, 193, 78, 0.09), var(--rink));
  box-shadow: var(--shadow-panel), 0 0 26px -8px var(--gold-glow);
}
.pack-card--t5 {
  border-color: var(--gold);
  background: linear-gradient(180deg, rgba(242, 193, 78, 0.17), rgba(184, 134, 15, 0.06) 55%, var(--rink));
  box-shadow: var(--shadow-panel), 0 0 42px -8px var(--gold-glow);
  overflow: hidden;
}
/* Top tier only: a slow foil sheen. One card doing this reads as premium;
   every card doing it reads as a slot machine. */
.pack-card--t5::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: -70%;
  width: 55%;
  background: linear-gradient(100deg, transparent, rgba(255, 245, 214, 0.22), transparent);
  transform: skewX(-20deg);
  animation: packFoil 4.5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes packFoil {
  0%, 55% { left: -70%; }
  100%    { left: 140%; }
}

/* Founder's pack — one wide card above the grid. */
.topup-founder {
  display: grid;
  grid-template-columns: minmax(120px, 190px) 1fr auto;
  align-items: center;
  gap: 18px;
  padding: 16px 18px;
  margin-bottom: 18px;
  border: 1px solid var(--gold);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
  background: linear-gradient(100deg, rgba(242, 193, 78, 0.16), rgba(12, 19, 31, 0.9) 62%);
  box-shadow: var(--shadow-panel), 0 0 40px -12px var(--gold-glow);
}
.topup-founder-art { width: 100%; border-radius: var(--radius); }
.topup-founder-title {
  font-family: var(--font-menu-display);
  font-size: 1.15rem;
  color: var(--gold);
}
.topup-founder-list {
  margin: 7px 0 0;
  padding-left: 17px;
  font-size: 0.83rem;
  color: var(--ice-cool);
  line-height: 1.6;
}
.topup-founder-buy { text-align: center; min-width: 130px; }
/* The "worth" line, as a TOTAL under the list it totals — a rule, then the figure.
   It used to sit above the price with a line through it, which is the convention for a price
   you are no longer being charged. This is not that: nothing here was ever sold for 36,100, so
   striking it read as a fake discount and was hard to read besides. Under the list it is simply
   the sum of what is above it. */
.topup-founder-total {
  margin-top: 8px;
  padding-top: 7px;
  border-top: 1px solid var(--edge);
  font-family: var(--font-data);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  color: var(--gold);
}

.topup-legal {
  margin-top: 18px;
  font-size: 0.72rem;
  line-height: 1.6;
  color: var(--ice-dim);
  text-align: center;
}
.topup-blocked {
  margin-bottom: 14px;
  padding: 10px 14px;
  border: 1px solid rgba(226, 69, 63, 0.5);
  border-left: 4px solid var(--red);
  border-radius: var(--radius);
  background: rgba(226, 69, 63, 0.1);
  color: #ff9a92;
  font-size: 0.84rem;
}

/* Featured rotation. The countdown is the point — it's what turns "some items"
   into "some items, until Thursday". */
.store-featured-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 2px 2px 12px;
}
.store-featured-title {
  font-family: var(--font-data);
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ice-cool);
}
.store-featured-timer {
  font-family: var(--font-data);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  color: var(--gold);
}
.store-card-was {
  margin-right: 6px;
  font-size: 0.78rem;
  color: var(--ice-dim);
  text-decoration: line-through;
}
/* Sits over the art in the top-right corner, deliberately NOT in the card footer: anything
   added to the footer changes the card's height, and one discounted item then knocks the whole
   grid out of alignment. Green because it is a saving, and because gold is already the price. */
.store-sale-tag {
  position: absolute;
  top: 6px; right: 6px;
  z-index: 2;
  padding: 3px 10px;
  border-radius: 2px;
  background: var(--xp-green);
  color: #06210f;
  font-family: var(--font-data);
  font-size: 0.88rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  pointer-events: none;   /* the whole card is the click target */
}
.store-card { position: relative; }

/* A bundle's thumbnail is its contents: three tiles of the real items. Showing
   what you actually get beats any amount of bundle-branded key art. */
.store-card-thumb--bundle {
  display: grid;
  grid-template-columns: 1fr 1fr;
  /* Characters get the tall row, the trail strip a short one — even rows left a
     band of dead space above and below the trail. */
  grid-template-rows: 1fr 0.42fr;
  gap: 3px;
  padding: 3px;
  background: var(--rink-deep);
  border-radius: var(--radius);
}
/* The trail is a wide strip — span it across the row instead of squeezing it
   into a square cell, and order it last so the two characters pair up on the
   row above instead of leaving a hole either side of it. */
.store-card-thumb--bundle .bundle-mini { order: 1; }
.store-card-thumb--bundle .bundle-mini--wide { order: 2; grid-column: 1 / -1; }
.bundle-mini {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--rink-hi), var(--rink));
  min-height: 0;
}
.bundle-mini canvas { width: 100%; height: 100%; object-fit: contain; }
/* Already-owned pieces are dimmed and ticked, so a partly-owned bundle reads
   honestly instead of looking like you're paying twice. */
.bundle-mini--owned { opacity: 0.45; }
.bundle-mini--owned::after {
  content: '\2713';
  position: absolute;
  top: 2px; right: 4px;
  font-size: 0.7rem;
  color: var(--green);
}

/* Owned state shaped like the buy button it replaces, so the card keeps its
   layout — a small pill where a full-width button was left the row ragged. */
.store-owned-btn {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: 1px solid rgba(63, 185, 107, 0.55);
  border-radius: var(--radius);
  background: rgba(63, 185, 107, 0.16);
  color: #8ce8ab;
  font-family: var(--font-data);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
}

/* Granted, never sold — pack rewards today, level-up rewards later.
   Red rather than the green of "Owned" or the gold of a price: this is the only
   thing in the store you cannot get by spending, and it should not be mistaken
   at a glance for either something you have or something you can buy. Same box
   metrics as .store-owned-btn so a grid mixing the two keeps its baseline. */
.store-exclusive-btn {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: 1px solid rgba(229, 83, 75, 0.6);
  border-radius: var(--radius);
  background: rgba(229, 83, 75, 0.18);
  color: #ff9b93;
  font-family: var(--font-data);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  cursor: default;
}

/* Saving band across a bundle card. Green rather than the gold used for sales
   and prices: this is not a limited-time cut, it is what the bundle IS, and a
   second gold element would compete with the price button below it. */
.store-save-band {
  display: block;
  margin: 8px -12px 2px;
  padding: 5px 10px;
  background: linear-gradient(90deg, rgba(63, 185, 107, 0.28), rgba(63, 185, 107, 0.12));
  border-top: 1px solid rgba(63, 185, 107, 0.5);
  border-bottom: 1px solid rgba(63, 185, 107, 0.5);
  font-family: var(--font-data);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #8ce8ab;
  text-align: center;
}
.store-save-pill {
  margin-left: 8px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(63, 185, 107, 0.18);
  border: 1px solid rgba(63, 185, 107, 0.45);
  font-family: var(--font-data);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  color: #8ce8ab;
}

/* Bundle preview: every piece at a size you can judge. */
.store-preview-art--bundle {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 18px;
  padding: 6px 0 2px;
}
.bundle-piece {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  max-width: 200px;
}
/* Fixed media height across every piece, so a wide trail and a tall character
   still leave their captions on one line rather than stepping down the row. */
.bundle-piece canvas {
  width: 180px;
  height: 180px;
  object-fit: contain;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--rink-hi), var(--rink-deep));
}
.bundle-piece--owned canvas { opacity: 0.5; }
.bundle-piece-name {
  margin-top: 6px;
  font-family: var(--font-menu-display);
  font-size: 0.82rem;
  color: var(--ice-bright);
}
.bundle-piece-kind {
  font-family: var(--font-data);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ice-dim);
}

/* XP boost marks — drawn, not illustrated, so they stay crisp and themed. */
.store-sku-art--xp .sku-xp-mult {
  font-family: var(--font-menu-display);
  font-size: 19px;
  fill: var(--gold);
  stroke: none;
  text-anchor: middle;
}
.store-sku-art--xp .sku-xp-mult--big { font-size: 26px; }
.store-sku-art--xp .sku-xp-label {
  font-family: var(--font-data);
  font-size: 9px;
  letter-spacing: 2.4px;
  fill: var(--ice-dim);
  stroke: none;
  text-anchor: middle;
}
.store-sku-art--xp .sku-chev { stroke: var(--gold); }
.store-sku-art--xp .sku-chev--1 { opacity: 0.66; }
.store-sku-art--xp .sku-chev--2 { opacity: 0.36; }
.store-sku-art--xp .sku-team-figure { stroke: var(--ice-cool); fill: none; }

/* Bundle cards list what's inside, with anything already owned struck through
   so a partly-owned bundle can't feel like a con. */
.store-bundle-contents {
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.74rem;
  color: var(--ice-dim);
  line-height: 1.5;
}
.store-bundle-contents li::before { content: '\2022'; margin-right: 6px; color: var(--gold); }
.store-bundle-contents li.owned { text-decoration: line-through; opacity: 0.55; }

/* XP boost cards. */
.store-boost-active {
  margin: 0 2px 12px;
  padding: 9px 14px;
  border: 1px solid rgba(63, 185, 107, 0.4);
  border-left: 3px solid var(--green);
  border-radius: var(--radius);
  background: rgba(63, 185, 107, 0.09);
  font-size: 0.83rem;
  color: #8be0a4;
}

/* ── Purchase moment ── */
.purchase-pending-card { text-align: center; }
.purchase-pending-text {
  margin: 10px 0 4px;
  font-size: 0.86rem;
  color: var(--ice-dim);
  line-height: 1.6;
}
.purchase-spinner {
  width: 34px; height: 34px;
  margin: 4px auto 12px;
  border: 3px solid rgba(242, 193, 78, 0.22);
  border-top-color: var(--gold);
  border-radius: 50%;
  animation: purchaseSpin 0.9s linear infinite;
}
@keyframes purchaseSpin { to { transform: rotate(360deg); } }

.purchase-celebration {
  position: fixed;
  inset: 0;
  z-index: 2600;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 7, 12, 0.9);
  backdrop-filter: blur(8px);
}
.purchase-burst {
  position: absolute;
  width: 760px; height: 760px;
  max-width: 150vw; max-height: 150vw;
  background: radial-gradient(circle, var(--gold-glow), transparent 62%);
  animation: purchaseBurst 1.1s cubic-bezier(0.22, 1, 0.36, 1) both;
  pointer-events: none;
}
@keyframes purchaseBurst {
  from { transform: scale(0.35); opacity: 0; }
  55%  { opacity: 1; }
  to   { transform: scale(1); opacity: 0.75; }
}
.purchase-celebration-inner {
  position: relative;
  text-align: center;
  animation: purchasePop 0.5s cubic-bezier(0.2, 1.1, 0.35, 1) both;
}
@keyframes purchasePop {
  from { transform: translateY(14px) scale(0.94); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.purchase-celebration-eyebrow {
  font-family: var(--font-data);
  font-size: 0.8rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ice-cool);
}
.purchase-celebration-amount {
  font-family: var(--font-menu-display);
  font-size: clamp(2.8rem, 11vw, 5rem);
  line-height: 1.05;
  color: var(--gold);
  text-shadow: 0 4px 40px var(--gold-glow);
}
.purchase-celebration-amount::before { content: '+'; opacity: 0.75; }
.purchase-celebration-label {
  font-family: var(--font-data);
  font-size: 0.86rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ice-cool);
}
.purchase-celebration-bonus {
  margin-top: 12px;
  font-family: var(--font-data);
  font-size: 0.86rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #6fd48f;
}
.purchase-celebration-balance {
  margin: 16px 0 20px;
  font-size: 0.9rem;
  color: var(--ice-dim);
}
.purchase-celebration-balance b { color: var(--ice-bright); }
#btn-purchase-celebration-close { width: auto; min-width: 190px; margin: 0 auto; }

/* House convention: every animated block here has a still equivalent. */
@media (prefers-reduced-motion: reduce) {
  .topup-hero::before,
  .topup-hero::after,
  .pack-card--t5::after { animation: none; }
  .topup-hero::after, .pack-card--t5::after { display: none; }
  .purchase-spinner { animation-duration: 2.4s; }
  .purchase-burst,
  .purchase-celebration-inner { animation: none; opacity: 1; transform: none; }
  .pack-card { transition: none; }
  .pack-card:hover { transform: none; }
}

@media (max-width: 620px) {
  .topup-founder { grid-template-columns: 1fr; text-align: center; }
  .topup-founder-art { max-width: 180px; margin: 0 auto; }
  .topup-grid { grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); }
}

.replays-shell { max-width: 880px; }
.replay-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
  max-height: min(66vh, 760px);
  overflow-y: auto;
  padding-right: 4px;
}
/* Each saved game is a card led by its scoreline — the list used to be flat
   text where the score was the third item in a run-on subtitle. */
.replay-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  background: var(--rink);
  border: 1px solid var(--edge);
  border-left: 3px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  transition: border-color 0.16s, background 0.16s;
}
.replay-row:hover { border-color: rgba(168, 212, 232, 0.3); background: var(--rink-hi); }
.replay-row--ranked { border-left-color: var(--gold); }
.replay-row-note { color: var(--ice-dim); border-left-color: transparent; }

.replay-score {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-shrink: 0;
  min-width: 116px;
  padding-right: 16px;
  border-right: 1px solid var(--edge);
}
.replay-score-side { display: flex; flex-direction: column; align-items: center; }
.replay-score-side b {
  font-family: var(--font-menu-display);
  font-size: 1.5rem;
  line-height: 1;
  font-weight: 400;
}
.replay-score-side i {
  font-family: var(--font-data);
  font-style: normal;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ice-dim);
  margin-top: 3px;
}
.replay-score-side--red b  { color: var(--red); }
.replay-score-side--blue b { color: var(--blue); }
.replay-score-dash { color: var(--ice-dim); font-size: 1.1rem; }
.replay-score-none { color: var(--ice-dim); font-size: 0.82rem; }

.replay-row-main { flex: 1; min-width: 0; }
.replay-row-name {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.replay-ranked-tag {
  flex-shrink: 0;
  font-family: var(--font-data);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #241a03;
  background: var(--gold);
  border-radius: 3px;
  padding: 2px 7px;
}
.replay-row-sub {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.84rem;
  letter-spacing: 0.04em;
  margin-top: 3px;
}
.replay-row-actions { display: flex; gap: 6px; flex-shrink: 0; align-items: center; }
.replay-watch-btn { display: inline-flex; align-items: center; gap: 6px; }
.replay-icon-btn { padding: 8px; width: auto; line-height: 0; }
.replay-icon-btn .ui-icon { width: 15px; height: 15px; }
.replay-rename-input {
  width: 100%;
  background: var(--rink-deep);
  border: 1px solid var(--gold);
  border-radius: var(--radius);
  color: var(--text);
  padding: 5px 9px;
  font: inherit;
}
.replay-row-expiry {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  opacity: 0.8;
  margin-top: 2px;
}

/* ── Library: tabs + clip grid ──────────────────────────────────────────────
   Clips are browsed as poster frames. Every tile here is a stored JPEG and some text —
   no 3D and nothing to decode — so scrolling a season of clips costs nothing. */
/* Segmented control, not underlined tabs. An underline marks the active view but says
   nothing about the inactive one — players read the dim half as disabled text rather than
   as somewhere they can go. An enclosed track with a solid filled thumb reads as a switch
   with two sides, so both destinations are legible at a glance. */
/* Tabs on the left, the destructive action pushed to the far right — far enough from the
   tab you just clicked that it cannot be hit by accident on the way past. */
.library-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.library-delete-all {
  margin: 0 0 16px;
  letter-spacing: .06em;
  white-space: nowrap;
}
.library-delete-all[disabled] { opacity: .6; cursor: default; }
.library-tabs {
  display: inline-flex;
  gap: 4px;
  margin: 0 0 16px;
  padding: 4px;
  background: var(--rink-deep);
  border: 1px solid rgba(168, 212, 232, 0.18);
  border-radius: calc(var(--radius) + 4px);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35);
}
.library-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  border-radius: var(--radius);
  /* Legible at rest — the inactive side has to look like a place you can GO. */
  color: var(--ice);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.88rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 9px 20px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.16s, background 0.16s;
}
.library-tab:hover:not(.library-tab--on) { background: var(--rink-hi); }
.library-tab--on {
  background: var(--gold);
  color: #0d1620;
  box-shadow: 0 1px 6px rgba(242, 193, 78, 0.28);
}
.library-tab:focus-visible { outline: 2px solid var(--ice); outline-offset: 2px; }
.library-tab .ui-icon { width: 15px; height: 15px; }
/* Count sits in its own pill so it never reads as part of the label. */
.library-tab-count {
  font-size: 0.74rem;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 999px;
  background: rgba(168, 212, 232, 0.16);
  color: var(--ice);
}
.library-tab-count:empty { display: none; }
.library-tab--on .library-tab-count {
  background: rgba(13, 22, 32, 0.22);
  color: #0d1620;
}
@media (max-width: 620px) {
  .library-tabs { display: flex; width: 100%; }
  .library-tab { flex: 1; justify-content: center; padding: 9px 10px; }
}

.clip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 14px;
  overflow-y: auto;
  align-content: start;
  padding-right: 4px;
}
.clip-grid-note {
  color: var(--ice-dim);
  font-family: var(--font-data);
  padding: 20px 4px;
  grid-column: 1 / -1;
}
.clip-tile {
  background: var(--rink-deep);
  border: 1px solid rgba(168, 212, 232, 0.14);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color 0.14s, transform 0.14s;
}
.clip-tile:hover { border-color: rgba(168, 212, 232, 0.34); transform: translateY(-2px); }
.clip-tile--potg { border-color: rgba(242, 193, 78, 0.45); }
.clip-tile-poster {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #0a1119;
  cursor: pointer;
  overflow: hidden;
}
.clip-tile-poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.clip-tile-noposter {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: rgba(168, 212, 232, 0.25);
}
.clip-tile-noposter .ui-icon { width: 34px; height: 34px; }
.clip-tile-dur,
.clip-tile-role,
.clip-tile-potg,
.clip-tile-pinned {
  position: absolute;
  font-family: var(--font-data);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  padding: 3px 6px;
  border-radius: 4px;
  background: rgba(6, 12, 20, 0.82);
  color: var(--ice);
  pointer-events: none;
}
.clip-tile-dur { right: 6px; bottom: 6px; }
.clip-tile-role { left: 6px; top: 6px; font-weight: 700; }
.clip-tile-role--goal { color: #0d1620; background: var(--gold); }
.clip-tile-role--assist { color: var(--ice); background: rgba(6, 12, 20, 0.86); }
.clip-tile-potg { left: 6px; bottom: 6px; color: var(--gold); border: 1px solid rgba(242, 193, 78, 0.5); }
.clip-tile-pinned { right: 6px; top: 6px; color: var(--gold); }
.clip-tile-play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 1px solid rgba(204, 232, 245, 0.5);
  background: rgba(6, 12, 20, 0.55);
  color: var(--ice);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.14s, background 0.14s;
}
.clip-tile-poster:hover .clip-tile-play { opacity: 1; }
.clip-tile-play:hover { background: rgba(242, 193, 78, 0.85); color: #0d1620; }
.clip-tile-play .ui-icon { width: 18px; height: 18px; }
.clip-tile-body { padding: 9px 11px 4px; flex: 1; min-width: 0; }
.clip-tile-name {
  color: var(--text);
  font-weight: 600;
  font-size: 0.92rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.clip-tile-sub,
.clip-tile-expiry {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  margin-top: 2px;
}
.clip-tile-expiry { opacity: 0.72; }
.clip-tile-actions {
  display: flex;
  gap: 5px;
  padding: 7px 9px 9px;
  align-items: center;
  flex-wrap: wrap;
}
.clip-tile-actions .btn { font-size: 0.7rem; padding: 5px 8px; }

/* ── Clip theater + export ───────────────────────────────────────────────── */
.clip-theater-bar {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 16px;
  background: rgba(8, 14, 22, 0.9);
  border: 1px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  backdrop-filter: blur(6px);
}
.clip-theater-title { color: var(--text); font-weight: 600; font-size: 0.95rem; }
.clip-theater-sub {
  display: block;
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  margin-top: 2px;
}
/* No in-game menu while a clip is playing — Done is the only way out, so the button that
   leads to Resume/Quit would only ever be a trap. */
body.clip-theater-active #btn-game-menu,
body.match-replay-active #btn-game-menu { display: none !important; }

.clip-theater-controls { display: flex; gap: 7px; align-items: center; }
/* Buttons here mix an icon with a label — without this the two stack onto separate lines. */
.clip-theater-controls .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.clip-theater-controls .ui-icon { width: 14px; height: 14px; }

.clip-export-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 14, 0.72);
}
.clip-export-card {
  background: var(--rink-deep);
  border: 1px solid rgba(168, 212, 232, 0.22);
  border-radius: var(--radius);
  padding: 22px 26px;
  min-width: 320px;
  text-align: center;
}
.clip-export-title {
  color: var(--text);
  font-family: var(--font-data);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 0.9rem;
  margin-bottom: 14px;
}
.clip-export-bar {
  height: 6px;
  border-radius: 3px;
  background: rgba(168, 212, 232, 0.14);
  overflow: hidden;
}
.clip-export-fill {
  height: 100%;
  width: 0;
  background: var(--gold);
  transition: width 0.12s linear;
}
.clip-export-status {
  color: var(--ice-dim);
  font-family: var(--font-data);
  font-size: 0.78rem;
  margin: 10px 0 14px;
}

/* Transport bar — bottom-right area, inset past the ~96px edge-pan bands so hovering it
   never scrolls the camera. HIDE/SHOW tab collapses everything but itself. */
.replay-controls {
  position: fixed;
  right: clamp(120px, 11vw, 220px);
  bottom: 112px;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
}
.replay-controls.hidden { display: none; }
.replay-bar-toggle {
  background: rgba(22, 27, 34, 0.92);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 1.5px;
  padding: 4px 14px 3px;
  cursor: pointer;
  margin-right: 10px;
}
.replay-bar-toggle:hover { color: var(--gold); }
.replay-controls--collapsed .replay-bar-toggle {
  border-bottom: 1px solid var(--border);
  border-radius: 8px;
}
.replay-controls--collapsed .replay-controls-body { display: none; }
.replay-controls-body {
  display: flex;
  align-items: center;
  gap: 7px;
  background: rgba(22, 27, 34, 0.92);
  border: 1px solid var(--border);
  border-radius: 12px 12px 12px 12px;
  padding: 8px 12px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
}
.replay-ctl-btn {
  width: 42px;
  height: 38px;
  font-size: 1.05rem;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.replay-ctl-btn:hover { border-color: var(--gold-dark); }
/* Text button (Puck Cam) — same chrome as the icon buttons, sized for a label. */
.replay-ctl-btn--wide {
  width: auto;
  padding: 0 12px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  white-space: nowrap;
}
/* The way out. Filled rather than outlined so it reads as the terminal action in the row,
   and separated from the transport controls it sits beside. */
.replay-ctl-btn--done {
  width: auto;
  padding: 0 14px;
  margin-left: 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  white-space: nowrap;
  background: var(--accent, #3d7fd6);
  border-color: transparent;
  color: #fff;
}
.replay-ctl-btn--done:hover { filter: brightness(1.12); border-color: transparent; }

.replay-ctl-btn.active {
  background: var(--gold);
  color: #16130a;
  border-color: var(--gold);
}
.replay-speed-chip {
  min-width: 54px;
  text-align: center;
  color: var(--gold);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
}
.replay-time-label {
  color: var(--muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  margin-right: 2px;
}
.replay-toast {
  position: fixed;
  left: 50%;
  top: 18%;
  transform: translateX(-50%);
  z-index: 41;
  background: rgba(22, 27, 34, 0.94);
  border: 1px solid var(--gold);
  color: var(--gold);
  font-family: var(--font-display);
  font-size: 1.5rem;
  letter-spacing: 3px;
  border-radius: 10px;
  padding: 10px 26px;
}
.replay-toast.hidden { display: none; }
.replay-menu-card { width: min(360px, 92vw); text-align: center; }
.replay-menu-actions { flex-direction: column; gap: 10px; }
.replay-menu-actions .btn { width: 100%; }

/* During replay playback: hide gameplay-only HUD chrome (you're a ghost, not a player) */
body.match-replay-active .desktop-ability-overlay,
body.match-replay-active .stick-zone-hud,
body.match-replay-active .desktop-shot-overlay,
body.match-replay-active #onetime-overlay {
  display: none !important;
}

/* ── Hardware-acceleration gate ─────────────────────────────────────────── */
.hw-accel-gate {
  position: fixed;
  inset: 0;
  z-index: 10000; /* above everything — this is a hard block */
  background: rgba(7, 9, 13, 0.96);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
}
.hw-accel-gate.hidden { display: none; }
.hw-accel-gate-card {
  background: var(--panel);
  border: 1px solid #a83232;
  border-radius: var(--radius);
  box-shadow: 0 18px 70px rgba(0, 0, 0, 0.7);
  max-width: 620px;
  width: 100%;
  padding: 26px 28px 22px;
  text-align: center;
}
.hw-accel-gate-icon { font-size: 2.2rem; margin-bottom: 8px; }
.hw-accel-gate-card h2 {
  font-family: var(--font-display);
  letter-spacing: 2px;
  font-size: 1.7rem;
  color: #ff8f8f;
  margin-bottom: 8px;
}
.hw-accel-gate-sub { color: var(--text); font-size: 0.95rem; line-height: 1.5; margin-bottom: 14px; }
.hw-accel-gate-steps {
  text-align: left;
  margin: 0 auto 18px;
  max-width: 520px;
  padding-left: 24px;
  color: var(--text);
  font-size: 0.95rem;
  line-height: 1.65;
}
.hw-accel-gate-steps code {
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 1px 8px;
  color: var(--gold);
  user-select: all; /* one click selects the whole address for copying */
}
.hw-accel-gate-btn { font-size: 1rem; padding: 10px 22px; }
.hw-accel-gate-detail { color: var(--muted); font-size: 0.74rem; margin-top: 14px; }
.replay-browser-note {
  color: var(--muted);
  font-size: 0.8rem;
  margin: 10px 4px 0;
  text-align: center;
}
.replay-browser-note.hidden { display: none; }

/* The generic empty-state block was never actually hideable — its .hidden variant was missing,
   so replay/leaderboard/stats empty messages showed alongside real content. */
.stats-empty.hidden { display: none; }
.opt-cursor-color {
  width: 52px;
  height: 30px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel2);
  cursor: pointer;
}

/* Tutorial button: highlighted until the player completes any tutorial section.
   A green spine and a "Start here" tag, rather than a solid green slab — the
   point is to draw the eye to one row, not to shout over the Ranked hero. */
#btn-tutorial.btn-tutorial-new {
  background: linear-gradient(90deg, rgba(63, 185, 107, 0.16) 0%, rgba(63, 185, 107, 0.03) 45%, transparent 78%);
  border-color: rgba(63, 185, 107, 0.35);
  border-left-color: var(--green);
}
#btn-tutorial.btn-tutorial-new .nav-icon { color: var(--green); }
#btn-tutorial.btn-tutorial-new::after {
  content: 'Start here';
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #0d1c12;
  background: var(--green);
  border-radius: 3px;
  padding: 2px 7px;
  margin-right: 2px;
}
#btn-tutorial.btn-tutorial-new:hover {
  background: linear-gradient(90deg, rgba(63, 185, 107, 0.26) 0%, rgba(63, 185, 107, 0.06) 45%, transparent 78%);
  border-color: rgba(63, 185, 107, 0.55);
  border-left-color: var(--green);
}
#btn-tutorial.btn-tutorial-new:hover .nav-icon,
#btn-tutorial.btn-tutorial-new:hover .nav-chevron { color: var(--green); }

/* ── Newcomer role swap ──────────────────────────────────────────────────────
   Ranked demoted to a plain nav row, matching Host a game / Browse games exactly.
   The whole point is that it stops competing with the Tutorial for attention, so
   the gold, the oversized type and the "play your first ranked game" subtitle all
   go — the subtitle in particular reads as a call to action, which is the wrong
   nudge for someone who hasn't learned the controls yet. */
#btn-ranked.btn-hero--asnav {
  gap: 12px;
  padding: 13px 16px 13px 18px;
  background: linear-gradient(90deg, rgba(20, 30, 44, 0.9) 0%, rgba(12, 19, 31, 0.55) 100%);
  border: 1px solid var(--edge);
  border-left: 3px solid rgba(168, 212, 232, 0.22);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  box-shadow: none;
}
#btn-ranked.btn-hero--asnav .hero-icon { width: 18px; height: 18px; color: var(--ice-cool); }
#btn-ranked.btn-hero--asnav .hero-title {
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  /* .btn-nav uppercases its rows and .btn-hero does not, so Ranked sitting in the nav list
     was the one mixed-case label among uppercase neighbours. */
  text-transform: uppercase;
  color: var(--text);
}
#btn-ranked.btn-hero--asnav .hero-sub { display: none; }
#btn-ranked.btn-hero--asnav .hero-chevron { width: 16px; height: 16px; color: var(--ice-cool); }
#btn-ranked.btn-hero--asnav:hover {
  background: linear-gradient(90deg, rgba(28, 41, 58, 0.95) 0%, rgba(16, 25, 39, 0.7) 100%);
  border-color: rgba(168, 212, 232, 0.3);
  border-left-color: var(--ice-cool);
  box-shadow: none;
  transform: none;
}

/* Tutorial promoted into the hero slot: green where Ranked is gold, so the two
   never read as the same call to action. Sub line only exists in this mode. */
#btn-tutorial.btn-tutorial-hero {
  align-items: center;
  gap: 16px;
  padding: 18px 20px;
  background:
    linear-gradient(90deg, rgba(63, 185, 107, 0.16) 0%, rgba(63, 185, 107, 0.04) 45%, transparent 78%),
    linear-gradient(180deg, var(--rink-hi) 0%, #101927 100%);
  border: 1px solid rgba(63, 185, 107, 0.35);
  border-left: 4px solid var(--green);
}
#btn-tutorial.btn-tutorial-hero .nav-icon { width: 26px; height: 26px; color: var(--green); }
#btn-tutorial.btn-tutorial-hero .nav-text { flex: 1; min-width: 0; text-align: left; }
#btn-tutorial.btn-tutorial-hero .nav-label {
  display: block;
  font-family: var(--font-menu-display);
  font-size: 1.4rem;
  line-height: 1.05;
  letter-spacing: 0.005em;
  color: var(--ice-bright);
}
#btn-tutorial.btn-tutorial-hero .nav-sub {
  display: block;
  margin-top: 4px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--green);
}
#btn-tutorial.btn-tutorial-hero:hover {
  background:
    linear-gradient(90deg, rgba(63, 185, 107, 0.26) 0%, rgba(63, 185, 107, 0.07) 45%, transparent 78%),
    linear-gradient(180deg, #1a2536 0%, #131d2c 100%);
  border-color: rgba(63, 185, 107, 0.55);
  border-left-color: var(--green);
  transform: translateY(-2px);
}
/* The "START HERE" tag belongs to the small nav-row treatment — at hero size the
   button is already unmistakably the thing to click. */
#btn-tutorial.btn-tutorial-hero::after { content: none; }
/* Sub line is hero-only; in the normal nav row the button is just "Tutorial". */
#btn-tutorial .nav-sub { display: none; }
#btn-tutorial .nav-text { display: contents; }
#btn-tutorial.btn-tutorial-hero .nav-text { display: block; }

/* One-time tutorial reward toast. Bottom-centre, above the tutorial HUD but out of
   the way of the rink — it appears mid-tutorial, so it must not cover the drill. */
.tutorial-reward-toast {
  position: fixed;
  left: 50%;
  bottom: clamp(70px, 12vh, 130px);
  transform: translate(-50%, 12px);
  z-index: 3300;
  pointer-events: none;
  padding: 10px 22px;
  border-radius: 10px;
  background: rgba(8, 12, 20, 0.94);
  border: 1px solid rgba(63, 185, 107, 0.45);
  box-shadow: 0 0 40px rgba(0, 0, 0, 0.7), 0 0 22px rgba(63, 185, 107, 0.18);
  color: var(--green);
  font-family: var(--font-data);
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.28s ease, transform 0.28s ease;
}
.tutorial-reward-toast.is-on { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) {
  .tutorial-reward-toast { transition: opacity 0.28s ease; transform: translate(-50%, 0); }
}

/* ── Controller support: first-run layout prompt + options input-layout toggle ── */
.input-mode-prompt {
  position: fixed;
  inset: 0;
  background: rgba(4, 7, 12, 0.88);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1200;
  padding: 18px;
}
.input-mode-prompt.hidden { display: none; }
.input-mode-card-wrap { text-align: center; max-width: 720px; }
.input-mode-card-wrap h2 {
  font-family: var(--font-menu-display, inherit);
  margin: 0 0 6px;
  letter-spacing: 0.04em;
}
.input-mode-sub { color: var(--ice-dim, #8aa4b8); font-size: 13px; margin-bottom: 22px; }
.input-mode-cards { display: flex; gap: 18px; justify-content: center; flex-wrap: wrap; }
.input-mode-card {
  width: 250px;
  padding: 26px 18px 22px;
  background: var(--rink, #0d1622);
  border: 2px solid rgba(168, 212, 232, 0.25);
  border-radius: 12px;
  color: var(--text, #e8f2f8);
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.input-mode-card:hover, .input-mode-card.pad-selected {
  border-color: var(--gold, #f2c14e);
  transform: translateY(-2px);
  box-shadow: 0 6px 24px rgba(242, 193, 78, 0.18);
}
.input-mode-card.pad-detected .input-mode-card-desc { color: var(--gold, #f2c14e); }
.input-mode-card-icon { font-size: 40px; margin-bottom: 10px; }
.input-mode-card-title { font-weight: 700; font-size: 17px; margin-bottom: 6px; }
.input-mode-card-desc { font-size: 12px; color: var(--ice-dim, #8aa4b8); }

.input-layout-row { display: flex; align-items: center; justify-content: space-between; margin: 4px 0 14px; }
.input-layout-toggle { display: inline-flex; border: 1px solid rgba(168, 212, 232, 0.3); border-radius: 8px; overflow: hidden; }
.input-layout-btn {
  padding: 6px 16px;
  background: transparent;
  border: none;
  color: var(--ice-dim, #8aa4b8);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
}
.input-layout-btn.active { background: var(--gold, #f2c14e); color: #141414; font-weight: 700; }
.options-subtitle { margin-top: 18px; font-size: 13px; opacity: 0.85; }
.pad-rebind-note { font-size: 11.5px; margin: 4px 0 10px; }

/* Gamepad focus ring — must be unmissable on a TV/Steam Deck */
.pad-focus {
  outline: 3px solid var(--gold, #f2c14e) !important;
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(242, 193, 78, 0.25), 0 0 18px rgba(242, 193, 78, 0.45) !important;
  border-radius: 6px;
  position: relative;
  z-index: 5;
}

/* Cursor color swatches (controller-friendly alternative to the OS color picker) */
.cursor-swatches { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
.cursor-swatch {
  width: 22px; height: 22px; padding: 0;
  border: 2px solid rgba(168, 212, 232, 0.35);
  border-radius: 50%;
  cursor: pointer;
}
.cursor-swatch:hover { border-color: var(--gold, #f2c14e); }

/* Generic in-app alert/confirm */
.zh-modal-card { max-width: 420px; }
.zh-modal-text { margin-bottom: 16px; white-space: pre-wrap; }

/* Controller-mode button hints on time-critical prompts (A = accept, B = decline).
   Only rendered while body.controller-mode is set; text comes from data-pad-glyph
   so PlayStation pads show their own symbols. */
body.controller-mode [data-pad-glyph]::before {
  content: attr(data-pad-glyph);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5em;
  height: 1.5em;
  padding: 0 0.2em;
  margin-right: 0.5em;
  border-radius: 999px;
  font-size: 0.82em;
  font-weight: 800;
  line-height: 1;
  color: #fff;
  vertical-align: middle;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
}
body.controller-mode [data-pad-glyph-kind="accept"]::before { background: #2e9e3f; }
body.controller-mode [data-pad-glyph-kind="decline"]::before { background: #c9403c; }

/* ── Cinematic Mode ─────────────────────────────────────────────────────────── */
/* Effects tray (top-center while Cinematic Mode is on) */
.cinematic-tray {
  position: fixed;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 42;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  background: linear-gradient(180deg, rgba(14, 17, 23, 0.92), rgba(22, 27, 34, 0.84));
  border: 1px solid rgba(240, 192, 64, 0.35);
  border-radius: 14px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(8px);
}
.cinematic-tray.hidden { display: none; }
.cine-tray-body {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Collapsed: everything folds away but the re-open tab, which dims to a quiet chip so a
   recorded frame stays clean. Centered it sat on top of the clock, so while collapsed JS
   parks it just right of the period / shot-counter block (see positionCollapsedCineTray). */
.cinematic-tray--collapsed {
  padding: 3px 10px;
  gap: 0;
  opacity: 0.45;
  border-color: rgba(240, 192, 64, 0.22);
  box-shadow: none;
  transition: opacity 0.25s ease;
}
.cinematic-tray--collapsed:hover,
.cinematic-tray--collapsed:focus-within { opacity: 1; }
.cinematic-tray--collapsed .cine-tray-title,
.cinematic-tray--collapsed .cine-tray-body { display: none; }
/* Labelled when collapsed — a lone film icon next to the clock read as scoreboard furniture. */
.cinematic-tray--collapsed .cine-tray-toggle {
  color: var(--gold);
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  padding: 4px 2px;
  margin-left: 0;
  white-space: nowrap;
}
.cine-tray-toggle {
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1;
  padding: 6px 6px 6px 8px;
  margin-left: 2px;
  cursor: pointer;
}
.cine-tray-toggle:hover { color: var(--gold); }
.cine-tray-sep {
  width: 1px;
  height: 26px;
  background: rgba(240, 192, 64, 0.25);
  margin: 0 2px;
}
.cine-tray-title {
  color: var(--gold);
  font-family: var(--font-display);
  font-size: 0.78rem;
  letter-spacing: 2.5px;
  margin-right: 4px;
  white-space: nowrap;
}
.cine-hint {
  color: var(--muted);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  margin-left: 4px;
  white-space: nowrap;
}
.cine-icon {
  position: relative;
  width: 40px;
  height: 38px;
  font-size: 1.15rem;
  line-height: 1;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s, transform 0.12s, box-shadow 0.12s;
}
.cine-icon:hover { border-color: var(--gold-dark); transform: translateY(-1px); }
.cine-icon.active {
  background: linear-gradient(180deg, rgba(240, 192, 64, 0.28), rgba(240, 192, 64, 0.12));
  border-color: var(--gold);
  box-shadow: 0 0 12px rgba(240, 192, 64, 0.35), inset 0 0 8px rgba(240, 192, 64, 0.18);
}
.cine-icon-disabled { opacity: 0.45; }
.cine-icon-tag {
  position: absolute;
  right: 2px;
  bottom: 1px;
  font-size: 0.58rem;
  font-weight: 700;
  color: var(--gold);
}
/* margin-left used to be the whole separation attempt; the flex gap does it properly now. */

/* Telestrator: pen tools + the screen-space drawing surface. */
.cine-draw-tools {
  display: flex;
  align-items: center;
  gap: 6px;
}
.cine-draw-tools.hidden { display: none; }
.cine-swatch {
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35);
  background: var(--swatch, #000);
  cursor: pointer;
  transition: transform 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.cine-swatch:hover { transform: scale(1.12); border-color: rgba(255, 255, 255, 0.7); }
.cine-swatch.active {
  border-color: var(--gold);
  box-shadow: 0 0 10px rgba(240, 192, 64, 0.55);
  transform: scale(1.12);
}
.cine-icon--clear { font-size: 1rem; }
.cine-draw-canvas {
  position: fixed;
  inset: 0;
  z-index: 41; /* over the rink + letterbox bars, under the tray (42) */
  pointer-events: none;
  touch-action: none;
}
.cine-draw-canvas.hidden { display: none; }
/* The surface never takes pointer events — it would bury the transport bar and the spectator
   buttons. Strokes come from capture-phase listeners in game.js instead; this only re-dresses
   the cursor so the armed pen is obvious. */
/* !important: the player's custom cursor is written as an inline style on the container. */
body.cine-drawing,
body.cine-drawing .canvas-container,
body.cine-drawing #game-canvas { cursor: crosshair !important; }

/* Letterbox bars + vignette — pointer-transparent, bars slide in smoothly. */
#cine-letterbox-overlay {
  position: fixed;
  inset: 0;
  z-index: 39;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.45s ease, box-shadow 0.45s ease;
}
#cine-letterbox-overlay::before,
#cine-letterbox-overlay::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 11vh;
  background: #000;
  transition: transform 0.45s ease;
}
#cine-letterbox-overlay::before { top: 0; transform: translateY(-101%); }
#cine-letterbox-overlay::after { bottom: 0; transform: translateY(101%); }
body.cine-letterbox #cine-letterbox-overlay {
  opacity: 1;
  box-shadow: inset 0 0 180px rgba(0, 0, 0, 0.55);
}
body.cine-letterbox #cine-letterbox-overlay::before,
body.cine-letterbox #cine-letterbox-overlay::after { transform: translateY(0); }

/* Clean Frame: strip gameplay chrome for recording. The cinematic tray and replay
   transport stay interactive so the shot can still be steered (each has its own
   collapse tab when a completely bare frame is wanted). Telestrator drawings are
   deliberately NOT stripped — they are the whole point of a teaching frame. */
body.cine-clean .hud,
body.cine-clean #shot-clock-bar,
body.cine-clean #hud-ping,
body.cine-clean #fight-spectator-bar,
body.cine-clean #game-chat-overlay,
body.cine-clean #spectator-puckcam-btn,
body.cine-clean #spectator-cinematic-btn,
body.cine-clean #replay-toast {
  display: none !important;
}

/* ── Redline mode ── */
.lobby-go-redline { color: #ff5544; font-weight: 700; }
.lobby-game-mode-redline { color: #ff5544; font-weight: 800; letter-spacing: 0.04em; }
.redline-goalie-slot {
  border: 1px solid rgba(255, 85, 68, 0.55);
  background: rgba(255, 85, 68, 0.08);
  border-radius: 10px;
  padding: 8px 10px;
  margin-bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
}
.redline-goalie-slot-title {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: #ff5544;
}
.redline-goalie-slot-empty { font-size: 0.8rem; opacity: 0.6; }

.lobby-go-hint-redline {
  font-weight: 700;
  color: #ff8877;
  font-size: 0.95em;
  line-height: 1.45;
}

/* ══ Gauntlet pre-game briefing ═══════════════════════════════════════════════
   Sits in the same slot as the broadcast matchup card and inherits its stage
   dressing (spotlights, scanlines, ice reflection), so it reads as part of the
   same production rather than a bolted-on tutorial popup. */
.gauntlet-intro-card {
  position: relative;
  z-index: 3;
  /* Deliberately large: this is the only screen that explains the mode, and it is on
     for ~12s. Sized off the viewport so it fills the stage on a monitor without
     overflowing a laptop; max-height + scroll is the safety net on short windows. */
  width: min(1240px, 95vw);
  max-height: 78vh;
  overflow-y: auto;
  /* Two columns: the rules read down the left, the board stands beside them. minmax(0,…)
     on the rules column is what stops long rule copy from pushing the board off-card. */
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(268px, 0.82fr);
  gap: clamp(22px, 3vw, 46px);
  align-items: start;
  /* auto on all sides, not just inline: the overlay is a column flex container, so this is
     what actually centres the card in the space left between the network bar and the
     bottom bar. `0 auto` pinned it to the top. */
  margin: auto;
  padding: clamp(26px, 3.4vh, 44px) clamp(26px, 4vw, 60px) clamp(22px, 3vh, 38px);
  border-radius: 22px;
  border: 1px solid rgba(255, 179, 64, 0.35);
  background: linear-gradient(165deg, rgba(24, 18, 8, 0.93), rgba(10, 12, 18, 0.95));
  box-shadow: 0 0 0 1px rgba(255, 179, 64, 0.10) inset,
              0 26px 70px -26px rgba(255, 179, 64, 0.55),
              0 8px 40px rgba(0, 0, 0, 0.6);
  text-align: left;
  animation: gnt-intro-in 0.5s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}
@keyframes gnt-intro-in {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
.gnt-intro-title {
  font-size: clamp(2.8rem, 7vw, 5rem);
  font-weight: 900;
  letter-spacing: 0.1em;
  line-height: 1;
  background: linear-gradient(180deg, #fff3dc 10%, #ffb340 95%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 0 34px rgba(255, 179, 64, 0.35);
}
.gnt-intro-tagline {
  font-size: clamp(1.05rem, 2.1vw, 1.5rem);
  opacity: 0.66;
  margin: 10px 0 clamp(18px, 2.8vh, 30px);
  letter-spacing: 0.02em;
}
.gnt-intro-rules { display: flex; flex-direction: column; gap: clamp(12px, 1.9vh, 20px); }
.gnt-intro-rule { display: flex; align-items: flex-start; gap: clamp(14px, 1.6vw, 20px); }
.gnt-intro-num {
  flex: 0 0 auto;
  width: clamp(28px, 2.6vw, 38px);
  height: clamp(28px, 2.6vw, 38px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: clamp(0.85rem, 1.35vw, 1.15rem);
  font-weight: 900;
  color: #14100a;
  background: #ffb340;
  box-shadow: 0 0 12px rgba(255, 179, 64, 0.5);
  margin-top: 1px;
}
.gnt-intro-text {
  font-size: clamp(1rem, 1.55vw, 1.32rem);
  line-height: 1.5;
  opacity: 0.9;
  /* The intro overlay sets a condensed display face for team names; rule copy is long-form
     and needs a normal-width face to stay comfortable. */
  font-family: 'Barlow', 'Segoe UI', sans-serif;
}
.gnt-intro-tagline { font-family: 'Barlow', 'Segoe UI', sans-serif; }
.gnt-intro-text b { color: #ffd79a; font-weight: 800; }
/* Rule 5 is the hook rather than a rule — brighter, and its badge glows a little harder. */
.gnt-intro-rule-challenge .gnt-intro-text b { color: #ffb340; }
.gnt-intro-rule-challenge .gnt-intro-num {
  background: linear-gradient(180deg, #ffd79a, #ffb340);
  box-shadow: 0 0 18px rgba(255, 179, 64, 0.75);
}

/* ── Gauntlet intro: global top 10 ──
   Sits beside the rules rather than under them. The rules are read once; the board is the
   thing a returning player actually looks at, so it gets its own column and its own frame
   instead of being a footnote. */
.gnt-intro-board {
  /* Sizes to its rows (the card grid is align-items:start). Stretching it to match the
     rules column left a tall empty well under a board that only has a few names on it —
     which is exactly how the board looks early in its life. */
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: clamp(14px, 1.8vh, 20px) clamp(14px, 1.2vw, 20px);
  border-radius: 16px;
  border: 1px solid rgba(255, 179, 64, 0.22);
  background: linear-gradient(170deg, rgba(255, 179, 64, 0.075), rgba(0, 0, 0, 0.26));
  box-shadow: 0 0 0 1px rgba(255, 179, 64, 0.06) inset;
}
.gnt-lb-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding-bottom: 10px;
  margin-bottom: 8px;
  border-bottom: 1px solid rgba(255, 179, 64, 0.20);
}
.gnt-lb-title {
  font-size: clamp(0.82rem, 1.05vw, 0.98rem);
  font-weight: 900;
  letter-spacing: 0.14em;
  color: #ffd79a;
}
.gnt-lb-unit {
  font-size: clamp(0.6rem, 0.78vw, 0.7rem);
  font-weight: 800;
  letter-spacing: 0.14em;
  opacity: 0.42;
}
.gnt-lb-title, .gnt-lb-unit, .gnt-lb-note, .gnt-lb-row { font-family: 'Barlow', 'Segoe UI', sans-serif; }
.gnt-lb-rows { display: flex; flex-direction: column; gap: 2px; }
.gnt-lb-row {
  display: grid;
  /* Fixed rank + trophy gutters keep every name starting on the same x, medal or not. */
  grid-template-columns: 1.55em 1.25em minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  padding: clamp(5px, 0.85vh, 8px) 8px;
  border-radius: 8px;
  font-size: clamp(0.82rem, 1.05vw, 0.96rem);
  line-height: 1.15;
}
.gnt-lb-row + .gnt-lb-row { box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.045); }
.gnt-lb-rank { font-weight: 800; opacity: 0.4; font-variant-numeric: tabular-nums; text-align: right; }
.gnt-lb-name {
  font-weight: 700;
  opacity: 0.92;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gnt-lb-rounds {
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: #ffd79a;
  letter-spacing: 0.01em;
  text-align: right;   /* single digits line up under double ones */
  min-width: 1.6em;
}
/* Trophies. One shape, three finishes — the metal is the only thing that changes, so the
   podium reads at a glance without three different silhouettes to decode. */
.gnt-lb-trophy { width: 1.05em; height: 1.05em; display: block; }
.gnt-lb-trophy svg { width: 100%; height: 100%; display: block; }
.gnt-lb-row-1 .gnt-lb-trophy svg { fill: #ffd257; filter: drop-shadow(0 0 5px rgba(255, 210, 87, 0.75)); }
.gnt-lb-row-2 .gnt-lb-trophy svg { fill: #dce4ee; filter: drop-shadow(0 0 4px rgba(220, 228, 238, 0.5)); }
.gnt-lb-row-3 .gnt-lb-trophy svg { fill: #e2965c; filter: drop-shadow(0 0 4px rgba(226, 150, 92, 0.5)); }
.gnt-lb-row-1 { background: linear-gradient(90deg, rgba(255, 210, 87, 0.16), rgba(255, 210, 87, 0)); }
.gnt-lb-row-2 { background: linear-gradient(90deg, rgba(220, 228, 238, 0.10), rgba(220, 228, 238, 0)); }
.gnt-lb-row-3 { background: linear-gradient(90deg, rgba(226, 150, 92, 0.10), rgba(226, 150, 92, 0)); }
.gnt-lb-row-1 .gnt-lb-rank,
.gnt-lb-row-2 .gnt-lb-rank,
.gnt-lb-row-3 .gnt-lb-rank { opacity: 0.75; }
.gnt-lb-row-1 .gnt-lb-name { opacity: 1; }
/* Your own row, wherever it lands. */
.gnt-lb-row-me {
  background: linear-gradient(90deg, rgba(120, 200, 255, 0.20), rgba(120, 200, 255, 0.02));
  box-shadow: inset 2px 0 0 #78c8ff;
}
.gnt-lb-row-me .gnt-lb-name { color: #cbe9ff; opacity: 1; }
.gnt-lb-note {
  margin-top: auto;
  padding-top: 10px;
  font-size: clamp(0.68rem, 0.9vw, 0.8rem);
  letter-spacing: 0.04em;
  opacity: 0.5;
  font-weight: 600;
}
.gnt-lb-note:empty { display: none; }

/* ── Start ladder ──
   The rungs a run may begin on, drawn as a rail of chips threaded by a single line — the
   ladder you have climbed, read left to right. Each unlocked chip wears the accent of the
   boss it stands behind (--chip, set inline by the renderer), so the rail doubles as a
   trophy shelf: the colours ARE the bosses you have put down. One locked chip closes the
   rail off, greyed, naming the boss that would open it.

   Shared verbatim by the pre-run briefing and the results card. Only .gnt-start-over's
   framing differs, because there the rail sits under a leaderboard rather than beside one. */
.gnt-start {
  margin-top: clamp(18px, 2.6vh, 30px);
  font-family: 'Barlow', 'Segoe UI', sans-serif;
}
.gnt-start-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.gnt-start-title {
  font-size: clamp(0.72rem, 1.05vw, 0.86rem);
  font-weight: 800;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #ffb340;
}
.gnt-start-hint {
  font-size: clamp(0.68rem, 0.95vw, 0.8rem);
  opacity: 0.55;
  font-weight: 600;
}
/* WRAPS rather than scrolls. The ladder only grows — a second-lap player has a dozen rungs
   and a third-lap one has twenty — and a horizontal scroller hides exactly the deep rungs
   they opened the panel for. Wrapped rows of number-sized chips fit them all on screen. */
.gnt-start-rail {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  /* Room for the focus ring and the hover lift, which a clipped box would cut off. */
  padding: 4px 2px 6px;
  /* Backstop for an absurd rung count; wrapping means it is almost never reached. */
  max-height: 30vh;
  overflow-y: auto;
  scrollbar-width: thin;
}
.gnt-chip {
  position: relative;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  min-width: 44px;
  height: 38px;
  padding: 0 10px;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--chip, #ffb340) 45%, transparent);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--chip, #ffb340) 16%, transparent),
      rgba(8, 10, 16, 0.86));
  color: var(--chip, #ffb340);
  cursor: pointer;
  font-family: 'Barlow Condensed', 'Impact', sans-serif;
  font-size: 1.3rem;
  font-weight: 900;
  line-height: 1;
  letter-spacing: 0.02em;
  transition: transform 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease;
}
.gnt-chip:hover {
  transform: translateY(-2px);
  border-color: var(--chip, #ffb340);
  box-shadow: 0 6px 20px -8px var(--chip, #ffb340);
}
.gnt-chip:focus-visible {
  outline: 2px solid var(--chip, #ffb340);
  outline-offset: 2px;
}
/* The current pick. Filled rather than merely outlined: at a glance the rail has to answer
   "where does this run start" without being read. */
.gnt-chip.is-picked {
  border-color: var(--chip, #ffb340);
  color: #0d1016;
  background: linear-gradient(180deg,
      color-mix(in srgb, var(--chip, #ffb340) 92%, #fff),
      var(--chip, #ffb340));
  box-shadow: 0 8px 26px -10px var(--chip, #ffb340);
}
/* Results card: the rail is one of three things competing for the same eye, so it is boxed
   off from the leaderboard above and the buttons below rather than floating between them. */
.gnt-start-over {
  margin: 14px auto 4px;
  max-width: 640px;
  padding: 10px 14px 4px;
  border-radius: 14px;
  border: 1px solid rgba(255, 179, 64, 0.18);
  background: rgba(255, 179, 64, 0.045);
}
.gnt-start-over .gnt-start-head { justify-content: center; }
/* Centred here: the results card is symmetric around its buttons, and a left-packed rail of
   numbers under a centred heading reads as misaligned rather than as a list. */
.gnt-start-over .gnt-start-rail { justify-content: center; max-height: 22vh; }
/* The head line carries the boss name now, so it must not collapse when a chip is not under
   the cursor — a reflowing panel under the pointer is worse than a line of resting text. */
.gnt-start-hint { min-height: 1.2em; }

/* ── Category tabs ──
   NO CHECKPOINTS vs ANY, on all three boards (menu panel, briefing, results card). A pill
   pair rather than a dropdown: there are exactly two, and which one you are reading has to be
   answerable at a glance from across the room. */
.gnt-cat-tabs {
  display: flex;
  gap: 4px;
  margin: 0 0 8px;
  padding: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.07);
}
.gnt-cat-tab {
  flex: 1 1 0;
  min-width: 0;
  padding: 5px 8px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: #cfd8e6;
  font-family: inherit;
  font-size: clamp(0.62rem, 0.9vw, 0.74rem);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  opacity: 0.6;
  transition: background 0.15s ease, opacity 0.15s ease, color 0.15s ease;
}
.gnt-cat-tab:hover { opacity: 0.85; }
.gnt-cat-tab.is-on {
  background: rgba(255, 179, 64, 0.9);
  color: #14100a;
  opacity: 1;
}
.gnt-cat-tab:focus-visible { outline: 2px solid #ffb340; outline-offset: 2px; }

/* ── Category badge ──
   Green for a clean run, amber for a checkpointed one. Green is already the mode's "banked"
   colour (the checkpoint flash, the life pips), and amber is already its "spent" colour, so
   the pair needs no legend. */
.gnt-category {
  margin: -2px 0 12px;
  font-size: clamp(0.68rem, 1vw, 0.8rem);
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.gnt-cat-clean { color: #6cf59b; }
.gnt-cat-any   { color: #ffb340; }
/* The HUD badge is a stat value, so it inherits that type and only takes the colour. */
.gnt-stat-val.gnt-cat-clean,
.gnt-stat-val.gnt-cat-any { font-size: 0.78em; letter-spacing: 0.06em; }

/* Below this the two columns stop fitting side by side — the board drops under the rules
   rather than squeezing names down to two characters. */
@media (max-width: 880px) {
  .gauntlet-intro-card { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .gauntlet-intro-card { padding: 20px 18px 16px; gap: 18px; }
  .gnt-intro-text { font-size: 0.82rem; }
}

/* ── Gauntlet lobby roster ──
   One runner and some spectators, so there are only two meaningful columns. Team Blue is
   removed from the grid entirely rather than rendered empty: an empty team column reads as
   a slot you're meant to be able to join, and in gauntlet the blue side is server-owned AI. */
.teams-container.gauntlet-teams { grid-template-columns: 1fr 1fr; }
.teams-container.gauntlet-teams .team-blue { display: none; }
.teams-container.gauntlet-teams .team-red { border-top-color: #ffb340; }
.gauntlet-runner-empty {
  font-size: 0.82rem;
  opacity: 0.45;
  padding: 6px 2px;
  font-style: italic;
}

/* Wider than the shared modal-card-wide (520px) so two mode cards sit side by side
   without the one-line descriptions wrapping to three lines. Scoped to this dialog so
   Game Options keeps its own width. */
#lobby-name-modal .modal-card { width: min(620px, 94vw); }

/* ══ Create-Lobby game-mode picker ══════════════════════════════════════════
   Four cards, each with a mini-rink diagram. Colour is the mode's identity and is
   reused everywhere else that mode appears (lobby header chip, options radio). */
.mode-pick-label {
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.5;
  margin: 16px 0 8px;
  text-align: left;
}
.mode-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 4px;
}
.mode-card {
  --mode-accent: #6ea8ff;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  padding: 10px 10px 12px;
  border-radius: 12px;
  border: 1.5px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.035);
  color: inherit;
  text-align: left;
  cursor: pointer;
  font: inherit;
  /* Cards are double-clickable to create; without this the second click selects the label. */
  user-select: none;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.14s ease;
}
.mode-card:hover { border-color: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.07); }
.mode-card:active { transform: scale(0.985); }
.mode-card.selected {
  border-color: var(--mode-accent);
  background: color-mix(in srgb, var(--mode-accent) 12%, transparent);
  box-shadow: 0 0 0 1px var(--mode-accent) inset, 0 4px 18px -8px var(--mode-accent);
}
.mode-card-standard { --mode-accent: #6ea8ff; }
.mode-card-custom   { --mode-accent: #9d8cf0; }
.mode-card-redline  { --mode-accent: #ff5544; }
.mode-card-gauntlet { --mode-accent: #ffb340; }

/* The four illustrations have very different aspect ratios (2.5:1 down to 1.1:1), so the
   slot is fixed and the image is CONTAINed inside it — otherwise the grid rows go ragged
   and the wide one dwarfs the square one. contain, not cover: cropping the Standard art
   to this ratio would cut the nets off both ends. */
.mode-art {
  display: block;
  aspect-ratio: 16 / 10;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.18);
}
.mode-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Lifts the art off the card without needing a baked-in glow per image. */
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.5));
  transition: transform 0.18s ease;
}
.mode-card:hover .mode-art img { transform: scale(1.035); }
.mode-card.selected .mode-art { background: color-mix(in srgb, var(--mode-accent) 10%, rgba(0,0,0,0.18)); }
.mode-name {
  font-weight: 800;
  font-size: 0.92rem;
  letter-spacing: 0.01em;
  color: var(--mode-accent);
}
.mode-desc { font-size: 0.76rem; line-height: 1.35; opacity: 0.66; }


@media (max-width: 620px) {
  .mode-grid { grid-template-columns: minmax(0, 1fr); }
  .mode-desc { font-size: 0.8rem; }
}

/* ── Gauntlet mode ──
   Arcade amber, deliberately distinct from redline's red and the ranked gold. */
.lobby-go-gauntlet { color: #ffb340; font-weight: 700; }
.lobby-game-mode-gauntlet { color: #ffb340; font-weight: 800; letter-spacing: 0.04em; }
.lobby-go-hint-gauntlet {
  font-weight: 700;
  color: #ffc978;
  font-size: 0.95em;
  line-height: 1.45;
}
/* Run-over card */
.gnt-card { max-width: 560px; }
.gnt-reason {
  font-size: 0.95rem;
  opacity: 0.75;
  margin-bottom: 14px;
  letter-spacing: 0.02em;
}
.gnt-figures {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin-bottom: 12px;
}
.gnt-figure { text-align: center; min-width: 96px; }
.gnt-figure-num {
  font-size: 2.1rem;
  font-weight: 900;
  color: #ffb340;
  line-height: 1.1;
}
.gnt-figure-label {
  font-size: 0.68rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0.62;
  margin-top: 2px;
}
.gnt-pb {
  font-weight: 900;
  letter-spacing: 0.12em;
  color: #6ee7a8;
  margin-bottom: 8px;
  animation: gnt-pb-pulse 1.1s ease-in-out infinite;
}
@keyframes gnt-pb-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
.gnt-payout { font-size: 0.86rem; opacity: 0.85; margin-bottom: 10px; }
.gnt-payout-capped { opacity: 0.55; font-style: italic; }
.gnt-board-wrap {
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  padding-top: 10px;
  margin-bottom: 14px;
}
.gnt-board-title {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 6px;
}
/* Exactly ten rows now (plus one transient row on its way out), so nothing scrolls — a board
   that clips its own bottom rank defeats the point of showing the top ten. */
.gnt-board { overflow: hidden; }
.gnt-board-row {
  display: grid;
  /* Fixed trophy gutter so every name starts on the same x, medal or not. */
  grid-template-columns: 34px 1.15em 1fr 56px 50px;
  gap: 6px;
  padding: 3px 4px;
  font-size: 0.82rem;
  border-radius: 5px;
  position: relative;
}
.gnt-b-trophy { width: 1.05em; height: 1.05em; display: block; align-self: center; }
.gnt-b-trophy svg { width: 100%; height: 100%; display: block; }
/* One cup, three finishes — same rule as the intro board's podium. */
.gnt-b-top1 .gnt-b-trophy svg { fill: #ffd257; filter: drop-shadow(0 0 5px rgba(255, 210, 87, 0.75)); }
.gnt-b-top2 .gnt-b-trophy svg { fill: #dce4ee; filter: drop-shadow(0 0 4px rgba(220, 228, 238, 0.5)); }
.gnt-b-top3 .gnt-b-trophy svg { fill: #e2965c; filter: drop-shadow(0 0 4px rgba(226, 150, 92, 0.5)); }
.gnt-b-top1 { background: linear-gradient(90deg, rgba(255, 210, 87, 0.14), rgba(255, 210, 87, 0)); }
.gnt-b-top2 { background: linear-gradient(90deg, rgba(220, 228, 238, 0.09), rgba(220, 228, 238, 0)); }
.gnt-b-top3 { background: linear-gradient(90deg, rgba(226, 150, 92, 0.09), rgba(226, 150, 92, 0)); }

/* ── The climb ──
   Every row is offset to where it used to sit, then released. Only rows that actually moved
   carry a transition, so a board that did not change has no animation at all. */
.gnt-b-sliding {
  transition: transform 0.9s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.55s ease 0.15s;
  will-change: transform;
}
/* The row being pushed out of the top ten. It falls rather than blinking out, so you can see
   what your run cost someone. */
.gnt-b-dropped { opacity: 0.75; }
.gnt-b-dropping {
  transition: transform 0.75s ease-in, opacity 0.75s ease-in;
  transform: translateY(26px);
  opacity: 0;
  pointer-events: none;
}
/* Landing flash on your own row, once it arrives. */
.gnt-b-landed { animation: gnt-b-land 0.85s ease-out; }
@keyframes gnt-b-land {
  0%   { box-shadow: 0 0 0 0 rgba(255, 179, 64, 0.85); background: rgba(255, 179, 64, 0.42); }
  40%  { box-shadow: 0 0 18px 3px rgba(255, 179, 64, 0.55); }
  100% { box-shadow: 0 0 0 0 rgba(255, 179, 64, 0); }
}
.gnt-climb-note {
  margin-top: 8px;
  text-align: center;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: #ffb340;
  animation: gnt-climb-note-in 0.45s ease both;
}
.gnt-climb-note.hidden { display: none; }
@keyframes gnt-climb-note-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* Header strip. Reuses .gnt-board-row purely for the grid track list, so the labels can
   never drift out of line with the numbers under them. */
.gnt-board-head {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0.5;
  background: none;
  border: none;
  padding-bottom: 2px;
  margin-bottom: 1px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.gnt-board-head.hidden { display: none; }
.gnt-board-head .gnt-b-rounds,
.gnt-board-head .gnt-b-cp,
.gnt-board-head .gnt-b-lives { text-align: right; opacity: 1; font-size: 1em; }
.gnt-board-row .gnt-b-rank { opacity: 0.55; }
.gnt-board-row .gnt-b-rounds,
.gnt-board-row .gnt-b-lives { text-align: right; font-variant-numeric: tabular-nums; }
/* Round is the headline; the two tiebreaks sit back so the eye reads distance first. */
.gnt-board-row .gnt-b-lives { opacity: 0.6; font-size: 0.92em; }
.gnt-board-row.gnt-b-me { background: rgba(255, 179, 64, 0.16); font-weight: 800; }
.gnt-board-empty { font-size: 0.82rem; opacity: 0.5; padding: 6px 4px; }
.gnt-actions { display: flex; gap: 10px; justify-content: center; }

/* Its own tab now, so none of the "subordinate section" treatment applies — no divider
   above it, no dimmed header. It stands as its own board. */

/* In-game HUD bits */
.hud-gauntlet-round { color: #ffb340; font-weight: 800; }
/* ══ Gauntlet boss rounds ══
   Everything below is driven by one custom property, --boss: the client sets it from the
   boss's accent colour and the whole takeover re-tints. That is the entire theming system —
   a new boss needs a hex, not a stylesheet.

   CONTRAST RULE, learned the hard way: this sits over WHITE ICE. The first version used a
   translucent vignette that was transparent in the middle and light type floating on top,
   which looked great over a dark test background and washed out completely over the rink.
   So: everything legible lives on an OPAQUE near-black plate, and the accent is used as
   rim, label and underline — never as a large light fill behind text. */
.gnt-boss-card {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  pointer-events: none;
  /* Opaque scrim, not a vignette. The ice must be dimmed to a backdrop before any type
     goes near it, or the white burns straight through the middle of the card. */
  background:
    radial-gradient(ellipse at 50% 50%, color-mix(in srgb, var(--boss, #ffb340) 22%, transparent) 0%, transparent 58%),
    rgba(4, 6, 10, 0.88);
  animation: gnt-boss-bg-in 0.45s ease both;
}
.gnt-boss-card.hidden { display: none; }
@keyframes gnt-boss-bg-in { from { opacity: 0; } to { opacity: 1; } }
/* Slash bars: dark bodies with a bright accent edge. The accent is the EDGE, not the fill —
   a big translucent accent wash is the thing that disappears over white. */
.gnt-boss-card::before,
.gnt-boss-card::after {
  content: '';
  position: absolute;
  left: -10%;
  width: 120%;
  height: clamp(60px, 10vh, 118px);
  background: linear-gradient(90deg,
    transparent,
    rgba(6, 9, 14, 0.94) 16%,
    color-mix(in srgb, var(--boss, #ffb340) 22%, rgba(6, 9, 14, 0.94)) 50%,
    rgba(6, 9, 14, 0.94) 84%,
    transparent);
  border-top: 2px solid color-mix(in srgb, var(--boss, #ffb340) 85%, transparent);
  border-bottom: 2px solid color-mix(in srgb, var(--boss, #ffb340) 45%, transparent);
  transform: skewY(-3.2deg);
}
.gnt-boss-card::before { top: 22%; animation: gnt-boss-slash-l 0.55s cubic-bezier(0.16, 1, 0.3, 1) both; }
.gnt-boss-card::after  { bottom: 20%; animation: gnt-boss-slash-r 0.55s cubic-bezier(0.16, 1, 0.3, 1) 0.06s both; }
@keyframes gnt-boss-slash-l { from { transform: skewY(-3.2deg) translateX(-100%); opacity: 0; } to { transform: skewY(-3.2deg) translateX(0); opacity: 1; } }
@keyframes gnt-boss-slash-r { from { transform: skewY(-3.2deg) translateX(100%);  opacity: 0; } to { transform: skewY(-3.2deg) translateX(0); opacity: 1; } }

/* The plate. Opaque on purpose — this is what guarantees the type reads no matter what is
   painted behind the card. */
.gnt-boss-card-inner {
  position: relative;
  text-align: center;
  padding: clamp(20px, 3.4vh, 40px) clamp(26px, 4.5vw, 68px) clamp(22px, 3.6vh, 42px);
  border-radius: 20px;
  border: 1px solid color-mix(in srgb, var(--boss, #ffb340) 55%, transparent);
  background: linear-gradient(180deg, rgba(13, 16, 23, 0.985), rgba(6, 8, 12, 0.995));
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.7),
    0 0 60px -10px color-mix(in srgb, var(--boss, #ffb340) 60%, transparent),
    0 30px 80px -20px rgba(0, 0, 0, 0.95);
  max-width: min(1180px, 92vw);
}
/* Accent rail down the top edge of the plate — reads as a title bar, and gives the boss
   colour somewhere strong to live that isn't behind text. */
.gnt-boss-card-inner::before {
  content: '';
  position: absolute;
  top: 0; left: 12%; right: 12%;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(90deg, transparent, var(--boss, #ffb340), transparent);
  box-shadow: 0 0 16px color-mix(in srgb, var(--boss, #ffb340) 80%, transparent);
}
.gnt-boss-eyebrow {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  font-size: clamp(0.68rem, 1vw, 0.86rem); font-weight: 800; letter-spacing: 0.4em;
  color: #cfd8e6; opacity: 0.72; margin-bottom: clamp(4px, 0.8vh, 10px);
  animation: gnt-boss-fade 0.5s ease 0.30s both;
}
.gnt-boss-eyebrow-line { width: clamp(24px, 4vw, 64px); height: 1px; background: currentColor; opacity: 0.45; }
.gnt-boss-kicker {
  font-size: clamp(0.72rem, 1.35vw, 1.02rem); font-weight: 900; letter-spacing: 0.62em;
  color: var(--boss, #ffb340); text-indent: 0.62em;
  text-shadow: 0 0 18px color-mix(in srgb, var(--boss, #ffb340) 60%, transparent);
  animation: gnt-boss-fade 0.5s ease 0.16s both;
}
.gnt-boss-name {
  font-size: clamp(2.6rem, 8vw, 7rem); font-weight: 900; line-height: 0.95;
  letter-spacing: 0.03em; margin: clamp(2px, 0.6vh, 10px) 0 clamp(6px, 1vh, 12px);
  /* Near-white through to the accent. On the opaque plate this holds its own; the top stop
     stays white rather than tinting, so the name never gets lighter than the plate. */
  background: linear-gradient(180deg, #ffffff 6%, #f3f6fb 34%, var(--boss, #ffb340) 96%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  /* Glow via text-shadow, NOT filter. A `filter` on this element painted NOTHING — the name
     was simply absent from the rendered card while every other element on it drew correctly
     (caught in a screenshot, fixed by this line). background-clip:text + a transparent fill
     does not survive being filtered; text-shadow paints from the glyph outline instead.
     Don't reintroduce filter here, including inside the keyframes. */
  text-shadow: 0 0 30px color-mix(in srgb, var(--boss, #ffb340) 45%, transparent);
  animation: gnt-boss-name-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes gnt-boss-name-in {
  from { opacity: 0; transform: scale(1.14); }
  to   { opacity: 1; transform: none; }
}
.gnt-boss-mark { font-size: 0.52em; opacity: 0.85; margin-left: 0.16em; letter-spacing: 0.06em; }
.gnt-boss-title {
  font-family: 'Barlow', 'Segoe UI', sans-serif;
  font-size: clamp(0.8rem, 1.55vw, 1.2rem); font-weight: 700;
  letter-spacing: 0.3em; text-transform: uppercase;
  color: #b9c6d8;
  animation: gnt-boss-fade 0.5s ease 0.24s both;
}
@keyframes gnt-boss-fade { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }

/* The round pill turns the boss colour too, so the HUD agrees with the takeover. */
.gauntlet-hud.gnt-hud-boss .gnt-stat-cur { color: var(--boss, #ffb340); text-shadow: 0 0 14px var(--boss, #ffb340); }

/* Replaces .hud-scoreboard in gauntlet. Each figure carries its own label so none of them
   can be misread as a scoreline; the live round is the only one in the accent colour. */
.gauntlet-hud {
  display: flex;
  align-items: stretch;
  gap: 18px;
}
.gnt-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  min-width: 62px;
}
.gnt-stat-label {
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.5;
  white-space: nowrap;
}
.gnt-stat-val {
  font-size: 1.35rem;
  font-weight: 900;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  opacity: 0.72;
}
.gnt-stat-cur { color: #ffb340; opacity: 1; font-size: 1.6rem; }
/* Lives pips — hollow while held, filled red once spent. Spending one is the single most
   important state change in the mode, so the fill animates rather than just swapping. */
.gnt-lives { display: flex; gap: 5px; align-items: center; height: 1.35rem; }
/* Lit green = a life you still have; going dark = one spent. Reads the way a row of lights
   does — you count what is still on, not what has turned red. */
.gnt-life {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1.8px solid #4bd97a;
  background: #37c866;
  box-shadow: 0 0 7px rgba(60, 214, 110, 0.75);
  transition: background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
.gnt-life-lost {
  background: #0b0f14;
  border-color: rgba(255, 255, 255, 0.22);
  box-shadow: none;
  animation: gnt-life-pop 0.35s ease;
}
@keyframes gnt-life-pop {
  0% { transform: scale(1.9); }
  60% { transform: scale(0.9); }
  100% { transform: scale(1); }
}
/* Down to the last one: the remaining pip pulses so the stakes read at a glance. */
.gnt-lives-critical .gnt-life:not(.gnt-life-lost) {
  border-color: #ffb340;
  box-shadow: 0 0 8px rgba(255, 179, 64, 0.9);
  animation: gnt-life-warn 1s ease-in-out infinite;
}
@keyframes gnt-life-warn { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (max-width: 760px) {
  .gauntlet-hud { gap: 10px; }
  .gnt-stat { min-width: 48px; }
  .gnt-stat-val { font-size: 1.1rem; }
  .gnt-stat-cur { font-size: 1.3rem; }
}
#gauntlet-lb-rows .leaderboard-row { font-size: 0.86rem; }

/* Top-bar menu/fullscreen buttons stay clickable over the pause veil (z 60).
   NOTE: this must NOT set `position` — .btn-game-menu / .btn-fullscreen are absolutely
   positioned against .hud (right: 44px / 10px, just under the bar). An earlier
   `position: relative` here won on specificity (#id > .class) and dropped both buttons
   back into the HUD's flex flow, so where they landed depended on how wide the centre
   block happened to be — which is why they sat somewhere else in redline (different
   scoreboard content) and could get pushed off the right edge entirely.
   z-index alone is enough; the class rules already establish positioning. */
#btn-game-menu, #btn-fullscreen {
  z-index: 65;
}

/* Options row disabled because the host forced third-person for this game (see
   gameForcesThirdPerson) — the player's own saved setting is untouched. */
.options-toggle-row.options-row-locked { opacity: 0.55; }
.options-toggle-row.options-row-locked input { cursor: not-allowed; }
.options-locked-note { color: #ffd479; }

/* In-game camera-view hint: bottom-left pill, deliberately clear of the centred ability row
   and the centre-screen recenter hint. Dim at rest so it never competes with play; the key
   glyph comes from the player's own binding (keyboard code or pad button for their controller
   type), so it reads correctly for every input layout. */
.canvas-container .camera-hint-chip {
  position: absolute;
  z-index: 8;
  left: 16px;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(12, 18, 28, 0.72);
  border: 1px solid rgba(120, 145, 178, 0.28);
  color: var(--text);
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0.62;
  pointer-events: none;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: opacity 160ms ease;
}
/* Brief emphasis right after a view switch, so the chip confirms what just happened. */
.canvas-container .camera-hint-chip.camera-hint-flash {
  opacity: 1;
  border-color: rgba(143, 208, 255, 0.75);
}
.camera-hint-key {
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 5px;
  padding: 1px 7px;
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 700;
}
.canvas-container .camera-hint-chip.hidden { display: none !important; }

/* One row of "label then key" pairs. Each pair is its own inline-flex so a label can never
   wrap away from the key it names; the whole row wraps between pairs if the viewport is
   narrow enough to force it. */
.camera-hint-parts { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.cam-hint-item { display: inline-flex; align-items: center; gap: 5px; }
.cam-hint-word { opacity: 0.85; }
.cam-hint-sep { opacity: 0.3; }

:root {
  /* Pacing dial for the celebration keyframes. MUST match RANK_ANIM_SLOW in game.js — the JS
     drives the waits between beats and CSS drives the beats themselves, so if the two disagree
     the pops finish while the bar is still climbing. */
  --rank-anim-scale: 1.8;
}

/* ── Post-match progression celebration ──────────────────────────────────────
   Two hosts, one animation (see playRankCelebration in game.js):
     · on the menu it drives the identity card's OWN rank block, so there is never a second
       bar sitting next to the real one
     · straight into matchmaking, it drives the standalone card below
   Everything here is presentation only — the XP and rank the server granted are unchanged. */
/* Boss card content notice (photosensitivity on STROBE). Amber and boxed rather than styled
   like the rest of the card: this is a real-world safety notice, not flavour text, and it must
   not read as part of the fiction. Sits under the title so it is the last thing before play. */
.gnt-boss-warning {
  margin-top: 14px;
  padding: 8px 14px;
  border: 1px solid rgba(255, 183, 3, 0.55);
  border-radius: 4px;
  background: rgba(70, 48, 4, 0.55);
  color: #ffd98a;
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.35;
  letter-spacing: 0.01em;
  text-transform: none;
  max-width: 30rem;
  margin-left: auto;
  margin-right: auto;
}
.gnt-boss-warning.hidden { display: none; }

/* THE TIME KEEPER: the round clock while it is burning at the penalty rate. A pulse rather
   than a static tint — the whole point is that this is happening because of something the
   player is doing right now, and stops the instant they let go of the puck. */
@keyframes gntClockBurn {
  0%, 100% { transform: scale(1);    text-shadow: 0 0 10px rgba(255, 183, 3, 0.45); }
  50%      { transform: scale(1.07); text-shadow: 0 0 22px rgba(255, 183, 3, 0.85); }
}
.gnt-clock-burning { animation: gntClockBurn 0.42s ease-in-out infinite; }

/* ── Admin reward notice ──────────────────────────────────────────────────────
   Shown once when an admin grants credits with a reason attached (see REWARD_REASONS).
   Borrows the rank celebration's language — dim + blur behind, gold top rule, tabular
   counter — but unlike that one it DOES take pointer events: a celebration is something you
   watch, this is something you acknowledge, and the ack is what stops it reappearing.

   The banner art is the same file the menu event banner uses, so the reward reads as the
   pay-off for the thing that banner was advertising. */
.reward-notice {
  position: fixed;
  inset: 0;
  z-index: 3200;                  /* over the rank celebration (3000) — this one needs the click */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(4, 8, 14, 0.72);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  animation: rewardNoticeIn 0.28s ease both;
}
.reward-notice.hidden { display: none; }
.reward-notice.is-leaving { animation: rewardNoticeOut 0.28s ease both; }
@keyframes rewardNoticeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes rewardNoticeOut { from { opacity: 1; } to { opacity: 0; } }
.reward-notice-card {
  position: relative;
  width: min(440px, 100%);
  max-height: 100%;
  overflow-y: auto;
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--gold);
  border-radius: var(--radius);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.65);
  animation: rewardCardIn 0.42s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes rewardCardIn {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
/* Bleeds to the card edges under the gold rule — it is the hero, not an inline image. */
.reward-notice-banner {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 6;
  object-fit: cover;
  border-bottom: 1px solid var(--edge);
}
.reward-notice-banner.hidden { display: none; }
.reward-notice-body { padding: 20px 26px 24px; }
/* Kicker and title are a matched pair at the same size — the kicker names the occasion, the
   title is the thank-you, and shrinking the first to a caption buried the reason the card
   exists. Gold vs text colour carries the hierarchy instead of scale. */
.reward-notice-kicker {
  font-family: var(--font-display);
  font-size: 1.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gold);
  line-height: 1.15;
  margin-bottom: 2px;
}
.reward-notice-title {
  font-family: var(--font-display);
  font-size: 1.6rem;
  letter-spacing: 0.02em;
  color: var(--text);
  line-height: 1.15;
}
.reward-notice-blurb {
  margin-top: 8px;
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--muted);
}
.reward-notice-amount {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 4px;
  margin: 18px 0 4px;
  font-family: var(--font-data);
  color: var(--gold);
  text-shadow: 0 0 26px rgba(242, 193, 78, 0.35);
}
.reward-notice-plus  { font-size: 1.5rem; font-weight: 700; }
.reward-notice-value {
  font-size: 2.9rem;
  font-weight: 700;
  line-height: 1;
  /* tabular-nums so the count-up animation doesn't jitter the layout as digits change */
  font-variant-numeric: tabular-nums;
}
.reward-notice-unit {
  margin-left: 4px;
  font-size: 0.82rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.reward-notice-ok { margin-top: 18px; min-width: 160px; }

.rank-celebration {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 14, 0.55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  pointer-events: none;              /* never blocks the queue underneath */
  animation: rankCelebIn 0.25s ease both;
}
.rank-celebration.hidden { display: none; }
.rank-celebration.is-leaving { animation: rankCelebOut 0.4s ease both; }
@keyframes rankCelebIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes rankCelebOut { from { opacity: 1; } to { opacity: 0; } }
.rank-celebration-card {
  position: relative;
  min-width: 320px;
  padding: 26px 34px 28px;
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-top: 2px solid var(--gold);
  border-radius: var(--radius);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.6);
}
.rank-celebration-badge { line-height: 0; margin-bottom: 10px; }
.rank-celebration-badge img { width: 96px; height: 96px; object-fit: contain; }
.rank-celebration-label {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 1.35rem;
  letter-spacing: 0.02em;
  color: var(--xp-green);
}
.rank-celebration-xp {
  font-variant-numeric: tabular-nums;
  font-family: var(--font-data);
  font-size: 0.9rem;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 4px 0 12px;
}
.rank-celebration-track {
  height: 12px;
  border-radius: 999px;
  background: var(--rink-deep);
  border: 1px solid var(--edge);
  overflow: hidden;
}
.rank-celebration-fill {
  height: 100%;
  width: 0;
  /* No radius of its own: the animation scales this on X, which would squash a rounded end into
     an ellipse. The track clips it instead (overflow hidden + its own radius). */
  border-radius: 0;
  background: linear-gradient(90deg, var(--xp-green-deep) 0%, var(--xp-green) 100%);
  box-shadow: 0 0 12px rgba(110, 232, 154, 0.55);
}

/* The rising +XP / +credits numbers. Absolutely positioned so they can float free of the
   layout — a popup that reflows the card it sits on defeats the point. */
.rank-celebration-pops,
.identity-rank-pops {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 0;
  pointer-events: none;
}
.identity-rank { position: relative; }
.rank-pop {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  font-family: var(--font-data);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  white-space: nowrap;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.65);
  animation: rankPopRise calc(1.5s * var(--rank-anim-scale)) cubic-bezier(0.2, 0.7, 0.3, 1) both;
}
/* Anchored variant: sits immediately to the RIGHT of the number it belongs to, rather than
   floating over the middle of the card. Rises straight up — no X centring to undo. */
.rank-pop--anchored {
  left: 100%;
  top: 0;
  /* Clear of the digits — it overhangs the neighbouring tile on purpose, so it needs enough
     gap to read as a separate label rather than as more of the number. */
  margin-left: 16px;
  transform: none;
  white-space: nowrap;
  /* Lifts above the neighbouring stat tile, which it deliberately overhangs. */
  z-index: 6;
  font-size: 0.95rem;
  animation-name: rankPopRiseAnchored;
}
@keyframes rankPopRiseAnchored {
  0%   { opacity: 0; transform: translateY(6px)   scale(0.85); }
  14%  { opacity: 1; transform: translateY(-4px)  scale(1.06); }
  30%  {             transform: translateY(-10px) scale(1); }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-40px) scale(1); }
}

.rank-celebration-credits {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 10px;
  margin-top: 14px;
  font-family: var(--font-data);
  letter-spacing: 0.06em;
}
.rank-celebration-credits-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  color: var(--muted);
}
.rank-celebration-credits-value {
  position: relative;            /* anchor for the +N pop */
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}
/* Anchor role on the menu card's credits figure.
   inline-block matters: the credits tile is centre-aligned and a <dd> is block-level, so it
   spans the whole tile — anchoring a pop at left:100% of THAT put it outside the tile entirely,
   which is why nothing appeared. Shrinking the box to hug the digits puts left:100% immediately
   after the number, where it belongs. */
#identity-credits {
  position: relative;
  display: inline-block;
  font-variant-numeric: tabular-nums;
}
/* Holds the ticking digits while the celebration runs, so the +N pop beside them is a sibling
   rather than a child of the node being retyped. */
.credits-num { font-variant-numeric: tabular-nums; }

.rank-pop--xp      { color: var(--xp-green); }
.rank-pop--credits { color: var(--gold); }
@keyframes rankPopRise {
  0%   { opacity: 0; transform: translate(-50%, 6px)   scale(0.85); }
  14%  { opacity: 1; transform: translate(-50%, -6px)  scale(1.06); }
  30%  {             transform: translate(-50%, -14px) scale(1); }
  70%  { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -54px) scale(1); }
}

/* Radial flare behind the badge. League promotions only. */
.rank-burst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, rgba(255,240,190,0.95) 0%, rgba(242,193,78,0.55) 40%, rgba(242,193,78,0) 70%);
  animation: rankBurst calc(1.1s * var(--rank-anim-scale)) cubic-bezier(0.15, 0.7, 0.3, 1) both;
}
@keyframes rankBurst {
  0%   { opacity: 0;   transform: scale(0.3); }
  18%  { opacity: 1;   transform: scale(6); }
  100% { opacity: 0;   transform: scale(15); }
}

/* Rank-up flourish: the badge and name jump once as the new rung lands. */
.rank-levelup-pulse { animation: rankLevelPulse calc(0.85s * var(--rank-anim-scale)) cubic-bezier(0.2, 0.8, 0.3, 1) both; }
@keyframes rankLevelPulse {
  0%   { transform: scale(1);    filter: brightness(1); }
  22%  { transform: scale(1.22); filter: brightness(1.9); }
  55%  { transform: scale(0.97); filter: brightness(1.15); }
  100% { transform: scale(1);    filter: brightness(1); }
}
/* League promotion: the same jump, but further and held longer. */
.rank-levelup-pulse--big { animation: rankLevelPulseBig calc(1.25s * var(--rank-anim-scale)) cubic-bezier(0.2, 0.8, 0.3, 1) both; }
@keyframes rankLevelPulseBig {
  0%   { transform: scale(1)    rotate(0deg);   filter: brightness(1); }
  16%  { transform: scale(1.55) rotate(-4deg);  filter: brightness(2.6); }
  34%  { transform: scale(1.12) rotate(3deg);   filter: brightness(1.5); }
  52%  { transform: scale(1.3)  rotate(-1.5deg); filter: brightness(1.9); }
  100% { transform: scale(1)    rotate(0deg);   filter: brightness(1); }
}

/* The track flashes with it, so the eye is drawn to the reset rather than surprised by it. */
.rank-track-flash { animation: rankTrackFlash calc(0.7s * var(--rank-anim-scale)) ease both; }
@keyframes rankTrackFlash {
  0%   { box-shadow: 0 0 0 0 rgba(242, 193, 78, 0); }
  25%  { box-shadow: 0 0 18px 4px rgba(242, 193, 78, 0.85); }
  100% { box-shadow: 0 0 0 0 rgba(242, 193, 78, 0); }
}
/* Louder, longer version of the track flash for a league promotion. */
.rank-track-flash--big { animation: rankTrackFlashBig calc(1.1s * var(--rank-anim-scale)) ease both; }
@keyframes rankTrackFlashBig {
  0%   { box-shadow: 0 0 0 0 rgba(242, 193, 78, 0); }
  18%  { box-shadow: 0 0 34px 10px rgba(255, 233, 168, 0.95); }
  45%  { box-shadow: 0 0 20px 5px rgba(242, 193, 78, 0.6); }
  100% { box-shadow: 0 0 0 0 rgba(242, 193, 78, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .rank-pop, .rank-levelup-pulse, .rank-levelup-pulse--big,
  .rank-track-flash, .rank-track-flash--big, .rank-burst,
  .rank-celebration, .rank-celebration.is-leaving { animation: none; }
}

/* ── Design Studio ───────────────────────────────────────────────────────────
   Two stacked halves: a COMPOSER (what do I want) and a LOCKER (which do I keep).
   The composer leads because until you own a pass it is the only thing worth showing, and the
   locker's own 3D preview is the point of the screen — a skin is a thing you look at, so the
   model gets the space and the chrome stays out of its way. */
.store-studio {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-height: 0;
  overflow-y: auto;
  padding: 2px 4px 4px;
}

/* The pitch, shown before a pass is owned. Deliberately the loudest thing in the store:
   it is a new idea, and nobody goes looking for a feature they don't know exists. */
.studio-pitch {
  display: flex;
  gap: 22px;
  align-items: center;
  padding: 22px 26px;
  border-radius: var(--radius-lg);
  border: 1px solid rgba(242, 193, 78, 0.28);
  background:
    radial-gradient(120% 140% at 12% 0%, rgba(242, 193, 78, 0.13), transparent 62%),
    linear-gradient(160deg, var(--rink-hi), var(--rink-deep));
  box-shadow: var(--shadow-panel);
}
.studio-pitch-art {
  position: relative;
  flex: 0 0 96px;
  height: 96px;
  display: grid;
  place-items: center;
}
.studio-pitch-glow {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle, var(--gold-glow), transparent 68%);
  animation: studio-breathe 3.6s ease-in-out infinite;
}
.studio-pitch-icon {
  position: relative;
  font-size: 2.6rem;
  color: var(--gold);
  text-shadow: 0 0 22px var(--gold-glow);
}
@keyframes studio-breathe {
  0%, 100% { transform: scale(1);    opacity: 0.75; }
  50%      { transform: scale(1.14); opacity: 1; }
}
.studio-pitch-body { min-width: 0; }
.studio-pitch-body h3 {
  font-family: var(--font-display);
  letter-spacing: 1.5px;
  font-size: 1.7rem;
  margin: 2px 0 6px;
  color: var(--ice-bright);
}
.studio-pitch-text { color: var(--ice-dim); line-height: 1.5; margin: 0 0 14px; max-width: 60ch; }
.studio-pitch-text b { color: var(--ice); }
.studio-pitch-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.studio-buy-pass { font-size: 1.02rem; padding: 11px 22px; }
.studio-short { color: var(--ice-dim); font-size: 0.86rem; }

/* Composer */
.studio-composer {
  padding: 18px 20px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--edge);
  background: linear-gradient(160deg, var(--rink-hi), var(--rink));
}
.studio-composer-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}
/* The title eats the slack so the token count and the top-up button sit together on the right,
   rather than space-between spreading all three evenly across the row. */
.studio-composer-head h3 { flex: 1 1 auto; }
.studio-composer-head .studio-topup-btn { align-self: center; }
.studio-composer-head h3,
.studio-locker-head h3 {
  font-family: var(--font-display);
  letter-spacing: 1.4px;
  font-size: 1.25rem;
  margin: 0;
  color: var(--ice-bright);
}
.studio-attempts {
  font-family: var(--font-data);
  font-size: 0.95rem;
  color: var(--ice-dim);
  padding: 3px 11px;
  border-radius: 999px;
  border: 1px solid var(--edge-hi);
  background: var(--rink-deep);
  white-space: nowrap;
}
.studio-attempts b { color: var(--gold); font-size: 1.05rem; }

.studio-prompt {
  width: 100%;
  resize: vertical;
  min-height: 74px;
  padding: 11px 13px;
  border-radius: var(--radius);
  border: 1px solid var(--edge-hi);
  background: var(--rink-deep);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.45;
}
.studio-prompt:focus {
  outline: none;
  border-color: rgba(242, 193, 78, 0.55);
  box-shadow: 0 0 0 3px rgba(242, 193, 78, 0.14);
}
.studio-prompt::placeholder { color: rgba(125, 153, 173, 0.7); }

/* Starter prompts. A blank box is the hardest part of this feature — these exist so the
   first design is one click away rather than a staring contest. */
.studio-ideas { display: flex; flex-wrap: wrap; gap: 7px; margin: 10px 0 14px; }
.studio-idea {
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--ice-cool);
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--edge);
  background: rgba(20, 30, 44, 0.7);
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
/* Colour only, same reason as .studio-card — these chips are small, so a 1px lift put the
   cursor outside them almost immediately and flickered. */
.studio-idea:hover {
  color: var(--ice-bright);
  border-color: var(--edge-hi);
  background: rgba(30, 44, 62, 0.9);
}

.studio-composer-foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.studio-kind { display: inline-flex; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--edge-hi); }
.studio-kind-btn {
  font-family: var(--font-data);
  letter-spacing: 0.6px;
  font-size: 0.95rem;
  padding: 8px 18px;
  color: var(--ice-dim);
  background: var(--rink-deep);
  border: 0;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.studio-kind-btn.active { background: var(--rink-hi); color: var(--ice-bright); }
.studio-generate { min-width: 178px; }
.studio-fineprint { color: var(--ice-dim); font-size: 0.83rem; margin: 12px 0 0; }

/* Locker */
.studio-locker {
  padding: 18px 20px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--edge);
  background: linear-gradient(160deg, var(--rink-hi), var(--rink));
  min-height: 0;
}
.studio-locker-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 12px; }
.studio-locker-count { font-family: var(--font-data); color: var(--ice-dim); font-size: 0.92rem; }
/* Grid beside preview, same relationship the skin tabs use — the list is for scanning,
   the panel is for deciding. */
.studio-locker-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(340px, 46%); gap: 18px; align-items: start; }
.studio-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 11px;
  max-height: 46vh;
  overflow-y: auto;
  padding-right: 4px;
}
.studio-card {
  display: flex;
  flex-direction: column;
  padding: 0;
  border-radius: var(--radius);
  border: 1px solid var(--edge);
  background: var(--rink-deep);
  cursor: pointer;
  overflow: hidden;
  text-align: left;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
/* Hover changes COLOUR only, never geometry.
   This used to lift the card 2px, which oscillates: the card moves out from under the cursor,
   :hover drops, it falls back, :hover fires again. In a tight grid the pointer sits near a card
   edge most of the time, so it read as a permanent jitter rather than as feedback. */
.studio-card:hover:not(:disabled) { border-color: var(--edge-hi); background: var(--rink-hi); }
.studio-card.selected {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px var(--gold), 0 10px 26px -14px var(--gold-glow);
}
.studio-card:disabled { cursor: default; opacity: 0.85; }
.studio-card-art {
  position: relative;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 50% 38%, rgba(143, 191, 214, 0.12), transparent 70%);
}
.studio-thumb { width: 100%; height: 100%; display: block; }
.studio-card-x { font-size: 2.4rem; color: rgba(226, 69, 63, 0.55); line-height: 1; }
.studio-card-meta { padding: 8px 9px 9px; display: flex; flex-direction: column; gap: 6px; }
.studio-card-prompt {
  font-size: 0.79rem;
  line-height: 1.32;
  color: var(--ice-cool);
  /* Two lines then ellipsis: enough to recognise which design this is, never enough to
     make the cards different heights. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.studio-badge {
  align-self: flex-start;
  font-family: var(--font-data);
  font-size: 0.78rem;
  letter-spacing: 0.4px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.studio-badge--ready  { color: var(--gold); border-color: rgba(242, 193, 78, 0.4); background: rgba(242, 193, 78, 0.08); }
.studio-badge--owned  { color: var(--xp-green); border-color: rgba(110, 232, 154, 0.4); background: rgba(110, 232, 154, 0.09); }
.studio-badge--free   { color: var(--xp-green); border-color: rgba(110, 232, 154, 0.55); background: rgba(110, 232, 154, 0.14); font-weight: 700; letter-spacing: 0.8px; }
.studio-badge--failed { color: #e8837e; border-color: rgba(226, 69, 63, 0.4); background: rgba(226, 69, 63, 0.08); }
.studio-badge--working { color: var(--ice-cool); border-color: var(--edge-hi); display: inline-flex; align-items: center; gap: 6px; }
.studio-badge--working i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold);
  animation: studio-pulse 1.1s ease-in-out infinite;
}
@keyframes studio-pulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } }

.studio-spinner { display: flex; gap: 5px; }
.studio-spinner span {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ice-cool);
  animation: studio-bounce 1.05s ease-in-out infinite;
}
.studio-spinner span:nth-child(2) { animation-delay: 0.14s; }
.studio-spinner span:nth-child(3) { animation-delay: 0.28s; }
@keyframes studio-bounce {
  0%, 80%, 100% { transform: translateY(0);    opacity: 0.35; }
  40%           { transform: translateY(-6px); opacity: 1; }
}

/* Preview panel */
.studio-preview {
  position: sticky;
  top: 0;
  border-radius: var(--radius);
  border: 1px solid var(--edge-hi);
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(143, 191, 214, 0.10), transparent 65%),
    var(--rink-deep);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.studio-preview-model {
  width: 100%;
  aspect-ratio: 1;
  display: block;
  border-radius: var(--radius);
  touch-action: none;   /* the canvas owns drag; without this a touch drag scrolls the page */
  cursor: grab;
}
.studio-preview-model:active { cursor: grabbing; }
/* Says the model turns without needing a legend. Fades once you start dragging. */
.studio-preview-hint {
  text-align: center;
  font-size: 0.78rem;
  color: var(--ice-dim);
  letter-spacing: 0.4px;
  margin: -4px 0 0;
  transition: opacity 0.3s;
}
.studio-preview.dragged .studio-preview-hint { opacity: 0; }
.studio-preview-empty { color: var(--ice-dim); text-align: center; padding: 48px 12px; font-size: 0.92rem; }
.studio-preview-prompt { color: var(--ice); font-size: 0.94rem; line-height: 1.45; margin: 0; }
.studio-preview-kind { color: var(--ice-dim); font-family: var(--font-data); font-size: 0.86rem; margin: 4px 0 0; }
.studio-preview-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.studio-preview-actions .btn-gold { flex: 1 1 auto; }
.studio-delete { color: var(--ice-dim); }
.studio-delete:hover { color: #e8837e; }
.studio-owned-note { color: var(--xp-green); font-size: 0.9rem; }

.studio-closed { text-align: center; padding: 56px 20px; color: var(--ice-dim); }
.studio-closed-icon { font-size: 2.6rem; margin-bottom: 10px; }
.studio-closed h3 { font-family: var(--font-display); letter-spacing: 1.2px; color: var(--ice-bright); margin: 0 0 6px; }

/* The tab itself, marked out because it is the one thing in the store that makes something
   new rather than selling something that already exists. */
.stats-tab--studio.active,
.stats-tab--studio:hover { color: var(--gold); }

/* Narrow / portrait: the preview goes under the grid rather than squeezing both. */
@media (max-width: 900px), (orientation: portrait) {
  .studio-locker-body { grid-template-columns: minmax(0, 1fr); }
  .studio-preview { position: static; }
  .studio-preview-model { max-width: 340px; margin: 0 auto; }
  .studio-grid { max-height: none; }
  .studio-pitch { flex-direction: column; text-align: center; }
  .studio-pitch-actions { justify-content: center; }
}

/* Locked boost trails: visible so the modal doubles as a shop window, but clearly not yours.
   Kept in the grid rather than hidden — this is where you go to think about your trail, so it
   is also the best place to find out a better one exists. */
/* Dimmed, but still a button: clicking one goes to the Store rather than doing nothing, so the
   cursor must not say not-allowed. */
.boost-skin-card--locked { opacity: 0.5; position: relative; }
.boost-skin-card--locked:hover { opacity: 0.85; border-color: var(--gold); background: rgba(240, 192, 64, 0.05); }
.boost-skin-lock {
  font-family: var(--font-data);
  font-size: 0.72rem;
  letter-spacing: 0.5px;
  color: var(--gold);
  text-align: center;
  margin-top: 2px;
}
/* Granted, never sold. Red rather than the gold used for "buy this in the store",
   matching .store-exclusive-btn — the point is that no amount of credits gets it. */
.boost-skin-lock--excl {
  color: #ff9b93;
  text-transform: uppercase;
  font-weight: 700;
}

/* Studio: renaming a design in place. */
.studio-name-row { display: flex; align-items: baseline; gap: 10px; }
.studio-name-row .studio-preview-prompt { flex: 1; min-width: 0; }
.studio-rename-btn {
  flex: 0 0 auto;
  font-family: var(--font-data);
  font-size: 0.78rem;
  letter-spacing: 0.5px;
  color: var(--ice-dim);
  background: none;
  border: 0;
  padding: 2px 4px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.studio-rename-btn:hover { color: var(--ice-bright); }
.studio-name-input {
  width: 100%;
  padding: 5px 8px;
  border-radius: var(--radius);
  border: 1px solid rgba(242, 193, 78, 0.55);
  background: var(--rink-deep);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 0.94rem;
}
.studio-name-input:focus { outline: none; box-shadow: 0 0 0 3px rgba(242, 193, 78, 0.14); }
/* The prompt, kept visible under a renamed design — it is the only record of how it was made. */
.studio-preview-origin {
  color: var(--ice-dim);
  font-size: 0.8rem;
  font-style: italic;
  line-height: 1.4;
  margin: 5px 0 0;
}

/* Top up without running dry first. Deliberately quiet — the composer's job is writing a
   prompt, not selling; this only has to be findable, not persuasive. The loud pitch still
   takes over the whole panel once the balance hits zero. */
.studio-topup-btn {
  font-family: var(--font-data);
  font-size: 0.82rem;
  letter-spacing: 0.4px;
  color: var(--gold);
  background: transparent;
  border: 1px solid rgba(242, 193, 78, 0.4);
  border-radius: 999px;
  padding: 3px 11px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s;
}
.studio-topup-btn:hover:not(:disabled) { background: rgba(242, 193, 78, 0.12); border-color: var(--gold); }
.studio-topup-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Studio demo reel ────────────────────────────────────────────────────────
   A ~10s loop shown only to players who don't own a pass. Three phases driven by a single
   data-phase attribute on the container, so the JS sets one value and CSS does the rest —
   no class juggling across half a dozen elements.
   Phases: type -> forge -> reveal. */
.studio-demo {
  margin-top: 14px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--edge);
  background:
    radial-gradient(90% 120% at 80% 0%, rgba(242, 122, 46, 0.10), transparent 60%),
    linear-gradient(160deg, var(--rink-deep), #0a0f18);
  overflow: hidden;
}
.studio-demo-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  font-family: var(--font-data);
  font-size: 0.76rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ice-dim);
  border-bottom: 1px solid var(--edge);
  background: rgba(0, 0, 0, 0.25);
}
.studio-demo-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #e2453f;
  box-shadow: 0 0 8px rgba(226, 69, 63, 0.9);
  animation: studio-demo-rec 1.6s ease-in-out infinite;
}
@keyframes studio-demo-rec { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

.studio-demo-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 220px;
  gap: 18px;
  padding: 16px;
  align-items: center;
}
.studio-demo-left { min-width: 0; }

/* The fake prompt box. Deliberately styled like the real composer above it, so the demo reads
   as "this is the thing you'll be using" rather than as generic marketing art. */
.studio-demo-box {
  min-height: 74px;
  padding: 11px 13px;
  border-radius: var(--radius);
  border: 1px solid var(--edge-hi);
  background: rgba(0, 0, 0, 0.35);
  font-family: var(--font-body);
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ice);
}
.studio-demo-caret {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 1px;
  vertical-align: -2px;
  background: var(--gold);
  animation: studio-demo-blink 1s steps(1) infinite;
}
@keyframes studio-demo-blink { 0%,50% { opacity: 1; } 51%,100% { opacity: 0; } }
/* The caret belongs to the typing phase only. */
.studio-demo[data-phase="forge"] .studio-demo-caret,
.studio-demo[data-phase="reveal"] .studio-demo-caret { opacity: 0; animation: none; }

/* One status line, three messages, only one visible per phase. Sits ABOVE the prompt box and is
   deliberately the largest text in the panel — it is the narration, and the demo is silent. */
.studio-demo-status {
  margin: 0 0 10px;
  height: 30px;
  position: relative;
  font-family: var(--font-display);
  font-size: 1.35rem;
  letter-spacing: 1px;
}
.studio-demo-status > span {
  position: absolute; inset: 0;
  display: flex; align-items: center; gap: 9px;
  opacity: 0; transition: opacity 0.35s;
}
/* Typing AND clicking share the first caption — nothing new has happened until the press. */
.studio-demo[data-phase="type"]  .studio-demo-status-type,
.studio-demo[data-phase="click"] .studio-demo-status-type { opacity: 1; color: var(--ice); }
/* Forging holds through the start of the reveal, until the armor has actually landed. */
.studio-demo[data-phase="forge"] .studio-demo-status-forge,
.studio-demo[data-phase="reveal"]:not([data-ready="1"]) .studio-demo-status-forge { opacity: 1; color: var(--gold); }
.studio-demo[data-phase="reveal"][data-ready="1"] .studio-demo-status-reveal { opacity: 1; color: var(--xp-green); }
.studio-demo-status-forge i {
  width: 7px; height: 7px; border-radius: 50%; background: currentColor;
  animation: studio-demo-bounce 1s ease-in-out infinite;
}
.studio-demo-status-forge i:nth-child(2) { animation-delay: 0.13s; }
.studio-demo-status-forge i:nth-child(3) { animation-delay: 0.26s; }
@keyframes studio-demo-bounce {
  0%,80%,100% { transform: translateY(0); opacity: 0.35; }
  40%         { transform: translateY(-5px); opacity: 1; }
}

/* The stage. Hidden until the reveal, then it drops in and settles. */
.studio-demo-stage { position: relative; aspect-ratio: 1; display: grid; place-items: center; }
.studio-demo-canvas {
  width: 100%; height: 100%;
  display: block;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 0.5s ease, transform 0.7s cubic-bezier(0.2, 0.9, 0.25, 1);
}
.studio-demo[data-phase="reveal"] .studio-demo-canvas { opacity: 1; transform: scale(1); }

/* Forge phase: a scan line sweeps the empty stage, so something is clearly happening. */
.studio-demo-scan {
  position: absolute; left: 8%; right: 8%; height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
  box-shadow: 0 0 14px 2px var(--gold-glow);
  opacity: 0;
}
.studio-demo[data-phase="forge"] .studio-demo-scan {
  opacity: 1;
  animation: studio-demo-scan 1.9s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
@keyframes studio-demo-scan {
  0%   { top: 14%; opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { top: 86%; opacity: 0; }
}

/* A warm bloom behind the model — sells "molten" without touching the render. */
.studio-demo-glow {
  position: absolute; inset: 12%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(242, 122, 46, 0.42), transparent 66%);
  opacity: 0;
  transition: opacity 0.6s;
  pointer-events: none;
  z-index: -1;
}
.studio-demo[data-phase="forge"]  .studio-demo-glow { opacity: 0.5; }
.studio-demo[data-phase="reveal"] .studio-demo-glow { opacity: 1; animation: studio-demo-pulse 2.6s ease-in-out infinite; }
@keyframes studio-demo-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.09); } }

@media (max-width: 900px), (orientation: portrait) {
  .studio-demo-body { grid-template-columns: minmax(0, 1fr); }
  .studio-demo-stage { max-width: 240px; margin: 0 auto; }
}
/* Respect a reduced-motion preference: the loop still advances, it just stops moving things. */
@media (prefers-reduced-motion: reduce) {
  .studio-demo-dot, .studio-demo-caret, .studio-demo-scan,
  .studio-demo-glow, .studio-demo-status-forge i { animation: none !important; }
  .studio-demo-canvas { transition: opacity 0.3s; transform: none; }
}

/* ── Studio demo: the fake Generate button and the cursor that presses it ─────
   The click is what makes the demo legible: without it the design appears on its own and the
   viewer never learns there is a button to press. */
.studio-demo-left { position: relative; }   /* the cursor is positioned against this */
.studio-demo-action { margin-top: 10px; }
/* Solid gold from the start, matching the REAL Generate button in the composer above — the demo
   should look like the thing it is demonstrating, not a dimmer variant of it.
   Label runs Generate design -> Generating… -> Done!, set in studioDemoBtnLabel(). */
.studio-demo-btn {
  display: inline-block;
  min-width: 148px;              /* the three labels differ in width; without this it jumps */
  text-align: center;
  font-family: var(--font-data);
  font-size: 0.92rem;
  letter-spacing: 0.6px;
  padding: 7px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--gold);
  background: var(--gold);
  color: #201704;
  font-weight: 700;
  transition: transform 0.12s, background 0.25s, border-color 0.25s, color 0.25s, box-shadow 0.2s;
}
/* The press: scale and a ring only — the colour is already gold, so the click reads as motion. */
.studio-demo[data-phase="click"] .studio-demo-btn {
  animation: studio-demo-press 1.3s ease-out 1 both;
}
@keyframes studio-demo-press {
  0%, 62% { transform: scale(1);    box-shadow: none; }
  70%     { transform: scale(0.93); box-shadow: 0 0 0 7px rgba(242,193,78,0.28); }
  100%    { transform: scale(1);    box-shadow: 0 0 0 16px rgba(242,193,78,0); }
}
/* Working: dimmed gold with a slow pulse, so "Generating…" looks busy rather than finished. */
.studio-demo[data-pressed="1"]:not([data-ready="1"]) .studio-demo-btn {
  background: rgba(242, 193, 78, 0.55);
  border-color: rgba(242, 193, 78, 0.7);
  animation: studio-demo-working 1.4s ease-in-out infinite;
}
@keyframes studio-demo-working { 0%,100% { opacity: 0.75; } 50% { opacity: 1; } }
/* Done: green, and it stops moving. */
.studio-demo[data-ready="1"] .studio-demo-btn {
  background: var(--xp-green);
  border-color: var(--xp-green);
  color: #06210f;
  animation: none;
  box-shadow: 0 0 18px -4px rgba(110, 232, 154, 0.7);
}

.studio-demo-cursor {
  position: absolute;
  width: 30px; height: 30px;      /* ~2x a real pointer — at true size the move is invisible */
  left: 62%; top: 34%;
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 3px 6px rgba(0,0,0,0.6));
  z-index: 3;
}
/* Travels from over the prompt box down to the button, then presses. */
.studio-demo[data-phase="click"] .studio-demo-cursor {
  animation: studio-demo-cursor-move 1.3s cubic-bezier(0.4, 0, 0.2, 1) 1 both;
}
@keyframes studio-demo-cursor-move {
  0%   { opacity: 0; left: 62%; top: 34%; transform: scale(1); }
  12%  { opacity: 1; }
  62%  { opacity: 1; left: 13%; top: calc(100% - 34px); transform: scale(1); }
  70%  { opacity: 1; left: 13%; top: calc(100% - 34px); transform: scale(0.82); }
  82%  { opacity: 1; left: 13%; top: calc(100% - 34px); transform: scale(1); }
  100% { opacity: 0; left: 13%; top: calc(100% - 34px); transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .studio-demo-cursor { animation: none !important; opacity: 0 !important; }
  .studio-demo-btn { animation: none !important; }
}

/* Speed claim in the demo header. Pushed to the right so the header reads as
   "what this is" on the left, "why you want it" on the right. */
.studio-demo-speed {
  margin-left: auto;
  font-family: var(--font-data);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  color: var(--ice-dim);
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid rgba(110, 232, 154, 0.35);
  background: rgba(110, 232, 154, 0.08);
}
.studio-demo-speed b { color: var(--xp-green); font-weight: 700; }

/* ── XP boost cards ──────────────────────────────────────────────────────────
   Real art with the multiplier as a DOM badge on top. The badge is not baked into the image
   because generated art cannot be trusted with text, and this way repricing a boost never
   means regenerating an asset. */
.store-card-thumb--xp {
  position: relative;
  background:
    radial-gradient(70% 70% at 50% 45%, rgba(242, 193, 78, 0.13), transparent 70%),
    var(--rink-deep);
  overflow: hidden;
}
.store-card-thumb--xp .store-thumb-img {
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 6%;
  /* Grounds the art on the card instead of letting it float on a flat panel. */
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.55));
  transition: transform 0.25s ease;
}
.store-card:hover .store-card-thumb--xp .store-thumb-img { transform: scale(1.05); }
/* Team boost is blue, so its glow must be too — a gold wash behind it fought the art.
   Flagged with its own class rather than :has(), which is newer than it needs to be here. */
.store-card-thumb--xp-team {
  background:
    radial-gradient(70% 70% at 50% 45%, rgba(62, 134, 232, 0.16), transparent 70%),
    var(--rink-deep);
}

.xp-boost-badge {
  position: absolute;
  top: 7px; left: 7px;
  z-index: 2;
  font-family: var(--font-display);
  font-size: 1.15rem;
  line-height: 1;
  letter-spacing: 0.5px;
  padding: 4px 9px 3px;
  border-radius: var(--radius);
  color: #201704;
  background: var(--gold);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  pointer-events: none;
}
.xp-boost-badge--team {
  font-size: 0.78rem;
  padding: 5px 8px 4px;
  color: #04121f;
  background: var(--blue);
  letter-spacing: 1.2px;
}

/* ── Bundles: three wide cards filling the row ────────────────────────────────
   A FIXED three columns, not auto-fit with a minimum width. A minimum is a guess about the
   window, and it guessed wrong in the desktop app — at 300px only two fitted and the third
   dropped to its own row. Three bundles, three columns, whatever the width. */
.store-browse--wide .store-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
/* Bigger art to match the bigger card — the contents tiles are the reason to look. */
.store-browse--wide .store-card-thumb--bundle { gap: 8px; padding: 10px; }
.store-browse--wide .store-card { padding: 14px; }
.store-browse--wide .store-card-name { font-size: 1.05rem; }
.store-browse--wide .store-card-meta { font-size: 0.86rem; }
/* Only below tablet width does three become unreadable — step down explicitly. */
@media (max-width: 760px) {
  .store-browse--wide .store-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
@media (max-width: 480px) {
  .store-browse--wide .store-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── Intermission announcer ──────────────────────────────────────────────────
   Sits under the break subtitle. Fades in rather than appearing, because it lands a
   second or two after the overlay and a hard pop reads as a glitch. */
.announcer-line {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  max-width: 720px;
  margin: 10px auto 4px;
  padding: 10px 16px;
  border-radius: 10px;
  background: rgba(10, 24, 38, 0.72);
  border: 1px solid rgba(63, 224, 224, 0.28);
  color: #eaf4fb;
  font-size: 15px;
  line-height: 1.45;
  font-style: italic;
  text-align: left;
  animation: announcer-in .45s ease-out both;
}
.announcer-mic { font-style: normal; opacity: .85; flex: 0 0 auto; }
@keyframes announcer-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (max-width: 760px) { .announcer-line { font-size: 13px; padding: 8px 12px; } }

/* ══ TEMPORARY DEV TEST PANEL (key 1) ══
   Remove with the markup in index.html and the handlers in game.js/server.js. Docked
   bottom-left so it never covers the net, the HUD or the boss card. */
.dev-test-panel {
  position: fixed;
  left: 14px; bottom: 14px;
  z-index: 70;
  width: 272px;
  padding: 12px 14px 14px;
  border-radius: 12px;
  border: 1px solid rgba(120, 200, 255, 0.45);
  background: rgba(6, 9, 14, 0.96);
  box-shadow: 0 10px 34px -8px rgba(0, 0, 0, 0.9);
  font-family: 'Barlow', 'Segoe UI', sans-serif;
  color: #dbe6f4;
  pointer-events: auto;
  user-select: none;
}
.dev-test-panel.hidden { display: none; }
.dev-test-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  font-size: 0.72rem; font-weight: 900; letter-spacing: 0.18em; color: #78c8ff;
  padding-bottom: 8px; margin-bottom: 10px;
  border-bottom: 1px solid rgba(120, 200, 255, 0.22);
}
.dev-test-hint { font-size: 0.62rem; font-weight: 600; letter-spacing: 0.04em; opacity: 0.5; }
.dev-test-hint b { color: #78c8ff; }
.dev-test-check { display: flex; align-items: center; gap: 9px; font-size: 0.82rem; font-weight: 700; cursor: pointer; }
.dev-test-check input { width: 15px; height: 15px; accent-color: #78c8ff; cursor: pointer; }
.dev-test-check i { opacity: 0.45; font-style: normal; font-weight: 600; }
.dev-test-sep {
  margin: 13px 0 9px; font-size: 0.6rem; font-weight: 900; letter-spacing: 0.16em;
  color: #8fa6c8; border-top: 1px solid rgba(255, 255, 255, 0.08); padding-top: 11px;
}
.dev-test-sep i { opacity: 0.5; font-style: normal; letter-spacing: 0.04em; }
.dev-test-row { margin-bottom: 11px; }
.dev-test-lbl { display: flex; justify-content: space-between; gap: 8px; font-size: 0.76rem; font-weight: 600; margin-bottom: 5px; }
.dev-test-lbl b { color: #78c8ff; font-variant-numeric: tabular-nums; }
.dev-test-sub { font-size: 0.62rem; opacity: 0.42; margin-top: 3px; }
.dev-test-panel input[type="range"] { width: 100%; accent-color: #78c8ff; cursor: pointer; }
.dev-test-copy {
  width: 100%; margin-top: 3px; padding: 7px 10px;
  border-radius: 7px; border: 1px solid rgba(120, 200, 255, 0.5);
  background: rgba(120, 200, 255, 0.14); color: #cfe8ff;
  font-family: inherit; font-size: 0.76rem; font-weight: 800; letter-spacing: 0.06em;
  cursor: pointer;
}
.dev-test-copy:hover { background: rgba(120, 200, 255, 0.24); }
.dev-test-note { margin-top: 8px; font-size: 0.62rem; opacity: 0.45; line-height: 1.35; }
.dev-test-bosses {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px 8px;
  font-size: 0.7rem; opacity: 0.75;
}
.dev-test-bosses b {
  display: inline-block; min-width: 1.1em; margin-right: 4px;
  color: #78c8ff; font-weight: 900;
}
/* Jump-to-round: number field + Go, on one line. */
.dev-test-jump { display: flex; gap: 6px; align-items: stretch; }
.dev-test-jump input {
  flex: 1 1 auto; min-width: 0; padding: 6px 8px;
  border-radius: 7px; border: 1px solid rgba(120, 200, 255, 0.35);
  background: rgba(8, 14, 22, 0.85); color: #eaf4ff;
  font-family: inherit; font-size: 0.86rem; font-weight: 800; text-align: center;
}
.dev-test-jump input:focus { outline: 1px solid #78c8ff; outline-offset: 0; }
.dev-test-jump .dev-test-copy { width: auto; flex: 0 0 auto; margin-top: 0; padding: 6px 14px; }
/* Preset chips reuse the boss grid, but they are clickable. */
#dev-jump-presets span {
  cursor: pointer; text-align: center; padding: 3px 0; border-radius: 5px;
  border: 1px solid rgba(120, 200, 255, 0.28); color: #cfe8ff; font-weight: 800;
}
#dev-jump-presets span:hover { background: rgba(120, 200, 255, 0.22); }

/* ── Leaderboard tabs ──
   Ranked and Gauntlet rank different things — per-region ELO vs a worldwide arcade run — so
   they get equal billing side by side instead of one sitting under the other. */
.lb-tabs {
  display: flex;
  gap: 6px;
  margin: 0 0 14px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.10);
}
.lb-tab {
  appearance: none;
  border: none;
  background: transparent;
  color: #8b9bb0;
  font-family: inherit;
  font-size: 0.86rem;
  font-weight: 800;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 9px 18px;
  cursor: pointer;
  border-radius: 8px 8px 0 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.lb-tab:hover { color: #cdd9e5; background: rgba(255, 255, 255, 0.04); }
.lb-tab.active {
  color: #ffb340;
  border-bottom-color: #ffb340;
  background: linear-gradient(180deg, rgba(255, 179, 64, 0.10), transparent);
}
/* Controller/keyboard focus has to be visible on a tab strip or nav feels blind. */
.lb-tab:focus-visible { outline: 2px solid #78c8ff; outline-offset: -2px; }
.lb-panel.hidden { display: none; }

/* ── Menu event banner ──
   Same fixed top strip as the iOS / standalone banners, but it is a LINK: the whole bar is
   the call to action, with dismiss carved out of it. */
.top-banner--event {
  background: linear-gradient(90deg, rgba(88, 101, 242, 0.22), rgba(12, 16, 24, 0.96) 45%);
  border-bottom: 1px solid rgba(88, 101, 242, 0.55);
  color: #e9eef6;
  text-decoration: none;
  cursor: pointer;
}
.top-banner--event:hover { background: linear-gradient(90deg, rgba(88, 101, 242, 0.32), rgba(12, 16, 24, 0.96) 45%); }
.event-banner-img {
  /* 4:1 art, so height x4 is the natural width — big enough that the two skaters actually
     read as figures rather than a smudge, which is the whole reason it is here. */
  height: 64px;
  width: auto;
  max-width: 256px;
  object-fit: cover;
  border-radius: 5px;
  flex: 0 0 auto;
}
/* The countdown is the part people re-read, so it gets the emphasis and tabular figures
   (proportional digits make a ticking clock jitter as the numbers change width). */
.event-banner-count {
  margin-left: 8px;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  color: #ffb340;
  letter-spacing: 0.02em;
}
.event-banner-count:empty { display: none; }
.event-banner-count::before { content: "·"; margin-right: 8px; color: rgba(233, 238, 246, 0.35); font-weight: 400; }
/* "Check-in opens in" — names what the clock is counting to, quieter than the time itself. */
.event-banner-count-label { font-weight: 600; color: rgba(233, 238, 246, 0.8); letter-spacing: 0; }
.event-banner-cta {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: #5865f2;
  color: #fff;
  border-radius: 6px;
  padding: 5px 12px;
  font-weight: 800;
  white-space: nowrap;
}
.top-banner--event:hover .event-banner-cta { background: #4752c4; }
.event-banner-icon { width: 17px; height: 17px; fill: currentColor; flex: 0 0 auto; }
/* Inside the last hour (and once live) the whole bar pulses — the one moment it should
   interrupt someone rather than sit there politely. */
.top-banner--event-urgent {
  border-bottom-color: #ffb340;
  animation: event-banner-pulse 2s ease-in-out infinite;
}
.top-banner--event-urgent .event-banner-count { color: #fff; }
@keyframes event-banner-pulse {
  0%, 100% { background: linear-gradient(90deg, rgba(255, 179, 64, 0.20), rgba(12, 16, 24, 0.96) 45%); }
  50%      { background: linear-gradient(90deg, rgba(255, 179, 64, 0.38), rgba(12, 16, 24, 0.96) 45%); }
}
@media (max-width: 620px) {
  .event-banner-img { display: none; }
}

/* Gauntlet board carries a third number now (round · checkpoints · lives), so it needs its
   own track list — the shared .leaderboard-row grid only declares five columns. */
.gauntlet-lb-row,
#gauntlet-lb-rows .leaderboard-row {
  /* One column fewer since the checkpoint count came out — see gauntletSplitRecord. */
  grid-template-columns: 58px 1.4em 1fr 88px 68px;
}
#gauntlet-lb-rows .leaderboard-row .lb-games,
.gauntlet-lb-row .lb-games { text-align: right; }

/* THE STATUE waking. A vignette pulse rather than a full-screen wash: the flash must not
   hide the goalie at the exact instant the player needs to react to him. */
.gnt-wake-flash {
  position: fixed;
  inset: 0;
  z-index: 2840;
  pointer-events: none;
  background: radial-gradient(120% 100% at 50% 50%, transparent 34%, rgba(190, 24, 24, 0.62) 100%);
  opacity: 0;
}
.gnt-wake-flash.hidden { display: none; }
.gnt-wake-flash.gnt-wake-in { animation: gntWake 0.9s cubic-bezier(0.2, 0.9, 0.3, 1) both; }
@keyframes gntWake {
  0%   { opacity: 0; transform: scale(1.16); }
  14%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}

/* ── Gauntlet checkpoint flash ─────────────────────────────────────────────────
   The only thing in the mode that banks progress, so it gets the biggest type on screen.
   Green rather than blue: goal-blue is already the colour of the banners that tell you a
   life just went, and this is the opposite news. Reparented to <body> on show — see
   showCheckpointFlash — because #game-content is transformed in fullscreen. */
.gnt-cp-flash {
  position: fixed;
  inset: 0;
  z-index: 2850;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Upper third, NOT centre. .goal-banner is pinned at top:50%, and the boss-down banner
     ("<BOSS> DOWN") is still on screen when the checkpoint fires 900ms later — dead centre
     put the two on top of each other. This clears the HUD above and the banner below. */
  justify-content: flex-start;
  padding-top: 15vh;
  pointer-events: none;
  font-family: var(--font-display);
  text-align: center;
  /* Soft pool of shade behind the word. The rink is white, and without this the green
     loses its glow against the ice — but a full scrim would black out the celebration
     it is announcing. Follows the word up. */
  background: radial-gradient(44% 20% at 50% 23%, rgba(2, 10, 6, 0.58), transparent 72%);
}
.gnt-cp-flash.hidden { display: none; }
.gnt-cp-word {
  font-size: clamp(2.8rem, 9vw, 6.5rem);
  font-weight: 900;
  letter-spacing: 0.08em;
  line-height: 1;
  color: #6cf59b;
  text-shadow:
    0 0 24px rgba(60, 226, 130, 0.75),
    0 0 70px rgba(60, 226, 130, 0.4),
    0 4px 14px rgba(0, 0, 0, 0.85);
}
/* The refill line. Deliberately much smaller and a beat behind the word: CHECKPOINT is the
   headline, the refill is the consequence — and on a clean run the consequence is the part
   that lets you keep going, so it has to be read rather than merely seen. */
.gnt-cp-sub {
  margin-top: 6px;
  font-size: clamp(0.85rem, 2.1vw, 1.5rem);
  font-weight: 800;
  letter-spacing: 0.24em;
  color: #d8ffe8;
  text-shadow: 0 0 18px rgba(60, 226, 130, 0.6), 0 3px 10px rgba(0, 0, 0, 0.85);
}
.gnt-cp-sub.hidden { display: none; }
.gnt-cp-flash.gnt-cp-in .gnt-cp-word { animation: gntCpWord 2.4s cubic-bezier(0.16, 1, 0.3, 1) both; }
.gnt-cp-flash.gnt-cp-in .gnt-cp-sub { animation: gntCpSub 2.4s cubic-bezier(0.16, 1, 0.3, 1) both; }
@keyframes gntCpSub {
  0%   { opacity: 0; transform: translateY(6px); }
  16%  { opacity: 0; transform: translateY(6px); }
  30%  { opacity: 1; transform: none; }
  78%  { opacity: 1; transform: none; }
  100% { opacity: 0; }
}
@keyframes gntCpWord {
  0%   { opacity: 0; transform: scale(1.5); }
  12%  { opacity: 1; transform: scale(1); }
  78%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.06); }
}

/* ── Run-over choices ──────────────────────────────────────────────────────────
   Three outcomes, three colours, ordered by how much of the run they keep: continue
   (green, keeps everything), restart (yellow, keeps nothing but the lobby), quit (red). */
.gnt-actions { flex-wrap: wrap; }
.gnt-btn-continue,
.gnt-btn-restart,
.gnt-btn-quit {
  font-weight: 800;
  letter-spacing: 0.04em;
  border-width: 2px;
  border-style: solid;
}
.gnt-btn-continue {
  color: #06210f;
  background: linear-gradient(180deg, #63e58e, #33bf68);
  border-color: #8df5ae;
  box-shadow: 0 0 18px rgba(70, 220, 125, 0.4);
}
.gnt-btn-continue:hover { background: linear-gradient(180deg, #78ef9f, #3ccd74); }
.gnt-btn-continue.hidden { display: none; }
.gnt-btn-restart {
  color: #241a03;
  background: linear-gradient(180deg, #ffc85a, #e8a01f);
  border-color: #ffdb8e;
}
.gnt-btn-restart:hover { background: linear-gradient(180deg, #ffd479, #f0ab2c); }
.gnt-btn-quit {
  color: #fff1f1;
  background: linear-gradient(180deg, #d94a4a, #ab2f2f);
  border-color: #ef7676;
}
.gnt-btn-quit:hover { background: linear-gradient(180deg, #e35a5a, #bd3636); }

/* ── Graphics options panel ──────────────────────────────────────────────────
   Rows are generated in game.js from the catalog in render3d-gfxlab.js; this block only has to
   style the three generated class names plus the disabled state. */
.gfxlab-row { align-items: flex-start; }
/* The per-setting explanation sits under its label in the grid's first column, so it must not
   inherit the row's 600 weight or push the 44px row height around. */
.gfxlab-item-help {
  margin-top: 3px;
  font-size: 0.78rem;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.35;
  color: var(--ice-dim);
}
/* "Demanding" chip on the most expensive effects. */
.gfxlab-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 6px;
  border: 1px solid var(--gold);
  border-radius: 999px;
  font-size: 0.63rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gold);
  vertical-align: 2px;
}
/* A setting that cannot take effect under the current 3D Graphics Quality tier. */
.options-row-disabled { opacity: 0.45; }
.options-row-disabled input[type="checkbox"] { cursor: not-allowed; }

/* ── Quick Play ───────────────────────────────────────────────────────────────
   The fastest route into a game, so the menu row carries a warmer left edge than its
   neighbours — enough to read as the primary of the nav group without competing with the
   Ranked hero button above it. */
.btn-nav.btn-quickplay {
  border-left-color: var(--ice-cool, #7fd4ec);
  background: linear-gradient(90deg, rgba(24, 46, 62, 0.95) 0%, rgba(12, 19, 31, 0.55) 100%);
}
.btn-nav.btn-quickplay:hover:not(:disabled) {
  border-left-color: #9fe6ff;
}
.btn-nav.btn-quickplay .nav-icon { color: #9fe6ff; }

/* Quick Play in the hero slot — same shape as the tutorial's hero treatment (see
   #btn-tutorial.btn-tutorial-hero), in ice-cyan rather than green so the two never read as
   the same button. Only one of them ever holds the slot; see applyMenuPriorityOrder. */
#btn-quickplay.btn-quickplay-hero {
  align-items: center;
  gap: 16px;
  padding: 18px 20px;
  background:
    linear-gradient(90deg, rgba(127, 212, 236, 0.16) 0%, rgba(127, 212, 236, 0.04) 45%, transparent 78%),
    linear-gradient(180deg, var(--rink-hi) 0%, #101927 100%);
  border: 1px solid rgba(127, 212, 236, 0.35);
  border-left: 4px solid var(--ice-cool, #7fd4ec);
}
#btn-quickplay.btn-quickplay-hero .nav-icon { width: 26px; height: 26px; color: #9fe6ff; }
#btn-quickplay.btn-quickplay-hero .nav-label {
  font-family: var(--font-menu-display);
  font-size: 1.4rem;
  line-height: 1.05;
  letter-spacing: 0.005em;
  color: var(--ice-bright);
}
#btn-quickplay.btn-quickplay-hero:hover {
  background:
    linear-gradient(90deg, rgba(127, 212, 236, 0.26) 0%, rgba(127, 212, 236, 0.07) 45%, transparent 78%),
    linear-gradient(180deg, #1a2536 0%, #131d2c 100%);
  border-color: rgba(127, 212, 236, 0.55);
  border-left-color: #9fe6ff;
  transform: translateY(-2px);
}

/* The sub-line stays visible in BOTH treatments, unlike the tutorial's: "fills with bots" is
   what makes the mode make sense when nobody is online, so it is never the hidden half.
   .nav-text must be a real block for it to sit under the label — the shared .btn-nav rule
   lays label and sub out inline otherwise. */
#btn-quickplay .nav-text { flex: 1; min-width: 0; text-align: left; }
#btn-quickplay .nav-label { display: block; }
#btn-quickplay .nav-sub {
  display: block;
  margin-top: 3px;
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ice-dim);
}
#btn-quickplay.btn-quickplay-hero .nav-sub {
  margin-top: 4px;
  font-size: 0.76rem;
  letter-spacing: 0.14em;
  color: #9fe6ff;
}

/* Sits where the sub-in button would be, while there is no seat to take. Quiet on purpose —
   it is an explanation, not a call to action. */
.quickplay-wait-note {
  position: absolute;
  bottom: 108px;                /* same line as .quickplay-sub-bar */
  left: 50%;
  transform: translateX(-50%);
  z-index: 41;
  padding: 9px 18px;
  border-radius: var(--radius);
  background: rgba(9, 15, 25, 0.72);
  border: 1px solid rgba(168, 212, 232, 0.22);
  font-family: var(--font-data);
  font-weight: 600;
  font-size: 0.76rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ice-dim);
  white-space: nowrap;
  pointer-events: none;
  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.8);
}
.quickplay-wait-note.hidden { display: none; }

/* Browse Games: the chip that marks a Quick Play match. Reads as a property of the row
   rather than a button — the Watch & Join action next to it is the thing to press. */
.qp-badge {
  display: inline-block;
  margin-right: 8px;
  padding: 2px 7px;
  border-radius: 4px;
  background: rgba(127, 212, 236, 0.16);
  border: 1px solid rgba(127, 212, 236, 0.4);
  font-family: var(--font-data);
  font-weight: 700;
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: #9fe6ff;
  vertical-align: middle;
  white-space: nowrap;
}

/* The "Invite Friends" button — in the HUD bar itself, between the ping readout and the
   scoreboard (see .hud-left). Sized to sit INSIDE the bar: the bar's height is measured into
   --fs-hud-h for the fullscreen layout, so a button taller than the scoreboard would push
   every top-of-rink overlay down with it. */
.quickplay-invite-bar {
  display: flex;
  align-items: center;
}
.quickplay-invite-bar.hidden { display: none; }
.qp-invite-btn {
  pointer-events: auto;
  width: auto;
  padding: 4px 12px;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  line-height: 1.3;
  white-space: nowrap;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3) inset, 0 2px 8px -2px rgba(0, 0, 0, 0.8);
}

/* The confirmation after pressing it: big, centred over the ice, gone in under two seconds.
   Deliberately not a modal — there is nothing to dismiss mid-shift. */
.invite-copied-toast {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 60;
  pointer-events: none;
  text-align: center;
  -webkit-user-select: none;
  user-select: none;
  animation: invite-copied-pop 1.9s ease-out forwards;
}
.invite-copied-toast.hidden { display: none; }
.invite-copied-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4vw, 2.9rem);
  letter-spacing: 0.05em;
  color: #ffdc84;
  white-space: nowrap;
  text-shadow:
    -3px -3px 0 #000, 3px -3px 0 #000, -3px 3px 0 #000, 3px 3px 0 #000,
    -3px 0 0 #000, 3px 0 0 #000, 0 -3px 0 #000, 0 3px 0 #000,
    0 0 28px rgba(255, 200, 90, 0.55);
}
/* The link itself, small underneath: it is what they are about to paste, and if the clipboard
   write ever half-works they can still read it off the screen. */
.invite-copied-url {
  margin-top: 8px;
  font-family: var(--font-data);
  font-size: clamp(0.72rem, 1.4vw, 0.95rem);
  letter-spacing: 0.06em;
  color: #cfe9f7;
  white-space: nowrap;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9);
}
@keyframes invite-copied-pop {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
  10%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  72%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.03); }
}
@media (prefers-reduced-motion: reduce) {
  .invite-copied-toast { animation: none; opacity: 1; transform: translate(-50%, -50%); }
}

/* In-game: the Quick Play sub-in prompt, sitting over the live rink above the spectator
   camera row. Deliberately over the PLAY rather than on a loading screen — when you step on
   is a decision worth timing, and you can only time it if you can see the shift. */
.quickplay-sub-bar {
  position: absolute;
  /* Lifted well clear of .spectator-cam-bar (14px) and off the bottom edge, where a button
     competes with the HUD and is easy to skim past. This is the one thing a parked player
     has to notice, so it sits up in the frame. */
  bottom: 108px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 41;
  display: flex;
  align-items: center;
  pointer-events: none;         /* layout box only — clicks belong to the button */
}
.quickplay-sub-bar.hidden { display: none; }
.qp-sub-btn {
  pointer-events: auto;
  width: auto;
  padding: 18px 46px;
  font-size: 1.3rem;
  letter-spacing: 0.1em;
  animation: qp-sub-pulse 1.9s ease-in-out infinite;
}
.qp-sub-btn:disabled { animation: none; opacity: 0.6; cursor: default; }
@keyframes qp-sub-pulse {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 200, 90, 0.38); }
  50% { transform: scale(1.04); box-shadow: 0 0 30px 8px rgba(255, 200, 90, 0.20); }
}
@media (prefers-reduced-motion: reduce) {
  .qp-sub-btn { animation: none; }
}

/* ── Arena vote (5 s map pick before hosted + ranked matches) ──────────────────
   Overlay, not a screen: it has to cover the lobby or the ranked dock depending on
   where the match came from. Grid is 2 columns; The Icehouse spans both on the top
   row because it is the default — the map you get by not voting. */
.arena-vote {
  position: fixed; inset: 0; z-index: 9200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(4, 8, 14, 0.82); backdrop-filter: blur(5px);
  padding: 16px; animation: arenaVoteIn 0.18s ease-out;
}
.arena-vote.hidden { display: none; }
@keyframes arenaVoteIn { from { opacity: 0; } to { opacity: 1; } }

.arena-vote-panel {
  width: min(760px, 100%); max-height: calc(100vh - 32px); overflow: auto;
  background: linear-gradient(180deg, #131b27, #0d131c);
  border: 1px solid #27384f; border-radius: 16px; padding: 18px 18px 16px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
}
.arena-vote-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.arena-vote-head h2 {
  margin: 0; font-size: 19px; letter-spacing: 0.06em; text-transform: uppercase; color: #e8f1fb;
}
.arena-vote-timer { font: 700 26px/1 ui-monospace, Menlo, Consolas, monospace; color: #6fd3ff; }
.arena-vote-bar { height: 4px; margin: 10px 0 14px; border-radius: 3px; background: #1d2a3b; overflow: hidden; }
.arena-vote-bar > i { display: block; height: 100%; width: 100%; background: linear-gradient(90deg, #6fd3ff, #3b8fe0); }

.arena-vote-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.arena-vote-card {
  position: relative; grid-column: span 1; padding: 0; overflow: hidden; cursor: pointer;
  border: 2px solid #27384f; border-radius: 12px; background: #0b1119;
  aspect-ratio: 16 / 9; transition: border-color 0.12s, transform 0.12s;
}
.arena-vote-card.wide { grid-column: 1 / -1; aspect-ratio: 21 / 8; }
.arena-vote-card:hover { border-color: #4d7ba8; transform: translateY(-2px); }
.arena-vote-card.picked { border-color: #6fd3ff; box-shadow: 0 0 0 3px rgba(111, 211, 255, 0.22); }
.arena-vote-card.won { border-color: #7de08a; box-shadow: 0 0 0 3px rgba(125, 224, 138, 0.28); }
.arena-vote-card.lost { opacity: 0.38; }
/* Absolutely positioned, like the caption and the badge: an in-flow <img> paints UNDER any
   positioned sibling no matter what the DOM order is, so the placeholder hid the art. */
.arena-vote-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* Sits behind the art, and shows through if the file is missing (the <img> hides itself). */
.arena-vote-card .ph { position: absolute; inset: 0; }
.arena-vote-card[data-env="default"] .ph { background: linear-gradient(160deg, #35506f, #16202e 70%); }
.arena-vote-card[data-env="pond"]    .ph { background: linear-gradient(160deg, #4a5a8c, #1b2140 70%); }
.arena-vote-card[data-env="storm"]   .ph { background: linear-gradient(160deg, #2f4256, #0c1219 70%); }
.arena-vote-card[data-env="frozen"]  .ph { background: linear-gradient(160deg, #8fb4d8, #cfe0ee 70%); }
.arena-vote-card[data-env="grand"]   .ph { background: linear-gradient(160deg, #6a1a24, #0b1222 70%); }
.arena-vote-card .cap {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 22px 12px 9px;
  background: linear-gradient(180deg, rgba(6, 10, 16, 0), rgba(6, 10, 16, 0.92));
  font-weight: 700; letter-spacing: 0.04em; color: #eaf3fc; text-align: left;
}
.arena-vote-card .n {
  position: absolute; top: 8px; right: 8px; min-width: 30px; height: 30px; padding: 0 8px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 15px; background: rgba(7, 12, 19, 0.85); border: 1px solid #38506e;
  font: 700 15px/1 ui-monospace, Menlo, Consolas, monospace; color: #9fd8ff;
}
.arena-vote-card.picked .n { background: #6fd3ff; border-color: #6fd3ff; color: #08121c; }
.arena-vote-foot { margin-top: 12px; text-align: center; color: #8ea6bf; font-size: 13px; min-height: 18px; }

@media (max-width: 560px) {
  .arena-vote-panel { padding: 14px 12px 12px; }
  .arena-vote-head h2 { font-size: 16px; }
  .arena-vote-grid { gap: 8px; }
  .arena-vote-card .cap { font-size: 13px; padding: 18px 9px 7px; }
}

/* ── Loading screen: which arena the vote picked ───────────────────────────────
   A letterboxed strip rather than a full 16:9 card — inside the 520px panel a full
   card would be ~290px tall and shout louder than the logo above it. */
.loading-arena {
  position: relative; width: 100%; margin: 4px 0 2px;
  aspect-ratio: 21 / 7; overflow: hidden;
  border: 1px solid #27384f; border-radius: 12px; background: #0b1119;
  animation: loadingArenaIn 0.45s ease-out both;
}
.loading-arena.hidden { display: none; }
@keyframes loadingArenaIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.loading-arena-ph { position: absolute; inset: 0; background: linear-gradient(160deg, #35506f, #16202e 70%); }
.loading-arena[data-env="pond"]   .loading-arena-ph { background: linear-gradient(160deg, #4a5a8c, #1b2140 70%); }
.loading-arena[data-env="storm"]  .loading-arena-ph { background: linear-gradient(160deg, #2f4256, #0c1219 70%); }
.loading-arena[data-env="frozen"] .loading-arena-ph { background: linear-gradient(160deg, #8fb4d8, #cfe0ee 70%); }
.loading-arena[data-env="grand"]  .loading-arena-ph { background: linear-gradient(160deg, #6a1a24, #0b1222 70%); }
/* Absolute for the same reason as the vote card's: an in-flow <img> paints under any
   positioned sibling, so the placeholder would cover the art. */
.loading-arena-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center 42%; display: block;
}
.loading-arena-meta {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 26px 14px 10px;
  display: flex; align-items: baseline; gap: 10px;
  background: linear-gradient(180deg, rgba(6,10,16,0), rgba(6,10,16,0.9));
}
.loading-arena-kicker {
  font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; color: #8fb6db;
}
.loading-arena-name {
  font-size: 16px; font-weight: 700; letter-spacing: 0.02em; color: #eaf3fc;
  text-shadow: 0 1px 6px rgba(0,0,0,0.7);
}
@media (max-width: 560px) {
  .loading-arena-name { font-size: 14px; }
  .loading-arena-meta { padding: 20px 11px 8px; }
}
