:root {
  --panel: #1b2430;
  --ink: #ffffff;
  --ink-soft: #a0abc0;
  --accent: #f1c40f;
  --panel-bg: rgba(18, 25, 38, 0.9);
  --panel-edge: rgba(255, 255, 255, 0.1);

  /* ===== HUD DESIGN TOKENS =====
     Every anchored HUD control derives its size and spacing from these, so the
     whole cluster scales as one unit. The px bounds in each clamp are physical
     floors/ceilings: a 44px button stays tappable on a small phone even when
     the rem scale wants 27px, and doesn't balloon on a 4K monitor. */
  --hud-inset: clamp(10px, 1.25rem, 24px);
  --hud-gap: clamp(6px, 0.625rem, 12px);
  --hud-btn: clamp(40px, 2.75rem, 46px);
  --hud-radius: clamp(8px, 0.625rem, 12px);

  /* Distance from the viewport edge to an anchor bar, notch included. */
  --hud-edge-top: calc(var(--hud-inset) + env(safe-area-inset-top, 0px));
  --hud-edge-right: calc(var(--hud-inset) + env(safe-area-inset-right, 0px));
  --hud-edge-bottom: calc(var(--hud-inset) + env(safe-area-inset-bottom, 0px));
  --hud-edge-left: calc(var(--hud-inset) + env(safe-area-inset-left, 0px));
}

/* ===== CANVAS SCALER =====
   The equivalent of Unity's CanvasScaler in "Scale With Screen Size" mode, or
   Phaser's Scale.FIT. One continuous root font-size drives every rem in the
   sheet, so the UI keeps the same proportions on a phone and a desktop instead
   of being re-positioned per breakpoint.

   The reference resolution is 1280x720 landscape. 1280/16 = 80 and 720/16 = 45,
   so min(100vw/80, 100vh/45) resolves to exactly 16px at the reference size and
   scales by whichever axis is tighter — the same "match the constrained axis"
   rule Unity applies. The outer clamp keeps text legible on small phones and
   stops the HUD from growing absurdly on very large displays.

   This replaces four stepped breakpoints (16px / 12px / 10px / 8px). Those
   steps were the root cause of the layout breaking: every jump silently
   rescaled all rem-based offsets while the clamp()-based button sizes stayed
   put, so hand-tuned gaps like "right: 10.5rem" only lined up in one band. */
html {
  font-size: clamp(10px, min(100vw / 80, 100vh / 45), 18px);
}

/* Portrait uses a rotated 720x1280 reference (720/16 = 45, 1280/16 = 80) with a
   slightly higher floor — a phone held upright needs proportionally larger
   controls than the same pixel count in landscape. */
@media (orientation: portrait) {
  html {
    font-size: clamp(11px, min(100vw / 45, 100vh / 80), 18px);
  }
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  user-select: none;
  -webkit-user-select: none;
}

body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #232a38;
  font-family: 'Segoe UI', system-ui, sans-serif;
  /* NOT `none`. touch-action is resolved by intersecting the value of the
     touched element with ALL its ancestors, so `none` here silently disabled
     touch scrolling in every scrollable menu on the page — the leaderboard list
     could be scrolled with a mouse wheel or by script, but a finger did nothing.
     `manipulation` still kills double-tap zoom (the thing this was really for)
     while leaving panning to the elements below. The play surface re-declares
     `none` for itself. */
  touch-action: manipulation
}

/* The 3D canvas and its wrapper: drags here aim and swing, and must never be
   read as a pan. This is where `none` belongs — on the surface being dragged,
   not on the document root. */
#game,
#game canvas {
  touch-action: none;
}

/* The four stepped root font-size breakpoints that used to live here
   (768px -> 12px, 400px -> 10px, height 500px -> 10px, height 400px -> 8px)
   are gone; the CANVAS SCALER block above covers the same range continuously. */

#game {
  position: fixed;
  inset: 0
}

canvas {
  display: block
}

.hud {
  position: fixed;
  z-index: 10;
  pointer-events: none
}

/* ===== HUD ANCHOR SYSTEM =====
   The top edge is ONE flex bar with three zones — left cluster, scoreboard,
   right cluster — instead of several independently position:fixed elements.
   This is the structural half of the fix; the CANVAS SCALER above is the
   sizing half.

   Why: the controls used to be positioned independently and several landed on
   the same corner, so each needed a hand-computed offset to dodge its
   neighbours (#soundBtn sat at right:1.5rem, identical to #homeBtn, and was
   pushed aside by `body.match-active #soundBtn { right: 10.5rem }`; #walletHud
   used right:5.75rem to clear #soundBtn). Those numbers only lined up at one
   root font-size, so every new breakpoint needed another patch.

   Making them siblings in a flex row removes the whole class of bug: flex
   cannot place two items on top of each other, so no offset is needed at any
   size. Crucially this also covers the scoreboard, which as a centred pill grew
   wide enough to reach the corners on mid-size screens — a case no fixed
   breakpoint catches cleanly, because it depends on the text inside it. */
#hudTopBar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 105;
  display: flex;
  align-items: flex-start;
  gap: var(--hud-gap);
  padding: var(--hud-edge-top) var(--hud-edge-right) 0 var(--hud-edge-left);
  /* The bar spans the full width, so it must not eat pointer events over the
     3D scene; individual controls opt back in below. */
  pointer-events: none;
}

/* Both side zones flex from a zero basis, so they always resolve to equal
   widths and the scoreboard between them sits on the true viewport centre —
   even during a match, when the left zone is empty because #playerCard is
   hidden.

   min-width: min-content is load-bearing. With `min-width: 0` the zones
   happily shrank to 27px while their 40px buttons spilled out of the box and
   straight over the scoreboard — the flex box no longer overflowed, but its
   contents did. The min-content floor makes a zone refuse to shrink past its
   buttons, so the scoreboard (which wraps) gives way instead. When space is so
   tight that a zone hits that floor, centring degrades gracefully rather than
   the two colliding. */
.hud-zone {
  flex: 1 1 0;
  min-width: min-content;
  display: flex;
  flex-direction: column;
  gap: var(--hud-gap);
  align-items: flex-start;
  pointer-events: none;
}

.hud-zone-right {
  align-items: flex-end;
}

.hud-zone>*,
.hud-cluster>* {
  pointer-events: auto;
}

/* Zone children are flex items now — any leftover fixed positioning on them
   would take them straight back out of the bar. */
.hud-zone>*,
.hud-cluster>* {
  position: relative;
  top: auto;
  right: auto;
  bottom: auto;
  left: auto;
}

/* The action cluster: controls, camera, sound, home. */
.hud-cluster {
  display: flex;
  gap: var(--hud-gap);
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  pointer-events: none;
}

/* When the shop is open, its absolute-positioned #shopWallet occupies the top
   right corner. Push the global actions down to make room. */
@media (orientation: landscape) {
  body:has(#groundsShopScreen.show) #hudActions {
    margin-top: 3.5rem;
  }
}

/* Unified skin for every square icon button in the cluster. Previously
   #soundBtn, #controlsBtn and #homeBtn each carried their own near-identical
   copy of this block, which is how they drifted apart (one kept a 44px ceiling
   while the others were pinned to 40px/38px, side by side). */
.icon-btn {
  width: var(--hud-btn);
  height: var(--hud-btn);
  flex: 0 0 auto;
  padding: 0;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: linear-gradient(180deg, #1b2e44 0%, #0d1624 100%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-bottom: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--hud-radius);
  box-shadow: 0 0.25rem 0.375rem rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.15);
  cursor: pointer;
  transition: transform 0.1s, filter 0.1s;
}

/* Icons scale with the button rather than sitting at a fixed 22px, which is
   what made them look oversized once the button hit its 40px floor. */
.icon-btn>svg {
  width: 55%;
  height: 55%;
}

/* The camera debug toggle is a labelled pill, not a square. */
.icon-btn-wide {
  width: auto;
  height: var(--hud-btn);
  padding: 0 0.75rem;
  font-size: clamp(10px, 0.8125rem, 14px);
  font-weight: bold;
  white-space: nowrap;
  border-color: #5cb531;
  text-shadow: 0 0.125rem 0 rgba(0, 0, 0, 0.5);
}

.icon-btn:hover {
  filter: brightness(1.2);
  transform: translateY(-2px);
}

.icon-btn:active {
  transform: translateY(1px);
  border-bottom-width: 1px;
}

#soundBtn.muted {
  color: #ff6b6b;
}

/* Radar minimap — a child of the left zone. Its size is one clamp instead of
   the four !important overrides it used to carry across three breakpoints, and
   it no longer positions itself at all: when the bar wraps on narrow screens
   the zone carries it below the scoreboard, which is what the old
   `top: calc(var(--scoreboard-h) + 0.5rem)` was approximating. That removed the
   dependency on a height measured by a ResizeObserver in ui.js, along with its
   8rem first-paint fallback. */
#minimap {
  width: clamp(64px, 7.5rem, 120px);
  height: clamp(64px, 7.5rem, 120px);
  flex: 0 0 auto;
  border-radius: 50%;
  border: 0.1875rem solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 0.25rem 0.625rem rgba(0, 0, 0, 0.5);
  pointer-events: none;
  opacity: 0.9;
}

/* ===== SCOREBOARD: the bar's centre zone =====
   Was position:fixed with left:50% + translateX(-50%). It is a flex item now,
   so it is centred by the two equal-width side zones instead. That is what
   makes overlap impossible: as the scoreboard grows (longer team names, the
   chase target appearing) it pushes the side zones outward rather than sliding
   underneath them, and when there is no room left the wrap rule below fires.

   position: static is deliberate — the old fixed positioning would pull it
   straight back out of the bar. #matchTimingPlaceholder inside it is
   position:absolute, so this element still needs to be a containing block. */
#scoreboard {
  position: relative;
  flex: 0 1 auto;
  min-width: 0;
  align-self: flex-start;
  background: #12161b;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: 2rem;
  padding: 0.625rem 1.5rem;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.6);
  display: flex;
  gap: 1.5rem;
  pointer-events: auto;
  /* Wraps at every width, not just on phones. A flex item cannot shrink below
     its min-content width, so without this the widest state (chase target row
     plus a long bowler name) stayed one long row and punched through the side
     zones on a mid-size screen. Wrapping lets it fold to a second row and stay
     inside the space the zones leave it. */
  flex-wrap: wrap;
  justify-content: center;
  row-gap: 0.25rem;
}

#scoreboard .stat {
  text-align: center;
  min-width: 3.5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

#scoreboard .stat+.stat {
  border-left: 1px solid rgba(255, 255, 255, 0.1);
  padding-left: 1.5rem;
}

/* clamp() in px, not rem: the <html> font-size already drops to 10px under
   400px width (see the global mobile rule above), and rem sizing here would
   shrink AGAIN on top of that — that's what was making this unreadable on
   phones. clamp() is immune to that cascade and scales off the viewport
   directly instead. */
#scoreboard .label {
  color: var(--ink-soft);
  font-size: clamp(9px, 2.4vw, 13px);
  font-weight: 700;
  letter-spacing: clamp(0.5px, 0.3vw, 2px);
}

#scoreboard .value {
  color: var(--ink);
  font-size: clamp(17px, 5.5vw, 28px);
  font-weight: 800;
  line-height: 1.15;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .4)
}

#scoreboard .value.accent {
  color: var(--accent)
}



#banner {
  left: 50%;
  top: 32%;
  transform: translateX(-50%) scale(.8);
  color: var(--ink);
  font-size: clamp(2.125rem, 7vw, 4rem);
  font-weight: 800;
  letter-spacing: 2px;
  text-align: center;
  text-shadow: 0 0.25rem 0 rgba(0, 0, 0, .5), 0 0.625rem 1.5rem rgba(0, 0, 0, .6);
  opacity: 0;
  transition: opacity .2s, transform .2s;
}

.slow-mo-active .slow-mo-vignette {
  opacity: 1;
}

#fadeOverlay {
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.2s ease-in-out;
  z-index: 200;
  position: fixed;
}

#fadeOverlay.show {
  opacity: 1;
}

#banner.show {
  opacity: 1;
  transform: translateX(-50%) scale(1)
}

/* Small contextual message — used for shop/locked-venue hints. High z-index so
   it reads above Team Select / Match Setup / Grounds Shop (all z-index: 100). */
#toastMsg {
  left: 50%;
  top: 6.5rem;
  transform: translate(-50%, -0.5rem);
  max-width: 90vw;
  padding: 0.625rem 1.25rem;
  background: rgba(10, 14, 20, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-align: center;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  /* Above the tutorial overlays (11500) and its cards (12500). The toast is
     how the tutorial gives feedback on a shot ("PERFECT TIMING!"), so being
     under the very overlay that triggers it made it invisible exactly when it
     mattered most. Kept below the 100000 full-screen modal, which is meant to
     block everything. */
  z-index: 13500;
}

#toastMsg.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Shot-mode switch toast — brief center-bottom notification */
#shotModeToast {
  left: 50%;
  bottom: 12%;
  transform: translate(-50%, 0.5rem) scale(0.9);
  padding: 0.5rem 1.5rem;
  background: rgba(10, 14, 20, 0.92);
  border: 2px solid var(--accent);
  border-radius: 0.75rem;
  color: #fff;
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-align: center;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 120;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

#shotModeToast.show {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}

#shotModeToast.aerial {
  border-color: #ff9f6b;
  color: #ff9f6b;
}

#shotModeToast.ground {
  border-color: #5bb8ff;
  color: #5bb8ff;
}

/* Minimal post-wicket "tap to continue" prompt — sits below the WICKET! flash */
#wicketTapPrompt {
  left: 50%;
  top: 58%;
  transform: translate(-50%, -50%) translateY(0.5rem);
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  padding: 0.625rem 1.25rem;
  background: rgba(10, 14, 20, 0.65);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  color: var(--ink);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s ease;
  z-index: 60;
}

#wicketTapPrompt .dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent);
  animation: wicketPromptPulse 1.1s ease-in-out infinite;
}

#wicketTapPrompt.show {
  opacity: 1;
  transform: translate(-50%, -50%) translateY(0);
}

@keyframes wicketPromptPulse {

  0%,
  100% {
    opacity: 0.4;
    transform: scale(0.85);
  }

  50% {
    opacity: 1;
    transform: scale(1.15);
  }
}

#overlay,
#exitOverlay {
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background: rgba(10, 14, 20, .55);
  backdrop-filter: blur(2px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .25s;
}

/* Must sit above every other HUD overlay (wicket prompt, toasts, fade
   transitions) since pausing can happen while any of those are showing.
   #overlay deliberately does NOT get this — it's the base title screen that
   Match Intro / Team Select / Match Setup / Grounds Shop (all z-index: 100)
   need to cover, same as Survival/Six Challenge already do. */
#exitOverlay {
  /* Above every result/progress panel (summary 500, showcase 999, chapter panel
     12000, unlock loader 14000). It is a modal question about leaving the match;
     if it is on screen at all it has to be the thing you can see and dismiss.
     It used to sit at 220, which put it UNDER the level-clear card whenever a tab
     switch landed on one. */
  z-index: 15000;
}

#overlay.show,
#exitOverlay.show {
  opacity: 1;
  visibility: visible;
  pointer-events: auto
}

#overlay h2,
#exitOverlay h2 {
  color: var(--ink);
  font-size: clamp(2rem, 6vw, 3.375rem);
  font-weight: 800;
  text-shadow: 0 0.25rem 0 rgba(0, 0, 0, .5)
}

#overlay p,
#exitOverlay p {
  color: var(--ink-soft);
  font-size: 1.125rem;
  margin: 0.625rem 0 1.375rem;
  max-width: 20rem;
  text-align: center;
  line-height: 1.4;
}

#overlay button.play,
#overlay .custom-match-nav-btn,
#overlay .menu-nav-btn,
#overlay .shop-btn,
#overlay .leaderboard-icon-btn,
#overlay .how-to-play-nav-btn {
  width: 18rem;
  height: 3.5rem;
  font-size: 1.15rem;
  max-width: 90vw;
  box-sizing: border-box;
}

.opt-row {
  display: flex;
  gap: 0.625rem;
  margin-bottom: 1.125rem
}

.opt-row button {
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--ink-soft);
  background: linear-gradient(180deg, #2a3849 0%, #1a2330 100%);
  border: 2px solid var(--panel-edge);
  border-radius: 0.625rem;
  padding: 0.5rem 1.125rem;
  box-shadow: 0 0.25rem 0 #10161f, 0 0.5rem 1rem rgba(0, 0, 0, .35);
  transition: transform .08s, box-shadow .08s;
}

/* .back-btn is a fixed-size icon square (see its own rule below) — it must
   keep padding:0 even inside .opt-row, otherwise .opt-row button's padding
   (higher specificity: class+element beats .back-btn's class-only rule)
   shrinks the icon's content box and its percentage-sized <svg> collapses
   to a sliver. */
.opt-row .back-btn {
  padding: 0;
}

.opt-row button:active {
  transform: translateY(0.1875rem);
  box-shadow: 0 1px 0 #10161f
}

.opt-row button.on {
  color: #3a2c05;
  background: linear-gradient(180deg, #ffe27a 0%, #ffc93a 100%);
  border-color: #c79a20;
  box-shadow: 0 0.25rem 0 #9c7714;
}

#overlay button.play,
#exitOverlay button.play {
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  font-size: 1.1875rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #1c2a17;
  background: linear-gradient(180deg, #8fdc4d 0%, #5cb531 100%);
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  box-shadow: 0 0.3125rem 0 #3c7d1e, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

#overlay button.play:active,
#exitOverlay button.play:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #3c7d1e, 0 0.25rem 0.625rem rgba(0, 0, 0, .4)
}

#overlay button.play:focus-visible {
  outline: 0.1875rem solid var(--accent);
  outline-offset: 0.1875rem
}

/* ============ MODE PICKER (opened by PLAY) ============ */
#modeSelectScreen {
  display: none;
  position: absolute;
  inset: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  background: rgba(10, 14, 20, 0.88);
  backdrop-filter: blur(0.1875rem);
  z-index: 100;
  pointer-events: auto;
  overflow-y: auto;
  padding: 3.25rem 1rem 1.5rem;
}

#modeSelectScreen.show {
  display: flex;
}

#modeSelectScreen #modeSelectBackBtn {
  position: absolute;
  top: 1.25rem;
  left: 1.5rem;
  pointer-events: auto;
}

#modeSelectTitle {
  margin: 0;
  color: var(--ink);
  font-size: clamp(1.25rem, 5vw, 1.75rem);
  font-weight: 800;
  letter-spacing: 2px;
  text-align: center;
  text-shadow: 0 0.25rem 0 rgba(0, 0, 0, 0.5);
}

/* 4 across on desktop, 2 on phones — matches the reference layout.
   max-width in px, not rem: the root font-size varies from 16px down to 8px
   across this project's width/height breakpoints, so 52rem collapsed to 416px
   on a 667px landscape phone — squeezing cards to 100px wide, wrapping every
   description to three lines and overflowing the screen. */
#modeGrid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
  width: 100%;
  max-width: 832px;
  pointer-events: auto;
  padding-bottom: 30px;
}

/* Same bevel-button mechanic as .play (the title screen PLAY button) and every
   other button in the game (RESUME, TOSS MATCH, CONTINUE, ...): solid step
   shadow in a darker shade of the button's own colour + a soft ambient shadow,
   collapsing to almost nothing on press. quickChaseBtn already does exactly
   this per-button colour override — --step just makes that reusable across
   eight colours instead of repeating the box-shadow rule for each. */
.mode-card {
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.375rem;
  /* min-height keeps every card the same height. */
  min-height: 6.5rem;
  padding: 1rem 0.75rem 0.875rem;
  font: inherit;
  font-weight: 800;
  letter-spacing: 1px;
  color: #fff;
  border: none;
  border-radius: 0.75rem;
  box-shadow: 0 0.3125rem 0 var(--step), 0 0.625rem 1.25rem rgba(0, 0, 0, 0.4);
  transition: transform 0.08s, box-shadow 0.08s;
}

.mode-card:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 var(--step), 0 0.25rem 0.625rem rgba(0, 0, 0, 0.4);
}

.mode-card:focus-visible {
  outline: 0.1875rem solid var(--accent);
  outline-offset: 0.1875rem;
}

.mode-icon {
  font-size: clamp(1.75rem, 7vw, 2.5rem);
  line-height: 1.1;
  filter: drop-shadow(0 0.1875rem 0.25rem rgba(0, 0, 0, 0.5));
}

.mode-name {
  font-size: clamp(11px, 2.9vw, 15px);
  font-weight: 800;
  letter-spacing: 0.5px;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.4);
}

.mode-desc {
  display: none;
  font-size: clamp(9px, 2.4vw, 11.5px);
  font-weight: 600;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

/* Leaderboards entry point — lives on the title screen below PLAY instead of
   its own mode-card, since it isn't a match mode. Same pill shape/sizing
   pattern as button.play, just in the gold accent to read as its own thing. */
/* Main-menu SHOP button, directly under PLAY. Same chunky pressed-button shape
   as .leaderboard-icon-btn (shared box-shadow "lip" + translateY on :active),
   in the blue the GROUNDS mode-card already uses for shop-ish things. */
.shop-btn {
  pointer-events: auto;
  cursor: pointer;
  margin-top: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #0b1a2e;
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  background: linear-gradient(180deg, #4a90e2 0%, #2560ad 100%);
  box-shadow: 0 0.3125rem 0 #1a4278, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

.shop-btn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #1a4278, 0 0.25rem 0.625rem rgba(0, 0, 0, 0.4);
}

/* ===== SHOP SCREEN =====
   A fixed frame, not a scrolling page. The chrome (back button, wallet, title,
   tabs) is flex-shrink:0 and stays put; the active panel takes the remaining
   height and its grid is the only thing that scrolls. Previously the whole
   screen was overflow-y:auto, so the tabs scrolled off the top as soon as a
   grid was longer than the viewport — with 27 character cards that was every
   time. `min-height: 0` on the flex children is what actually lets the grid
   shrink to the frame and scroll instead of stretching it. */
#groundsShopScreen {
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  background: rgba(10, 14, 20, 0.85);
  backdrop-filter: blur(0.1875rem);
  overflow: hidden;
  padding: 2rem 1rem 1.5rem;
}

#groundsShopScreen>h2,
#groundsShopScreen>#shopTabs,
#groundsShopScreen>.hud {
  flex-shrink: 0;
}

/* Coins/level readout. In the column by default (portrait has height to
   spare), where it reads as part of the header stack. */
#shopWallet {
  position: static;
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

/* Landscape: out of the flow and into the top-right corner, opposite the
   back button — the same corner the main menu's #walletHud occupies.
   position:absolute is the point, not just the placement: as a flow item this
   row cost the item grid its full height plus margin, which is the scarcest
   thing on a 375px-tall phone. Offsets use the HUD edge tokens so it lines up
   with the back button and clears a notch. */
@media (orientation: landscape) {
  #shopWallet {
    position: absolute;
    top: var(--hud-edge-top);
    right: var(--hud-edge-right);
    margin-bottom: 0;
    z-index: 2;
  }
}

/* ===== SHARED 3D PICKER CAROUSEL =====
   The layout behind both the Character and Grounds tabs: a big name across the
   top, ‹ › arrows flanking the live 3D subject, requirement pills and one
   action button. Lifted out of WARDROBE_CSS in js/voxel-skin.js so the Grounds
   picker doesn't depend on that module having booted. */
.wd-center {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16rem;
  padding: 0 1rem;
  pointer-events: none;
}

.wd-arrow {
  pointer-events: auto;
  background: rgba(0, 0, 0, 0.5);
  border: 2px solid rgba(255, 255, 255, 0.2);
  color: #fff;
  font-size: 2.5rem;
  width: 4rem;
  height: 4rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(4px);
  transition: all 0.2s;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

/* The old glow+scale :hover/:active pair lived here. Superseded by the
   .back-btn-matching skin further down this sheet; the base rule above is
   kept because it still supplies border-radius / display / cursor. */

.wd-char-name {
  position: absolute;
  top: 1rem;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 2.5rem;
  font-weight: 900;
  letter-spacing: 2px;
  color: #fff;
  text-transform: uppercase;
  text-shadow: 0 4px 12px rgba(0, 0, 0, 0.8), 0 0 20px rgba(255, 255, 255, 0.3);
  pointer-events: none;
}

/* Sub-label under the name — the ground's city/country. Characters don't set
   one, so it simply stays empty for them. */
.wd-sub-name {
  position: absolute;
  top: 3.5rem;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--ink-soft);
  text-transform: uppercase;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9);
  pointer-events: none;
}

/* Position in the carousel — "3 / 10". Sits under the name (and under the
   ground's city sub-label, which characters leave empty) so both tabs can use
   the same offset. */
.wd-count {
  position: absolute;
  top: 5.25rem;
  left: 0;
  right: 0;
  margin: 0 auto;
  width: fit-content;
  padding: 0.25rem 0.9rem;
  border-radius: 2rem;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--ink-soft);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9);
  backdrop-filter: blur(4px);
  pointer-events: none;
}

.wd-count b {
  color: #fff;
  font-weight: 900;
}

/* ACTIVE — marks the character you are currently wearing. Pinned to the dock's
   top edge as an overlay, mirroring the price tag on the right, so it labels
   the card without taking a row inside it or costing any height.
   Deliberately the SAME green as .wd-action.selected: the badge and that button
   are one statement, and two different greens would read as two different
   states. Centred on the card's top edge; it can never collide with the price
   tag pinned right, because a locked character is never the active one — the
   two states are mutually exclusive and never render together.
   Hidden by default; updatePreviewUI adds .show. */
.wd-active-badge {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin: 0 auto;
  width: fit-content;
  z-index: 2;
  padding: 0.28rem 0.85rem;
  border-radius: 2rem;
  background: linear-gradient(180deg, #24541b 0%, #12300e 100%);
  border: 2px solid #6cd44a;
  color: #c2f591;
  font-size: 0.68rem;
  font-weight: 900;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
  box-shadow: 0 0.25rem 0.6rem rgba(0, 0, 0, 0.5), 0 0 0.9rem rgba(108, 212, 74, 0.4);
  pointer-events: none;
  /* visibility, not display: the badge is absolutely positioned, so hiding it
     costs nothing in layout either way, and this keeps the transition alive. */
  visibility: hidden;
  opacity: 0;
  /* -55% straddles the card's top border, exactly as .wd-dock .wd-reqs does.
     The hidden state adds a small extra rise so it fades in downward onto the
     edge rather than appearing in place. */
  transform: translateY(calc(-55% - 0.3rem));
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.wd-active-badge.show {
  visibility: visible;
  opacity: 1;
  transform: translateY(-55%);
}

/* A live dot, so the badge reads as a status rather than a rarity tag. */
.wd-active-badge::before {
  content: '';
  display: inline-block;
  width: 0.4rem;
  height: 0.4rem;
  margin-right: 0.4rem;
  vertical-align: middle;
  border-radius: 50%;
  background: #9ae84f;
  box-shadow: 0 0 0.4rem rgba(154, 232, 79, 0.95);
}

.wd-foot {
  flex-shrink: 0;
  padding: 2rem 2rem 3rem;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.9) 20%, transparent);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.wd-reqs {
  display: flex;
  gap: 1.5rem;
  margin-bottom: 1.5rem;
}

.wd-req {
  background: rgba(0, 0, 0, 0.6);
  padding: 0.75rem 1.5rem;
  border-radius: 2rem;
  border: 1px solid rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  backdrop-filter: blur(4px);
  color: #fff;
}

.wd-req.unmet {
  color: #ff4444;
  border-color: rgba(255, 68, 68, 0.3);
}

.wd-req-icon {
  font-size: 1.5rem;
}

.wd-req-val {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 1px;
}

.wd-action {
  width: 100%;
  max-width: 24rem;
  padding: 1.25rem;
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: 2px;
  border-radius: 3rem;
  border: none;
  cursor: pointer;
  transition: transform 0.1s;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

.wd-action:active {
  transform: scale(0.96);
}

/* The flat-colour SELECT / UNLOCK / LOCKED / SELECTED rules that used to sit
   here are gone. Every property they set was re-set by the "Action button: one
   geometry, three states" block further down this sheet, which wins on source
   order, so they had no effect on anything rendered. Search .wd-action.select
   for the live rules. */

/* Both pickers sit inside a shop panel and must not scroll — the 3D subject
   behind them is the content. Scaled down from the full-screen customize
   panel's proportions, since a tab is shorter than the viewport. */
#shopCharacterSelect,
#shopGroundSelect {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  width: 100%;
}

#shopCharacterSelect .wd-center,
#shopGroundSelect .wd-center {
  gap: clamp(6rem, 40vw, 16rem);
}

#shopCharacterSelect .wd-char-name,
#shopGroundSelect .wd-char-name {
  font-size: clamp(1.25rem, 4vw, 2.5rem);
}

#shopCharacterSelect .wd-foot,
#shopGroundSelect .wd-foot {
  padding: 1rem 1rem 0.5rem;
  background: none;
}

#shopCharacterSelect .wd-arrow,
#shopGroundSelect .wd-arrow {
  width: clamp(2.5rem, 8vw, 4rem);
  height: clamp(2.5rem, 8vw, 4rem);
  font-size: clamp(1.5rem, 5vw, 2.5rem);
}

#shopCharacterSelect .wd-reqs,
#shopGroundSelect .wd-reqs {
  margin-bottom: 0.875rem;
}

/* The shop tab is shorter than the full-screen customize panel, so its action
   buttons are scaled down from .wd-action's defaults. UPGRADE is listed here
   too: it sits directly under SELECT, and the two must be the same button in
   different colours — matching only the base rule left UPGRADE at the
   full-panel size, noticeably wider and taller than the SELECT above it. */
#shopCharacterSelect .wd-action,
#shopGroundSelect .wd-action,
#shopCharacterSelect .wd-upgrade {
  padding: 0.875rem;
  font-size: 1rem;
  max-width: 18rem;
}

/* Character and Grounds are the tabs you must see THROUGH — the subject stands
   in the 3D scene behind the shop. */
#shopPanel-character.shop-panel,
#shopPanel-grounds.shop-panel {
  justify-content: space-between;
  overflow: hidden;
}

body.shop-3d-tab #groundsShopScreen {
  background: transparent;
  backdrop-filter: none;
}

/* ===== SHOP TABS ===== */
#shopTabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin: 0.25rem 0 0.5rem;
}

.shop-tab {
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  font-size: 0.875rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--ink-soft);
  background: linear-gradient(180deg, #1e2a3a 0%, #131b26 100%);
  border: 2px solid var(--panel-edge);
  border-radius: 999px;
  padding: 0.5rem 1.125rem;
  transition: color .12s, border-color .12s, background .12s;
}

.shop-tab:hover {
  color: var(--ink);
}

.shop-tab.on {
  color: #fff;
  border-color: #4a90e2;
  background: linear-gradient(180deg, #4a90e2 0%, #2560ad 100%);
}

/* Only the active panel is in flow; the others are display:none so their grids
   cost nothing to leave rendered between switches. The panel claims the frame's
   leftover height so its grid has something definite to scroll within. */
.shop-panel {
  display: none;
  flex-direction: column;
  align-items: center;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
}

.shop-panel.on {
  display: flex;
}

.shop-panel>.setup-label {
  flex-shrink: 0;
}

/* Character and Bat grids reuse .ground-card, so they match #groundsGrid. */
.shop-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
  justify-content: center;
  max-width: 40rem;
  margin-top: 1.25rem;
}

/* The only scrolling region on the shop screen. align-content:flex-start stops
   a short grid (5 bats) from stretching its rows down the whole frame. */
#groundsGrid,
.shop-grid {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  align-content: flex-start;
  padding-bottom: 0.5rem;
  width: 100%;
}

/* Colour chip on a bat card — the only thing distinguishing one bat from
   another, so it leads the card. */
.bat-swatch {
  width: 100%;
  height: 2.25rem;
  border-radius: 0.5rem;
  margin-bottom: 0.5rem;
  border: 2px solid rgba(255, 255, 255, 0.25);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.45);
}

.leaderboard-icon-btn {
  pointer-events: auto;
  cursor: pointer;
  margin-top: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #3a2c05;
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  background: linear-gradient(180deg, #ffd94a 0%, #d9a017 100%);
  box-shadow: 0 0.3125rem 0 #8a6a0e, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

.leaderboard-icon-btn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #8a6a0e, 0 0.25rem 0.625rem rgba(0, 0, 0, 0.4);
}

/* Per-mode colours: gradient + --step (a darker shade of the gradient's own
   bottom stop, same relationship .play's #5cb531/#3c7d1e green pair has). */
.mode-green {
  background: linear-gradient(180deg, #4cbb52 0%, #2b8c3a 100%);
  --step: #1e6229;
}

.mode-purple {
  background: linear-gradient(180deg, #9a5cd0 0%, #6b32a8 100%);
  --step: #4a2277;
}

.mode-red {
  background: linear-gradient(180deg, #e35d4a 0%, #b12f28 100%);
  --step: #7c1f1a;
}

.mode-magenta {
  background: linear-gradient(180deg, #d94fa4 0%, #a3236f 100%);
  --step: #6f174b;
}

.mode-teal {
  background: linear-gradient(180deg, #34b3a5 0%, #1a7d78 100%);
  --step: #125650;
}

.mode-blue {
  background: linear-gradient(180deg, #4a90e2 0%, #2560ad 100%);
  --step: #1a4278;
}

.mode-orange {
  background: linear-gradient(180deg, #f0913c 0%, #c35f14 100%);
  --step: #8a430e;
}

.mode-indigo {
  background: linear-gradient(180deg, #5b6bd6 0%, #343f9e 100%);
  --step: #242b6e;
}

.mode-gold {
  background: linear-gradient(180deg, #ffd94a 0%, #d9a017 100%);
  --step: #8a6a0e;
}

@media (max-width: 40rem) {
  #modeGrid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    /* px: 26rem resolved to only 260px at the 10px mobile root, wasting 130px of
       a 390px screen. */
    max-width: 420px;
  }

  /* px, not rem: the root font-size drops 12px -> 10px at the 400px width
     breakpoint, so rem sizing here would shrink the cards unpredictably across
     that cliff (an 8.5rem card measured 92px on a 390px phone while there was
     400px of unused vertical space). Fixed px keeps them comfortable. */
  .mode-card {
    min-height: 76px;
    padding: 12px 8px 11px;
    border-radius: 0.75rem;
  }
}

/* Short landscape screens: two rows of full-height cards don't fit, so trim the
   vertical footprint rather than letting the grid overflow off-screen. px again —
   the root drops to 8px under 400px height, which would over-shrink rem here. */
@media (max-height: 30rem) and (min-width: 40rem) {
  #modeSelectScreen {
    padding-top: 40px;
    gap: 8px;
  }

  #modeGrid {
    gap: 6px;
  }

  .mode-card {
    min-height: 60px;
    padding: 7px 8px 6px;
    gap: 2px;
  }

  /* Icon/name were still sized off vw clamps that only account for width, so
     on a short landscape phone (plenty of width, not much height) they stayed
     at full size and the whole grid read oversized for the space. */
  .mode-icon {
    font-size: 18px;
  }

  .mode-name {
    font-size: 10px;
    letter-spacing: 0.3px;
  }
}

/* tutorial coaching prompt (top-center) + skip link */
#tutorialCoach {
  position: absolute;
  top: 5.25rem;
  left: 50%;
  transform: translateX(-50%) translateY(-0.5rem);
  max-width: 86vw;
  padding: 0.75rem 1.375rem;
  background: rgba(10, 14, 20, .82);
  border: 2px solid var(--accent);
  border-radius: 0.75rem;
  color: #fff;
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: .0.3125rem;
  text-align: center;
  line-height: 1.45;
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, .5);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .25s, transform .25s;
  z-index: 90;
}

#tutorialCoach.show {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

#tutorialSkip {
  position: absolute;
  top: 1.125rem;
  right: 1.125rem;
  padding: 0.4375rem 0.875rem;
  background: rgba(10, 14, 20, .7);
  border: 1px solid var(--panel-edge);
  border-radius: 0.5rem;
  color: var(--ink-soft);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 1px;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .2s;
  z-index: 90;
}

#tutorialSkip.show {
  opacity: .85;
  visibility: visible;
  pointer-events: auto;
}

#tutorialSkip:hover {
  opacity: 1;
}

/* ---- setup / selection / toss screens (match hub style) ---- */
#teamSelectScreen h2,
#matchSetupScreen h2,
#tossScreen h2,
#groundsShopScreen h2,
#leaderboardScreen h2 {
  color: var(--ink);
  font-size: clamp(1.625rem, 5.5vw, 2.875rem);
  font-weight: 800;
  letter-spacing: 1px;
  text-align: center;
  text-shadow: 0 0.25rem 0 rgba(0, 0, 0, .5);
}

.setup-label {
  color: var(--ink-soft);
  font-size: 0.8125rem;
  font-weight: 800;
  letter-spacing: 0.1875rem;
  text-transform: uppercase;
  margin: 1.25rem 0 0.75rem;
  text-align: center;
}

.setup-group {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 17.5rem;
  margin: 0.5rem 0;
  padding: 0.75rem 1rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 0.5rem;
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.setup-group .setup-label {
  margin: 0;
  text-align: left;
}

.name-entry-input {
  pointer-events: auto;
  width: 100%;
  font: inherit;
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.06);
  border: 2px solid var(--panel-edge);
  border-radius: 0.625rem;
  padding: 0.75rem 1rem;
  text-align: center;
}

.name-entry-input::placeholder {
  color: var(--ink-soft);
}

.name-entry-input:focus {
  outline: none;
  border-color: var(--accent);
}

/* chunky green action button available on every screen, not just #overlay */
button.play {
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #1c2a17;
  background: linear-gradient(180deg, #8fdc4d 0%, #5cb531 100%);
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  box-shadow: 0 0.3125rem 0 #3c7d1e, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

button.play:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #3c7d1e, 0 0.25rem 0.625rem rgba(0, 0, 0, .4);
}

/* team buttons */
#teamGrid {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 32.5rem;
  margin-top: 1.375rem;
}

.team-btn {
  pointer-events: auto;
  cursor: pointer;
  width: 8.25rem;
  padding: 1rem 0.625rem 0.8125rem;
  border-radius: 0.875rem;
  font-size: 1.375rem;
  font-weight: 800;
  letter-spacing: 2px;
  color: #fff;
  border: 2px solid rgba(255, 255, 255, .85);
  text-shadow: 0 2px 0.1875rem rgba(0, 0, 0, .55);
  box-shadow: 0 0.3125rem 0 rgba(0, 0, 0, .45), 0 0.625rem 1.125rem rgba(0, 0, 0, .4), inset 0 2px 0 rgba(255, 255, 255, .25);
  transition: transform .08s, box-shadow .08s;
}

.team-btn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 rgba(0, 0, 0, .45), 0 0.25rem 0.625rem rgba(0, 0, 0, .4), inset 0 2px 0 rgba(255, 255, 255, .25);
}

.team-btn .flag {
  display: block;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 1px;
  color: rgba(255, 255, 255, .82);
  margin-top: 0.25rem;
}

@media (max-width:35rem) {
  #teamGrid {
    max-width: 21.25rem;
    gap: 0.625rem;
  }

  .team-btn {
    width: 6.25rem;
    font-size: 1.125rem;
    padding: 0.8125rem 0.5rem 0.625rem;
  }

  .setup-label {
    letter-spacing: 2px;
    margin: 0.875rem 0 0.625rem;
  }
}

/* ============ WALLET (coins + level) ============ */
/* Player card + coins/trophy row only ever belong on the true home/title
   screen — hidden by default and revealed solely while #overlay carries
   .show, so mode-select, shop, tutorial, and live matches never show them
   (all of those hide #overlay first). This replaces the old scattered
   JS style.display toggles in startGame()/returnToMenu()/summaryContinueBtn
   with one rule driven entirely by the overlay's own state. */
#walletHud,
#playerCard {
  display: none;
}

body:has(#overlay.show) #walletHud,
body:has(#overlay.show) #playerCard {
  display: flex;
}

/* top/right removed — #hudTopRight anchors this now. The old right:5.75rem was
   a manual offset to clear #soundBtn's corner; flex gap handles it. */
#walletHud {
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
  max-width: min(16rem, 40vw);
  z-index: 105;
  pointer-events: none;
}

.wallet-pill {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.4375rem 0.75rem;
  background: rgba(10, 14, 20, 0.75);
  border: 1px solid var(--panel-edge);
  border-radius: 999px;
  color: var(--ink);
  font-size: 0.8125rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.4);
}

.wallet-pill.wallet-trophy b {
  color: #ffcc4d;
}

.wallet-pill.wallet-coins b {
  color: #ffd94a;
}

/* Player identity card: avatar + name + level badge + XP bar, top-left on
   the menu — mirrors the reference layout with a currency row and gear
   button opposite it on the right. */
#playerCard {
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.875rem 0.5rem 0.5rem;
  background: linear-gradient(180deg, #1b2633 0%, #0d131a 100%);
  border: 1px solid var(--panel-edge);
  border-radius: 999px;
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.4);
  z-index: 105;
  pointer-events: auto;
  cursor: pointer;
  max-width: min(19rem, 74vw);
}

#playerCard:hover {
  filter: brightness(1.1);
}

.player-avatar {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #f1c40f, #e67e22);
  color: #1b2633;
  font-weight: 900;
  font-size: 1.125rem;
  border: 2px solid rgba(255, 255, 255, 0.25);
}

.player-card-body {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
  text-align: left;
}

.player-name-row {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 800;
  overflow: hidden;
  white-space: nowrap;
}

.player-name-row span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.player-name-row svg {
  flex: 0 0 auto;
  color: var(--ink-soft);
}

.player-level-row {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}

.player-level-badge {
  flex: 0 0 auto;
  min-width: 1.375rem;
  height: 1.375rem;
  padding: 0 0.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  background: linear-gradient(180deg, #ffe066, #f1a624);
  color: #4a2e00;
  font-size: 0.75rem;
  font-weight: 900;
}

.player-xp-track {
  flex: 1 1 auto;
  min-width: 3.5rem;
  height: 0.5rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}

.player-xp-fill {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, #ffcc33, #ff9f1c);
  transition: width 0.3s ease;
}

.player-xp-text {
  flex: 0 0 auto;
  color: var(--ink-soft);
  font-size: 0.625rem;
  font-weight: 700;
  white-space: nowrap;
}

/* ============ GROUNDS SHOP ============ */
#groundsGrid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
  justify-content: center;
  max-width: 40rem;
  margin-top: 1.25rem;
}

.ground-card {
  width: 11rem;
  padding: 1rem 0.9375rem;
  border-radius: 1rem;
  background: linear-gradient(180deg, #1e2a3a 0%, #131b26 100%);
  border: 2px solid var(--panel-edge);
  box-shadow: 0 0.375rem 1rem rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  position: relative;
}

.ground-card.unlocked {
  border-color: rgba(143, 220, 77, 0.5);
}

.ground-card.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent), 0 0.375rem 1rem rgba(0, 0, 0, 0.4);
}

.ground-card .ground-name {
  color: var(--ink);
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: 1px;
}

.ground-card .ground-subtitle {
  color: var(--ink-soft);
  font-size: 0.6875rem;
  letter-spacing: 0.5px;
  margin-top: -0.25rem;
}

.ground-card .ground-req {
  color: var(--ink-soft);
  font-size: 0.75rem;
  display: flex;
  gap: 0.625rem;
}

.ground-card .ground-req span.met {
  color: #8fdc4d;
}

.ground-card .ground-req span.unmet {
  color: #f57a5a;
}

.ground-card button {
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  border-radius: 0.625rem;
  padding: 0.5rem;
  border: none;
  transition: transform .08s, box-shadow .08s;
}

.ground-card button.btn-select {
  color: #1c2a17;
  background: linear-gradient(180deg, #8fdc4d 0%, #5cb531 100%);
  box-shadow: 0 0.1875rem 0 #3c7d1e;
}

.ground-card button.btn-selected {
  color: var(--accent);
  background: rgba(241, 196, 15, 0.15);
  border: 1px solid rgba(241, 196, 15, 0.5);
  cursor: default;
}

.ground-card button.btn-unlock {
  color: #3a2c05;
  background: linear-gradient(180deg, #ffe27a 0%, #ffc93a 100%);
  box-shadow: 0 0.1875rem 0 #9c7714;
}

.ground-card button.btn-locked {
  color: var(--ink-soft);
  background: rgba(255, 255, 255, 0.06);
  cursor: not-allowed;
}

.ground-card button:active {
  transform: translateY(0.125rem);
}

/* Locked venue buttons in Custom Match's setup screen */
.opt-row button.locked {
  opacity: 0.5;
  cursor: not-allowed;
}

.opt-row button .lock-icon {
  margin-left: 0.25rem;
}

/* ============ LEADERBOARDS ============ */
/* .leaderboard-row / .lb-rank / .lb-name / .lb-score used to be styled here
   (a flat translucent strip, bare "#3" rank text). That whole treatment was
   replaced — see the "LEADERBOARD SCREEN" block further down this sheet,
   which adds medal badges, a placeholder avatar, and real row cards. Left as
   a marker rather than silently vanishing, since "LEADERBOARDS" is what
   someone searching this file for the feature would type. */

/* "···" divider between the top-30 list and the player's own pinned row when
   they're outside it — renderLeaderboardRows() (js/leaderboard-ui.js) inserts
   this wherever consecutive rows' ranks aren't adjacent, so it also just works
   if a future board pins someone in from further down than #31. */
.leaderboard-gap {
  text-align: center;
  color: #64748b;
  font-size: 1.1rem;
  font-weight: 800;
  letter-spacing: 3px;
  margin: 0.1rem 0 0.5rem;
  user-select: none;
}

/* Loading spinner shown while the leaderboard fetch (a real network round
   trip to Supabase) is in flight. */
.leaderboard-spinner {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 0;
}

.lb-spinner-ring {
  width: 2.25rem;
  height: 2.25rem;
  border: 0.2rem solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: lbSpin 0.8s linear infinite;
}

@keyframes lbSpin {
  to {
    transform: rotate(360deg);
  }
}

#exitOverlay .row {
  display: flex;
  gap: 1rem
}

#exitOverlay a#exitBtn {
  pointer-events: auto;
  cursor: pointer;
  text-decoration: none;
  font-size: 1.1875rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #3d0f08;
  background: linear-gradient(180deg, #f57a5a 0%, #d8402f 100%);
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  box-shadow: 0 0.3125rem 0 #8f2417, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

#exitOverlay a#exitBtn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #8f2417, 0 0.25rem 0.625rem rgba(0, 0, 0, .4)
}

/* .back-btn is the standalone back arrow used by the menu sub-screens. It is
   not in an anchor bar (each screen places its own), so it keeps its own
   position:fixed offsets — but it shares the icon-button size tokens so it
   stays visually identical to the cluster buttons at every scale.
   #homeBtn's copy of this block is gone; it uses .icon-btn now. */
.back-btn {
  pointer-events: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--hud-btn);
  height: var(--hud-btn);
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  color: #ffffff;
  background: linear-gradient(180deg, #1b2e44 0%, #0d1624 100%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-bottom: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--hud-radius);
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.2);
  transition: transform 0.1s, filter 0.1s;
  cursor: pointer;
}

.back-btn>svg {
  width: 55%;
  height: 55%;
}

.back-btn:hover {
  filter: brightness(1.2);
  transform: translateY(-2px);
}

.back-btn:active {
  transform: translateY(1px);
  border-bottom-width: 1px;
}

@media (max-width:40rem) {
  #scoreboard {
    gap: 0.875rem;
    padding: 0.5rem 0.875rem
  }

  #scoreboard .stat {
    min-width: 2.625rem
  }

  #scoreboard .stat+.stat {
    padding-left: 0.875rem
  }

  /* .label / .value font-size intentionally not overridden here — they're
     clamp()-based now and already scale for this width without help. */

  /* The #homeBtn reposition that used to sit here (bottom-left at narrow
     widths) is gone — it travels with #hudActions now. */
}

#speedPanel {
  top: 5.125rem;
  bottom: auto;
  right: 1rem;
  transform: none;
  padding: 0.4375rem 0.6875rem
}

/* Bowler-info text: clamped px rather than rem. These were rem (partly inline in
   index.html) and the mobile root font-size drop to 12px/10px/8px crushed them —
   "Elite Fast" measured 5px and the delivery type 6px on a landscape phone. The
   upper bounds match the previous desktop sizes so nothing changes there. */
#bowlerName {
  font-size: clamp(11px, 2.6vw, 14px);
}

#bowlerStyle {
  font-size: clamp(9px, 1.9vw, 10px);
  letter-spacing: 1px;
}

#speedPanel .val {
  font-size: clamp(11px, 2.6vw, 14px);
}

#speedPanel .type {
  font-size: clamp(9px, 2vw, 11px);
}

/* #soundBtn and #controlsBtn had near-identical copies of the icon-button skin
   here, plus their own corner offsets and the `body.match-active #soundBtn
   { right: 10.5rem }` dodge. All of it is now .icon-btn inside #hudActions. */




#flash {
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(216, 64, 47, .45) 0%, rgba(216, 64, 47, .15) 60%, transparent 100%);
  opacity: 0;
  transition: opacity .18s;
}

#flash.show {
  opacity: 1
}

#timingFeedback {
  position: absolute;
  bottom: -1.5rem;
  left: 0;
  width: 100%;
  text-align: center;
  transform: scale(0.5);
  font-size: 0.875rem;
  font-weight: 900;
  color: white;
  text-shadow: 0 2px 0.25rem rgba(0, 0, 0, 0.8), 0 0 0.625rem rgba(0, 0, 0, 0.5);
  opacity: 0;
  pointer-events: none;
  z-index: 100;
  transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.2s;
  letter-spacing: 1px;
}

#timingFeedback.show {
  transform: scale(1.0);
  opacity: 1;
}

/* Controls popup. Play is paused while this is open, so it reads as a full
   modal rather than the old always-on bottom strip. */
#controlsOverlay {
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(6, 10, 18, 0.72);
  backdrop-filter: blur(0.25rem);
  z-index: 400;
  pointer-events: auto;
}

#controlsOverlay.show {
  display: flex;
}

.controls-card {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.75rem;
  width: 90%;
  max-width: 20rem;
  padding: 1.375rem 1.25rem 1.25rem;
  background: linear-gradient(180deg, rgba(23, 34, 52, 0.97) 0%, rgba(10, 17, 28, 0.98) 100%);
  border: 1px solid rgba(120, 160, 220, 0.28);
  border-radius: 1rem;
  box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.06);
  text-shadow: none;
  animation: slideUpFade 0.28s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

.controls-card h2 {
  margin: 0 0 0.25rem;
  color: #fff;
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: 2px;
  text-align: center;
}

/* One row per control: name on the left, key caps on the right. */
.hint-keys {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.hint-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.375rem 0.5rem;
  background: rgba(255, 255, 255, 0.04);
  border-radius: 0.375rem;
}

.hint-caps {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}

.hint-label {
  color: #8fa8c8;
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 1.5px;
}

.controls-card kbd {
  min-width: 1.25rem;
  padding: 0.125rem 0.375rem;
  color: #fff;
  font-family: inherit;
  font-size: 0.6875rem;
  font-weight: 800;
  line-height: 1.35;
  text-align: center;
  letter-spacing: 0.5px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-bottom-width: 2px;
  border-radius: 0.3125rem;
  text-shadow: none;
}

.hint-drag {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4375rem;
  padding-top: 0.125rem;
  color: #7ea9dd;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-align: center;
  text-transform: none;
}

.controls-card .play {
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.6875rem 0;
  font-size: 1rem;
}

/* Touch devices have no keyboard, so drop the key caps and keep the gesture line. */
@media (pointer:coarse) {
  .hint-keys {
    display: none;
  }
}

/* Touch-only controls — WASD already covers movement on a mouse/keyboard
   device, so these are just clutter there. Gated on the actual input type
   (not screen size) so a touch laptop keeps it and a narrow desktop window
   doesn't gain it. revealMatchHud() sets display:block inline, hence
   !important to still win here. */
@media (hover: hover) and (pointer: fine) {

  #touchZoneLeft,
  #touchZoneRight,
  #floatingJoystick {
    display: none !important;
  }
}

/* ── Split-screen touch zones ──
   Two invisible overlays that split the screen in half.
   Left zone  → floating joystick for movement
   Right zone → drag-to-aim slingshot for batting */
#touchZoneLeft,
#touchZoneRight {
  position: fixed;
  top: 0;
  bottom: 0;
  pointer-events: auto;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  z-index: 5;
}

#touchZoneLeft {
  left: 0;
  width: 50%;
}

#touchZoneRight {
  right: 0;
  width: 50%;
}

/* Tutorial step 3 glow — highlights the left zone to teach joystick */
#touchZoneLeft.glow {
  background: rgba(61, 120, 232, 0.12);
  border-right: 2px dashed rgba(61, 120, 232, 0.5);
  animation: zoneGlow 1.5s infinite alternate;
}

@keyframes zoneGlow {
  from {
    background: rgba(61, 120, 232, 0.08);
  }

  to {
    background: rgba(61, 120, 232, 0.2);
  }
}

/* ── Floating virtual joystick ──
   Appears at the thumb's touch point on the left zone.
   Visual only — the touch zone handles input. */
#floatingJoystick {
  position: fixed;
  z-index: 6;
  pointer-events: none;
}

.joy-base {
  width: 7.5rem;
  height: 7.5rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  border: 2px solid rgba(255, 255, 255, 0.2);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.3);
  position: absolute;
  transform: translate(-50%, -50%);
}

.joy-knob {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: linear-gradient(180deg, #3d78e8 0%, #2a5bb5 100%);
  border: 2px solid rgba(255, 255, 255, 0.5);
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.35);
  position: absolute;
  transform: translate(-50%, -50%);
}

/* Icon-over-label panel, matching the menu button treatment. */
#shotModeBtn {
  right: 1.5rem;
  bottom: 5rem;
  width: 4.75rem;
  padding: 0.6875rem 0.5rem 0.5625rem;
  background: linear-gradient(180deg, #1b2e44 0%, #0d1624 100%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-bottom: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  color: #fff;
  font-weight: 800;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  pointer-events: auto;
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.15);
  transition: transform 0.1s, filter 0.1s, border-color 0.2s, color 0.2s;
  cursor: pointer;
}

#shotModeBtn:hover {
  filter: brightness(1.2);
}

#shotModeBtn:active {
  transform: translateY(1px);
  border-bottom-width: 1px;
}

/* Aerial keeps the same panel and signals state through colour, like the
   reference buttons, rather than repainting the whole chip. */
#shotModeBtn.aerial {
  color: #ff9f6b;
  border-color: rgba(255, 140, 90, 0.55);
}

#shotModeBtn.glow {
  animation: buttonGlow 1.5s infinite alternate;
  border-color: #ffd94a;
}

@keyframes buttonGlow {
  from {
    box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5), 0 0 0.625rem 2px rgba(255, 217, 74, 0.4);
  }

  to {
    box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5), 0 0 1.5625rem 0.5rem rgba(255, 217, 74, 0.9);
  }
}

#shotModeBtn span.icon {
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

#shotModeBtn .lbl {
  /* clamped px: 0.625rem collapsed to 5px on a landscape phone (8px root). */
  font-size: clamp(9px, 2vw, 10px);
  font-weight: 800;
  letter-spacing: 1px;
  line-height: 1;
}

/* Both glyphs live in the markup; the class picks which one shows, so toggling
   the mode doesn't have to rewrite icon markup in JS. */
#shotModeBtn .icon-aerial,
#shotModeBtn.aerial .icon-ground {
  display: none;
}

#shotModeBtn.aerial .icon-aerial {
  display: block;
}

/* Matches #scoreboard's card language: same background, border, radius family
   and shadow, so the training panel reads as part of the same HUD system
   instead of a separate leftover style. */
#trainingUI {
  top: 1rem;
  left: 1rem;
  width: 12.5rem;
  background: #12161b;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: 1rem;
  padding: 0.875rem;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.6);
  color: var(--ink);
  z-index: 10;
  pointer-events: auto;
}

.training-title {
  color: var(--ink-soft);
  font-size: clamp(10px, 2.6vw, 13px);
  font-weight: 700;
  letter-spacing: clamp(0.5px, 0.3vw, 2px);
  text-align: center;
  margin-bottom: 0.75rem;
}

.training-field {
  margin-bottom: 0.625rem;
}

.training-label {
  color: var(--ink-soft);
  font-size: clamp(9px, 2.2vw, 11px);
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin-bottom: 0.25rem;
}

.training-select {
  width: 100%;
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: clamp(11px, 2.6vw, 13px);
  font-weight: 700;
  border: 1px solid var(--panel-edge);
  border-radius: 0.5rem;
  padding: 0.4375rem 0.5rem;
  cursor: pointer;
}

.training-select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.training-end-btn {
  width: 100%;
  margin-top: 0.25rem;
  font-size: clamp(10px, 2.4vw, 12px);
  padding: 0.5rem;
  border-radius: 0.5rem;
  box-shadow: none;
}

body.match-active #trainingUI {
  animation: uiSlideInTopLeft 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) both 0.4s;
}

#timingMeterContainer {
  left: 50%;
  top: 13rem;
  transform: translateX(-50%);
  width: 17.5rem;
  background: rgba(18, 25, 38, 0.85);
  border: 2px solid var(--panel-edge);
  border-radius: 0.5rem;
  padding: 0.625rem 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  transition: opacity 0.2s, box-shadow 0.2s, transform 0.3s ease, top 0.3s ease;
  opacity: 0;
  pointer-events: none;
}

#timingMeterContainer.tutorial-scale {
  transform: translateX(-50%) scale(1.4);
  top: auto !important;
  bottom: 1.875rem !important;
}

/* Tutorial's practice position: mounted inside #tutTimingSlot in flex flow */
#timingMeterContainer.tutorial-pos {
  position: relative !important;
  top: auto !important;
  left: auto !important;
  transform: none !important;
  width: 100% !important;
  max-width: 290px !important;
  background: rgba(10, 18, 35, 0.94);
  border: 2px solid #38bdf8;
  border-radius: 0.85rem;
  padding: 0.45rem 0.85rem;
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.85), 0 0 15px rgba(56, 189, 248, 0.4);
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  box-sizing: border-box;
}

#timingMeterContainer.match-mode {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0;
  transform: none;
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
}

#timingMeterContainer.match-mode.glow {
  box-shadow: none;
  border-color: transparent;
}

#timingMeterContainer.match-mode .label {
  display: block;
  margin-bottom: 0;
  /* px, not rem: 0.5rem here collapsed to 5px once the root font-size drops to
     10px on phones, which was unreadable. 8px matches the previous desktop
     result (0.5rem x 16px) exactly, so only mobile changes. */
  font-size: 8px;
  letter-spacing: 0.5px;
}

#timingMeterContainer.match-mode #timingMeterBar {
  flex: 1;
  height: 100%;
  border-radius: 4px;
}

#timingMeterContainer.match-mode #timingFeedback {
  top: 12px;
  bottom: auto;
  left: 0;
  width: 100%;
  transform: scale(0.5);
  /* match initial scale */
}

#timingMeterContainer.match-mode #timingFeedback.show {
  transform: scale(0.7);
}

#timingMeterContainer.show {
  opacity: 1;
}

#timingMeterContainer.glow {
  box-shadow: 0 0 1.25rem 0.3125rem rgba(46, 204, 113, 0.8);
  border-color: #2ecc71;
}

#timingMeterContainer .label {
  font-size: 0.6875rem;
  color: #aaa;
  letter-spacing: 2px;
  margin-bottom: 0.5rem;
  font-weight: bold;
}

#timingMeterBar {
  width: 100%;
  height: 1rem;
  background: #111;
  border-radius: 0.5rem;
  position: relative;
  overflow: hidden;
  display: flex;
  box-shadow: inset 0 2px 0.25rem rgba(0, 0, 0, 0.8);
  transition: all 0.2s ease;
}

#timingMeterBar .zone {
  height: 100%;
}

.red-early {
  flex: 1;
  background: linear-gradient(90deg, #8b0000, #ff4444);
}

.orange-early {
  width: 15%;
  background: linear-gradient(90deg, #ff4444, #ffb84d);
}

.green-perfect {
  width: 24%;
  background: #2ecc71;
  box-shadow: 0 0 0.5rem #2ecc71;
  z-index: 2;
}

.orange-late {
  width: 15%;
  background: linear-gradient(90deg, #ffb84d, #ff4444);
}

.red-late {
  flex: 1;
  background: linear-gradient(90deg, #ff4444, #8b0000);
}

#timingNeedle {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 0.25rem;
  background: #fff;
  box-shadow: 0 0 0.375rem #fff;
  border-radius: 2px;
  left: 0%;
  z-index: 5;
}

#batPowerMeterContainer {
  position: fixed;
  left: 1.5rem;
  top: 50%;
  transform: translateY(-50%) scale(0.92);
  background: rgba(15, 23, 42, 0.88);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1.5px solid rgba(255, 255, 255, 0.18);
  border-radius: 1.25rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.65rem 0.6rem 0.6rem 0.6rem;
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.55), 0 0 15px rgba(0, 0, 0, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s cubic-bezier(0.16, 1, 0.3, 1), transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
  z-index: 100;
}

#batPowerMeterContainer.show {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}

#batPowerMeterContainer.max-power {
  border-color: rgba(239, 68, 68, 0.85);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6), 0 0 20px rgba(239, 68, 68, 0.65);
}

#batPowerHeader {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  margin-bottom: 0.35rem;
}

.power-icon {
  font-size: 0.75rem;
  filter: drop-shadow(0 0 4px rgba(250, 204, 21, 0.8));
}

#batPowerVal {
  color: #ffffff;
  font-size: 0.75rem;
  font-weight: 900;
  font-family: inherit;
  letter-spacing: 0.5px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}

.vert-power-meter-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.vert-power-meter-box .vert-meter-label {
  font-size: 0.58rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

.vert-power-meter-box .vert-meter-label.max {
  color: #f87171;
  margin-bottom: 0.2rem;
  text-shadow: 0 0 6px rgba(248, 113, 113, 0.6);
}

.vert-power-meter-box .vert-meter-label.min {
  color: #34d399;
  margin-top: 0.2rem;
  text-shadow: 0 0 6px rgba(52, 211, 153, 0.6);
}

#batPowerTrack.vert-meter-track {
  position: relative;
  width: 15px;
  height: 110px;
  background: rgba(0, 0, 0, 0.75);
  border-radius: 8px;
  border: 1.5px solid rgba(255, 255, 255, 0.25);
  box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.7);
  overflow: hidden;
}

#batPowerFill {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 0%;
  background: linear-gradient(0deg, #10b981 0%, #facc15 50%, #f97316 75%, #ef4444 100%);
  border-radius: 6px;
  animation: none !important;
  transition: height 0.04s ease-out;
  box-shadow: 0 0 10px rgba(250, 204, 21, 0.4);
}

#batPowerThumb {
  position: absolute;
  left: -2px;
  width: calc(100% + 4px);
  height: 4px;
  background: #ffffff;
  box-shadow: 0 0 8px #ffffff, 0 0 14px rgba(56, 189, 248, 0.9);
  border-radius: 2px;
  bottom: 0%;
  transform: translateY(50%);
  animation: none !important;
  transition: bottom 0.04s ease-out;
  z-index: 2;
}

/* font-size/letter-spacing were fixed rem, so the longer venue names
   ("MELBOURNE CRICKET GROUND", "SYDNEY CRICKET GROUND") ran off both edges
   of a portrait phone — nothing here scaled down with viewport width, only
   with the (unrelated) root font-size cascade. vw-based clamp() keeps the
   dramatic large title on desktop while guaranteeing it fits on narrow
   screens; max-width + nowrap+ellipsis is a last-resort safety net rather
   than the primary fix. */
#venueCinematicText {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  max-width: 92vw;
  font-family: 'Montserrat', sans-serif;
  font-size: clamp(1.125rem, 6.5vw, 2.5rem);
  font-weight: 800;
  letter-spacing: clamp(0.1875rem, 2vw, 0.75rem);
  color: rgba(255, 255, 255, 0.95);
  text-transform: uppercase;
  text-shadow: 0 0 1.25rem rgba(0, 0, 0, 0.8), 2px 2px 0.25rem rgba(0, 0, 0, 1);
  opacity: 0;
  pointer-events: none;
  z-index: 60;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
  transition: opacity 1.2s ease-in-out, transform 4s cubic-bezier(0.1, 0.8, 0.2, 1);
}

#venueCinematicText.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1.1);
}

/* Lets the player skip the ground's drone fly-in intro. Same bevel-button
   language as every other icon/action button in the game (soundBtn,
   homeBtn, .play, mode cards, ...) — this one was a flat semi-transparent
   pill with muted grey text, standing out as visually unrelated to the rest
   of the HUD instead of reading as "part of the game". */
#groundIntroSkipBtn {
  right: 1.5rem;
  bottom: 1.5rem;
  display: flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.625rem 1.0625rem;
  background: linear-gradient(180deg, #1b2e44 0%, #0d1624 100%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-bottom: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: 999px;
  box-shadow: 0 0.25rem 0.375rem rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.15);
  color: #fff;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.1s, filter 0.1s;
  z-index: 210;
}

#groundIntroSkipBtn.show {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

#groundIntroSkipBtn:hover {
  filter: brightness(1.2);
}

#groundIntroSkipBtn:active {
  transform: translateY(1px);
  border-bottom-width: 1px;
}

#groundIntroSkipBtn svg {
  flex-shrink: 0;
}



@media (max-width:40rem) {

  #shotModeBtn {
    bottom: 6.875rem;
    right: 1rem;
    width: 4.25rem;
    padding: 0.5625rem 0.375rem 0.4375rem;
  }

  #shotModeBtn .icon svg {
    width: 22px;
    height: 22px;
  }

  /* .lbl font-size intentionally not overridden — it's clamped now and already
     scales for this width without shrinking below legibility. */

  #timingMeterContainer {
    top: 13rem;
    width: 15rem;
  }

  /* #scoreboard's `top` override is gone — #hudTopBar's padding sets the
     offset from the screen edge for the whole row now. */

  /* #minimap's size/offset overrides are gone — the single clamp()-based rule
     in the HUD ANCHOR SYSTEM block covers every viewport. */
}

/* Phones: the scoreboard becomes a full-bleed top bar with the minimap and
   controls button stacked underneath it.
   Why full width rather than the desktop centred pill: with only `left: 50%`
   set and no `right`, a fixed element's shrink-to-fit width is capped at the
   *remaining* 50% of the viewport (187px of 375px), so `max-width` was never
   reachable and the five stat groups were forced to wrap onto three rows — a
   tall narrow blob with half the screen width sitting unused. The minimap and
   controls button used to flank it, squeezing it into an even narrower middle
   column, so they move below. 40rem (not 26rem) because a single row of all
   five groups needs ~410px, so anything under ~820px hits the 50% cap. */
@media (max-width:40rem) {

  /* The bar wraps: the scoreboard claims a full row and the two side zones
     drop below it. `order: -1` puts it on the first row regardless of DOM
     order. This replaces the old approach of pushing each corner control down
     by `calc(var(--scoreboard-h) + ...)` — the zones now sit below the
     scoreboard because they wrapped, so the clearance is exact by construction
     and no longer depends on a measured custom property. */
  #hudTopBar {
    flex-wrap: wrap;
    /* The bar's own inset is dropped so the scoreboard can bleed to the screen
       edges; each zone re-applies it below. row-gap goes to 0 for the same
       reason — the zones' own padding-top provides the separation, so it isn't
       counted twice. */
    padding: 0;
    row-gap: 0;
  }

  /* Zones re-apply the edge inset themselves. padding-top (not the bar's) is
     what keeps them off the top of the screen on the MENU, where the
     scoreboard is hidden and a zone is the first thing in the row. */
  #hudTopBar>.hud-zone {
    padding: var(--hud-edge-top) var(--hud-edge-right) 0 var(--hud-edge-left);
  }

  #scoreboard {
    order: -1;
    flex: 1 0 100%;
    max-width: none;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    row-gap: 0.25rem;
    column-gap: 0.5rem;
    /* Square off the top edge since it's flush with the screen. */
    border-radius: 0 0 0.875rem 0.875rem;
    border-top: none;
    /* The background bleeds under a notch; the padding keeps the text out of
       it. env() is 0 on hardware without one. */
    padding: calc(0.375rem + env(safe-area-inset-top, 0px)) 0.625rem 0.625rem;
  }

  /* Tighter dividers so Score/Overs/RRR/Target still share one row on a 320px
     screen — at the wider spacing they needed 234px of a 215px inner width and
     Target dropped to its own line, making the bar noticeably taller. */
  #scoreboard .stat+.stat {
    padding-left: 0.5rem;
  }

  /* #minimap no longer offsets itself by --scoreboard-h here: it is a child of
     the left zone, and the zone wraps below the full-bleed scoreboard, so the
     clearance is exact rather than measured. */

  /* The portrait #soundBtn / #controlsBtn repositions that used to sit here —
     stacking them below the full-width scoreboard, one needing a
     body.match-active selector purely to win a specificity fight — are gone.
     The PORTRAIT rule in the HUD ANCHOR SYSTEM block moves the whole cluster
     to the bottom-right instead, clear of the scoreboard entirely. */

  /* The timing meter is moved into this placeholder during matches (see
     updateTimingMeterPosition() in gameplay.js). It used to be pushed below the
     panel with a negative offset, which left it floating over the pitch
     detached from the HUD; now the bar is full width it sits back inside the
     bottom edge where it belongs.
     !important needed: the placeholder's offsets are inline in index.html. */
  #matchTimingPlaceholder {
    left: 0.5rem !important;
    right: 0.5rem !important;
    width: auto !important;
    bottom: 0.25rem !important;
    height: 0.75rem !important;
  }
}

/* Narrow phones: keep the bowler info and ball timeline inline, to the right of
   Target, instead of dropping them onto their own row.
   The four stat groups only need ~234px of a 377px inner width, so pushing the
   bowler panel to its own line left ~144px empty at each end of the row AND made
   the bar a row taller. It fits in that leftover space once it's allowed to
   shrink (min-width:0) and the timeline dots are scaled down. */
@media (max-width: 26rem) {
  #scoreboard {
    /* Spread across the full width so the leftover space is consumed by the
       gaps between groups rather than dumped at both ends. */
    justify-content: space-between;
    flex-wrap: nowrap;
  }

  #scoreboard #speedPanel {
    flex: 1 1 auto;
    /* Without this a flex item won't shrink below its content width, which is
       what would force the wrap back. */
    min-width: 0;
    text-align: left;
    overflow: hidden;
  }

  /* Dots are sized inline by refreshHud() in ui.js, hence !important. Smaller
     here so six of them fit the narrower column. Wrapping matters because an
     over with wides/no-balls renders more than six (refreshHud uses
     max(6, history.length)) — without it they'd be clipped by the overflow
     rule above and the player would lose track of the over. */
  #scoreboard #overTimeline {
    gap: 0.25rem !important;
    flex-wrap: wrap;
  }

  #scoreboard #overTimeline>div {
    width: 11px !important;
    height: 11px !important;
    font-size: 8px !important;
    flex: 0 0 auto;
  }

  /* Long names (e.g. "The Destroyer") ellipsise rather than being hard-cut by
     the panel's overflow:hidden. */
  #scoreboard #bowlerName,
  #scoreboard #bowlerStyle {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

@keyframes uiSlideInTopBar {
  0% {
    transform: translateY(-150%);
    opacity: 0;
  }

  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

/* TUTORIAL STYLES */
.tutorial-screen {
  transition: opacity 0.3s ease-in-out;
}

@keyframes fingerSwipe {
  0% {
    transform: translateY(0) scale(1);
    opacity: 0;
  }

  15% {
    opacity: 1;
    transform: translateY(0) scale(0.9);
  }

  75% {
    transform: translateY(7.5rem) scale(0.9);
    opacity: 1;
  }

  100% {
    transform: translateY(7.5rem) scale(1);
    opacity: 0;
  }
}

/* Tutorial pointer hand — bounces next to the target button */
#tutPointer {
  position: fixed;
  transition: opacity 0.25s ease;
}

#tutPointer.show {
  display: block !important;
  animation: tutPointerBounce 1s infinite ease-in-out;
}

@keyframes tutPointerBounce {

  0%,
  100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(-0.75rem);
  }
}

.tut-circle {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  border: 0.1875rem solid rgba(255, 255, 255, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.4);
  font-weight: bold;
  font-size: 0.875rem;
  background: rgba(0, 0, 0, 0.5);
  transition: all 0.3s ease;
}

.tut-circle.success {
  border-color: #5cb531;
  color: #1c2a17;
  background: #5cb531;
  box-shadow: 0 0 0.625rem #5cb531;
}

@keyframes introFadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes introPopElastic {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }

  50% {
    transform: scale(1.05);
    opacity: 1;
  }

  75% {
    transform: scale(0.95);
    opacity: 1;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes introSlideUp {
  0% {
    transform: translateY(2rem);
    opacity: 0;
  }

  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

#matchIntroScreen {
  animation: introFadeIn 0.3s ease-out forwards;
}

#matchIntroScreen h2 {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.1s;
}

#matchIntroScreen>div {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.2s;
}

#matchIntroScreen button {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.4s;
}

#overlay.show h2,
#exitOverlay.show h2 {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.1s;
}

#overlay.show p,
#exitOverlay.show p {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.2s;
}

#overlay.show .play,
#exitOverlay.show .row {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.3s;
}

#overlay.show .custom-match-nav-btn {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.38s;
}

/* Survival and Six Challenge slot into the stagger between Custom Match and
   Leaderboards, so the menu still rises in one continuous sequence. */
#overlay.show #homeSurvivalBtn {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.46s;
}

#overlay.show #homeSixChallengeBtn {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.54s;
}

#overlay.show .shop-btn {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.62s;
}

#overlay.show .leaderboard-icon-btn {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.70s;
}

#overlay.show .how-to-play-nav-btn {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.78s;
}

/* Cards rise in on open. Staggered via nth-child so the grid populates in
   sequence rather than all eight appearing at once. */
#modeSelectScreen.show #modeSelectTitle {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.05s;
}

#modeSelectScreen.show .mode-card {
  opacity: 0;
  animation: introSlideUp 0.35s ease-out forwards;
}

#modeSelectScreen.show .mode-card:nth-child(1) {
  animation-delay: 0.10s;
}

#modeSelectScreen.show .mode-card:nth-child(2) {
  animation-delay: 0.15s;
}

#modeSelectScreen.show .mode-card:nth-child(3) {
  animation-delay: 0.20s;
}

#modeSelectScreen.show .mode-card:nth-child(4) {
  animation-delay: 0.25s;
}

#modeSelectScreen.show .mode-card:nth-child(5) {
  animation-delay: 0.30s;
}

#modeSelectScreen.show .mode-card:nth-child(6) {
  animation-delay: 0.35s;
}

#modeSelectScreen.show .mode-card:nth-child(7) {
  animation-delay: 0.40s;
}

#modeSelectScreen.show .mode-card:nth-child(8) {
  animation-delay: 0.45s;
}

#modeSelectScreen.show .mode-card:nth-child(9) {
  animation-delay: 0.50s;
}

#modeSelectScreen.show .back-btn {
  opacity: 1;
  animation: backBtnSlideIn 0.45s cubic-bezier(0.25, 1, 0.5, 1) backwards;
  animation-delay: 0.15s;
}

.loader-spinner {
  width: 3rem;
  height: 3rem;
  border: 4px solid rgba(255, 255, 255, 0.1);
  border-top-color: #5cb531;
  border-radius: 50%;
  animation: spinLoader 1s linear infinite;
}

@keyframes spinLoader {
  to {
    transform: rotate(360deg);
  }
}

#matchIntroScreen.closing,
#exitOverlay.closing {
  animation: introFadeOut 0.2s ease-in forwards !important;
}

#matchIntroScreen.closing h2,
#matchIntroScreen.closing>div,
#matchIntroScreen.closing button,
#exitOverlay.closing h2,
#exitOverlay.closing p,
#exitOverlay.closing .row {
  animation: introPopOut 0.2s ease-in forwards !important;
  animation-delay: 0s !important;
}

@keyframes introFadeOut {
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
}

@keyframes introPopOut {
  0% {
    transform: scale(1);
    opacity: 1;
  }

  100% {
    transform: scale(0.8);
    opacity: 0;
  }
}

@keyframes uiSlideInTopCenter {
  0% {
    transform: translate(-50%, -150%);
    opacity: 0;
  }

  100% {
    transform: translate(-50%, 0);
    opacity: 1;
  }
}

@keyframes uiSlideInBottomCenter {
  0% {
    transform: translate(-50%, 150%);
    opacity: 0;
  }

  100% {
    transform: translate(-50%, 0);
    opacity: 1;
  }
}

@keyframes uiSlideInTopRight {
  0% {
    transform: translate(150%, 0);
    opacity: 0;
  }

  100% {
    transform: translate(0, 0);
    opacity: 1;
  }
}

@keyframes uiSlideInTopLeft {
  0% {
    transform: translate(-150%, 0);
    opacity: 0;
  }

  100% {
    transform: translate(0, 0);
    opacity: 1;
  }
}

@keyframes uiSlideInBottomLeft {
  0% {
    transform: translate(-150%, 0);
    opacity: 0;
  }

  100% {
    transform: translate(0, 0);
    opacity: 1;
  }
}

@keyframes uiSlideInBottomRight {
  0% {
    transform: translate(150%, 0);
    opacity: 0;
  }

  100% {
    transform: translate(0, 0);
    opacity: 1;
  }
}

/* uiSlideInTopBar (vertical only), not uiSlideInTopCenter: the scoreboard is
   centred by its flex siblings now rather than by translateX(-50%), so the
   -50% baked into uiSlideInTopCenter would hold with `both` fill and park it
   half a width off to the left. This used to need a max-width:40rem override
   swapping the keyframes for exactly the same reason on mobile; now that the
   element never carries a centring transform, one animation covers both. */
body.match-active #scoreboard {
  animation: uiSlideInTopBar 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) both 0.4s;
}

body.match-active #minimap {
  animation: uiSlideInTopLeft 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) both 0.4s;
}

body.match-active #homeBtn,
body.match-active #cameraBtn {
  animation: uiSlideInTopRight 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) both 0.4s;
}

/* Touch zones (#touchZoneLeft / #touchZoneRight) are invisible overlays —
   no slide-in animation needed. */

body.match-active #shotModeBtn {
  animation: uiSlideInBottomRight 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) both 0.4s;
}

body.match-active #controlsBtn {
  animation: uiSlideInTopRight 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) both 0.4s;
}

/* --- Boundary Animation CSS --- */
#banner.boundary-mode {
  text-shadow: none;
  font-size: initial;
}

.boundary-wrapper.four {
  --bnd-glow1: rgba(0, 180, 255, .8);
  --bnd-glow2: rgba(0, 180, 255, .3);
  --bnd-text-color: var(--ink);
}

.boundary-wrapper.six {
  --bnd-glow1: rgba(255, 100, 0, .8);
  --bnd-glow2: rgba(255, 100, 0, .3);
  --bnd-text-color: var(--accent);
}

.boundary-wrapper.wicket {
  --bnd-glow1: rgba(231, 76, 60, .85);
  --bnd-glow2: rgba(231, 76, 60, .35);
  --bnd-text-color: #ffffff;
}

.boundary-wrapper {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
}

.boundary-wrapper::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(200px, 40vw, 350px);
  height: clamp(100px, 20vw, 180px);
  transform: translate(-50%, -50%);
  background: radial-gradient(circle,
      var(--bnd-glow1) 0%,
      var(--bnd-glow2) 40%,
      transparent 75%);
  filter: blur(25px);
  z-index: -1;
  mix-blend-mode: screen;
}

/* Numeral over word. Column so the two stack, with the numeral doing the
   shouting and the word reading as its caption. */
.boundary-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Shares .boundary-text's glow, skew and shine so the pair reads as one mark
   rather than two separate labels — only the scale differs. */
.boundary-num {
  position: relative;
  display: inline-block;
  font-size: clamp(5rem, 16vw, 9.5rem);
  font-weight: 900;
  font-family: inherit;
  line-height: 0.9;
  letter-spacing: 0;
  color: var(--bnd-text-color);
  transform: skewX(-8deg);
  text-shadow: 0 0 18px var(--bnd-glow1), 0 0 36px var(--bnd-glow2);
  animation: bndPop .55s cubic-bezier(.18, .89, .32, 1.28) forwards;
}

.boundary-num::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg,
      transparent 20%,
      rgba(255, 255, 255, .8) 50%,
      transparent 80%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  mix-blend-mode: screen;
  animation: bndShine 2.5s infinite;
}

/* With a numeral above it the word is the smaller half of the pair; on its own
   (WICKET) it keeps the original full size, so that banner is unchanged. */
.has-num .boundary-text {
  font-size: clamp(1.6rem, 4.6vw, 2.8rem);
  letter-spacing: 6px;
  /* Optical: the skew pulls the shorter word left of the numeral's centre. */
  margin-left: 0.35rem;
  animation-delay: .06s;
}

.boundary-text {
  position: relative;
  display: inline-block;
  font-size: clamp(3.5rem, 10vw, 6rem);
  font-weight: 800;
  font-family: inherit;
  text-transform: uppercase;
  letter-spacing: 2px;
  line-height: 1.1;

  color: var(--bnd-text-color);

  transform: skewX(-8deg);

  /* Colored glow instead of drop shadow */
  text-shadow: 0 0 15px var(--bnd-glow1), 0 0 30px var(--bnd-glow2);

  animation: bndPop .55s cubic-bezier(.18, .89, .32, 1.28) forwards;
}

.boundary-text::before {
  content: attr(data-text);
  position: absolute;
  inset: 0;

  background: linear-gradient(120deg,
      transparent 20%,
      rgba(255, 255, 255, .8) 50%,
      transparent 80%);

  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;

  mix-blend-mode: screen;
  animation: bndShine 2.5s infinite;
}

.boundary-shadow {
  position: absolute;
  width: clamp(150px, 30vw, 280px);
  height: clamp(20px, 4vw, 40px);
  left: 50%;
  bottom: clamp(-20px, -4vw, -40px);
  transform: translateX(-50%);

  /* Screen reflection on the pitch */
  background: radial-gradient(ellipse at center, var(--bnd-glow1) 0%, transparent 70%);
  filter: blur(10px);
  border-radius: 50%;
  mix-blend-mode: screen;
}

@keyframes bndPop {
  0% {
    transform: scale(.1) rotate(-10deg) skewX(-8deg);
    opacity: 0;
  }

  60% {
    transform: scale(1.18) rotate(2deg) skewX(-8deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0deg) skewX(-8deg);
    opacity: 1;
  }
}

@keyframes bndShine {
  0% {
    transform: translateX(-180%);
  }

  100% {
    transform: translateX(180%);
  }
}

.modal-transition {
  opacity: 0;
  transform: translateY(15px) scale(0.98);
  transition: opacity 0.3s cubic-bezier(0.25, 1, 0.5, 1), transform 0.3s cubic-bezier(0.25, 1, 0.5, 1);
}

.modal-transition.show {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.fade-transition {
  opacity: 0;
  transition: opacity 0.3s cubic-bezier(0.25, 1, 0.5, 1);
}

.fade-transition.show {
  opacity: 1;
}

/* Staggered entrance for the menu screens, mirroring #matchIntroScreen so
   Team Select, Match Setup and the tutorial don't just hard-cut into view.
   Transform-based keyframes stay on containers (#teamGrid, .opt-row) — putting
   them on a button would let `forwards` pin its transform and kill :active. */
#teamSelectScreen.show h2,
#matchSetupScreen.show h2,
#tutScreen1.show h2,
#tutScreen5.show h2,
#leaderboardScreen.show h2 {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.1s;
}

#teamSelectScreen.show .setup-label,
#matchSetupScreen.show .setup-label,
#tutScreen1.show p,
#tutScreen5.show p,
#leaderboardScreen.show .summary-card {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.2s;
}

#teamSelectScreen.show #teamGrid,
#matchSetupScreen.show #oversRow {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.26s;
}

#matchSetupScreen.show #setupDiffRow {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.32s;
}

#matchSetupScreen.show .custom-venue-carousel,
#matchSetupScreen.show .custom-venue-dots {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.38s;
}

#matchSetupScreen.show .opt-row:last-of-type {
  opacity: 0;
  animation: introSlideUp 0.4s ease-out forwards;
  animation-delay: 0.44s;
}

/* Trophy badge and coin reward on the tutorial complete screen. */
#tutScreen5.show>div {
  opacity: 0;
  animation: introPopElastic 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
  animation-delay: 0.3s;
}

/* Opacity-only so the buttons keep their :active press transform. */
#tutScreen1.show .play,
#tutScreen5.show .play {
  opacity: 0;
  animation: introFadeIn 0.4s ease-out forwards;
  animation-delay: 0.4s;
}

/* Top-left back buttons fly in from off-screen left. `backwards` fill keeps the
   button hidden off-stage during the delay but releases the transform once the
   animation ends — with `forwards` it would pin transform and kill :hover/:active. */
@keyframes backBtnSlideIn {
  0% {
    transform: translateX(-8rem);
    opacity: 0;
  }

  100% {
    transform: translateX(0);
    opacity: 1;
  }
}

#teamSelectScreen.show .back-btn,
#matchSetupScreen.show .back-btn,
#tutScreen1.show .back-btn,
#matchIntroScreen .back-btn,
#leaderboardScreen.show .back-btn {
  /* Explicit so the button stays visible after the fill is released —
     #matchIntroScreen button otherwise leaves a static opacity:0 behind. */
  opacity: 1;
  animation: backBtnSlideIn 0.45s cubic-bezier(0.25, 1, 0.5, 1) backwards;
  animation-delay: 0.3s;
}

/* Custom Player Edit Animations */
#playerEditModal #playerEditPanel {
  transition: opacity 0.4s cubic-bezier(0.25, 1, 0.5, 1), transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
  transform: translate(50px, -50%) !important;
  opacity: 0;
}

#playerEditModal.show #playerEditPanel {
  transform: translate(0, -50%) !important;
  opacity: 1;
}

#playerEditModal #playerEditBackBtn {
  transition: opacity 0.4s cubic-bezier(0.25, 1, 0.5, 1), transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
  transform: translateX(-50px);
  opacity: 0;
}

#playerEditModal.show #playerEditBackBtn {
  transform: translateX(0);
  opacity: 1;
}

/* Reward-collect flight: a small emoji clone flying from a summary reward
   row to its HUD target (see flyReward() in js/state.js), plus a quick
   landing pulse on the target pill/xp-bar. */
.reward-fly {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 600;
  font-size: 1.5rem;
  transform: translate(-50%, -50%) scale(1);
  opacity: 1;
  transition: left 0.65s cubic-bezier(0.2, 0.8, 0.2, 1), top 0.65s cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 0.65s ease, opacity 0.65s ease;
  pointer-events: none;
}

@keyframes rewardPulse {
  0% {
    transform: scale(1);
  }

  45% {
    transform: scale(1.35);
  }

  100% {
    transform: scale(1);
  }
}

.reward-pulse {
  animation: rewardPulse 0.4s ease;
}

/* Match Summary Overlay */
.summary-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
  backdrop-filter: blur(10px);
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
}

.summary-card {
  background: linear-gradient(135deg, rgba(25, 42, 63, 0.96), rgba(12, 22, 36, 0.96));
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 18px;
  padding: 18px 20px 18px 20px;
  width: 90%;
  max-width: 360px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.85), inset 0 2px 5px rgba(255, 255, 255, 0.1);
  text-align: center;
  color: white;
  will-change: transform, opacity;
  animation: slideUpFade 0.28s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

@keyframes slideUpFade {
  0% {
    opacity: 0;
    transform: translateY(20px) scale(0.96);
  }

  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes slideDownFadeOut {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }

  100% {
    opacity: 0;
    transform: translateY(20px) scale(0.96);
  }
}

.summary-card.exit-anim {
  animation: slideDownFadeOut 0.18s cubic-bezier(0.4, 0, 0.2, 1) forwards !important;
}

.summary-card h2#summaryTitle {
  font-size: 1.3rem;
  font-weight: 900;
  letter-spacing: 1px;
  margin: 0 0 0.5rem 0;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

.summary-stats {
  margin: 6px 0 8px 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  text-align: left;
}

.summary-rewards {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
}

.rewards-pills-row {
  display: flex;
  gap: 6px;
  width: 100%;
}

.rewards-pills-row .stat-row {
  flex: 1;
  justify-content: center;
  gap: 6px;
  padding: 5px 8px;
}

/* ===========================================================================
   LEVEL CLEAR CARD (#finalSummaryCard)
   Deliberately the MATCH TARGET card's language (.mi-card / .mi-row, further
   down this file): same navy gradient, same blue border and glow, same
   icon | label | value grid. The panel before a level and the panel after it
   were previously two different visual systems — grey translucent pills in a
   two-column grid here, a bordered blue card with aligned rows there — so the
   loop looked like it belonged to two games. Scoped to #finalSummaryCard so the
   revive card and the leaderboard keep the plain .stat-row they were built on.
   =========================================================================== */
#finalSummaryCard {
  background: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  border: 2px solid #2f5fa8;
  border-radius: 1.1rem;
  box-shadow:
    0 0 1.5rem -0.35rem rgba(59, 130, 246, 0.85),
    0 0.7rem 1.5rem rgba(0, 0, 0, 0.6),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

#finalSummaryCard h2#summaryTitle {
  font-size: clamp(1.25rem, 5.5vw, 1.6rem);
  letter-spacing: 2px;
  text-shadow:
    0 0 1.2rem rgba(125, 211, 252, 0.45),
    0 0.2rem 0 rgba(8, 20, 44, 0.9),
    0 0.45rem 1rem rgba(0, 0, 0, 0.65);
}

/* TWO columns of rows.
   The card keeps the target panel's row language — icon | label | value, ruled
   underneath — but pairs the rows up, because it carries SIX stats where the
   target card carries five and then stacks a bonus, two payout pills, the 2X
   button and three actions underneath them. In one column that stat block alone
   was 181px, 36% of a 505px card. Paired, it is three lines instead of six.

   Still ONE container, deliberately: Six Challenge hides Runs/Strike Rate/Fours
   and pulls Sixes to the front with CSS `order`, which only works between
   siblings. Splitting the stats into groups would have broken that. */
#finalSummaryCard .summary-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 0.6rem;
  row-gap: 0.15rem;
  margin: 0.3rem 0 0.15rem;
  text-align: left;
}

/* Half the width means the row has to give up some of its air. Type and the
   icon column shrink together so the icon | label | value rhythm survives at
   the smaller size. */
/* No per-row rule in this grid. Which cell is visually last is unknowable to
   CSS here: Six Challenge hides three rows AND reorders a fourth with
   `order: -1`, while :nth-last-child counts DOM position, not painted position.
   A "last pair" selector therefore stripped the border from the wrong cell and
   left a rule hanging under half a row. The cells are short and paired, so the
   grid gap separates them well enough without rules, and this cannot desync
   from whatever combination of hiding and ordering a mode applies. */
#finalSummaryCard .summary-stats .sc-row {
  grid-template-columns: 1.15rem 1fr auto;
  gap: 0.45rem;
  padding: 0.38rem 0.1rem;
  border-bottom: none;
}

#finalSummaryCard .summary-stats .sc-icon {
  font-size: 0.92rem;
}

#finalSummaryCard .summary-stats .sc-label {
  font-size: 0.62rem;
  letter-spacing: 0.5px;
}

#finalSummaryCard .summary-stats .sc-value {
  font-size: clamp(0.85rem, 3.4vw, 1.02rem);
}


/* icon | label | value. The 1fr on the label column pins every value to the same
   right edge regardless of digit count — same trick as .mi-row. */
.sc-row {
  display: grid;
  grid-template-columns: 1.6rem 1fr auto;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0.15rem;
  border-bottom: 1px solid rgba(125, 211, 252, 0.14);
}

.sc-row:last-child {
  border-bottom: none;
}

.sc-icon {
  font-size: 1.05rem;
  line-height: 1;
  text-align: center;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.6));
}

.sc-label {
  color: #dbe6f5;
  font-size: clamp(0.7rem, 2.8vw, 0.82rem);
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.sc-value {
  justify-self: end;
  color: #fff;
  font-size: clamp(0.98rem, 4vw, 1.2rem);
  font-weight: 900;
  text-align: right;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
}

.sc-value-gold {
  color: #ffc93a;
  text-shadow: 0 0 0.7rem rgba(255, 201, 58, 0.5), 0 2px 4px rgba(0, 0, 0, 0.7);
}

.sc-value-green {
  color: #6ee06e;
  text-shadow: 0 0 0.7rem rgba(110, 224, 110, 0.45), 0 2px 4px rgba(0, 0, 0, 0.7);
}

.sc-value-blue {
  color: #4d9bf5;
  text-shadow: 0 0 0.7rem rgba(77, 155, 245, 0.45), 0 2px 4px rgba(0, 0, 0, 0.7);
}

/* First-clear bonus: a row, not a banner — it keeps the grid so its label and
   value line up with the stats above, and earns its emphasis from colour alone. */
.sc-row-bonus {
  margin-top: 0.25rem;
  padding: 0.42rem 0.6rem;
  border: 1px solid rgba(245, 158, 11, 0.55);
  border-radius: 0.7rem;
  background: rgba(245, 158, 11, 0.16);
}

.sc-row-bonus .sc-label,
.sc-row-bonus .sc-value {
  color: #fbbf24;
}

/* The two payouts. Bigger than a stat row and side by side, because they are
   what the card is actually reporting. */
.sc-pill {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.05rem;
  padding: 0.38rem 0.4rem;
  border-radius: 0.8rem;
  border: 1px solid rgba(125, 211, 252, 0.3);
  background: rgba(8, 20, 44, 0.6);
}

.sc-pill-icon {
  font-size: 1.15rem;
  line-height: 1;
}

.sc-pill-label {
  color: #94a3b8;
  font-size: 0.6rem;
  font-weight: 900;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

.sc-pill-value {
  color: #fff;
  font-size: clamp(1rem, 4.2vw, 1.25rem);
  font-weight: 900;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
}

.sc-pill.reward-coins {
  border-color: rgba(250, 204, 21, 0.45);
  background: rgba(250, 204, 21, 0.12);
}

.sc-pill.reward-coins .sc-pill-value {
  color: #facc15;
}

.sc-pill.reward-stars {
  border-color: rgba(74, 222, 128, 0.45);
  background: rgba(74, 222, 128, 0.12);
}

.sc-pill.reward-stars .sc-pill-value {
  color: #4ade80;
  letter-spacing: 1px;
}

.stat-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.85rem;
  background: rgba(255, 255, 255, 0.06);
  padding: 5px 10px;
  border-radius: 6px;
}

.stat-row span {
  color: #94a3b8;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.stat-row strong {
  font-size: 1.05rem;
  color: #fff;
  font-family: inherit;
  font-weight: 800;
}

.reward-stars {
  background: rgba(74, 222, 128, 0.12) !important;
  border: 1px solid rgba(74, 222, 128, 0.3) !important;
}

.reward-stars strong {
  color: #4ade80;
}

.reward-coins {
  background: rgba(250, 204, 21, 0.12) !important;
  border: 1px solid rgba(250, 204, 21, 0.3) !important;
}

.reward-coins strong {
  color: #facc15;
}

/* ---- Match Summary Action Buttons ---- */
#summaryHomeBtn {
  position: static !important;
  flex: 0 0 3.2rem !important;
  height: 3.1rem !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 0.75rem !important;
  background: linear-gradient(180deg, #3a506b 0%, #1f2c3f 100%) !important;
  border: 2px solid #5a7b9e !important;
  box-shadow: 0 0.25rem 0 #121b28, 0 0.35rem 0.75rem rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.25) !important;
  cursor: pointer !important;
  transition: transform 0.1s ease, filter 0.15s ease, box-shadow 0.1s ease !important;
}

/* Matches #summaryHomeBtn's box exactly — .back-btn alone sizes this via
   var(--hud-btn), which doesn't agree with the Home button's hardcoded
   3.2rem/3.1rem, so the two looked mismatched sitting side by side. Only
   sizing/layout is pinned here; background/border/color stay on the
   .char-shop-cta(.has-new) rules so the cyan theming is untouched. */
#summaryShopBtn {
  position: static !important;
  flex: 0 0 3.2rem !important;
  height: 3.1rem !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 0.75rem !important;
}

#summaryHomeBtn svg {
  width: 22px !important;
  height: 22px !important;
  stroke: #ffd166 !important;
  fill: rgba(255, 209, 102, 0.25) !important;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.6)) !important;
}

#summaryHomeBtn:hover {
  filter: brightness(1.2) !important;
  border-color: #7ca4cf !important;
  transform: translateY(-1px) !important;
  box-shadow: 0 0.3rem 0 #121b28, 0 0.5rem 1rem rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35) !important;
}

#summaryHomeBtn:active {
  transform: translateY(2px) !important;
  box-shadow: 0 0.1rem 0 #121b28, 0 0.15rem 0.3rem rgba(0, 0, 0, 0.5) !important;
}

#summaryRetryBtn {
  flex: 1 !important;
  height: 3.1rem !important;
  font-size: 1.05rem !important;
  padding: 0 1rem !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 0.5rem !important;
  border-radius: 0.75rem !important;
  background: linear-gradient(180deg, #4cd964 0%, #2ecc71 100%) !important;
  border: 2px solid #6eed84 !important;
  box-shadow: 0 0.25rem 0 #1e8449, 0 0.35rem 0.75rem rgba(46, 204, 113, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.35) !important;
  color: #ffffff !important;
  font-weight: 800 !important;
  letter-spacing: 1px !important;
  cursor: pointer !important;
  transition: transform 0.1s ease, filter 0.15s ease, box-shadow 0.1s ease !important;
}

#summaryRetryBtn svg {
  width: 20px !important;
  height: 20px !important;
  stroke: #ffffff !important;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)) !important;
}

#summaryRetryBtn:hover {
  filter: brightness(1.1) !important;
  transform: translateY(-1px) !important;
  box-shadow: 0 0.3rem 0 #1e8449, 0 0.55rem 1.1rem rgba(46, 204, 113, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.4) !important;
}

#summaryRetryBtn:active {
  transform: translateY(2px) !important;
  box-shadow: 0 0.1rem 0 #1e8449, 0 0.15rem 0.3rem rgba(46, 204, 113, 0.4) !important;
}

#summaryRetryBtn.retry-mode {
  background: linear-gradient(180deg, #f59e0b 0%, #d97706 100%) !important;
  border: 2px solid #fbbf24 !important;
  box-shadow: 0 0.25rem 0 #92400e, 0 0.35rem 0.75rem rgba(245, 158, 11, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.35) !important;
}

#summaryRetryBtn.retry-mode:hover {
  filter: brightness(1.1) !important;
  transform: translateY(-1px) !important;
  box-shadow: 0 0.3rem 0 #92400e, 0 0.55rem 1.1rem rgba(245, 158, 11, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.4) !important;
}

#summaryRetryBtn.retry-mode:active {
  transform: translateY(2px) !important;
  box-shadow: 0 0.1rem 0 #92400e, 0 0.15rem 0.3rem rgba(245, 158, 11, 0.4) !important;
}

/* ---- 2X Rewarded Ad Button & Badges ---- */
/* The button needs `display: flex !important` for its layout, which beats the
   inline `display:none` gameOver() sets when a match paid no coins — so the 2X
   REWARDS button stayed on screen after every loss offering to double nothing.
   Hiding is therefore a CLASS, not an inline style: matching on the style
   attribute was tried and broke on serialisation ("display:none" vs
   "display: none"), which is exactly the kind of silent failure a class avoids. */
.reward-double-btn.is-hidden,
#summaryDoubleBtn.is-hidden {
  display: none !important;
}

.reward-double-btn,
#summaryDoubleBtn {
  width: 100% !important;
  height: 3.1rem !important;
  margin-top: 0.5rem !important;
  margin-bottom: 0 !important;
  padding: 0.35rem 1rem !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 0.6rem !important;
  border-radius: 0.75rem !important;
  background: linear-gradient(180deg, #f59e0b 0%, #d97706 100%) !important;
  border: 2px solid #fcd34d !important;
  box-shadow: 0 0.25rem 0 #92400e, 0 0.35rem 0.75rem rgba(217, 119, 6, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.4) !important;
  color: #ffffff !important;
  font-family: inherit !important;
  cursor: pointer !important;
  pointer-events: auto !important;
  z-index: 10 !important;
  position: relative !important;
  overflow: hidden !important;
  transition: transform 0.1s ease, filter 0.15s ease, box-shadow 0.1s ease !important;
}

.double-btn-content {
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  justify-content: center !important;
  line-height: 1.1 !important;
  pointer-events: none !important;
}

.double-btn-main {
  font-size: 1.05rem !important;
  font-weight: 900 !important;
  letter-spacing: 1px !important;
  color: #ffffff !important;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4) !important;
  pointer-events: none !important;
}

.double-btn-sub {
  font-size: 0.75rem !important;
  font-weight: 800 !important;
  letter-spacing: 0.5px !important;
  color: #fff6d1 !important;
  opacity: 0.95 !important;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) !important;
  pointer-events: none !important;
}

.reward-double-btn svg,
#summaryDoubleBtn svg {
  width: 24px !important;
  height: 24px !important;
  stroke: #ffffff !important;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5)) !important;
  pointer-events: none !important;
}

.reward-double-btn::after,
#summaryDoubleBtn::after {
  content: '';
  position: absolute;
  top: -50%;
  left: -60%;
  width: 40%;
  height: 200%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.45), transparent);
  transform: rotate(25deg);
  animation: doubleShimmer 2.5s infinite;
  pointer-events: none !important;
}

@keyframes doubleShimmer {
  0% {
    left: -60%;
  }

  30%,
  100% {
    left: 140%;
  }
}

.reward-double-btn:hover,
#summaryDoubleBtn:hover {
  filter: brightness(1.1) !important;
  transform: translateY(-1px) !important;
  box-shadow: 0 0.3rem 0 #92400e, 0 0.55rem 1.1rem rgba(217, 119, 6, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.45) !important;
}

.reward-double-btn:active,
#summaryDoubleBtn:active {
  transform: translateY(2px) !important;
  box-shadow: 0 0.1rem 0 #92400e, 0 0.15rem 0.3rem rgba(217, 119, 6, 0.4) !important;
}

.reward-double-btn.claimed,
#summaryDoubleBtn.claimed {
  background: linear-gradient(180deg, #3cd070 0%, #22a050 100%) !important;
  border-color: #5fe08c !important;
  box-shadow: 0 0.25rem 0 #146c33, 0 0.35rem 0.75rem rgba(46, 204, 113, 0.3) !important;
  cursor: default !important;
  pointer-events: none !important;
}

.reward-double-btn.claimed::after,
#summaryDoubleBtn.claimed::after {
  display: none !important;
}

.reward-double-btn.loading,
#summaryDoubleBtn.loading {
  opacity: 0.85 !important;
  cursor: wait !important;
  pointer-events: none !important;
}

.doubled-badge {
  display: inline-block;
  background: linear-gradient(135deg, #f59e0b, #d97706);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 800;
  padding: 2px 6px;
  border-radius: 4px;
  margin-left: 6px;
  letter-spacing: 0.5px;
  box-shadow: 0 2px 6px rgba(245, 158, 11, 0.5);
  animation: popBadge 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

@keyframes popBadge {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }

  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* ---- Second Chance Revive Modal (Survival & Six Challenge) ---- */
.revive-decline-btn {
  width: 100% !important;
  height: 3.2rem !important;
  margin-top: 0.75rem !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  background: rgba(255, 255, 255, 0.08) !important;
  border: 1px solid rgba(255, 255, 255, 0.2) !important;
  border-radius: 0.85rem !important;
  color: #cbd5e0 !important;
  font-family: inherit !important;
  font-size: 0.85rem !important;
  font-weight: 800 !important;
  letter-spacing: 1px !important;
  cursor: pointer !important;
  pointer-events: auto !important;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.1s ease !important;
}

.revive-decline-btn:hover {
  background: rgba(255, 255, 255, 0.15) !important;
  border-color: rgba(255, 255, 255, 0.4) !important;
  color: #ffffff !important;
  transform: translateY(-1px) !important;
}

.revive-decline-btn:active {
  transform: translateY(1px) !important;
}

/* ---- Simulated Rewarded Ad Modal ---- */
.simulated-ad-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.88);
  backdrop-filter: blur(8px);
  z-index: 100000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.simulated-ad-card {
  background: linear-gradient(145deg, #1e293b, #0f172a);
  border: 2px solid #38bdf8;
  border-radius: 20px;
  padding: 30px;
  max-width: 420px;
  width: 90%;
  text-align: center;
  color: #ffffff;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8), 0 0 30px rgba(56, 189, 248, 0.25);
  animation: slideUpFade 0.35s ease-out forwards;
}

.simulated-ad-badge {
  display: inline-block;
  background: rgba(56, 189, 248, 0.15);
  border: 1px solid rgba(56, 189, 248, 0.4);
  color: #38bdf8;
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 1.5px;
  padding: 4px 12px;
  border-radius: 20px;
  margin-bottom: 12px;
}

.simulated-ad-title {
  font-size: 1.1rem;
  font-weight: 800;
  margin-bottom: 20px;
  color: #f1f5f9;
}

.simulated-ad-spinner {
  font-size: 3rem;
  margin: 15px 0;
  animation: bounceAd 1s infinite alternate;
}

@keyframes bounceAd {
  0% {
    transform: scale(1);
  }

  100% {
    transform: scale(1.15);
  }
}

.simulated-ad-timer {
  font-size: 1rem;
  color: #94a3b8;
  margin-bottom: 15px;
}

.simulated-ad-timer b {
  color: #38bdf8;
}

.simulated-ad-progress {
  width: 100%;
  height: 8px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 20px;
}

.simulated-ad-bar {
  width: 0%;
  height: 100%;
  background: linear-gradient(90deg, #38bdf8, #818cf8);
  transition: width 0.1s linear;
}

.simulated-ad-close {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #cbd5e1;
  padding: 8px 20px;
  border-radius: 8px;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.2s ease;
}

.simulated-ad-close:hover {
  background: rgba(255, 255, 255, 0.2);
  color: #ffffff;
}

/* Interstitial specific styling */
.interstitial-card {
  background: linear-gradient(145deg, #181126, #0d0914);
  border: 2px solid #a855f7;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.85), 0 0 35px rgba(168, 85, 247, 0.35);
}

.interstitial-badge {
  color: #c084fc;
  background: rgba(168, 85, 247, 0.15);
  border-color: rgba(192, 132, 252, 0.4);
}

.interstitial-bar {
  background: linear-gradient(90deg, #a855f7, #ec4899);
}

@keyframes slideUpFade {
  0% {
    transform: translateY(40px) scale(0.95);
    opacity: 0;
  }

  100% {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}

/* ============================================================
   LANDSCAPE PHONES (short viewports)
   The width-based phone rules above never fire here — a landscape phone is
   WIDE (667-900px) but only ~360-420px tall — while the root font-size drops to
   8px, so everything rem-based shrinks without the layout fixes coming along.
   Sized in px so the shrunken root can't crush these further.
   ============================================================ */
/* Sizes only — safe whether or not the width-based phone rules also apply. */
@media (max-height: 500px) {
  /* The minimap's size override is gone — its clamp()'s 64px floor already
     covers this case, and the canvas width="120" attribute can no longer leak
     through because the rule sets an explicit width at every size. */

  /* The touch-target override that used to sit here is gone: --hud-btn's 40px
     clamp floor already guarantees the minimum tap size at every scale, and it
     applies to all four buttons by construction rather than by an explicit
     selector list that #soundBtn kept falling out of. */

  /* Keep the scoreboard from eating a quarter of the visible pitch. */
  #scoreboard {
    padding: 3px 10px;
    gap: 8px;
    border-radius: 14px;
  }

  #scoreboard .stat+.stat {
    padding-left: 8px;
  }

  /* SCORE/OVERS/RRR/TARGET and the bowler-info panel were still sized off
     clamp(..., Nvw, ...) — fine on a narrow tall phone, but on a short WIDE
     landscape phone the vw side of that clamp never kicks in (plenty of
     width available) so the text stayed at its full desktop size while the
     available height shrank, eating a third of the pitch. Fixed px here
     overrides that regardless of width. */
  /* letter-spacing scales down alongside each font-size drop below — left at
     its full desktop value the same absolute spacing (e.g. 1px) reads much
     wider relative to a shrunk 8-9px font than it did at the original
     11-14px, so the text still looked loosely tracked even after shrinking. */
  #scoreboard .label {
    font-size: 9px;
    letter-spacing: 0.3px;
  }

  #scoreboard .value {
    font-size: 18px;
  }

  #bowlerName,
  #speedPanel .val {
    font-size: 10px;
  }

  #bowlerStyle,
  #speedPanel .type {
    font-size: 8px;
    letter-spacing: 0.5px;
  }

  #scoreboard #overTimeline>div {
    width: 9px !important;
    height: 9px !important;
    font-size: 7px !important;
  }

  /* The bowler panel is a flex item with no flex-basis of its own, so it was
     shrinking below its text's natural width and wrapping "BEGINNER SPIN" /
     "OFF SPIN" onto a second line each — taller, not shorter. There's plenty
     of spare width on a short-but-wide landscape phone, so let it claim what
     it needs instead of getting squeezed. */
  #speedPanel {
    padding: 3px 6px;
    flex-shrink: 0;
  }

  #bowlerName,
  #bowlerStyle,
  #speedPanel .val,
  #speedPanel .type {
    white-space: nowrap;
  }

  /* Same root-font-size cliff as the scoreboard text above: the button's
     rem-based width (4.75rem) shrank to 38px at an 8px root, but "GROUND"/
     "AERIAL" didn't shrink to match, so the label overflowed the chip on
     both sides. Fixed px width + a touch less letter-spacing gets it back
     inside the button. */
  #shotModeBtn {
    width: 58px;
    padding: 6px 4px 5px;
  }

  #shotModeBtn .icon svg {
    width: 20px;
    height: 20px;
  }

  #shotModeBtn .lbl {
    font-size: 9px;
    letter-spacing: 0.5px;
  }
}

/* A `(max-height: 500px) and (min-width: 40.0625rem)` block used to sit here,
   repositioning #minimap, #controlsBtn and #homeBtn for short-and-wide landscape
   phones, carefully scoped so its offsets wouldn't fight the narrow-screen
   full-width-bar rules. Both sets of offsets are gone: the top bar lays all
   three out with flex at every size, so there is nothing left to scope. */

/* Safety net for short screens: the summary card came within 1.6px of the
   viewport edge at 568x320 and the overlay could not scroll, so any shorter
   screen (or a visible browser URL bar) would clip it with no way to reach the
   CONTINUE button. `align-items: center` clips the TOP of overflowing flex
   content, so centre via auto margins instead — that stays centred when it fits
   and scrolls when it doesn't. */
@media (max-height: 560px) {

  .summary-overlay,
  #matchIntroScreen,
  #matchSetupScreen {
    align-items: flex-start;
    overflow-y: auto;
  }

  .summary-overlay .summary-card,
  #matchIntroScreen>*:not(.back-btn),
  #matchSetupScreen>*:not(.back-btn) {
    flex-shrink: 0;
  }

  .summary-overlay .summary-card {
    margin: auto;
  }
}

/* ---- Hover feedback across all interactive UI. Scoped to real mouse
   pointers (hover:hover + pointer:fine) so a touch tap doesn't leave a
   button stuck looking "hovered" after the finger lifts. ---- */
@media (hover: hover) and (pointer: fine) {

  /* !important on transform: #overlay.show .play / .leaderboard-icon-btn and
     #modeSelectScreen.show .mode-card each carry their own entrance
     animation (introSlideUp ... forwards) directly on the element, which
     pins its own final transform indefinitely via fill-mode and otherwise
     wins over a plain :hover transform regardless of source order. */
  button.play:hover,
  .custom-match-nav-btn:hover,
  .shop-btn:hover,
  .leaderboard-icon-btn:hover,
  .how-to-play-nav-btn:hover {
    filter: brightness(1.08);
    transform: translateY(-0.125rem) !important;
  }

  .mode-card:hover {
    filter: brightness(1.1);
    transform: translateY(-0.1875rem) !important;
  }

  .team-btn:hover {
    filter: brightness(1.1);
    transform: translateY(-0.125rem);
  }

  .opt-row button:not(.locked):hover {
    filter: brightness(1.15);
    transform: translateY(-0.125rem);
  }

  .opt-row button.locked:hover {
    filter: none;
    transform: none;
  }

  .ground-card button.btn-select:hover,
  .ground-card button.btn-unlock:hover {
    filter: brightness(1.1);
    transform: translateY(-0.125rem);
  }

  #cameraBtn:hover {
    filter: brightness(1.3);
  }
}

/* ===== VIRTUAL SLINGSHOT UI ===== */
#virtualSlingshot {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1000;
  display: none;
}

#vsAnchor {
  position: absolute;
  width: 48px;
  height: 48px;
  margin-left: -24px;
  margin-top: -24px;
  border: 3px solid rgba(255, 255, 255, 0.4);
  border-radius: 50%;
  box-shadow: 0 0 12px rgba(255, 255, 255, 0.2), inset 0 0 10px rgba(255, 255, 255, 0.1);
  background: radial-gradient(circle, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0) 70%);
}

#vsHead {
  position: absolute;
  width: 24px;
  height: 24px;
  margin-left: -12px;
  margin-top: -12px;
  background: #ffffff;
  border-radius: 50%;
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.9), 0 0 5px #ffffff;
}

#vsLine {
  position: absolute;
  height: 4px;
  margin-top: -2px;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.9) 100%);
  transform-origin: 0 50%;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
  border-radius: 2px;
}

/* ===========================================================================
   HYPER-CASUAL CHARACTER PROGRESSION & UNMASKING MODAL STYLES
   =========================================================================== */
.char-prog-modal {
  position: fixed;
  inset: 0;
  z-index: 12000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 35%, rgba(30, 27, 75, 0.96) 0%, rgba(10, 14, 26, 0.98) 100%);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.char-prog-modal.show {
  opacity: 1;
  pointer-events: auto;
}

.char-prog-card {
  position: relative;
  width: 92%;
  max-width: 410px;
  max-height: 94vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: linear-gradient(180deg, #0b1528 0%, #060b16 100%);
  border: 2px solid rgba(56, 189, 248, 0.4);
  border-radius: 1.6rem;
  padding: 1.25rem 1.25rem 1.1rem;
  box-shadow: 0 0 50px rgba(5, 10, 23, 0.95), 0 0 25px rgba(56, 189, 248, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.15);
  text-align: center;
  color: white;
  transform: scale(0.92);
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
  overflow: hidden;
}

.char-prog-modal.show .char-prog-card {
  transform: scale(1);
}

.char-prog-level-badge,
.char-prog-chapter-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 1.5rem;
  background: linear-gradient(135deg, rgba(234, 179, 8, 0.25), rgba(249, 115, 22, 0.3));
  border: 2px solid rgba(234, 179, 8, 0.75);
  border-radius: 9999px;
  color: #facc15;
  font-size: 0.95rem;
  font-weight: 900;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  margin-bottom: 0.7rem;
  box-shadow: 0 0 16px rgba(234, 179, 8, 0.3);
  animation: levelBadgePulse 2s infinite ease-in-out;
}

@keyframes levelBadgePulse {

  0%,
  100% {
    transform: scale(1);
    box-shadow: 0 0 12px rgba(234, 179, 8, 0.2);
  }

  50% {
    transform: scale(1.04);
    box-shadow: 0 0 24px rgba(234, 179, 8, 0.5);
  }
}

.char-prog-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font-size: 1.18rem;
  font-weight: 900;
  letter-spacing: 0.8px;
  color: #ffffff;
  margin: 0.15rem 0 0.7rem 0;
  white-space: nowrap;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
}

.char-prog-title .title-icon {
  font-size: 1.15rem;
  filter: drop-shadow(0 0 8px rgba(56, 189, 248, 0.8));
}

.char-prog-title .title-highlight {
  color: #38bdf8;
  text-shadow: 0 0 16px rgba(56, 189, 248, 0.85);
}

.char-prog-stage-wrapper {
  position: relative;
  width: 100%;
  height: 310px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.1rem 0 0.6rem 0;
}

.char-prog-stage-glow {
  position: absolute;
  width: 280px;
  height: 280px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(56, 189, 248, 0.25) 0%, rgba(99, 102, 241, 0.15) 50%, transparent 70%);
  filter: blur(25px);
  pointer-events: none;
}

.char-prog-rays {
  position: absolute;
  width: 380px;
  height: 380px;
  background: conic-gradient(from 0deg, transparent 0deg 20deg, rgba(234, 179, 8, 0.15) 20deg 40deg, transparent 40deg 60deg, rgba(234, 179, 8, 0.15) 60deg 80deg, transparent 80deg 100deg, rgba(234, 179, 8, 0.15) 100deg 120deg, transparent 120deg 140deg, rgba(234, 179, 8, 0.15) 140deg 160deg, transparent 160deg 180deg, rgba(234, 179, 8, 0.15) 180deg 200deg, transparent 200deg 220deg, rgba(234, 179, 8, 0.15) 220deg 240deg, transparent 240deg 260deg, rgba(234, 179, 8, 0.15) 260deg 280deg, transparent 280deg 300deg, rgba(234, 179, 8, 0.15) 300deg 320deg, transparent 320deg 340deg, rgba(234, 179, 8, 0.15) 340deg 360deg);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  animation: spinRays 12s linear infinite;
  transition: opacity 0.5s ease;
}

.char-prog-modal.unlocked .char-prog-rays {
  opacity: 1;
}

@keyframes spinRays {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

@keyframes groundShotFade {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.ground-3d-viewport {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
}

.ground-3d-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 21, 40, 0.1) 0%, rgba(6, 11, 22, 0.4) 45%, rgba(6, 11, 22, 0.88) 100%);
  pointer-events: none;
  z-index: 2;
}

/* Ground Name & Location — sits above the stage card, matching the
   "NEW CHAPTER" reveal layout: big title, subtitle, then the stadium art. */
.ground-prog-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Was 0.6rem when a thumbnail card sat directly under it; with the panel
     stripped to header + meter the two need real air between them. */
  margin-bottom: 1.1rem;
}

.ground-prog-icon {
  font-size: 2rem;
  margin-bottom: 0.1rem;
  filter: drop-shadow(0 0 16px rgba(56, 189, 248, 0.9));
  animation: floatIcon 2.5s ease-in-out infinite alternate;
}

@keyframes floatIcon {
  0% {
    transform: translateY(0) scale(1);
  }

  100% {
    transform: translateY(-5px) scale(1.05);
  }
}

.ground-prog-name {
  font-size: 1.5rem;
  font-weight: 900;
  color: #ffffff;
  letter-spacing: 1.5px;
  text-shadow: 0 0 16px rgba(56, 189, 248, 0.8), 0 2px 5px rgba(0, 0, 0, 0.9);
  margin-bottom: 0.2rem;
}

.ground-prog-location {
  font-size: 0.76rem;
  font-weight: 700;
  color: #94a3b8;
  letter-spacing: 0.3px;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
  max-width: 94%;
  line-height: 1.25;
  text-align: center;
}

/* ===========================================================================
   360 GROUND VIEW POPUP — fullscreen interactive stadium view opened from
   the 360 button on the progression card.
   =========================================================================== */

/* Every HUD layer is a .hud element (#hudTopBar wraps the scoreboard, minimap,
   wallet and player card; the touch zones, shot-mode button and power meter are
   siblings), so one rule clears the screen for the tour. Class-based rather
   than inline styles: nothing to capture, nothing to leak on close. */
body.ground-360-active .hud {
  display: none !important;
}

/* Zoom stepper — rides the frame's right edge, inside it, so it sits over the
   stadium rather than in the dimmed margin where the title and close button
   live. The frame itself is pointer-events:none; this opts back in. */
.ground-360-zoom {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.35rem;
  border-radius: 9999px;
  background: rgba(8, 15, 30, 0.8);
  border: 1px solid rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(6px);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.6);
  pointer-events: auto;
}

.ground-360-zoom-btn {
  /* The root font-size scales down to 11px on a phone, which would leave these
     at 25px — too small to hit. The px floor keeps a usable tap target while
     still growing with the rest of the UI on bigger screens. */
  width: max(2.3rem, 38px);
  height: max(2.3rem, 38px);
  flex: none;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.35);
  background: rgba(30, 46, 68, 0.95);
  color: #fff;
  font-family: inherit;
  font-size: 1.25rem;
  font-weight: 900;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  /* The orbit handlers read raw pointer/touch events off the document, so the
     button must not pass a drag through to them. */
  touch-action: none;
  transition: background 0.15s ease, transform 0.12s ease, opacity 0.15s ease;
}

.ground-360-zoom-btn:hover:not(:disabled) {
  background: rgba(56, 189, 248, 0.85);
}

.ground-360-zoom-btn:active:not(:disabled) {
  transform: scale(0.92);
}

/* At the end of the range the button stays visible but clearly spent, so the
   limit reads as a limit and not as a broken control. */
.ground-360-zoom-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.ground-360-zoom-track {
  position: relative;
  width: 0.3rem;
  height: 4.5rem;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.18);
  overflow: hidden;
}

/* Anchored to the bottom so the bar grows upward as you zoom in. */
.ground-360-zoom-fill {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 50%;
  border-radius: 9999px;
  background: linear-gradient(180deg, #7dd3fc 0%, #0284c7 100%);
  transition: height 0.15s ease;
}

/* Short screens can't afford the title/hint sitting outside the frame. */
@media (max-height: 560px) {

  .ground-360-zoom {
    gap: 0.3rem;
    padding: 0.35rem 0.3rem;
  }

  .ground-360-zoom-btn {
    width: max(1.9rem, 32px);
    height: max(1.9rem, 32px);
    font-size: 1.05rem;
  }

  .ground-360-zoom-track {
    height: 3rem;
  }
}

.char-prog-stage {
  position: relative;
  width: 100%;
  max-width: 330px;
  height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 1.5rem;
  background: radial-gradient(circle at 50% 65%, rgba(99, 102, 241, 0.22) 0%, rgba(15, 23, 42, 0.65) 100%);
  border: 1px solid rgba(129, 140, 248, 0.45);
  box-shadow: inset 0 0 35px rgba(0, 0, 0, 0.6), 0 10px 30px rgba(0, 0, 0, 0.4);
}

.char-prog-layer-silhouette {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
}

.char-prog-layer-silhouette canvas {
  width: 100% !important;
  height: 100% !important;
  display: block;
}

.char-prog-layer-color {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  clip-path: inset(calc(100% - var(--unmask-pct, 0%)) 0 0 0);
  transition: clip-path 1.2s cubic-bezier(0.34, 1.56, 0.64, 1);
  will-change: clip-path;
}

.char-prog-layer-color canvas {
  width: 100% !important;
  height: 100% !important;
  display: block;
}

/* Neon Laser Scanning Energy Line */
.char-prog-scan-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 4px;
  bottom: var(--unmask-pct, 0%);
  background: #38bdf8;
  box-shadow: 0 0 15px #38bdf8, 0 0 30px #818cf8, 0 0 45px #38bdf8;
  transition: bottom 1.2s cubic-bezier(0.34, 1.56, 0.64, 1);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 5;
}

.scan-spark {
  width: 14px;
  height: 14px;
  background: #ffffff;
  border-radius: 50%;
  box-shadow: 0 0 15px #ffffff, 0 0 25px #38bdf8;
  animation: sparkPulse 0.4s infinite alternate;
}

.scan-beam {
  position: absolute;
  left: -20%;
  right: -20%;
  height: 18px;
  background: linear-gradient(180deg, rgba(56, 189, 248, 0.4) 0%, transparent 100%);
  pointer-events: none;
}

@keyframes sparkPulse {
  from {
    transform: scale(0.75);
    opacity: 0.85;
  }

  to {
    transform: scale(1.35);
    opacity: 1;
  }
}

.char-prog-hero-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0.45rem 1.1rem;
  margin-bottom: 0.75rem;
  background: rgba(30, 41, 59, 0.65);
  border-radius: 9999px;
  border: 1px solid rgba(148, 163, 184, 0.25);
  backdrop-filter: blur(8px);
}

.char-prog-hero-name {
  font-size: 1.05rem;
  font-weight: 900;
  color: #38bdf8;
  letter-spacing: 1px;
}

.char-prog-hero-tier {
  font-size: 0.8rem;
  font-weight: 800;
  color: #fbbf24;
  letter-spacing: 0.5px;
}

.char-prog-meter-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.5px;
}

.xp-count-cur {
  color: #38bdf8;
  text-shadow: 0 0 10px rgba(56, 189, 248, 0.6);
}

.xp-count-sep {
  color: #475569;
  margin: 0 0.15rem;
}

.xp-count-max {
  color: #94a3b8;
  font-size: 0.9rem;
}

.char-prog-modal.unlocked .xp-count-cur {
  color: #facc15;
  text-shadow: 0 0 12px rgba(250, 204, 21, 0.7);
}

.meter-label {
  color: #94a3b8;
  text-transform: uppercase;
}

.meter-val {
  color: #38bdf8;
  font-size: 1.15rem;
  font-weight: 900;
  text-shadow: 0 0 10px rgba(56, 189, 248, 0.6);
}

/* Flat meter: a label row over a bar, with no box or badge chip around it. The
   panel is one card, not a stack of nested cards. */
.char-prog-meter {
  width: 100%;
  margin-top: 0.2rem;
}

.char-prog-meter-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.72rem;
  font-weight: 900;
  letter-spacing: 1px;
}

.char-prog-meter-count {
  white-space: nowrap;
  font-weight: 900;
}

.char-prog-track {
  width: 100%;
  height: 12px;
  background: rgba(15, 23, 42, 0.9);
  border-radius: 9999px;
  overflow: hidden;
  position: relative;
  margin: 0.45rem 0;
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.7);
}

.char-prog-fill {
  height: 100%;
  width: 0%;
  border-radius: 9999px;
  background: linear-gradient(90deg, #38bdf8 0%, #818cf8 50%, #c084fc 100%);
  box-shadow: 0 0 16px rgba(56, 189, 248, 0.85);
  transition: width 1.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.char-prog-coins-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.78rem;
  font-weight: 800;
  color: #e2e8f0;
}

.char-prog-gain-chip {
  padding: 0.2rem 0.6rem;
  background: rgba(16, 185, 129, 0.2);
  border: 1px solid rgba(16, 185, 129, 0.6);
  border-radius: 9999px;
  color: #34d399;
  font-size: 0.75rem;
  font-weight: 800;
  white-space: nowrap;
}

.char-prog-btn {
  width: 100%;
  height: 3.4rem;
  border-radius: 0.95rem;
  background: linear-gradient(180deg, #10b981 0%, #059669 100%);
  border: 2px solid #34d399;
  box-shadow: 0 0.25rem 0 #047857, 0 0.4rem 1rem rgba(16, 185, 129, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  color: #ffffff;
  font-size: 1.2rem;
  font-weight: 900;
  letter-spacing: 1.5px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  cursor: pointer;
  transition: transform 0.1s ease, filter 0.15s ease, box-shadow 0.1s ease;
}

.char-prog-btn .btn-icon {
  font-size: 1.3rem;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
}

.char-prog-btn svg {
  width: 22px;
  height: 22px;
  stroke: #ffffff;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
}

.char-prog-btn:hover {
  filter: brightness(1.1);
  transform: translateY(-2px);
  box-shadow: 0 0.35rem 0 #047857, 0 0.55rem 1.2rem rgba(16, 185, 129, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.45);
}

.char-prog-btn:active {
  transform: translateY(2px);
  box-shadow: 0 0.1rem 0 #047857, 0 0.15rem 0.3rem rgba(16, 185, 129, 0.4);
}

/* Match Summary Stars Row */
.summary-stars-container {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: -0.4rem 0 0.45rem;
}

.summary-star-icon {
  font-size: 1.85rem;
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  filter: drop-shadow(0 0 10px rgba(250, 204, 21, 0.8));
}

.summary-star-icon.earned {
  animation: summaryStarBounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.summary-star-icon.empty {
  opacity: 0.2;
  filter: grayscale(1);
}

@keyframes summaryStarBounce {
  0% {
    transform: scale(0) rotate(-45deg);
    opacity: 0;
  }

  70% {
    transform: scale(1.3) rotate(10deg);
    opacity: 1;
  }

  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
}

/* ===========================================================================
   CUSTOM MATCH BUTTON & HOW TO PLAY MODAL
   =========================================================================== */
.custom-match-nav-btn {
  pointer-events: auto;
  cursor: pointer;
  margin-top: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #ffffff;
  background: linear-gradient(180deg, #6366f1 0%, #4338ca 100%);
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  box-shadow: 0 0.3125rem 0 #312e81, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

.custom-match-nav-btn:hover {
  filter: brightness(1.1);
}

.custom-match-nav-btn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #312e81, 0 0.25rem 0.625rem rgba(0, 0, 0, 0.4);
}

.how-to-play-nav-btn {
  pointer-events: auto;
  cursor: pointer;
  margin-top: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #ffffff;
  background: linear-gradient(180deg, #3b82f6 0%, #1d4ed8 100%);
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  box-shadow: 0 0.3125rem 0 #1e40af, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

.how-to-play-nav-btn:hover {
  filter: brightness(1.1);
}

.how-to-play-nav-btn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #1e40af, 0 0.25rem 0.625rem rgba(0, 0, 0, 0.4);
}

/* ===========================================================================
   CUSTOM MATCH VENUE CAROUSEL WITH ARROWS & CARDS
   =========================================================================== */
.custom-venue-carousel {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  width: 96%;
  max-width: 390px;
  margin: 0.2rem 0 0.35rem;
}

.venue-nav-arrow {
  pointer-events: auto;
  cursor: pointer;
  width: 2.5rem;
  height: 2.5rem;
  min-width: 2.5rem;
  border-radius: 50%;
  background: rgba(30, 41, 59, 0.85);
  border: 1.5px solid rgba(56, 189, 248, 0.5);
  color: #38bdf8;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.2);
  transition: transform 0.1s, background 0.15s, border-color 0.15s;
}

.venue-nav-arrow:hover {
  background: rgba(56, 189, 248, 0.2);
  border-color: #38bdf8;
  transform: scale(1.08);
}

.venue-nav-arrow:active {
  transform: scale(0.92);
}

.custom-venue-card-wrapper {
  flex: 1;
  min-height: 135px;
}

.custom-venue-card {
  position: relative;
  border-radius: 1.15rem;
  padding: 0.75rem 0.85rem 0.65rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: linear-gradient(180deg, #0d1b33 0%, #080f1e 100%);
  border: 2px solid rgba(56, 189, 248, 0.5);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.15);
  transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), border-color 0.2s, box-shadow 0.2s;
  overflow: hidden;
}

.custom-venue-card.unlocked {
  border-color: #38bdf8;
  box-shadow: 0 0 20px rgba(56, 189, 248, 0.3), inset 0 0 15px rgba(56, 189, 248, 0.1);
}

.custom-venue-card.locked {
  opacity: 0.7;
  border-color: rgba(239, 68, 68, 0.4);
  background: linear-gradient(180deg, #181c26 0%, #0d1017 100%);
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.7);
}

.custom-venue-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.18rem 0.65rem;
  border-radius: 9999px;
  font-size: 0.68rem;
  font-weight: 900;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  margin-bottom: 0.25rem;
}

.custom-venue-badge.badge-unlocked {
  background: rgba(16, 185, 129, 0.2);
  border: 1px solid rgba(16, 185, 129, 0.6);
  color: #34d399;
}

.custom-venue-badge.badge-locked {
  background: rgba(239, 68, 68, 0.2);
  border: 1px solid rgba(239, 68, 68, 0.6);
  color: #f87171;
}

.custom-venue-icon {
  font-size: 2.2rem;
  margin-bottom: 0.1rem;
  filter: drop-shadow(0 0 10px rgba(56, 189, 248, 0.7));
}

.custom-venue-card.locked .custom-venue-icon {
  filter: grayscale(0.8) drop-shadow(0 0 4px rgba(0, 0, 0, 0.8));
}

.custom-venue-name {
  font-size: 1.15rem;
  font-weight: 900;
  color: #ffffff;
  letter-spacing: 1px;
  margin-bottom: 0.15rem;
}

.custom-venue-desc {
  font-size: 0.72rem;
  color: #94a3b8;
  letter-spacing: 0.3px;
  margin-bottom: 0.35rem;
  line-height: 1.2;
}

.custom-venue-footer {
  width: 100%;
  padding-top: 0.35rem;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-size: 0.74rem;
}

.custom-venue-squad {
  color: #38bdf8;
  font-weight: 800;
}

.custom-venue-squad strong {
  color: #facc15;
}

.custom-venue-unlock-hint {
  color: #f87171;
  font-weight: 800;
}

.custom-venue-dots {
  display: flex;
  gap: 6px;
  justify-content: center;
  align-items: center;
  margin: 0.25rem 0 0.5rem;
}

.custom-venue-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  cursor: pointer;
  transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.custom-venue-dot.active {
  width: 18px;
  border-radius: 9999px;
  background: #38bdf8;
  box-shadow: 0 0 8px rgba(56, 189, 248, 0.8);
}

.custom-venue-dot.locked {
  background: rgba(239, 68, 68, 0.35);
}

#matchSetupScreen {
  position: fixed;
  inset: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center !important;
  padding: 1.5rem 1rem !important;
  overflow-y: auto;
}

#matchSetupScreen #matchSetupBackBtn,
#teamSelectScreen #teamSelectBackBtn {
  position: absolute;
  top: 1.25rem;
  left: 1.5rem;
  z-index: 1000;
  pointer-events: auto;
  cursor: pointer;
}

#matchSetupScreen h2 {
  font-size: 1.85rem !important;
  margin: 0 0 0.6rem 0 !important;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8) !important;
}

#matchSetupScreen .setup-label {
  margin: 0.65rem 0 0.35rem !important;
  font-size: 0.8rem !important;
}

#matchSetupScreen .opt-row {
  margin-bottom: 0.55rem !important;
}

.how-to-play-modal {
  position: fixed;
  inset: 0;
  z-index: 12000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 7, 15, 0.85);
  backdrop-filter: blur(12px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  padding: 0.75rem;
  box-sizing: border-box;
}

.how-to-play-modal.show {
  opacity: 1;
  pointer-events: auto;
}

.how-to-play-card {
  position: relative;
  width: 100%;
  max-width: 880px;
  max-height: 94vh;
  overflow-y: auto;
  background: radial-gradient(circle at 50% 0%, #0d1e3a 0%, #081120 100%);
  border: 2px solid #1d4ed8;
  border-radius: 1.5rem;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.9), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 0 40px rgba(29, 78, 216, 0.35);
  padding: 1.25rem 1.25rem 1rem;
  box-sizing: border-box;
  color: #ffffff;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: scale(0.92);
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.how-to-play-modal.show .how-to-play-card {
  transform: scale(1);
}

.how-close-btn {
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 0.5rem;
  background: linear-gradient(135deg, #ef4444, #dc2626);
  border: 1px solid #f87171;
  color: #ffffff;
  font-size: 1.1rem;
  font-weight: 900;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 10px rgba(239, 68, 68, 0.4);
  transition: transform 0.1s;
  z-index: 10;
}

.how-close-btn:hover {
  transform: scale(1.1);
}

.how-close-btn:active {
  transform: scale(0.95);
}

.how-header {
  text-align: center;
  margin-bottom: 0.9rem;
}

.how-title {
  font-size: 1.75rem;
  font-weight: 900;
  letter-spacing: 2px;
  color: #ffffff;
  margin: 0 0 0.2rem;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.how-title .blue-star {
  color: #38bdf8;
  font-size: 1.5rem;
}

.how-subtitle {
  font-size: 0.95rem;
  font-weight: 700;
  color: #93c5fd;
  margin: 0;
}

/* 4 Columns Grid */
.how-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.65rem;
  width: 100%;
  margin-bottom: 0.85rem;
}

@media (max-width: 768px) {
  .how-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.6rem;
  }
}

.how-col {
  background: rgba(15, 23, 42, 0.7);
  border: 1px solid rgba(59, 130, 246, 0.3);
  border-radius: 1rem;
  padding: 0.75rem 0.5rem;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.how-col-num {
  font-size: 0.82rem;
  font-weight: 900;
  color: #facc15;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  margin-bottom: 0.25rem;
}

.how-col-desc {
  font-size: 0.78rem;
  font-weight: 600;
  color: #cbd5e1;
  margin: 0 0 0.5rem;
  line-height: 1.25;
  min-height: 2.2rem;
}

.how-col-desc b {
  color: #ffffff;
  font-weight: 900;
}

.how-col-visual {
  width: 100%;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 50%, rgba(29, 78, 216, 0.15) 0%, rgba(0, 0, 0, 0.4) 100%);
  border-radius: 0.75rem;
  padding: 0.5rem 0.25rem;
  min-height: 110px;
}

/* WASD Keycap & Pitch Grid Styles */
.wasd-visual-container {
  background: radial-gradient(circle at 50% 50%, rgba(30, 41, 59, 0.9) 0%, rgba(15, 23, 42, 0.95) 100%) !important;
  position: relative;
  overflow: hidden;
}

.wasd-pitch-grid {
  position: relative;
  width: 100%;
  height: 95px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.wasd-key-row {
  display: flex;
  align-items: center;
  gap: 3.5rem;
  margin: 0.15rem 0;
}

.wasd-key-cap {
  width: 1.5rem;
  height: 1.5rem;
  background: #1e293b;
  border: 1.5px solid #475569;
  border-radius: 0.35rem;
  color: #94a3b8;
  font-size: 0.78rem;
  font-weight: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 3px 0 #0f172a;
  transition: all 0.2s ease;
  z-index: 2;
}

.wasd-key-cap.key-w {
  animation: keyGlowW_Anim 3.6s infinite ease-in-out;
}

.wasd-key-cap.key-d {
  animation: keyGlowD_Anim 3.6s infinite ease-in-out;
}

.wasd-key-cap.key-s {
  animation: keyGlowS_Anim 3.6s infinite ease-in-out;
}

.wasd-key-cap.key-a {
  animation: keyGlowA_Anim 3.6s infinite ease-in-out;
}

@keyframes keyGlowW_Anim {

  0%,
  20% {
    background: #0284c7;
    border-color: #38bdf8;
    color: #ffffff;
    box-shadow: 0 3px 0 #0369a1, 0 0 12px rgba(56, 189, 248, 0.85);
    transform: translateY(-2px);
  }

  25%,
  100% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }
}

@keyframes keyGlowD_Anim {

  0%,
  25% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }

  30%,
  45% {
    background: #0284c7;
    border-color: #38bdf8;
    color: #ffffff;
    box-shadow: 0 3px 0 #0369a1, 0 0 12px rgba(56, 189, 248, 0.85);
    transform: translateY(-2px);
  }

  50%,
  100% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }
}

@keyframes keyGlowS_Anim {

  0%,
  50% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }

  55%,
  70% {
    background: #0284c7;
    border-color: #38bdf8;
    color: #ffffff;
    box-shadow: 0 3px 0 #0369a1, 0 0 12px rgba(56, 189, 248, 0.85);
    transform: translateY(-2px);
  }

  75%,
  100% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }
}

@keyframes keyGlowA_Anim {

  0%,
  75% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }

  80%,
  95% {
    background: #0284c7;
    border-color: #38bdf8;
    color: #ffffff;
    box-shadow: 0 3px 0 #0369a1, 0 0 12px rgba(56, 189, 248, 0.85);
    transform: translateY(-2px);
  }

  100% {
    background: #1e293b;
    border-color: #475569;
    color: #94a3b8;
    box-shadow: 0 3px 0 #0f172a;
    transform: translateY(0);
  }
}

.wasd-animated-batter {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
  animation: batterFootworkAnim 3.6s infinite ease-in-out;
  z-index: 3;
}

@keyframes batterFootworkAnim {

  0%,
  20% {
    transform: translate(-50%, -50%) translate(0, -11px);
  }

  25% {
    transform: translate(-50%, -50%) translate(0, 0);
  }

  30%,
  45% {
    transform: translate(-50%, -50%) translate(13px, 0);
  }

  50% {
    transform: translate(-50%, -50%) translate(0, 0);
  }

  55%,
  70% {
    transform: translate(-50%, -50%) translate(0, 11px);
  }

  75% {
    transform: translate(-50%, -50%) translate(0, 0);
  }

  80%,
  95% {
    transform: translate(-50%, -50%) translate(-13px, 0);
  }

  100% {
    transform: translate(-50%, -50%) translate(0, 0);
  }
}

.batter-icon-art {
  font-size: 1.45rem;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8));
}

.batter-shadow-art {
  width: 14px;
  height: 4px;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  margin-top: -2px;
}

/* Striped Green Pitch Turf for Slingshot Visuals */
.slingshot-pitch-turf {
  position: relative;
  width: 100%;
  max-width: 110px;
  height: 90px;
  border-radius: 0.6rem;
  background: repeating-linear-gradient(135deg,
      #2e7d32,
      #2e7d32 10px,
      #388e3c 10px,
      #388e3c 20px);
  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  border: 1.5px solid rgba(255, 255, 255, 0.2);
}

.slingshot-pitch-turf.mini-turf {
  max-width: 75px;
  height: 90px;
}

/* Animated Slingshot Elements (Matching Game Screenshot) */
.anim-slingshot-anchor {
  position: absolute;
  top: 25px;
  left: 55px;
  transform: translate(-50%, -50%);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
}

.anim-slingshot-anchor.anchor-top {
  top: 18px;
  left: 38px;
}

.anim-slingshot-line {
  position: absolute;
  top: 25px;
  left: 55px;
  width: 2.5px;
  background: #ffffff;
  box-shadow: 0 0 6px rgba(255, 255, 255, 0.9);
  transform-origin: top center;
  animation: slingshotLinePull 2.2s infinite ease-in-out;
}

.anim-slingshot-line.line-deep {
  top: 18px;
  left: 38px;
  animation: slingshotLineDeepPull 2.2s infinite ease-in-out;
}

@keyframes slingshotLinePull {

  0%,
  15% {
    height: 0px;
    transform: rotate(15deg);
    opacity: 0;
  }

  50%,
  80% {
    height: 42px;
    transform: rotate(15deg);
    opacity: 1;
  }

  90%,
  100% {
    height: 0px;
    transform: rotate(15deg);
    opacity: 0;
  }
}

@keyframes slingshotLineDeepPull {

  0%,
  15% {
    height: 0px;
    transform: rotate(8deg);
    opacity: 0;
  }

  50%,
  80% {
    height: 55px;
    transform: rotate(8deg);
    opacity: 1;
  }

  90%,
  100% {
    height: 0px;
    transform: rotate(8deg);
    opacity: 0;
  }
}

.anim-slingshot-head {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 0 10px rgba(255, 255, 255, 1);
  transform: translate(-50%, -50%);
  animation: slingshotHeadPull 2.2s infinite ease-in-out;
}

.anim-slingshot-head.head-deep {
  animation: slingshotHeadDeepPull 2.2s infinite ease-in-out;
}

@keyframes slingshotHeadPull {

  0%,
  15% {
    top: 25px;
    left: 55px;
    opacity: 0;
  }

  50%,
  80% {
    top: 65px;
    left: 44px;
    opacity: 1;
  }

  90%,
  100% {
    top: 25px;
    left: 55px;
    opacity: 0;
  }
}

@keyframes slingshotHeadDeepPull {

  0%,
  15% {
    top: 18px;
    left: 38px;
    opacity: 0;
  }

  50%,
  80% {
    top: 72px;
    left: 30px;
    opacity: 1;
  }

  90%,
  100% {
    top: 18px;
    left: 38px;
    opacity: 0;
  }
}

.anim-slingshot-hand {
  position: absolute;
  font-size: 1.4rem;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.7));
  animation: slingshotHandPull 2.2s infinite ease-in-out;
  pointer-events: none;
}

@keyframes slingshotHandPull {

  0%,
  15% {
    top: 20px;
    left: 52px;
    opacity: 0;
  }

  50%,
  80% {
    top: 60px;
    left: 40px;
    opacity: 1;
  }

  90%,
  100% {
    top: 20px;
    left: 52px;
    opacity: 0;
  }
}

/* Dotted Aim Trajectory */
.anim-aim-trajectory {
  position: absolute;
  top: 25px;
  left: 55px;
  width: 38px;
  height: 2px;
  background: repeating-linear-gradient(90deg, #38bdf8, #38bdf8 4px, transparent 4px, transparent 8px);
  transform-origin: left center;
  transform: rotate(-55deg);
  animation: aimTrajectoryPulse 2.2s infinite ease-in-out;
}

@keyframes aimTrajectoryPulse {

  0%,
  15% {
    opacity: 0;
  }

  50%,
  80% {
    opacity: 1;
  }

  90%,
  100% {
    opacity: 0;
  }
}

.anim-aim-crosshair {
  position: absolute;
  top: -2px;
  right: 18px;
  font-size: 0.95rem;
  animation: aimCrosshairPulse 2.2s infinite ease-in-out;
}

@keyframes aimCrosshairPulse {

  0%,
  15% {
    opacity: 0;
    transform: scale(0.6);
  }

  50%,
  80% {
    opacity: 1;
    transform: scale(1.1);
  }

  90%,
  100% {
    opacity: 0;
    transform: scale(0.6);
  }
}

/* Card 3: Vertical Power Meter */
.power-col-layout {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  width: 100%;
}

.anim-vert-power-meter {
  display: flex;
  flex-direction: column;
  align-items: center;
  height: 90px;
}

.vert-meter-label {
  font-size: 0.58rem;
  font-weight: 900;
  color: #cbd5e1;
}

.vert-meter-label.max {
  color: #f87171;
  margin-bottom: 0.1rem;
}

.vert-meter-label.min {
  color: #34d399;
  margin-top: 0.1rem;
}

.vert-meter-track {
  position: relative;
  width: 14px;
  height: 60px;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 6px;
  border: 1.5px solid rgba(255, 255, 255, 0.3);
  overflow: hidden;
}

.anim-vert-power-meter .vert-meter-fill {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  background: linear-gradient(0deg, #10b981 0%, #facc15 55%, #ef4444 100%);
  animation: vertPowerFillAnim 2.2s infinite ease-in-out;
  border-radius: 4px;
}

@keyframes vertPowerFillAnim {

  0%,
  15% {
    height: 15%;
  }

  50%,
  80% {
    height: 95%;
  }

  90%,
  100% {
    height: 15%;
  }
}

.anim-vert-power-meter .vert-meter-cursor {
  position: absolute;
  left: -2px;
  width: calc(100% + 4px);
  height: 3px;
  background: #ffffff;
  box-shadow: 0 0 5px #ffffff;
  border-radius: 2px;
  animation: vertPowerCursorAnim 2.2s infinite ease-in-out;
}

@keyframes vertPowerCursorAnim {

  0%,
  15% {
    bottom: 15%;
  }

  50%,
  80% {
    bottom: 95%;
  }

  90%,
  100% {
    bottom: 15%;
  }
}

/* Card 4: Release & Timing Gauge */
.anim-timing-meter-card {
  position: relative;
  width: 95px;
  height: 52px;
}

.anim-gauge-label {
  position: absolute;
  top: -2px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.65rem;
  font-weight: 900;
  color: #10b981;
  letter-spacing: 0.5px;
}

.anim-gauge-svg {
  width: 100%;
  height: 100%;
}

.anim-gauge-needle {
  transform-origin: 50px 48px;
  animation: gaugeNeedleSweep 2.2s infinite ease-in-out;
}

@keyframes gaugeNeedleSweep {
  0% {
    transform: rotate(-55deg);
  }

  45%,
  70% {
    transform: rotate(0deg);
  }

  90%,
  100% {
    transform: rotate(55deg);
  }
}

.anim-release-shot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  position: relative;
  margin-top: 0.3rem;
}

.anim-batter-swing {
  font-size: 1.3rem;
  animation: batterSwingHit 2.2s infinite ease-in-out;
}

@keyframes batterSwingHit {

  0%,
  40% {
    transform: rotate(0deg);
  }

  45%,
  70% {
    transform: rotate(-25deg) scale(1.1);
  }

  90%,
  100% {
    transform: rotate(0deg);
  }
}

.anim-smash-spark {
  font-size: 1.2rem;
  animation: sparkFlash 2.2s infinite ease-in-out;
}

@keyframes sparkFlash {

  0%,
  42% {
    opacity: 0;
    transform: scale(0.5);
  }

  46%,
  65% {
    opacity: 1;
    transform: scale(1.2);
  }

  75%,
  100% {
    opacity: 0;
    transform: scale(0.5);
  }
}

.anim-flying-ball {
  font-size: 0.9rem;
  animation: ballFlyAway 2.2s infinite ease-in-out;
}

@keyframes ballFlyAway {

  0%,
  45% {
    transform: translate(0, 0);
    opacity: 0;
  }

  50% {
    opacity: 1;
  }

  75%,
  100% {
    transform: translate(25px, -18px) scale(0.6);
    opacity: 0;
  }
}

/* Bottom Box Timing Meter */
.how-bottom-box {
  width: 100%;
  background: rgba(10, 18, 35, 0.9);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 1rem;
  padding: 0.65rem 0.85rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  box-sizing: border-box;
  margin-bottom: 0.75rem;
}

@media (max-width: 768px) {
  .how-bottom-box {
    flex-direction: column;
    text-align: center;
    gap: 0.5rem;
  }
}

.how-bottom-left {
  flex: 1;
  text-align: left;
}

@media (max-width: 768px) {
  .how-bottom-left {
    text-align: center;
  }
}

.bottom-box-title {
  font-size: 0.9rem;
  font-weight: 900;
  color: #facc15;
  margin-bottom: 0.2rem;
}

.bottom-box-desc {
  font-size: 0.75rem;
  color: #cbd5e1;
  margin: 0;
  line-height: 1.3;
}

.how-bottom-gauges {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.gauge-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 65px;
}

.gauge-status {
  font-size: 0.62rem;
  font-weight: 900;
  margin-bottom: 0.1rem;
}

.gauge-status.red {
  color: #f87171;
}

.gauge-status.green {
  color: #34d399;
}

.gauge-comp-svg {
  width: 100%;
  height: 32px;
}

/* Footer */
.how-footer {
  font-size: 0.9rem;
  font-weight: 900;
  color: #facc15;
  letter-spacing: 1px;
  text-align: center;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.8);
}

.huge-shot-text {
  color: #38bdf8;
  font-size: 1rem;
}

/* ===========================================================================
   LIVE IN-GAME TUTORIAL HUD STYLES (FLEX FLOW, ZERO OVERLAP)
   =========================================================================== */
.tut-hud-container {
  position: fixed;
  inset: 0;
  z-index: 10000;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.75rem 0.75rem;
  box-sizing: border-box;
  opacity: 0;
  transition: opacity 0.25s ease;
}

.tut-hud-container.show {
  opacity: 1;
}

.tut-skip-btn {
  position: fixed !important;
  bottom: 1.25rem !important;
  right: 1.25rem !important;
  top: auto !important;
  left: auto !important;
  z-index: 12500 !important;
  pointer-events: auto !important;
  background: rgba(10, 18, 35, 0.92) !important;
  border: 1.5px solid rgba(250, 204, 21, 0.8) !important;
  border-radius: 9999px !important;
  color: #facc15 !important;
  font-size: 0.84rem !important;
  font-weight: 900 !important;
  letter-spacing: 0.75px !important;
  padding: 0.42rem 1.05rem !important;
  cursor: pointer !important;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.85), 0 0 12px rgba(250, 204, 21, 0.4) !important;
  transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
}

.tut-skip-btn:hover {
  background: #facc15 !important;
  color: #0f172a !important;
  transform: scale(1.08) !important;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.9), 0 0 20px rgba(250, 204, 21, 0.8) !important;
}

.tut-practice-flow {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  max-width: 360px;
  pointer-events: auto;
}

.tut-prompt-card {
  position: relative;
  width: 100%;
  background: rgba(10, 18, 35, 0.95);
  border: 2px solid #38bdf8;
  border-radius: 1.15rem;
  padding: 0.65rem 0.9rem;
  text-align: center;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.85), 0 0 20px rgba(56, 189, 248, 0.35);
  box-sizing: border-box;
}

.tut-prompt-header {
  font-size: 1.1rem;
  font-weight: 900;
  letter-spacing: 1px;
  color: #facc15;
  margin-bottom: 0.2rem;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8);
}

.tut-prompt-sub {
  font-size: 0.82rem;
  font-weight: 700;
  color: #e2e8f0;
  line-height: 1.35;
  text-align: center;
}

.tut-ball-timeline {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
  padding-top: 0.45rem;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
}

.tut-timeline-label {
  font-size: 0.72rem;
  font-weight: 800;
  color: #94a3b8;
  letter-spacing: 0.5px;
}

.tut-dots-row {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.tut-ball-dot {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.8rem;
  font-weight: 900;
  border: 1.5px solid rgba(255, 255, 255, 0.25);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tut-ball-dot.pending {
  background: rgba(15, 23, 42, 0.7);
  color: #64748b;
  border-color: rgba(255, 255, 255, 0.15);
}

.tut-ball-dot.current {
  background: radial-gradient(circle at 35% 35%, #38bdf8, #0284c7);
  color: #ffffff;
  border-color: #ffffff;
  box-shadow: 0 0 10px rgba(56, 189, 248, 0.9);
  transform: scale(1.15);
}

.tut-ball-dot.completed {
  background: radial-gradient(circle at 35% 35%, #10b981, #059669);
  color: #ffffff;
  border-color: #a7f3d0;
  box-shadow: 0 0 12px rgba(16, 185, 129, 0.9);
  font-size: 0.95rem;
}

.tut-dot-line {
  width: 14px;
  height: 2px;
  background: rgba(255, 255, 255, 0.2);
}

.tut-timing-slot {
  width: 100%;
  display: flex;
  justify-content: center;
}

/* ===========================================================================
   ON-PITCH 4-STEP CAROUSEL WALKTHROUGH OVERLAY STYLES
   =========================================================================== */
.tut-walkthrough-overlay {
  position: fixed;
  inset: 0;
  z-index: 11500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 7, 15, 0.82);
  backdrop-filter: blur(10px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  padding: 0.75rem;
  box-sizing: border-box;
  cursor: pointer;
}

.tut-walkthrough-overlay.show {
  opacity: 1;
  pointer-events: auto;
}

.tut-walkthrough-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 380px;
  gap: 0.9rem;
  pointer-events: auto;
}

.tut-walkthrough-card {
  position: relative;
  width: 100%;
  background: radial-gradient(circle at 50% 0%, #0e2246 0%, #071120 100%);
  border: 2px solid #38bdf8;
  border-radius: 1.5rem;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.92), 0 0 35px rgba(56, 189, 248, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.25);
  padding: 1.35rem 1.2rem 1.35rem;
  box-sizing: border-box;
  color: #ffffff;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  transform: scale(0.92);
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tut-walkthrough-overlay.show .tut-walkthrough-card {
  transform: scale(1);
}

.tut-walkthrough-skip-btn {
  position: fixed !important;
  bottom: 1.25rem !important;
  right: 1.25rem !important;
  top: auto !important;
  left: auto !important;
  z-index: 12500 !important;
  pointer-events: auto !important;
  background: rgba(10, 18, 35, 0.92) !important;
  border: 1.5px solid rgba(250, 204, 21, 0.8) !important;
  border-radius: 9999px !important;
  color: #facc15 !important;
  font-size: 0.84rem !important;
  font-weight: 900 !important;
  letter-spacing: 0.75px !important;
  padding: 0.42rem 1.05rem !important;
  cursor: pointer !important;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.85), 0 0 12px rgba(250, 204, 21, 0.4) !important;
  transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
}

.tut-walkthrough-skip-btn:hover {
  background: #facc15 !important;
  color: #0f172a !important;
  transform: scale(1.08) !important;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.9), 0 0 20px rgba(250, 204, 21, 0.8) !important;
}

.tut-step-header {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 0.15rem;
  width: 100%;
  pointer-events: auto;
}

.tut-step-pips {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  /* background: rgba(10, 18, 35, 0.85); */
  padding: 0.35rem 0.95rem;
  border-radius: 9999px;
  /* border: 1.5px solid rgba(56, 189, 248, 0.35); */
  /* box-shadow: 0 4px 15px rgba(0, 0, 0, 0.6), 0 0 12px rgba(56, 189, 248, 0.2); */
  pointer-events: auto;
}

.tut-step-pip {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: 0.78rem;
  font-weight: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
  cursor: pointer;
  user-select: none;
}

.tut-step-pip:hover {
  transform: scale(1.22);
  filter: brightness(1.25);
}

.tut-step-pip:active {
  transform: scale(0.92);
}

.tut-step-pip.pending {
  background: rgba(255, 255, 255, 0.08);
  color: #64748b;
  border: 1px solid rgba(255, 255, 255, 0.15);
}

.tut-step-pip.active {
  background: #38bdf8;
  color: #0f172a;
  border: 1.5px solid #ffffff;
  box-shadow: 0 0 12px rgba(56, 189, 248, 0.95);
  transform: scale(1.15);
}

.tut-step-pip.completed {
  background: #facc15;
  color: #0f172a;
  border: 1px solid #ffffff;
  box-shadow: 0 0 10px rgba(250, 204, 21, 0.8);
}

.tut-carousel-viewport {
  width: 100%;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tut-card-body.tut-carousel-slide {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  animation: carouselSlideFadeIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes carouselSlideFadeIn {
  0% {
    opacity: 0;
    transform: translateY(10px) scale(0.96);
  }

  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.tut-card-title {
  font-size: 1.35rem;
  font-weight: 900;
  color: #facc15;
  letter-spacing: 0.8px;
  margin: 0 0 0.55rem 0;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.85);
}

.tut-card-desc {
  font-size: 0.86rem;
  font-weight: 600;
  color: #cbd5e1;
  margin: 0 0 1.15rem 0;
  line-height: 1.45;
  max-width: 94%;
}

.tut-card-desc b {
  color: #ffffff;
  font-weight: 900;
}

.tut-card-body .how-col-visual {
  width: 100%;
  max-width: 330px;
  height: 140px;
  margin-bottom: 0.15rem;
  border-radius: 0.95rem;
  border: 1px solid rgba(56, 189, 248, 0.35);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.4);
}

/* ===========================================================================
   ADAPTIVE DEVICE VIEW (DESKTOP / PC vs MOBILE / TOUCH)
   =========================================================================== */
.tut-device-view {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.tut-desktop-view {
  display: flex;
}

.tut-touch-view {
  display: none;
}

#tutWalkthroughOverlay.is-desktop-device .tut-desktop-view {
  display: flex !important;
}

#tutWalkthroughOverlay.is-desktop-device .tut-touch-view {
  display: none !important;
}

#tutWalkthroughOverlay.is-touch-device .tut-desktop-view {
  display: none !important;
}

#tutWalkthroughOverlay.is-touch-device .tut-touch-view {
  display: flex !important;
}

@media (pointer: coarse),
(hover: none) {
  .tut-desktop-view {
    display: none;
  }

  .tut-touch-view {
    display: flex;
  }
}

@media (hover: hover) and (pointer: fine) {
  .tut-desktop-view {
    display: flex;
  }

  .tut-touch-view {
    display: none;
  }
}

/* ===========================================================================
   DESKTOP / PC VISUAL (WASD GRID & MOUSE SLINGSHOT)
   =========================================================================== */
.wasd-visual-container {
  background: #071120;
}

.wasd-pitch-grid {
  height: 140px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 100%;
}

.wasd-key-cap {
  width: 2.1rem;
  height: 2.1rem;
  font-size: 0.95rem;
  border-radius: 0.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 900;
  border: 1px solid #475569;
  background: #1e293b;
  color: #94a3b8;
  box-shadow: 0 3px 0 #0f172a;
}

.wasd-key-row {
  display: flex;
  align-items: center;
  gap: 4.8rem;
  margin: 0.2rem 0;
}

.batter-icon-art {
  font-size: 1.85rem;
}

.power-visual-container {
  background: #071120;
}

.power-visual-container .power-col-layout {
  height: 140px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  width: 100%;
}

.power-visual-container .slingshot-pitch-turf {
  width: 120px;
  height: 120px;
  border-radius: 0.65rem;
  position: relative;
  overflow: hidden;
  background: repeating-linear-gradient(135deg, #2e7d32, #2e7d32 10px, #388e3c 10px, #388e3c 20px);
  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.4);
  border: 1.5px solid rgba(255, 255, 255, 0.2);
}

.power-visual-container .anim-slingshot-anchor {
  top: 28px;
  left: 60px;
  width: 26px;
  height: 26px;
}

.power-visual-container .anim-slingshot-line {
  top: 28px;
  left: 60px;
  width: 3px;
}

.power-visual-container .anim-slingshot-head {
  width: 15px;
  height: 15px;
}

.power-visual-container .anim-slingshot-hand {
  font-size: 1.5rem;
}

.power-visual-container .anim-vert-power-meter {
  height: 120px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

.power-visual-container .vert-meter-track {
  width: 16px;
  height: 80px;
  border-radius: 8px;
}

.power-visual-container .vert-meter-label {
  font-size: 0.7rem;
}

/* ===========================================================================
   SPLIT-SCREEN PHONE VISUAL (LEFT: MOVE JOYSTICK, RIGHT: AIM SLINGSHOT)
   =========================================================================== */
.split-phone-visual {
  padding: 0 !important;
  overflow: hidden;
  background: #071120;
}

.tut-split-phone {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: stretch;
  position: relative;
}

.tut-split-half {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding: 0.3rem 0.25rem;
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  transition: all 0.3s ease;
}

.tut-split-half.active-left {
  background: radial-gradient(circle at center, rgba(14, 165, 233, 0.22) 0%, rgba(7, 17, 32, 0.95) 100%);
}

.tut-split-half.active-right {
  background: radial-gradient(circle at center, rgba(234, 179, 8, 0.2) 0%, rgba(7, 17, 32, 0.95) 100%);
}

.tut-split-half.dimmed-half {
  opacity: 0.35;
  filter: grayscale(0.6);
  background: rgba(0, 0, 0, 0.5);
  justify-content: center;
}

.tut-split-divider {
  width: 2px;
  height: 82%;
  align-self: center;
  border-left: 2px dashed rgba(56, 189, 248, 0.4);
  z-index: 4;
}

.tut-zone-badge {
  font-size: 0.64rem;
  font-weight: 900;
  letter-spacing: 0.5px;
  border-radius: 9999px;
  padding: 0.15rem 0.55rem;
  margin-bottom: 0.25rem;
  white-space: nowrap;
  z-index: 5;
}

.tut-zone-badge.blue-glow {
  background: rgba(56, 189, 248, 0.25);
  color: #38bdf8;
  border: 1px solid rgba(56, 189, 248, 0.6);
  box-shadow: 0 0 8px rgba(56, 189, 248, 0.35);
}

.tut-zone-badge.gold-glow {
  background: rgba(250, 204, 21, 0.25);
  color: #facc15;
  border: 1px solid rgba(250, 204, 21, 0.6);
  box-shadow: 0 0 8px rgba(250, 204, 21, 0.35);
}

.tut-zone-badge.dimmed-badge {
  background: rgba(255, 255, 255, 0.06);
  color: #64748b;
  border: 1px solid rgba(255, 255, 255, 0.12);
}

.tut-dimmed-center-icon {
  font-size: 2.2rem;
  opacity: 0.4;
  margin-top: 0.35rem;
}

/* Left Half Interactive Joystick Scene */
.tut-phone-joy-scene {
  position: relative;
  width: 100%;
  height: 98px;
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 0 0.4rem;
  box-sizing: border-box;
}

.tut-anim-joy-base {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(14, 165, 233, 0.25) 0%, rgba(2, 132, 199, 0.4) 100%);
  border: 2px solid rgba(56, 189, 248, 0.7);
  box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.6), 0 0 10px rgba(56, 189, 248, 0.3);
  position: relative;
  flex-shrink: 0;
}

.tut-anim-joy-knob {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: linear-gradient(135deg, #38bdf8 0%, #0284c7 100%);
  border: 1.5px solid #ffffff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.8), 0 0 8px rgba(56, 189, 248, 0.9);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  animation: tutJoyKnobAnim 3.2s infinite ease-in-out;
}

@keyframes tutJoyKnobAnim {

  0%,
  100% {
    transform: translate(-50%, -50%);
  }

  20% {
    transform: translate(-14px, -50%);
  }

  40% {
    transform: translate(4px, -50%);
  }

  60% {
    transform: translate(-50%, -14px);
  }

  80% {
    transform: translate(-50%, 4px);
  }
}

.tut-anim-touch-hand.joy-hand {
  position: absolute;
  font-size: 1.4rem;
  pointer-events: none;
  z-index: 6;
  top: 24px;
  left: 20px;
  animation: tutJoyHandAnim 3.2s infinite ease-in-out;
}

@keyframes tutJoyHandAnim {

  0%,
  100% {
    transform: translate(0, 0);
  }

  20% {
    transform: translate(-9px, 0);
  }

  40% {
    transform: translate(9px, 0);
  }

  60% {
    transform: translate(0, -9px);
  }

  80% {
    transform: translate(0, 9px);
  }
}

.tut-anim-batter-box {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  animation: tutBatterMoveAnim 3.2s infinite ease-in-out;
}

@keyframes tutBatterMoveAnim {

  0%,
  100% {
    transform: translate(0, 0);
  }

  20% {
    transform: translate(-7px, 0);
  }

  40% {
    transform: translate(7px, 0);
  }

  60% {
    transform: translate(0, -5px);
  }

  80% {
    transform: translate(0, 5px);
  }
}

/* Slide 2 Split Right Aim Layout */
.tut-split-half.active-right .power-col-layout {
  height: 98px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  width: 100%;
}

.tut-split-half.active-right .slingshot-pitch-turf {
  width: 90px;
  height: 90px;
  border-radius: 0.55rem;
  position: relative;
  overflow: hidden;
  background: repeating-linear-gradient(135deg, #2e7d32, #2e7d32 8px, #388e3c 8px, #388e3c 16px);
  border: 1px solid rgba(255, 255, 255, 0.2);
  box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.5);
}

.tut-split-half.active-right .anim-slingshot-anchor {
  top: 22px;
  left: 45px;
  width: 22px;
  height: 22px;
}

.tut-split-half.active-right .anim-slingshot-line {
  top: 22px;
  left: 45px;
  width: 3px;
}

.tut-split-half.active-right .anim-slingshot-head {
  width: 14px;
  height: 14px;
}

.tut-split-half.active-right .anim-slingshot-hand {
  font-size: 1.35rem;
}

@keyframes slingshotHeadPull {

  0%,
  15% {
    top: 22px;
    left: 45px;
    opacity: 0;
  }

  50%,
  80% {
    top: 62px;
    left: 32px;
    opacity: 1;
  }

  90%,
  100% {
    top: 22px;
    left: 45px;
    opacity: 0;
  }
}

@keyframes slingshotHandPull {

  0%,
  15% {
    top: 16px;
    left: 41px;
    opacity: 0;
  }

  50%,
  80% {
    top: 56px;
    left: 28px;
    opacity: 1;
  }

  90%,
  100% {
    top: 16px;
    left: 41px;
    opacity: 0;
  }
}

@keyframes slingshotLinePull {

  0%,
  15% {
    height: 0px;
    transform: rotate(15deg);
    opacity: 0;
  }

  50%,
  80% {
    height: 42px;
    transform: rotate(15deg);
    opacity: 1;
  }

  90%,
  100% {
    height: 0px;
    transform: rotate(15deg);
    opacity: 0;
  }
}

.tut-split-half.active-right .anim-vert-power-meter {
  height: 90px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
}

.tut-split-half.active-right .vert-meter-track {
  width: 14px;
  height: 60px;
  border-radius: 7px;
}

.tut-split-half.active-right .vert-meter-label {
  font-size: 0.62rem;
}

/* Slide 3 Shot Mode Adjustments */
.shotmode-visual-container {
  height: 140px !important;
  min-height: 140px !important;
  gap: 0.5rem;
  padding: 0.35rem 0.6rem;
}

/* Button replica on the left, the two trajectories it produces on the right, so
   the 140px visual reads as "this button -> these two outcomes". */
.tut-shotmode-demo {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}

/* Deliberately mirrors #shotModeBtn's panel — same gradient, border, radius and
   icon/label stack — so the tutorial teaches the button players actually see.
   Keep the two in sync if the HUD button is restyled. */
.tut-shotmode-btn {
  position: relative;
  flex: 0 0 auto;
  width: 4.5rem;
  padding: 0.6rem 0.5rem 0.5rem;
  background: linear-gradient(180deg, #1b2e44 0%, #0d1624 100%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-bottom: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  color: #fff;
  font-weight: 800;
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.15);
  animation: tutShotModeCycle 4s infinite;
}

.tut-shotmode-btn .icon {
  display: grid;
  place-items: center;
  line-height: 1;
}

/* Both glyphs stack in the same grid cell and cross-fade, matching the real
   button's swap without the class toggle JS does in a match. */
.tut-shotmode-btn .icon svg {
  grid-area: 1 / 1;
  width: 22px;
  height: 22px;
}

.tut-shotmode-btn .icon-ground {
  animation: tutShotModeGroundFace 4s infinite;
}

.tut-shotmode-btn .icon-aerial {
  animation: tutShotModeAerialFace 4s infinite;
}

.tut-shotmode-btn .lbl {
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1px;
  line-height: 1;
}

.tut-shotmode-btn .lbl>span {
  grid-area: 1 / 1;
}

.tut-shotmode-btn .lbl-ground {
  animation: tutShotModeGroundFace 4s infinite;
}

.tut-shotmode-btn .lbl-aerial {
  animation: tutShotModeAerialFace 4s infinite;
}

/* Tap hint lands right as the button flips, so the flip reads as caused by it. */
.tut-shotmode-tap {
  position: absolute;
  right: -0.55rem;
  bottom: -0.65rem;
  font-size: 1.1rem;
  animation: tutShotModeTap 4s infinite;
}

/* Ground for the first half of the cycle, aerial (orange, like the live
   button's .aerial state) for the second. */
@keyframes tutShotModeCycle {

  0%,
  44% {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.15);
  }

  48%,
  50% {
    transform: translateY(1px) scale(0.96);
  }

  52%,
  94% {
    color: #ff9f6b;
    border-color: rgba(255, 140, 90, 0.55);
  }

  98%,
  100% {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.15);
  }
}

@keyframes tutShotModeGroundFace {

  0%,
  46% {
    opacity: 1;
  }

  50%,
  95% {
    opacity: 0;
  }

  99%,
  100% {
    opacity: 1;
  }
}

@keyframes tutShotModeAerialFace {

  0%,
  46% {
    opacity: 0;
  }

  50%,
  95% {
    opacity: 1;
  }

  99%,
  100% {
    opacity: 0;
  }
}

@keyframes tutShotModeTap {

  0%,
  38% {
    transform: translate(0, 0);
    opacity: 0.55;
  }

  46%,
  52% {
    transform: translate(-0.35rem, -0.5rem);
    opacity: 1;
  }

  60%,
  100% {
    transform: translate(0, 0);
    opacity: 0.55;
  }
}

.shotmode-trajectories {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.traj-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.5rem;
  font-size: 0.78rem;
  border-radius: 0.5rem;
  border: 1px solid transparent;
  background: rgba(8, 17, 32, 0.75);
  white-space: nowrap;
}

/* Each row lights up while the button is showing its mode, tying the toggle to
   the outcome instead of listing both as static text. */
.traj-ground {
  animation: tutTrajGround 4s infinite;
}

.traj-aerial {
  animation: tutTrajAerial 4s infinite;
}

@keyframes tutTrajGround {

  0%,
  46% {
    opacity: 1;
    border-color: rgba(52, 211, 153, 0.55);
    background: rgba(16, 185, 129, 0.12);
  }

  50%,
  95% {
    opacity: 0.4;
    border-color: transparent;
    background: rgba(8, 17, 32, 0.75);
  }

  99%,
  100% {
    opacity: 1;
    border-color: rgba(52, 211, 153, 0.55);
    background: rgba(16, 185, 129, 0.12);
  }
}

@keyframes tutTrajAerial {

  0%,
  46% {
    opacity: 0.4;
    border-color: transparent;
    background: rgba(8, 17, 32, 0.75);
  }

  50%,
  95% {
    opacity: 1;
    border-color: rgba(251, 191, 36, 0.55);
    background: rgba(245, 158, 11, 0.14);
  }

  99%,
  100% {
    opacity: 0.4;
    border-color: transparent;
    background: rgba(8, 17, 32, 0.75);
  }
}

.traj-tag {
  flex: 0 0 auto;
  font-size: 0.62rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

.green-tag {
  color: #34d399;
}

.gold-tag {
  color: #fbbf24;
}

.traj-line {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-size: 0.7rem;
  color: rgba(255, 255, 255, 0.75);
}

.traj-result {
  flex: 0 0 auto;
  font-size: 0.68rem;
  font-weight: 800;
  color: #fff;
}

/* Slide 4 Horizontal Timing Bar Container */
.release-visual-container {
  height: 140px;
  min-height: 140px !important;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.4rem 0.75rem;
  box-sizing: border-box;
  background: #071120;
}

.tut-anim-timing-meter-box {
  width: 100%;
  max-width: 280px;
  background: rgba(15, 23, 42, 0.85);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 0.75rem;
  padding: 0.35rem 0.65rem 0.3rem;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.tut-timing-top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin-bottom: 0.25rem;
}

.tut-timing-title-lbl {
  font-size: 0.68rem;
  font-weight: 900;
  color: #94a3b8;
  letter-spacing: 1.5px;
}

.tut-timing-perfect-pill {
  font-size: 0.6rem;
  font-weight: 900;
  color: #34d399;
  letter-spacing: 0.8px;
  background: rgba(16, 185, 129, 0.2);
  border: 1px solid rgba(16, 185, 129, 0.5);
  padding: 0.1rem 0.45rem;
  border-radius: 9999px;
  text-shadow: 0 0 6px rgba(52, 211, 153, 0.8);
}

.tut-anim-timing-bar {
  width: 100%;
  height: 18px;
  background: #111827;
  border-radius: 9px;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.8), 0 0 8px rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.12);
}

.tut-anim-timing-bar .tut-zone {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.tut-anim-timing-bar .tut-zone span {
  font-size: 0.52rem;
  font-weight: 900;
  color: rgba(255, 255, 255, 0.85);
  letter-spacing: 0.5px;
  pointer-events: none;
}

.tut-anim-timing-bar .red-early {
  flex: 1;
  background: linear-gradient(90deg, #7f1d1d 0%, #ef4444 100%);
}

.tut-anim-timing-bar .orange-early {
  width: 14%;
  background: linear-gradient(90deg, #ef4444 0%, #f59e0b 100%);
}

.tut-anim-timing-bar .green-perfect {
  width: 28%;
  background: linear-gradient(180deg, #34d399 0%, #059669 100%);
  box-shadow: 0 0 10px rgba(52, 211, 153, 0.95);
  z-index: 2;
  border-left: 1px solid #ffffff;
  border-right: 1px solid #ffffff;
}

.tut-anim-timing-bar .orange-late {
  width: 14%;
  background: linear-gradient(90deg, #f59e0b 0%, #ef4444 100%);
}

.tut-anim-timing-bar .red-late {
  flex: 1;
  background: linear-gradient(90deg, #ef4444 0%, #7f1d1d 100%);
}

.tut-anim-timing-needle {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 4px;
  background: #ffffff;
  box-shadow: 0 0 8px #ffffff, 0 0 14px rgba(56, 189, 248, 1);
  border-radius: 2px;
  z-index: 10;
  animation: tutTimingNeedleSweep 2.4s infinite cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes tutTimingNeedleSweep {
  0% {
    left: 4%;
    transform: scaleY(0.9);
  }

  45%,
  68% {
    left: 50%;
    transform: scaleY(1.15);
    box-shadow: 0 0 12px #ffffff, 0 0 20px #34d399;
  }

  92% {
    left: 96%;
    transform: scaleY(0.9);
  }

  100% {
    left: 4%;
  }
}

.tut-timing-feedback-anim {
  font-size: 0.74rem;
  font-weight: 900;
  color: #34d399;
  letter-spacing: 1px;
  margin-top: 0.2rem;
  text-shadow: 0 0 10px rgba(52, 211, 153, 0.85);
  animation: tutFeedbackPop 2.4s infinite ease-in-out;
}

@keyframes tutFeedbackPop {

  0%,
  35% {
    opacity: 0;
    transform: scale(0.7);
  }

  45%,
  70% {
    opacity: 1;
    transform: scale(1.1);
    color: #34d399;
  }

  85%,
  100% {
    opacity: 0;
    transform: scale(0.7);
  }
}

/* Shot Release & Flying Ball Synchronized Animation */
.anim-release-shot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  position: relative;
  height: 38px;
}

.anim-batter-swing {
  font-size: 1.85rem;
  animation: tutBatterHitSwing 2.4s infinite ease-in-out;
}

@keyframes tutBatterHitSwing {

  0%,
  38% {
    transform: rotate(0deg) scale(1);
  }

  45%,
  70% {
    transform: rotate(-25deg) scale(1.2);
  }

  90%,
  100% {
    transform: rotate(0deg) scale(1);
  }
}

.anim-smash-spark {
  font-size: 1.55rem;
  animation: tutSparkFlash 2.4s infinite ease-in-out;
}

@keyframes tutSparkFlash {

  0%,
  40% {
    opacity: 0;
    transform: scale(0.3);
  }

  45%,
  68% {
    opacity: 1;
    transform: scale(1.3);
  }

  78%,
  100% {
    opacity: 0;
    transform: scale(0.3);
  }
}

.anim-flying-ball {
  font-size: 1.15rem;
  animation: tutBallFlight 2.4s infinite cubic-bezier(0.2, 0.8, 0.2, 1);
}

@keyframes tutBallFlight {

  0%,
  42% {
    transform: translate(0, 0);
    opacity: 0;
  }

  45% {
    opacity: 1;
  }

  75%,
  100% {
    transform: translate(45px, -24px) scale(0.5);
    opacity: 0;
  }
}

/* ===========================================================================
   PULSING TAP ANYWHERE TO CONTINUE (CLEAN TEXT OUTSIDE OF CARD)
   =========================================================================== */
.tut-tap-outside-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  color: #facc15;
  font-size: 0.95rem;
  font-weight: 900;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.95), 0 0 16px rgba(250, 204, 21, 0.8);
  animation: tapHintFloat 1.8s infinite ease-in-out;
  pointer-events: none;
  user-select: none;
  margin-top: 0.35rem;
}

@keyframes tapHintFloat {

  0%,
  100% {
    transform: translateY(0);
    opacity: 0.88;
    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.95), 0 0 14px rgba(250, 204, 21, 0.7);
  }

  50% {
    transform: translateY(-4px);
    opacity: 1;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.95), 0 0 24px rgba(250, 204, 21, 1);
  }
}

.tut-tap-hand {
  font-size: 1.15rem;
  animation: tapHandBounce 1.2s infinite ease-in-out;
}

@keyframes tapHandBounce {

  0%,
  100% {
    transform: translateY(0);
  }

  50% {
    transform: translateY(-3px);
  }
}

.tut-tap-text {
  font-weight: 900;
  letter-spacing: 1.2px;
}

/* ===========================================================================
   MOBILE FRIENDLY RESPONSIVE STYLING
   =========================================================================== */
@media (max-width: 480px) {
  .tut-walkthrough-container {
    max-width: 90vw;
    gap: 0.75rem;
  }

  .tut-walkthrough-card {
    padding: 1.25rem 1rem 1.25rem;
    border-radius: 1.35rem;
  }

  .tut-step-header {
    margin-bottom: 0.15rem;
  }

  .tut-card-title {
    font-size: 1.28rem;
    margin: 0 0 0.55rem 0;
  }

  .tut-card-desc {
    font-size: 0.82rem;
    margin: 0 0 1.1rem 0;
    line-height: 1.4;
  }

  .tut-card-body .how-col-visual,
  .split-phone-visual {
    height: 130px;
    max-width: 100%;
  }

  .tut-anim-timing-meter-box {
    max-width: 250px;
    padding: 0.28rem 0.55rem 0.25rem;
  }

  .wasd-pitch-grid,
  .power-col-layout,
  .shotmode-visual-container,
  .release-visual-container {
    height: 130px !important;
  }

  .tut-tap-outside-hint {
    font-size: 0.85rem;
    letter-spacing: 1px;
    margin-top: 0.25rem;
  }

  .tut-practice-flow {
    max-width: 92vw;
  }

  .tut-walkthrough-skip-btn,
  .tut-skip-btn {
    bottom: 0.85rem !important;
    right: 0.85rem !important;
    font-size: 0.76rem !important;
    padding: 0.32rem 0.85rem !important;
  }

  .tut-prompt-card {
    padding: 0.65rem 0.85rem;
  }

  .tut-prompt-header {
    font-size: 1.05rem;
    margin-bottom: 0.25rem;
  }

  .tut-prompt-sub {
    font-size: 0.8rem;
  }
}

@media (max-height: 680px) {
  .tut-walkthrough-container {
    gap: 0.5rem;
  }

  .tut-walkthrough-card {
    padding: 1rem 0.85rem;
  }

  .tut-step-header {
    margin-bottom: 0.75rem;
  }

  .tut-card-title {
    margin-bottom: 0.4rem;
  }

  .tut-card-desc {
    margin-bottom: 0.85rem;
  }

  .tut-card-body .how-col-visual,
  .split-phone-visual {
    height: 115px;
  }

  .shotmode-visual-container,
  .release-visual-container {
    height: 115px !important;
    min-height: 115px !important;
  }

  .vert-meter-track {
    height: 60px !important;
  }

  .tut-tap-outside-hint {
    font-size: 0.78rem;
  }
}

/* ==========================================================================
   NEW GROUND UNLOCKED 3D SHOWCASE OVERLAY
   ========================================================================== */
/* ---- Ground unlock loader ---- */
.ground-unlock-loader {
  position: fixed;
  inset: 0;
  z-index: 14000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  background: radial-gradient(circle at 50% 45%, #143055 0%, #050a17 70%);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: auto;
}

.ground-unlock-loader.show {
  opacity: 1;
}

.gul-icon {
  font-size: 4rem;
  line-height: 1;
  filter: drop-shadow(0 0 1.5rem rgba(56, 189, 248, 0.8));
  animation: gulPulse 1.1s ease-in-out infinite;
}

@keyframes gulPulse {

  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.12);
  }
}

.gul-label {
  color: #7dd3fc;
  font-size: 0.75rem;
  font-weight: 900;
  letter-spacing: 3px;
}

.gul-name {
  color: #fff;
  font-size: clamp(1.3rem, 6vw, 2rem);
  font-weight: 900;
  letter-spacing: 1.5px;
  text-shadow: 0 0 1.5rem rgba(56, 189, 248, 0.7);
}

.gul-track {
  width: min(60vw, 16rem);
  height: 0.4rem;
  margin-top: 0.5rem;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.15);
  overflow: hidden;
}

.gul-fill {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 9999px;
  background: linear-gradient(90deg, #38bdf8, #fbbf24);
}

.ground-showcase-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 999;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  padding: 1.5rem 1rem 1.8rem;
  background: radial-gradient(circle at 50% 50%, transparent 40%, rgba(5, 10, 23, 0.45) 100%);
  animation: fadeIn 0.4s ease;
}

.ground-showcase-top-badge {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 1.4rem;
  background: linear-gradient(135deg, rgba(245, 158, 11, 0.95), rgba(217, 119, 6, 0.95));
  border: 2px solid #fde047;
  border-radius: 9999px;
  color: #ffffff;
  font-size: 0.95rem;
  font-weight: 900;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  box-shadow: 0 4px 20px rgba(245, 158, 11, 0.6), 0 0 15px rgba(253, 224, 71, 0.4);
  animation: levelBadgePulse 2s infinite ease-in-out;
}

/* Browsing a ground you haven't earned: same badge, cooled off so it reads as a
   look ahead instead of a celebration. */
.showcase-badge-locked {
  background: linear-gradient(135deg, rgba(56, 189, 248, 0.9), rgba(2, 132, 199, 0.9));
  border-color: #7dd3fc;
  box-shadow: 0 4px 20px rgba(2, 132, 199, 0.55), 0 0 15px rgba(125, 211, 252, 0.35);
  animation: none;
}

/* Only the browse modes get an X — the unlock flow leaves through CONTINUE. */
.showcase-close-btn {
  position: absolute;
  top: 1.2rem;
  right: 1.2rem;
  pointer-events: auto;
  width: max(2.6rem, 40px);
  height: max(2.6rem, 40px);
  border-radius: 50%;
  background: rgba(8, 15, 30, 0.85);
  border: 2px solid rgba(255, 255, 255, 0.45);
  color: #fff;
  font-family: inherit;
  font-size: 1.1rem;
  font-weight: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(6px);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.6);
  transition: transform 0.15s ease, background 0.15s ease;
}

.showcase-close-btn:hover {
  background: rgba(220, 38, 38, 0.9);
  transform: scale(1.08);
}

/* The showcase is fullscreen, so its stepper anchors to the viewport rather
   than to the 360 popup's inset frame. Sits above the bottom card's top edge. */
.showcase-zoom {
  position: absolute;
  right: 1rem;
  top: 42%;
  transform: translateY(-50%);
}

.ground-showcase-hint-chip {
  pointer-events: auto;
  background: rgba(15, 23, 42, 0.8);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 9999px;
  padding: 0.35rem 1.1rem;
  font-size: 0.8rem;
  font-weight: 800;
  color: #cbd5e1;
  letter-spacing: 0.5px;
  backdrop-filter: blur(8px);
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
}

.ground-showcase-bottom-card {
  pointer-events: auto;
  width: 94%;
  max-width: 440px;
  background: linear-gradient(180deg, rgba(11, 21, 40, 0.94) 0%, rgba(6, 11, 22, 0.98) 100%);
  border: 2px solid rgba(56, 189, 248, 0.5);
  border-radius: 1.5rem;
  padding: 1.15rem 1.25rem;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.85), 0 0 25px rgba(56, 189, 248, 0.3);
  backdrop-filter: blur(12px);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.showcase-header-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}

.showcase-venue-icon {
  font-size: 2.6rem;
  filter: drop-shadow(0 0 12px rgba(56, 189, 248, 0.8));
}

.showcase-venue-details {
  display: flex;
  flex-direction: column;
  text-align: left;
}

.showcase-venue-name {
  margin: 0;
  font-size: 1.45rem;
  font-weight: 900;
  color: #ffffff;
  letter-spacing: 1.5px;
  text-shadow: 0 0 15px rgba(56, 189, 248, 0.7);
}

.showcase-venue-desc {
  font-size: 0.76rem;
  font-weight: 700;
  color: #94a3b8;
  letter-spacing: 0.3px;
  margin-top: 0.15rem;
}

.showcase-opponents-row {
  display: flex;
  gap: 0.5rem;
}

.showcase-opp-pill {
  flex: 1;
  background: rgba(15, 23, 42, 0.85);
  border: 1px solid rgba(56, 189, 248, 0.25);
  border-radius: 0.75rem;
  padding: 0.4rem 0.65rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.15rem;
}

.showcase-opp-pill .pill-label {
  font-size: 0.65rem;
  font-weight: 800;
  color: #94a3b8;
  letter-spacing: 0.5px;
}

.showcase-opp-pill .pill-val {
  font-size: 0.82rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

.showcase-opp-pill .squad-val {
  color: #38bdf8;
}

.showcase-opp-pill .boss-val {
  color: #fbbf24;
}

.showcase-continue-btn {
  width: 100%;
  height: 3.4rem;
  border-radius: 1rem;
  background: linear-gradient(180deg, #10b981 0%, #059669 100%);
  border: 2px solid #34d399;
  box-shadow: 0 0.25rem 0 #047857, 0 0.4rem 1.2rem rgba(16, 185, 129, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4);
  color: #ffffff;
  font-size: 1.15rem;
  font-weight: 900;
  letter-spacing: 1.5px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  cursor: pointer;
  transition: transform 0.1s ease, filter 0.15s ease, box-shadow 0.1s ease;
}

.showcase-continue-btn:hover {
  filter: brightness(1.1);
  transform: translateY(-2px);
  box-shadow: 0 0.35rem 0 #047857, 0 0.55rem 1.4rem rgba(16, 185, 129, 0.7);
}

.showcase-continue-btn:active {
  transform: translateY(2px);
  box-shadow: 0 0.1rem 0 #047857, 0 0.15rem 0.3rem rgba(16, 185, 129, 0.4);
}

/* ===========================================================================
   CHARACTER SHOP CTA (match target + post-match panels)
   Most players never leave the match loop to visit the shop, so the shop comes
   to them. The badge only lights up when a hero is actually affordable — see
   countPurchasableCharacters() in js/state.js.
   =========================================================================== */
.char-shop-cta {
  position: relative;
  flex: none;
}

/* Wide variant for the pre-match panel, where it sits beside CONTINUE.
   Deliberately built from the same recipe as button.play — same radius, same
   solid bottom edge, same press-down — just in cyan instead of green, so it
   reads as part of the same button family rather than a stray outlined pill.
   It stretches to CONTINUE's height via align-items:stretch on their row. */
.char-shop-cta-wide {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 1px;
  color: #06283d;
  background: linear-gradient(180deg, #7dd3fc 0%, #38bdf8 100%);
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 1.5rem;
  box-shadow: 0 0.3125rem 0 #0369a1, 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s, filter .15s ease;
}

.char-shop-cta-wide:hover {
  filter: brightness(1.08);
}

.char-shop-cta-wide:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 #0369a1, 0 0.25rem 0.625rem rgba(0, 0, 0, .4);
}

.char-shop-cta .cta-dot {
  position: absolute;
  top: -0.35rem;
  right: -0.35rem;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.2rem;
  border-radius: 9999px;
  background: #ef4444;
  border: 2px solid #0d131a;
  color: #ffffff;
  font-size: 0.68rem;
  font-weight: 900;
  line-height: 1;
  display: none;
  /* shown only via .has-new */
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 10px rgba(239, 68, 68, 0.9);
  pointer-events: none;
}

/* Only a genuinely affordable hero turns the button into an attractor. */
.char-shop-cta.has-new .cta-dot {
  display: flex;
}

.char-shop-cta.has-new {
  animation: charShopCtaPulse 1.5s ease-in-out infinite;
}

/* Lit state: the compact (icon-only) button sits next to a plain Home button,
   so a dot alone left it reading as just another grey control. Same light-cyan
   fill + dark-navy icon as the wide variant (so the two read as one button
   family, not two different color schemes), and critically the glow is
   ADDED to .back-btn's embossed shadow rather than replacing it outright —
   an earlier version overwrote box-shadow wholesale, which dropped the
   drop-shadow + inset highlight every other button here has and left this
   one looking flat and plasticky next to them. */
.char-shop-cta.has-new:not(.char-shop-cta-wide) {
  background: linear-gradient(180deg, #7dd3fc 0%, #38bdf8 100%);
  border: 1px solid rgba(191, 219, 254, 0.9);
  border-bottom: 2px solid rgba(255, 255, 255, 0.4);
  color: #06283d;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.4),
    0 0 16px rgba(56, 189, 248, 0.7);
}

.char-shop-cta.has-new:not(.char-shop-cta-wide):active {
  transform: translateY(2px);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(255, 255, 255, 0.3),
    0 0 10px rgba(56, 189, 248, 0.6);
}

/* Keeps the solid bottom edge — a bare glow here would flatten the button and
   break it out of the .play family again. */
.char-shop-cta.has-new.char-shop-cta-wide {
  box-shadow: 0 0.3125rem 0 #0369a1, 0 0.625rem 1.25rem rgba(0, 0, 0, .4),
    0 0 20px rgba(56, 189, 248, 0.65);
}

@keyframes charShopCtaPulse {

  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.09);
  }
}

/* The pulse is decorative; players who ask for reduced motion get the dot only. */
@media (prefers-reduced-motion: reduce) {
  .char-shop-cta.has-new {
    animation: none;
  }
}

/* ===========================================================================
   HOME MENU: SURVIVAL / SIX CHALLENGE shortcuts
   Same recipe as .custom-match-nav-btn and .leaderboard-icon-btn (radius,
   solid bottom edge, press-down) so the menu reads as one button family —
   only the colour pair changes, matched to the mode picker's cards.
   =========================================================================== */
.menu-nav-btn {
  pointer-events: auto;
  cursor: pointer;
  margin-top: 0.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font: inherit;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: #ffffff;
  border: none;
  border-radius: 0.75rem;
  padding: 0.875rem 2.375rem;
  box-shadow: 0 0.3125rem 0 var(--step), 0 0.625rem 1.25rem rgba(0, 0, 0, .4);
  transition: transform .08s, box-shadow .08s;
}

.menu-nav-btn:active {
  transform: translateY(0.25rem);
  box-shadow: 0 1px 0 var(--step), 0 0.25rem 0.625rem rgba(0, 0, 0, 0.4);
}

/* Colour pairs lifted from the mode picker's cards, so the same mode looks the
   same wherever it is offered. */
.menu-nav-red {
  background: linear-gradient(180deg, #e35d4a 0%, #b12f28 100%);
  --step: #7c1f1a;
}

.menu-nav-magenta {
  background: linear-gradient(180deg, #d94fa4 0%, #a3236f 100%);
  --step: #6f174b;
}

/* ===========================================================================
   LEADERBOARD "HOW IS THIS CALCULATED" POPOVER
   One (i) button next to YOUR BEST; its explanation swaps per active tab in
   refreshLeaderboardScreen() (js/leaderboard-ui.js). Hover opens it on
   desktop; a tap toggles it on touch, since :hover never fires reliably there.

   A true popover: absolutely positioned and floating OVER whatever is below
   (the leaderboard list), not an inline block that shoves that content down
   when it opens. .lb-best-label is its positioning anchor, so it's nested
   right there in the HTML rather than off in a shared ancestor.
   =========================================================================== */
.lb-best-label {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.lb-info-btn {
  pointer-events: auto;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #94a3b8;
  font-size: 0.85rem;
  line-height: 1;
  transition: background 0.15s ease, color 0.15s ease;
}

.lb-info-btn:hover,
.lb-info-btn.open {
  background: rgba(56, 189, 248, 0.25);
  color: #7dd3fc;
}

.lb-info-tooltip {
  display: none;
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.65rem);
  left: 50%;
  transform: translateX(-38%);
  width: max-content;
  max-width: 15.5rem;
  padding: 0.65rem 0.85rem;
  background: rgba(15, 23, 42, 0.98);
  border: 1px solid rgba(56, 189, 248, 0.4);
  border-radius: 0.6rem;
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.55);
  color: #cbd5e1;
  font-size: 0.8rem;
  line-height: 1.35;
  text-align: left;
  pointer-events: auto;
}

/* Small arrow pointing back up at the (i) button, so the floating card still
   reads as anchored to it rather than a stray box. */
.lb-info-tooltip::before {
  content: '';
  position: absolute;
  top: -0.4rem;
  left: 38%;
  transform: translateX(-50%);
  width: 0.8rem;
  height: 0.8rem;
  background: rgba(15, 23, 42, 0.98);
  border-left: 1px solid rgba(56, 189, 248, 0.4);
  border-top: 1px solid rgba(56, 189, 248, 0.4);
  border-radius: 2px 0 0 0;
  transform: translateX(-50%) rotate(45deg);
}

/* Shown/hidden entirely from JS (js/leaderboard-ui.js): mouseenter/mouseleave
   opens/closes it on desktop, click toggles it on touch (where hover never
   fires reliably), and a click anywhere else on the page closes it. */
.lb-info-tooltip.show {
  display: block;
}

/* Right-anchored tabs (e.g. narrow phones) can push this off the left edge of
   the screen — clamp its width and let it wrap rather than overflow. */
@media (max-width: 420px) {
  .lb-info-tooltip {
    max-width: min(15.5rem, 78vw);
  }
}

/* ===========================================================================
   CAMPAIGN PATHWAY (#levelMapScreen)
   The 50-level journey, rendered by renderLevelMap() in js/state.js. A vertical
   scroll of world cards rather than a free-panning map: it has to work on a
   phone in portrait, and scrolling is the one gesture that needs no explaining.

   Each world card is themed from one inline custom property, --w-accent (set
   from WORLDS[].themeColor in js/config.js), which drives its border, glow,
   and lock bar. Adding an 11th world therefore needs no CSS here at all.
   =========================================================================== */
#levelMapScreen {
  display: none;
  position: absolute;
  inset: 0;
  flex-direction: column;
  align-items: center;
  z-index: 100;
  pointer-events: auto;
  padding: 3.25rem 0 0;
  /* Deep space ground with a faint starfield. Small radial-gradient tiles at
     differing sizes read as scattered stars with no image request. */
  background:
    radial-gradient(1.5px 1.5px at 20% 12%, rgba(255, 255, 255, 0.55) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 72% 34%, rgba(255, 255, 255, 0.40) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 45% 68%, rgba(255, 255, 255, 0.45) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 88% 82%, rgba(255, 255, 255, 0.35) 50%, transparent 51%),
    radial-gradient(120% 80% at 50% 0%, #142a52 0%, #0b1730 45%, #070d1c 100%);
  background-size: 320px 280px, 260px 300px, 300px 240px, 280px 320px, 100% 100%;
}

#levelMapScreen.show {
  display: flex;
}

#levelMapScreen #levelMapBackBtn {
  position: absolute;
  top: 1.25rem;
  left: 1.5rem;
  pointer-events: auto;
  z-index: 3;
}

/* Fixed header: the "where am I" answer stays on screen while the path
   scrolls, or the player loses their sense of overall progress. */
#levelMapHeader {
  flex: none;
  width: 100%;
  max-width: 34rem;
  padding: 0 1.25rem 0.85rem;
  text-align: center;
}

#levelMapTitle {
  margin: 0 0 0.4rem;
  color: #fff;
  font-size: clamp(1.4rem, 6vw, 2.1rem);
  font-weight: 900;
  letter-spacing: 2px;
  text-shadow: 0 0 1.5rem rgba(125, 211, 252, 0.5), 0 0.25rem 0 rgba(0, 0, 0, 0.55);
}

/* Sparkles either side of the title. Decorative, so they are pseudo elements
   rather than markup a screen reader would have to announce. */
#levelMapTitle::before,
#levelMapTitle::after {
  content: "\2726";
  margin: 0 0.6rem;
  color: #ffd166;
  font-size: 0.6em;
  vertical-align: 0.25em;
  text-shadow: 0 0 0.6rem rgba(255, 209, 102, 0.9);
}

#levelMapProgress {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  font-size: clamp(0.72rem, 2.6vw, 0.85rem);
  font-weight: 800;
  letter-spacing: 1.5px;
  color: #cbd5e1;
}

#levelMapStars {
  color: #ffd166;
  text-shadow: 0 0 0.6rem rgba(255, 209, 102, 0.55);
}

.level-map-track {
  margin-top: 0.6rem;
  height: 0.6rem;
  border-radius: 999px;
  background: rgba(8, 14, 30, 0.9);
  border: 1px solid rgba(125, 211, 252, 0.28);
  overflow: hidden;
}

.level-map-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #38bdf8, #7dd3fc);
  box-shadow: 0 0 0.75rem rgba(56, 189, 248, 0.8);
  transition: width 0.4s ease;
}

/* ---- Scroll area + its custom scrollbar ---- */
#levelMapScroll {
  position: relative;
  /* offsetParent for the auto-scroll-to-current maths */
  flex: 1;
  width: 100%;
  max-width: 34rem;
  overflow-y: auto;
  padding: 0.25rem 1.25rem 2rem;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  /* Firefox */
  scrollbar-color: #4f7ff5 rgba(8, 14, 30, 0.8);
}

/* WebKit/Blink. Firefox honours only the two properties above, so the bar is
   styled there too, just without the rounded inset track. */
#levelMapScroll::-webkit-scrollbar {
  width: 0.7rem;
}

#levelMapScroll::-webkit-scrollbar-track {
  background: rgba(8, 14, 30, 0.85);
  border: 1px solid rgba(125, 211, 252, 0.22);
  border-radius: 999px;
  margin: 0.25rem 0;
}

#levelMapScroll::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: linear-gradient(180deg, #7dd3fc 0%, #4f7ff5 100%);
  border: 1px solid rgba(255, 255, 255, 0.35);
  box-shadow: 0 0 0.5rem rgba(79, 127, 245, 0.8);
}

#levelMapScroll::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, #a5e2ff 0%, #6b93ff 100%);
}

/* ---- One world card ---- */
.lm-world {
  position: relative;
  margin-bottom: 1.1rem;
  padding: 0.9rem 1rem 1rem;
  border-radius: 1.15rem;
  border: 2px solid var(--w-accent, #64748b);
  background: var(--w-bg, linear-gradient(135deg, #1e293b, #0f172a));
  box-shadow:
    0 0 1.4rem -0.3rem var(--w-accent, #64748b),
    0 0.5rem 1.1rem rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.16);
  /* Entrance: each card fades/slides in on its own delay, set inline by
     renderLevelMap() (js/state.js) rather than nth-child, since the teaser
     banner above the first card would throw off a pure-CSS sibling count. */
  opacity: 0;
  animation: lmWorldIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes lmWorldIn {
  from { opacity: 0; transform: translateY(1.1rem); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .lm-world { animation: none; opacity: 1; }
}

/* Locked worlds keep their colour and stay legible: they are the reward being
   advertised, so draining them to grey removes the pull they exist for. The
   lock reads from the dark nodes and the bar underneath instead. */

.lm-world-head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 0.9rem;
}

.lm-world-icon {
  flex: none;
  font-size: 1.9rem;
  line-height: 1;
  filter: drop-shadow(0 0.15rem 0.3rem rgba(0, 0, 0, 0.6));
}

.lm-world-text {
  flex: 1;
  min-width: 0;
  text-align: left;
}

.lm-world-name {
  display: block;
  color: #fff;
  font-size: clamp(0.95rem, 3.6vw, 1.2rem);
  font-weight: 900;
  letter-spacing: 1px;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
}

.lm-world-sub {
  display: block;
  margin-top: 0.1rem;
  color: rgba(255, 255, 255, 0.82);
  font-size: 0.7rem;
  font-weight: 700;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}

/* Closing card after the last real world — signals the journey keeps going
   rather than the scroll just dead-ending. Shares .lm-world's card shell
   (border/glow/entrance animation) but has no node row or lock note, so its
   own rules only need to retune spacing and swap the palette for a neutral
   "not a real world yet" look instead of a --w-accent colour. */
.lm-coming-soon {
  --w-accent: rgba(148, 163, 184, 0.55);
  --w-bg: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
  border-style: dashed;
  text-align: center;
}

.lm-coming-soon .lm-world-head {
  margin-bottom: 0;
  justify-content: center;
}

.lm-coming-soon .lm-world-text {
  flex: none;
  text-align: center;
}

.lm-coming-soon .lm-world-icon {
  font-size: 1.6rem;
  animation: lmComingSoonTwinkle 2.4s ease-in-out infinite;
}

@keyframes lmComingSoonTwinkle {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50%      { opacity: 1; transform: scale(1.15); }
}

@media (prefers-reduced-motion: reduce) {
  .lm-coming-soon .lm-world-icon { animation: none; }
}

.lm-coming-soon .lm-world-name {
  color: rgba(255, 255, 255, 0.85);
  letter-spacing: 2px;
}

.lm-coming-soon .lm-world-sub {
  color: rgba(255, 255, 255, 0.55);
}

.lm-world-stars {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  color: #fff;
  font-size: 0.8rem;
  font-weight: 900;
  white-space: nowrap;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
}

.lm-world-stars .lm-ws-icon {
  color: #ffd166;
  filter: drop-shadow(0 0 0.35rem rgba(255, 209, 102, 0.8));
}

/* A world with none of its stars collected greys its icon, so a full card and
   an untouched one differ at a glance. */
.lm-world-stars.lm-ws-none .lm-ws-icon {
  color: #94a3b8;
  filter: none;
}

/* SQUAD chip — opens the world dossier. Sits on the header's own line rather
   than in the node row so it never competes with a level for the same tap. */
.lm-world-info {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3rem 0.55rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(2, 6, 16, 0.55);
  color: #fff;
  font-family: inherit;
  font-size: 0.6rem;
  font-weight: 900;
  letter-spacing: 0.6px;
  cursor: pointer;
  pointer-events: auto;
  transition: background 0.15s, border-color 0.15s, transform 0.1s;
}

.lm-world-info:hover {
  background: rgba(2, 6, 16, 0.8);
  border-color: rgba(255, 255, 255, 0.55);
}

.lm-world-info:active {
  transform: translateY(1px);
}

.lm-wi-icon {
  font-size: 0.75rem;
  line-height: 1;
}

/* A locked world's card is dimmed as a whole, but its dossier is the one thing
   still worth opening there — it's what says why the world is worth unlocking. */
.lm-world.lm-locked .lm-world-info {
  opacity: 1;
}

/* ---- World dossier modal ---- */
.world-info-modal {
  position: absolute;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(2, 6, 16, 0.82);
  backdrop-filter: blur(0.25rem);
  opacity: 0;
  transition: opacity 0.22s ease;
  pointer-events: auto;
}

.world-info-modal.show {
  opacity: 1;
}

.wi-card {
  position: relative;
  width: 100%;
  max-width: 25rem;
  max-height: 100%;
  overflow-y: auto;
  padding: 1.1rem 1.15rem 1rem;
  border-radius: 1.1rem;
  border: 1px solid var(--w-accent, #64748b);
  background: linear-gradient(160deg, #17233a 0%, #0b1220 100%);
  box-shadow: 0 1.25rem 2.5rem rgba(0, 0, 0, 0.7), 0 0 1.5rem -0.25rem var(--w-accent, transparent);
  transform: translateY(0.75rem) scale(0.97);
  transition: transform 0.22s cubic-bezier(0.34, 1.4, 0.64, 1);
}

.world-info-modal.show .wi-card {
  transform: translateY(0) scale(1);
}

.wi-close {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  background: rgba(0, 0, 0, 0.4);
  color: #fff;
  font-size: 0.85rem;
  font-family: inherit;
  line-height: 1;
  cursor: pointer;
}

.wi-head {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding-right: 2rem;
}

.wi-icon {
  flex: none;
  font-size: 2rem;
  line-height: 1;
}

.wi-titles {
  min-width: 0;
}

.wi-name {
  display: block;
  color: #fff;
  font-size: 1.05rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

.wi-loc {
  display: block;
  margin-top: 0.1rem;
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.7rem;
  font-weight: 700;
}

.wi-meta {
  margin-top: 0.7rem;
  color: var(--w-accent, #cbd5e1);
  font-size: 0.72rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

.wi-desc {
  margin-top: 0.2rem;
  color: rgba(255, 255, 255, 0.75);
  font-size: 0.74rem;
  font-weight: 600;
  line-height: 1.35;
}

.wi-team-row {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.85rem;
  padding-top: 0.7rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.wi-team-swatch {
  flex: none;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 0.25rem;
  border: 1px solid rgba(255, 255, 255, 0.35);
}

.wi-team-label {
  color: rgba(255, 255, 255, 0.55);
  font-size: 0.62rem;
  font-weight: 900;
  letter-spacing: 1px;
}

.wi-team-name {
  color: #fff;
  font-size: 0.8rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

.wi-squad {
  margin-top: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.wi-bowler {
  display: grid;
  grid-template-columns: 1.4rem minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.5rem;
  border-radius: 0.5rem;
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.05);
  font-size: 0.68rem;
  font-weight: 700;
}

/* The chapter's last bowler is its boss — same row, gold, so it reads as the
   wall at the end rather than a sixth name on the list. */
.wi-boss {
  border-color: rgba(251, 191, 36, 0.5);
  background: rgba(245, 158, 11, 0.12);
}

.wi-b-num {
  color: rgba(255, 255, 255, 0.55);
  font-size: 0.65rem;
  font-weight: 900;
  text-align: center;
}

.wi-b-name {
  color: #fff;
  font-weight: 800;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wi-b-type,
.wi-b-tier,
.wi-b-speed {
  font-size: 0.58rem;
  font-weight: 900;
  letter-spacing: 0.4px;
  white-space: nowrap;
}

.wi-b-type {
  color: #7dd3fc;
}

.wi-b-tier {
  color: rgba(255, 255, 255, 0.6);
}

.wi-boss .wi-b-tier {
  color: #fbbf24;
}

.wi-b-speed {
  color: rgba(255, 255, 255, 0.45);
}

.wi-foot {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.9rem;
}

.wi-btn {
  flex: 1;
  padding: 0.6rem 0.5rem;
  border-radius: 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 900;
  letter-spacing: 0.5px;
  cursor: pointer;
  transition: filter 0.15s, transform 0.1s;
}

.wi-btn:active:not(:disabled) {
  transform: translateY(1px);
}

.wi-btn:hover:not(:disabled) {
  filter: brightness(1.15);
}

.wi-btn-360 {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
}

/* Touring a ground you haven't earned yet — still a live button, but tinted so
   it reads as a look ahead rather than somewhere you already play. */
.wi-btn-preview {
  background: rgba(56, 189, 248, 0.16);
  border-color: rgba(56, 189, 248, 0.5);
  color: #bae6fd;
}

.wi-btn-play {
  background: linear-gradient(180deg, #22c55e 0%, #15803d 100%);
  border-color: rgba(255, 255, 255, 0.3);
  color: #fff;
}

.wi-btn-play:disabled {
  background: rgba(255, 255, 255, 0.07);
  color: rgba(255, 255, 255, 0.45);
  cursor: not-allowed;
}

@media (max-width: 30rem) {
  .wi-b-speed {
    display: none;
  }

  .wi-bowler {
    grid-template-columns: 1.4rem minmax(0, 1fr) auto auto;
  }
}

/* ---- The path of 5 level nodes ---- */
.lm-nodes {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.2rem;
}

.lm-node {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0 0 1.1rem;
  /* room for the YOU ARE HERE tag */
  border: none;
  background: none;
  cursor: pointer;
  pointer-events: auto;
  font: inherit;
  /* Entrance: pops in after its world's card, left to right — delay set
     inline in js/state.js. A scale+rise reads more like "arriving on the
     path" than the card's plain slide-up. */
  opacity: 0;
  animation: lmNodeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@keyframes lmNodeIn {
  from { opacity: 0; transform: translateY(0.6rem) scale(0.8); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .lm-node { animation: none; opacity: 1; }
}

.lm-node:disabled {
  cursor: default;
}

/* Connector between nodes, drawn on the EARLIER node extending forward to the
   next one (not on the later node reaching back) — deliberately, so it paints
   behind both dots it touches. None of these .lm-node siblings set their own
   z-index, so they stack purely by DOM order: a later node's whole box always
   paints above an earlier one's. A backward-reaching line on the later node
   was therefore painting on TOP of the earlier node's dot at the join, cutting
   across it instead of running behind it — moving it here means the line's
   own low z-index only ever has to lose to a dot within its OWN node (the one
   it started from), and the NEXT node's dot covers its far end simply by
   virtue of painting after it. */
.lm-node:not(:last-child)::after {
  content: '';
  position: absolute;
  top: 1.35rem;
  left: 50%;
  width: 100%;
  height: 0.25rem;
  background: rgba(255, 255, 255, 0.18);
  z-index: 0;
}

/* The travelled part of the path lights up: the segment LEAVING a node you've
   reached (done or current) is lit, same meaning as before the move above. */
.lm-node.lm-done::after,
.lm-node.lm-current::after {
  background: linear-gradient(90deg, #3b82f6, #60a5fa);
  box-shadow: 0 0 0.4rem rgba(96, 165, 250, 0.7);
}

.lm-dot {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.7rem;
  height: 2.7rem;
  border-radius: 50%;
  font-size: 1.05rem;
  font-weight: 900;
  color: #fff;
  background: linear-gradient(180deg, #1e293b 0%, #0f172a 100%);
  border: 2px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

/* Cleared: solid blue, the colour of the travelled path. */
.lm-node.lm-done .lm-dot {
  background: linear-gradient(180deg, #3b82f6 0%, #1d4ed8 100%);
  border-color: rgba(191, 219, 254, 0.85);
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5), 0 0 0.7rem rgba(59, 130, 246, 0.65),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

.lm-node.lm-locked .lm-dot {
  background: linear-gradient(180deg, #334155 0%, #1e293b 100%);
  color: rgba(255, 255, 255, 0.45);
  border-color: rgba(255, 255, 255, 0.16);
  font-size: 0.95rem;
}

/* The one node that answers "where am I now". It has to win the eye against 49
   others, so it takes the only saturated warm colour and the only animation. */
.lm-node.lm-current .lm-dot {
  color: #3a2c05;
  background: linear-gradient(180deg, #ffe27a 0%, #f5b425 100%);
  border-color: #fff8dc;
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5), 0 0 1rem rgba(245, 180, 37, 0.95),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  animation: lmPulse 1.7s ease-in-out infinite;
}

@keyframes lmPulse {

  0%,
  100% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.09);
  }
}

@media (prefers-reduced-motion: reduce) {
  .lm-node.lm-current .lm-dot {
    animation: none;
  }
}

/* Boss node closes each world. A rounded square reads as "different" at a
   glance without needing another colour. */
.lm-node.lm-boss .lm-dot {
  border-radius: 0.9rem;
}

.lm-stars {
  display: flex;
  gap: 0.05rem;
  font-size: 0.72rem;
  line-height: 1;
  min-height: 0.8rem;
}

.lm-star-on {
  color: #ffd166;
  text-shadow: 0 0 0.35rem rgba(255, 209, 102, 0.9);
}

.lm-star-off {
  color: rgba(255, 255, 255, 0.28);
}

/* "YOU ARE HERE" tag with a pointer up at its node. Absolutely positioned so
   it cannot push the node row taller than its neighbours. */
.lm-here {
  position: absolute;
  top: calc(100% - 0.95rem);
  left: 50%;
  transform: translateX(-50%);
  padding: 0.15rem 0.4rem;
  border-radius: 0.35rem;
  background: linear-gradient(180deg, #ffe27a 0%, #f5b425 100%);
  color: #3a2c05;
  font-size: 0.5rem;
  font-weight: 900;
  letter-spacing: 0.5px;
  white-space: nowrap;
  box-shadow: 0 0.15rem 0.4rem rgba(0, 0, 0, 0.55);
}

.lm-here::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 0.28rem solid transparent;
  border-bottom-color: #ffe27a;
}

/* Locked-world bar: states the goal rather than just dimming the world, so a
   locked section still reads as something to aim at. */
.lm-lock-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin-top: 0.85rem;
  padding: 0.45rem 0.5rem;
  border-radius: 0.55rem;
  background: rgba(4, 8, 18, 0.68);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--w-accent, #cbd5e1);
  font-size: 0.66rem;
  font-weight: 900;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
}

/* The forward pull: what the very next clear actually earns. */
.lm-next-up {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  margin: 0 0 1.1rem;
  padding: 0.6rem 0.75rem;
  border-radius: 0.7rem;
  border: 1px solid rgba(125, 211, 252, 0.45);
  background: rgba(12, 26, 56, 0.85);
  color: #e2e8f0;
  font-size: 0.72rem;
  font-weight: 900;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  text-align: center;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
  /* Leads the stagger, before the first world card. */
  opacity: 0;
  animation: lmWorldIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

@media (prefers-reduced-motion: reduce) {
  .lm-next-up { animation: none; opacity: 1; }
}

.lm-next-up .lm-next-world {
  color: #7dd3fc;
}

@media (max-width: 26rem) {
  .lm-dot {
    width: 2.2rem;
    height: 2.2rem;
    font-size: 0.9rem;
  }

  .lm-node.lm-boss .lm-dot {
    border-radius: 0.7rem;
  }

  .lm-world-icon {
    font-size: 1.55rem;
  }

  .lm-here {
    font-size: 0.44rem;
    padding: 0.12rem 0.3rem;
  }
}

/* ===========================================================================
   HOME MENU SKIN
   PLAY / CUSTOM MATCH / SURVIVAL / SIX CHALLENGE / LEADERBOARDS grew up as five
   separate components (button.play, .custom-match-nav-btn, .menu-nav-btn,
   .leaderboard-icon-btn), each with its own radius, text colour and shadow, so
   the stack read as five unrelated controls. They already share a size rule
   further up this sheet; this gives them one shared SKIN too.

   Every button is the same recipe and differs only in four custom properties,
   so a new menu entry is one selector with four colours — no new geometry.
   Declared after the originals: same specificity, later wins.
   =========================================================================== */
#overlay button.play,
#overlay .custom-match-nav-btn,
#overlay .menu-nav-btn,
#overlay .leaderboard-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 0 1.1rem;
  border-radius: 0.9rem;
  border: 2px solid var(--btn-edge, rgba(255, 255, 255, 0.5));
  background: linear-gradient(180deg, var(--btn-top) 0%, var(--btn-bot) 100%);
  color: var(--btn-ink, #fff);
  font: inherit;
  font-size: clamp(1rem, 3.6vw, 1.15rem);
  font-weight: 900;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  text-shadow: var(--btn-shadow, 0 2px 3px rgba(0, 0, 0, 0.45));
  cursor: pointer;
  pointer-events: auto;
  /* Solid bottom edge + ambient drop, the same 3D recipe the mode cards use. */
  box-shadow:
    0 0.3rem 0 var(--btn-step),
    0 0.55rem 1.1rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
  transition: transform .08s, box-shadow .08s, filter .15s ease;
}

#overlay button.play:hover,
#overlay .custom-match-nav-btn:hover,
#overlay .menu-nav-btn:hover,
#overlay .leaderboard-icon-btn:hover {
  filter: brightness(1.08);
}

/* Press-down: the button travels exactly as far as its solid edge is deep, so
   it reads as the surface meeting the base rather than sliding. */
#overlay button.play:active,
#overlay .custom-match-nav-btn:active,
#overlay .menu-nav-btn:active,
#overlay .leaderboard-icon-btn:active {
  transform: translateY(0.3rem);
  box-shadow:
    0 0 0 var(--btn-step),
    0 0.2rem 0.5rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

/* Icons: match the cap-height of the label rather than each carrying its own
   size, which is what made the old row look ragged. */
#overlay button.play>svg,
#overlay .leaderboard-icon-btn>svg {
  flex: none;
  width: 1.35rem;
  height: 1.35rem;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
}

#overlay .custom-match-nav-btn>span,
#overlay .menu-nav-btn>span {
  flex: none;
  font-size: 1.3rem !important;
  /* beats the inline font-size in index.html */
  line-height: 1;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
}

/* ---- Per-button palettes ---- */
#overlay button.play {
  --btn-top: #8fdc4d;
  --btn-bot: #5cb531;
  --btn-step: #3c7d1e;
  --btn-edge: #b6f07d;
}

#overlay .custom-match-nav-btn {
  --btn-top: #7b83f0;
  --btn-bot: #4a52c8;
  --btn-step: #2f3690;
  --btn-edge: #a5abff;
}

#overlay #homeSurvivalBtn {
  --btn-top: #f0574a;
  --btn-bot: #c62828;
  --btn-step: #8e1b1b;
  --btn-edge: #ff8a80;
}

#overlay #homeSixChallengeBtn {
  --btn-top: #ee5aa8;
  --btn-bot: #c2185b;
  --btn-step: #8a1043;
  --btn-edge: #ff92c8;
}

/* Gold is light enough that white text on it fails contrast — this one takes
   dark ink and a light shadow instead. */
#overlay .leaderboard-icon-btn {
  --btn-top: #ffd24a;
  --btn-bot: #eba31a;
  --btn-step: #a8720c;
  --btn-edge: #ffe9a3;
  --btn-ink: #4a3306;
  --btn-shadow: 0 1px 1px rgba(255, 255, 255, 0.45);
}

#overlay .leaderboard-icon-btn>svg {
  filter: drop-shadow(0 1px 1px rgba(255, 255, 255, 0.4));
}

/* ---- Title + tagline ---- */
#overlay h2 {
  font-weight: 900;
  letter-spacing: 2px;
  text-shadow:
    0 0 1.8rem rgba(125, 211, 252, 0.45),
    0 0.25rem 0 rgba(0, 0, 0, 0.55);
}

/* Diamonds either side of the tagline, matching the sparkles on the pathway
   header so the two screens read as one game. Decorative, so pseudo elements
   rather than markup a screen reader would announce. */
#overlay p {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  color: #e2e8f0;
  font-weight: 700;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
}

#overlay p::before,
#overlay p::after {
  content: "\25C6";
  color: #ffd166;
  font-size: 0.6em;
  text-shadow: 0 0 0.5rem rgba(255, 209, 102, 0.9);
}

/* ===========================================================================
   MATCH TARGET CARD (#matchIntroScreen)
   Was ~60 lines of inline style on the markup, which meant no hover/press
   states, no breakpoints, and five rows whose alignment depended on each
   value's own text length. Now a 3-column grid — icon / label / value — so
   every row lines up regardless of what the numbers are.
   =========================================================================== */

/* Match number ribbon: a banner rather than a caption, with folded tails
   either side built from borders (no images). */
#introMatchNum {
  position: relative;
  margin-bottom: 0.6rem;
  padding: 0.3rem 1.6rem;
  border-radius: 0.4rem;
  background: linear-gradient(180deg, #3b6fd4 0%, #24499b 100%);
  border: 2px solid #7ea8f5;
  color: #fff;
  font-size: 0.85rem;
  font-weight: 900;
  letter-spacing: 3px;
  text-transform: uppercase;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
  box-shadow: 0 0.25rem 0.6rem rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

/* Ribbon tails. Pure borders so they inherit no image cost and scale with
   the type. */
#introMatchNum::before,
#introMatchNum::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  transform: translateY(-50%);
  border: 0.55rem solid transparent;
}

#introMatchNum::before {
  left: -0.95rem;
  border-right-color: #24499b;
}

#introMatchNum::after {
  right: -0.95rem;
  border-left-color: #24499b;
}

#matchIntroScreen #introHeading {
  margin: 0 0 1.25rem;
  color: #fff;
  font-size: clamp(1.9rem, 8vw, 3rem);
  font-weight: 900;
  letter-spacing: 2px;
  text-align: center;
  text-shadow:
    0 0 1.6rem rgba(125, 211, 252, 0.45),
    0 0.28rem 0 rgba(8, 20, 44, 0.9),
    0 0.6rem 1.4rem rgba(0, 0, 0, 0.65);
}

.mi-card {
  width: 88%;
  max-width: 26.25rem;
  margin-bottom: 1.5rem;
  padding: 0.35rem 1.15rem;
  border-radius: 1.1rem;
  border: 2px solid #2f5fa8;
  background: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  box-shadow:
    0 0 1.5rem -0.35rem rgba(59, 130, 246, 0.85),
    0 0.7rem 1.5rem rgba(0, 0, 0, 0.6),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* icon | label | value. The 1fr on the label column is what pins every value
   to the same right edge no matter how long the number is. */
.mi-row {
  display: grid;
  grid-template-columns: 2rem 1fr auto;
  align-items: center;
  gap: 0.85rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid rgba(125, 211, 252, 0.14);
}

.mi-row:last-child {
  border-bottom: none;
}

.mi-icon {
  font-size: 1.35rem;
  line-height: 1;
  text-align: center;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.6));
}

.mi-label {
  color: #dbe6f5;
  font-size: clamp(0.78rem, 3vw, 0.92rem);
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.mi-value {
  justify-self: end;
  color: #fff;
  font-size: clamp(1.1rem, 4.6vw, 1.5rem);
  font-weight: 900;
  letter-spacing: 0.5px;
  text-align: right;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
}

/* The unit half of a value ("RUNS", "(12 Balls)") is set apart in blue so the
   NUMBER is what the eye lands on — showMatchIntro() wraps it in this span. */
.mi-value .mi-unit {
  margin-left: 0.3rem;
  color: #4d9bf5;
  font-size: 0.72em;
  font-weight: 800;
}

.mi-value-gold {
  color: #ffc93a;
  text-shadow: 0 0 0.7rem rgba(255, 201, 58, 0.5), 0 2px 4px rgba(0, 0, 0, 0.7);
}

.mi-value-green {
  color: #6ee06e;
  text-shadow: 0 0 0.7rem rgba(110, 224, 110, 0.45), 0 2px 4px rgba(0, 0, 0, 0.7);
}

.mi-value-blue {
  color: #4d9bf5;
  text-shadow: 0 0 0.7rem rgba(77, 155, 245, 0.45), 0 2px 4px rgba(0, 0, 0, 0.7);
}

.mi-actions {
  display: flex;
  align-items: stretch;
  gap: 0.75rem;
  width: 88%;
  max-width: 26.25rem;
}

/* CONTINUE carries the row: it takes the remaining width so HEROES stays a
   compact secondary action, matching their relative weight in the mockup. */
#matchIntroScreen #introContinueBtn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 0.95rem 1.5rem;
  border-radius: 0.9rem;
  border: 2px solid #b6f07d;
  background: linear-gradient(180deg, #9ae84f 0%, #5cb531 100%);
  color: #17300a;
  font-size: clamp(1.05rem, 4vw, 1.4rem);
  font-weight: 900;
  letter-spacing: 2px;
  text-transform: uppercase;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.35);
  box-shadow:
    0 0.3rem 0 #3c7d1e,
    0 0.55rem 1.1rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.4);
}

#matchIntroScreen #introContinueBtn:active {
  transform: translateY(0.3rem);
  box-shadow: 0 0 0 #3c7d1e, 0 0.2rem 0.5rem rgba(0, 0, 0, 0.45);
}

#matchIntroScreen #introContinueBtn>svg {
  flex: none;
}

/* HEROES: same geometry, blue, sized to its label so CONTINUE keeps the room. */
#matchIntroScreen #introShopBtn {
  flex: none;
  padding: 0.95rem 1.1rem;
  border-radius: 0.9rem;
  border: 2px solid #7ec8ff;
  background: linear-gradient(180deg, #4da3f5 0%, #2668c4 100%);
  color: #fff;
  font-size: clamp(0.95rem, 3.4vw, 1.15rem);
  font-weight: 900;
  letter-spacing: 1.5px;
  text-shadow: 0 2px 3px rgba(0, 0, 0, 0.5);
  box-shadow:
    0 0.3rem 0 #17457f,
    0 0.55rem 1.1rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

#matchIntroScreen #introShopBtn:active {
  transform: translateY(0.3rem);
  box-shadow: 0 0 0 #17457f, 0 0.2rem 0.5rem rgba(0, 0, 0, 0.45);
}

/* The lit "new hero affordable" state still wins over the blue base, but only
   its colours — geometry above is shared. */
#matchIntroScreen #introShopBtn.has-new {
  border-color: #fff3b0;
  background: linear-gradient(180deg, #ffe27a 0%, #f5b425 100%);
  color: #3a2c05;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
  box-shadow:
    0 0.3rem 0 #a8720c,
    0 0.55rem 1.1rem rgba(0, 0, 0, 0.45),
    0 0 1rem rgba(245, 180, 37, 0.8),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

@media (max-width: 26rem) {
  .mi-row {
    grid-template-columns: 1.6rem 1fr auto;
    gap: 0.6rem;
    padding: 0.65rem 0;
  }

  .mi-icon {
    font-size: 1.1rem;
  }

  .mi-card {
    padding: 0.3rem 0.9rem;
  }

  #matchIntroScreen #introShopBtn .cta-label {
    display: none;
  }

  /* icon only */
}

/* ===========================================================================
   SHARED PANEL + BUTTON SYSTEM
   The journey map, home menu and match-target card were restyled individually;
   this generalises that language to every remaining screen so the game reads
   as one product instead of a dozen separately-skinned panels.

   Everything below is expressed through the tokens in this first block, so a
   future palette change is edited once here rather than in fifty rules. It is
   appended last on purpose: same specificity as the originals, later wins.
   =========================================================================== */
:root {
  /* Panels */
  --ui-panel-bg: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  --ui-panel-edge: #2f5fa8;
  --ui-panel-glow: rgba(59, 130, 246, 0.85);
  --ui-panel-radius: 1.1rem;

  /* Inset rows inside a panel */
  --ui-row-bg: rgba(125, 211, 252, 0.07);
  --ui-row-edge: rgba(125, 211, 252, 0.16);

  /* Screen backdrop: the deep-space ground shared with the pathway map. */
  --ui-screen-bg:
    radial-gradient(1.5px 1.5px at 20% 12%, rgba(255, 255, 255, 0.5) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 72% 34%, rgba(255, 255, 255, 0.36) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 45% 68%, rgba(255, 255, 255, 0.4) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 88% 82%, rgba(255, 255, 255, 0.3) 50%, transparent 51%),
    radial-gradient(120% 80% at 50% 0%, rgba(20, 42, 82, 0.97) 0%, rgba(11, 23, 48, 0.97) 45%, rgba(7, 13, 28, 0.98) 100%);
  --ui-screen-bg-size: 320px 280px, 260px 300px, 300px 240px, 280px 320px, 100% 100%;
}

/* ---- Screen backdrops ------------------------------------------------- */
/* Every full-screen surface gets the same ground. #levelMapScreen already has
   it from its own block and #overlay keeps the 3D scene visible behind it. */
#nameEntryScreen,
#modeSelectScreen,
#teamSelectScreen,
#matchSetupScreen,
#groundsShopScreen,
#leaderboardScreen,
#tossScreen,
#matchIntroScreen,
#exitOverlay,
.summary-overlay {
  background: var(--ui-screen-bg);
  background-size: var(--ui-screen-bg-size);
}

/* ---- Screen headings --------------------------------------------------- */
#nameEntryScreen h2,
#teamSelectScreen h2,
#matchSetupScreen h2,
#groundsShopScreen h2,
#leaderboardScreen h2,
#tossScreen h2,
#exitOverlay h2,
#modeSelectTitle {
  color: #fff;
  font-weight: 900;
  letter-spacing: 2px;
  text-shadow:
    0 0 1.5rem rgba(125, 211, 252, 0.45),
    0 0.25rem 0 rgba(8, 20, 44, 0.9),
    0 0.5rem 1.2rem rgba(0, 0, 0, 0.6);
}

/* ---- Panels ------------------------------------------------------------ */
.summary-card {
  background: var(--ui-panel-bg);
  border: 2px solid var(--ui-panel-edge);
  border-radius: var(--ui-panel-radius);
  box-shadow:
    0 0 1.5rem -0.35rem var(--ui-panel-glow),
    0 0.7rem 1.6rem rgba(0, 0, 0, 0.65),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* ---- Inset rows (match stats, setup rows) ------------------------------ */
.stat-row,
.setup-group {
  background: var(--ui-row-bg);
  border: 1px solid var(--ui-row-edge);
  border-radius: 0.55rem;
}

.stat-row span,
.setup-group .setup-label {
  color: #dbe6f5;
  font-weight: 800;
  letter-spacing: 1px;
}

.stat-row strong {
  color: #fff;
  font-weight: 900;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
}

/* ---- Shared button recipe --------------------------------------------- */
/* One geometry for every non-icon button in the game. Colour still comes from
   each button's own rule (or --btn-* where it sets them), so this only
   unifies shape, weight and the press. */
.opt-row button,
.mode-card,
button.play,
.shop-tab {
  border-radius: 0.8rem;
  font-weight: 900;
  letter-spacing: 1.2px;
  transition: transform .08s, box-shadow .08s, filter .15s ease;
}

.opt-row button:hover,
.mode-card:hover,
button.play:hover,
.shop-tab:hover {
  filter: brightness(1.08);
}

/* Mode cards keep their per-mode gradient but gain the light rim every other
   button in the game now has. */
.mode-card {
  border: 2px solid rgba(255, 255, 255, 0.32);
  text-shadow: 0 2px 3px rgba(0, 0, 0, 0.5);
}

.mode-card .mode-name {
  font-weight: 900;
  letter-spacing: 1.2px;
}

.mode-card .mode-desc {
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* Neutral option buttons (overs/difficulty pickers, etc.) */
.opt-row button {
  color: #dbe6f5;
  background: linear-gradient(180deg, #22304e 0%, #131f38 100%);
  border: 2px solid #2f5fa8;
  box-shadow: 0 0.25rem 0 #0b1830, 0 0.45rem 0.9rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

/* Selected option: gold, matching the "current level" node on the pathway so
   "this is the live one" means the same thing everywhere. */
.opt-row button.on {
  color: #3a2c05;
  background: linear-gradient(180deg, #ffe27a 0%, #f5b425 100%);
  border-color: #fff3b0;
  box-shadow: 0 0.25rem 0 #a8720c, 0 0.45rem 0.9rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

/* Generic PLAY/primary green, for the copies outside #overlay (toss screen,
   setup screens, summary). #overlay's own and the match-intro one are already
   styled in their blocks. */
button.play {
  border: 2px solid #b6f07d;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.3);
}

/* ---- Back button ------------------------------------------------------- */
/* Was flat navy; now matches the panel edge so it belongs to the same set. */
.back-btn {
  background: linear-gradient(180deg, #22304e 0%, #0f1c36 100%);
  border: 2px solid var(--ui-panel-edge);
  border-bottom-width: 2px;
  box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.back-btn:hover {
  filter: brightness(1.25);
}

/* ---- Shop tabs --------------------------------------------------------- */
.shop-tab {
  color: #dbe6f5;
  background: linear-gradient(180deg, #22304e 0%, #131f38 100%);
  border: 2px solid #2f5fa8;
}

.shop-tab.on {
  color: #06283d;
  background: linear-gradient(180deg, #7dd3fc 0%, #38bdf8 100%);
  border-color: #bae6fd;
}

/* ---- Wallet pills ------------------------------------------------------ */
.wallet-pill {
  border: 1px solid var(--ui-row-edge);
  background: rgba(11, 24, 48, 0.85);
  font-weight: 800;
}

/* ---- Text inputs ------------------------------------------------------- */
.name-entry-input {
  color: #fff;
  background: rgba(8, 16, 34, 0.9);
  border: 2px solid var(--ui-panel-edge);
  border-radius: 0.7rem;
}

.name-entry-input:focus {
  outline: none;
  border-color: #7dd3fc;
  box-shadow: 0 0 0.9rem rgba(125, 211, 252, 0.5);
}

/* ===========================================================================
   CHARACTER / GROUND SHOP CAROUSEL
   Restyled to the same language as the rest of the UI. The three states were
   the weak point: SELECT and UNLOCK were flat fills with no depth, and
   SELECTED was dark grey with green text, which read as DISABLED — the exact
   opposite of "this is the hero you're wearing".

   They now differ in kind, not just hue:
     UNLOCK   - gold, raised, the loudest thing on screen (costs money)
     SELECT   - green, raised, actionable
     SELECTED - cyan, RECESSED and inert (a state, not a button)
   =========================================================================== */

/* ---- Carousel arrows ----
   Same skin as .back-btn so every "navigate" control in the game feels alike:
   navy face, panel-edge border, embossed shadow, and a plain brightness lift
   on hover. The previous version grew a cyan glow and scaled up, which made
   them the loudest thing on a screen whose subject is the 3D character.

   The chevron is an SVG (see WD_CHEVRON_* in js/state.js and js/voxel-skin.js);
   the "‹"/"›" glyphs it replaced sat high and left of the disc centre because
   of their own font metrics, which no amount of container centring fixes. */
.wd-arrow {
  width: 3.4rem;
  height: 3.4rem;
  padding: 0;
  color: #eaf4ff;
  background: linear-gradient(180deg, #22304e 0%, #0f1c36 100%);
  border: 2px solid var(--ui-panel-edge, #2f5fa8);
  box-shadow:
    0 0.25rem 0.5rem rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(4px);
  transition: filter 0.15s ease, transform 0.08s ease;
}

/* Matches .back-btn's hover exactly: brighten, no glow, no scale. */
.wd-arrow:hover {
  filter: brightness(1.25);
  background: linear-gradient(180deg, #22304e 0%, #0f1c36 100%);
  border-color: var(--ui-panel-edge, #2f5fa8);
  box-shadow:
    0 0.25rem 0.5rem rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
  transform: none;
}

.wd-arrow:active {
  transform: translateY(1px);
  box-shadow: 0 0.1rem 0.3rem rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

/* Sized as a share of the disc, so the two stay in proportion at every
   breakpoint without a second media query. */
.wd-arrow>svg {
  width: 50%;
  height: 50%;
  display: block;
}

/* ---- Name + position counter ---- */
.wd-char-name {
  font-weight: 900;
  text-shadow:
    0 0 1.8rem rgba(125, 211, 252, 0.5),
    0 0.28rem 0 rgba(8, 20, 44, 0.85),
    0 0.55rem 1.3rem rgba(0, 0, 0, 0.85);
}

/* Card rather than a faint pill, so "2 / 24" reads as information about the
   collection instead of a caption. */
.wd-count {
  padding: 0.3rem 1.1rem;
  border-radius: 0.65rem;
  background: rgba(9, 18, 38, 0.9);
  border: 1px solid rgba(125, 211, 252, 0.35);
  color: #4d9bf5;
  font-size: 0.95rem;
  font-weight: 900;
  box-shadow: 0 0.3rem 0.7rem rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.wd-count b {
  color: #fff;
  font-size: 1.1em;
}

/* ---- Price ---- */
.wd-req {
  gap: 0.5rem;
  padding: 0.55rem 1.4rem;
  border-radius: 0.8rem;
  background: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  border: 2px solid #2f5fa8;
  box-shadow: 0 0.35rem 0.8rem rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

.wd-req-icon {
  font-size: 1.4rem;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.6));
}

.wd-req-val {
  font-size: 1.5rem;
  font-weight: 900;
  color: #fff;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7);
}

/* Can't afford it: the number goes red, the card keeps its shape. */
.wd-req.unmet {
  border-color: #b3423a;
  box-shadow: 0 0.35rem 0.8rem rgba(0, 0, 0, 0.6), 0 0 0.9rem rgba(239, 68, 68, 0.35);
}

.wd-req.unmet .wd-req-val {
  color: #ff8a80;
}

/* Owned items have no price to show. visibility (not display) is kept because
   the Grounds picker still lays this row out in flow, where removing it would
   collapse .wd-foot and — since .wd-center is flex:1 — shift the vertically
   centred arrows the instant something was bought. On the Character tab the
   row is an absolute overlay, so either would do. */
.wd-reqs-hidden {
  visibility: hidden;
}

/* ---- Action button: one geometry, three states ---- */
.wd-action {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  max-width: 22rem;
  padding: 0.95rem 1.5rem;
  border-radius: 0.9rem;
  /* <button> does NOT inherit font-family, so without this these buttons render
     in the browser's default (Arial) while the rest of the UI is Segoe UI —
     visibly a different typeface, and 2px shorter, than the UPGRADE button
     stacked directly beneath them. */
  font-family: inherit;
  font-size: clamp(1.05rem, 4vw, 1.35rem);
  font-weight: 900;
  letter-spacing: 2px;
  text-transform: uppercase;
  transition: transform .08s, box-shadow .08s, filter .15s ease;
}

.wd-action-icon {
  font-size: 1.15em;
  line-height: 1;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
}

.wd-action:not(:disabled):hover {
  filter: brightness(1.08);
}

/* Raised states press down by exactly their edge depth. */
.wd-action.select:active,
.wd-action.unlock:active {
  transform: translateY(0.3rem);
  box-shadow: 0 0 0 var(--wd-step), 0 0.2rem 0.5rem rgba(0, 0, 0, 0.5);
}

.wd-action.unlock {
  --wd-step: #a8600c;
  background: linear-gradient(180deg, #ffc14a 0%, #ef8f16 100%);
  border: 2px solid #ffdf9e;
  color: #4a2d06;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.4);
  box-shadow:
    0 0.35rem 0 var(--wd-step),
    0 0.6rem 1.2rem rgba(0, 0, 0, 0.5),
    0 0 1.2rem rgba(239, 143, 22, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

/* Unaffordable: still pressable (it explains the shortfall), just not shouting. */
.wd-action.unlock.short {
  filter: saturate(0.7) brightness(0.85);
  box-shadow:
    0 0.35rem 0 var(--wd-step),
    0 0.6rem 1.2rem rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

/* Blue is the pressable one. Only the PALETTES are swapped here — the geometry
   stays put, because raised-vs-recessed is what separates "an action" from "a
   state" and does not belong to either colour. */
.wd-action.select {
  --wd-step: #0d4a7a;
  background: linear-gradient(180deg, #5cc8f7 0%, #1e86d4 100%);
  border: 2px solid #a5e3fd;
  color: #062a45;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.4);
  box-shadow:
    0 0.35rem 0 var(--wd-step),
    0 0.6rem 1.2rem rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.45);
}

/* SELECTED is a STATE, so it is deliberately the one button that sits INTO the
   surface: no bottom edge, an inset shadow, and no press response. Green now
   carries the "this is the one you're wearing" read — kept as a dark well with
   a bright rim rather than a bright face, so it cannot be mistaken for the
   pressable button it sits next to. */
.wd-action.selected {
  background: linear-gradient(180deg, #24541b 0%, #12300e 100%);
  border: 2px solid #6cd44a;
  color: #c2f591;
  cursor: default;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  box-shadow:
    inset 0 0.2rem 0.5rem rgba(0, 0, 0, 0.55),
    0 0 1rem rgba(108, 212, 74, 0.4);
  transform: none;
}

/* The tick takes the colour the button just gave up. A green tick on the new
   green face would have all but disappeared. */
.wd-action.selected .wd-action-icon {
  color: #7dd3fc;
  filter: drop-shadow(0 0 0.4rem rgba(125, 211, 252, 0.9));
}

.wd-action.locked {
  background: linear-gradient(180deg, #2a3548 0%, #1a2230 100%);
  border: 2px solid #3d4a5f;
  color: #7b8ba3;
  cursor: not-allowed;
  box-shadow: inset 0 0.2rem 0.5rem rgba(0, 0, 0, 0.5);
}

@media (max-width: 26rem) {
  .wd-arrow {
    width: 2.9rem;
    height: 2.9rem;
    font-size: 1.7rem;
  }

  .wd-req-val {
    font-size: 1.25rem;
  }

  .wd-action {
    padding: 0.85rem 1.1rem;
    letter-spacing: 1.5px;
  }
}

/* ===========================================================================
   LEADERBOARD SCREEN
   Rows were a flat translucent strip with a bare "#3" and no avatar — this
   gives every row a real card, a medal badge for the top 3 (crown on #1), and
   a placeholder avatar so a wall of text reads as a roster of players.
   =========================================================================== */

/* ---- Tabs: RUNS / HI-SCORE / SIXES ----
   These live in .opt-row for the click wiring in js/leaderboard-ui.js, but
   need their own look — .opt-row's shared skin is for compact setup pickers,
   not a 3-wide primary nav. Scoped to the id so nothing else is affected. */
#leaderboardTabRow {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.6rem;
  width: 100%;
  margin: 0 0 1.1rem;
}

#leaderboardTabRow button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.75rem 0.5rem;
  border-radius: 0.85rem;
  color: #dbe6f5;
  background: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  border: 2px solid #2f5fa8;
  font-size: clamp(0.75rem, 3vw, 0.9rem);
  font-weight: 900;
  letter-spacing: 0.5px;
  box-shadow: 0 0.25rem 0 #0b1830, 0 0.4rem 0.8rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

#leaderboardTabRow button.on {
  color: #3a2c05;
  background: linear-gradient(180deg, #ffe27a 0%, #f5b425 100%);
  border-color: #fff3b0;
  box-shadow: 0 0.25rem 0 #a8720c, 0 0.4rem 0.8rem rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

/* ---- "YOUR BEST" strip ---- */
#leaderboardYourBest .lb-best-label {
  color: #dbe6f5;
  font-weight: 900;
  letter-spacing: 1.5px;
}

#leaderboardYourBestVal {
  color: #fff;
  font-size: 1.3rem;
}

/* ---- Scroll region -----------------------------------------------------
   The screen itself is a fixed frame (overflow:hidden, set inline), so the back
   button, title, tabs and YOUR BEST hold their place and only this list moves.

   touch-action must be declared here: the document root sets `manipulation`, and
   the resolved behaviour is the intersection down the ancestor chain — without
   an explicit `pan-y` an ancestor's stricter value can still swallow the gesture.
   This is the whole reason the board could be wheel-scrolled but not finger-
   scrolled. */
#leaderboardCard {
  display: flex;
  flex-direction: column;
  /* `0 1 auto`, not `1 1 auto`: the card shrinks to fit the frame when a board is
     long, but a board with three entries stays a three-entry card instead of
     stretching to fill the screen. min-height:0 is what actually permits the
     shrink — without it a flex item refuses to go below its content height, and
     the card would overrun the bottom of the screen with nowhere to scroll. */
  flex: 0 1 auto;
  min-height: 0;
}

#leaderboardBody {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;   /* no scroll chaining out to the page */
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  /* Room for the last row's shadow, and so a mid-scroll row isn't flush to the
     card's edge. */
  padding-right: 0.25rem;
}

/* Thin, in-theme scrollbar on desktop; mobile draws its own overlay bar. */
#leaderboardBody::-webkit-scrollbar {
  width: 0.4rem;
}

#leaderboardBody::-webkit-scrollbar-thumb {
  background: rgba(125, 211, 252, 0.45);
  border-radius: 9999px;
}

#leaderboardBody::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.06);
  border-radius: 9999px;
}

/* ---- One row = one player ---- */
.leaderboard-row {
  display: grid;
  grid-template-columns: 2.6rem 1fr auto;
  align-items: center;
  gap: 1.75rem;
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.6rem;
  border-radius: 0.85rem;
  background: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  border: 2px solid #23345c;
  box-shadow: 0 0.3rem 0.7rem rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

/* The player's own row: same gold the current-level node and selected tab
   already use for "this one is you/live", so the language stays consistent
   across every screen rather than leaderboard rows inventing their own.
   A thicker (3px, up from 2px) border plus the explicit YOU tag on the name
   — see .lb-you-tag — rather than relying on the border colour alone to
   read as "this one's different" against 5+ other navy cards. */
.leaderboard-row.is-player {
  border-width: 3px;
  border-color: #ffc93a;
  background: linear-gradient(180deg, #2a2412 0%, #1a1608 100%);
  box-shadow: 0 0.3rem 0.7rem rgba(0, 0, 0, 0.45), 0 0 1.1rem rgba(255, 201, 58, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.12);
}

.leaderboard-row.is-player .lb-name,
.leaderboard-row.is-player /* The name itself keeps the single-line truncation the row has always had. */
.lb-name-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Deliberately quiet: it is a disambiguator, not information anyone reads for
   its own sake. Monospace so tags line up down the column and two similar ones
   are easy to compare. */
.lb-id {
  font-family: ui-monospace, 'Cascadia Mono', 'Consolas', monospace;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 1px;
  color: rgba(148, 163, 184, 0.85);
  text-shadow: none;
}

.leaderboard-row.is-player .lb-id {
  color: rgba(255, 201, 58, 0.9);
}

.lb-score {
  color: #ffd166;
}

/* Unmissable "this is you" marker, independent of the row's border/background
   treatment — a small solid pill next to the name rather than a colour
   difference alone. */
.lb-you-tag {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.4rem;
  margin-left: 0.3rem;
  border-radius: 0.35rem;
  background: #ffc93a;
  color: #3a2c05;
  font-size: 0.62rem;
  font-weight: 900;
  letter-spacing: 0.5px;
  vertical-align: 0.1rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* ---- Rank badge ---- */
.lb-rank {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  font-size: 1.15rem;
  font-weight: 900;
  color: #dbe6f5;
  background: linear-gradient(180deg, #2a3a5c 0%, #182644 100%);
  border: 2px solid #3d5488;
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

.lb-rank-gold {
  color: #4a2d06;
  background: linear-gradient(180deg, #ffe27a 0%, #f5b425 100%);
  border-color: #fff3b0;
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5), 0 0 0.8rem rgba(245, 180, 37, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

.lb-rank-silver {
  color: #2c3444;
  background: linear-gradient(180deg, #e6ecf5 0%, #a9b6c9 100%);
  border-color: #fff;
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.lb-rank-bronze {
  color: #3a1f08;
  background: linear-gradient(180deg, #e0a458 0%, #b5762c 100%);
  border-color: #f5c98a;
  box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

.lb-crown {
  position: absolute;
  top: -0.85rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 1rem;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.6));
}

/* Two lines now: the name, and a short player-id tag under it. Column rather
   than a single line so a long name still truncates on its own line instead of
   pushing the tag out of the row. */
.lb-name {
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.05rem;
  color: #fff;
  font-weight: 900;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  text-shadow: 0 2px 3px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  /* .summary-card (this row's container) sets text-align:center for the whole
     card — without this override every name inherited that and rendered
     centred in its grid cell instead of flush left. */
  text-align: left;
}

.lb-score {
  color: #ffd166;
  font-size: 1.35rem;
  font-weight: 900;
  text-shadow: 0 0 0.6rem rgba(255, 209, 102, 0.4), 0 2px 3px rgba(0, 0, 0, 0.6);
}

@media (max-width: 26rem) {
  .leaderboard-row {
    grid-template-columns: 2.1rem 1fr auto;
    gap: 0.5rem;
    padding: 0.5rem 0.7rem;
  }

  .lb-rank {
    width: 2.1rem;
    height: 2.1rem;
    font-size: 0.95rem;
  }

  .lb-score {
    font-size: 1.1rem;
  }
}
/* ===========================================================================
   BUILD VERSION STAMP
   Bottom-left, deliberately low-contrast: it's for identifying a build in a
   bug report, not something a player should ever read mid-match. Sits above
   the canvas but below every panel (z-index 90 vs the screens' 100+) and is
   click-through so it can never eat a tap near the corner.
   =========================================================================== */
#gameVersion {
  position: fixed;
  left: 0.55rem;
  bottom: 0.45rem;
  z-index: 90;
  pointer-events: none;
  user-select: none;
  color: rgba(219, 230, 245, 0.45);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}

/* ===========================================================================
   BUILD EXPORTER PANEL (js/export-tool.js)
   Dev-only surface: the script that creates this markup refuses to run unless
   the page is on localhost AND ?export is in the URL, so these rules never
   match anything on a shipped build.
   =========================================================================== */
#exportTool {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 18, 0.82);
  backdrop-filter: blur(3px);
  pointer-events: auto;
  font-family: inherit;
}

#exportTool .exp-panel {
  width: min(34rem, 92vw);
  padding: 1.2rem 1.3rem;
  border-radius: 1rem;
  background: linear-gradient(180deg, #16264a 0%, #0b1830 100%);
  border: 2px solid #2f5fa8;
  box-shadow: 0 0 1.5rem -0.3rem rgba(59, 130, 246, 0.8), 0 0.8rem 1.6rem rgba(0, 0, 0, 0.65);
}

#exportTool .exp-title {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: #fff;
  font-size: 1.05rem;
  font-weight: 900;
  letter-spacing: 1.5px;
}

#exportTool .exp-badge {
  padding: 0.12rem 0.45rem;
  border-radius: 0.35rem;
  background: #ffc93a;
  color: #3a2c05;
  font-size: 0.58rem;
  font-weight: 900;
  letter-spacing: 0.5px;
}

#exportTool .exp-desc {
  margin: 0.5rem 0 0.9rem;
  color: #cbd5e1;
  font-size: 0.78rem;
  line-height: 1.45;
}

#exportTool .exp-desc b { color: #7dd3fc; }

#exportTool .exp-select {
  width: 100%;
  margin: 0.35rem 0 0.2rem;
  padding: 0.6rem 0.7rem;
  border-radius: 0.7rem;
  border: 2px solid #2f5fa8;
  background: linear-gradient(180deg, #22304e 0%, #131f38 100%);
  color: #dbe6f5;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  cursor: pointer;
}

#exportTool .exp-select option { background: #131f38; color: #dbe6f5; }

#exportTool #expTargetNote {
  margin: 0.15rem 0 0.9rem;
  color: #93b4dd;
  font-size: 0.72rem;
}

#exportTool .exp-actions { display: flex; gap: 0.6rem; }

#exportTool .exp-btn {
  flex: 1;
  padding: 0.7rem 1rem;
  border-radius: 0.7rem;
  border: 2px solid #2f5fa8;
  background: linear-gradient(180deg, #22304e 0%, #131f38 100%);
  color: #dbe6f5;
  font: inherit;
  font-weight: 900;
  letter-spacing: 1px;
  cursor: pointer;
}

#exportTool .exp-btn-go {
  border-color: #b6f07d;
  background: linear-gradient(180deg, #9ae84f 0%, #5cb531 100%);
  color: #17300a;
}

#exportTool .exp-btn:disabled { opacity: 0.55; cursor: default; }

#exportTool .exp-log {
  margin-top: 0.9rem;
  max-height: 14rem;
  overflow-y: auto;
  padding: 0.6rem 0.7rem;
  border-radius: 0.55rem;
  background: rgba(4, 8, 18, 0.7);
  border: 1px solid rgba(125, 211, 252, 0.18);
  color: #94a3b8;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.68rem;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
}

#exportTool .exp-ok   { color: #6ee06e; font-weight: 700; }
#exportTool .exp-warn { color: #ffc93a; }
#exportTool .exp-err  { color: #ff8a80; font-weight: 700; }

/* ===========================================================================
   IN-MATCH MODE BADGE (#matchModeBadge)
   Survival and Six Challenge play by different rules (one wicket, unlimited
   balls) but their HUD is otherwise identical to a normal match, so there was
   nothing on screen saying which one you were in. Shown by refreshHud() for
   those two modes only — campaign/Quick/Custom deliberately get no badge,
   since a label on the default mode is just noise.

   A normal flex item in #scoreboard rather than an absolutely-positioned tab
   above it: the scoreboard becomes a full-width bar hard against the top edge
   on phones, where anything overhanging its top border would be clipped.
   =========================================================================== */
#matchModeBadge {
  align-self: center;
  flex: none;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  font-size: 0.6rem;
  font-weight: 900;
  letter-spacing: 1.5px;
  line-height: 1;
  white-space: nowrap;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  box-shadow: 0 0.15rem 0.35rem rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

/* Colours match each mode's own button on the home menu, so the badge reads
   as "this is the red SURVIVAL mode you tapped" rather than a new signal. */
#matchModeBadge.mode-survival {
  background: linear-gradient(180deg, #f0574a 0%, #c62828 100%);
  border: 1px solid #ff8a80;
}

#matchModeBadge.mode-six {
  background: linear-gradient(180deg, #ee5aa8 0%, #c2185b 100%);
  border: 1px solid #ff92c8;
}

@media (max-width: 40rem) {
  #matchModeBadge {
    font-size: 0.52rem;
    padding: 0.2rem 0.5rem;
    letter-spacing: 1px;
  }
}

/* Level Clear card on a short screen (landscape phones). Six single-column rows
   is taller than a 390px-high viewport, so the stats fold into two columns —
   still the same icon | label | value row, just paired up. The row borders read
   as a table rather than a list at this size, which is fine. */
@media (max-height: 560px) {
  #finalSummaryCard .summary-stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 0.9rem;
  }

  #finalSummaryCard .sc-row {
    padding: 0.32rem 0.1rem;
    gap: 0.45rem;
    grid-template-columns: 1.2rem 1fr auto;
  }

  /* The last row in each column has no row under it to divide from. */
  #finalSummaryCard .summary-stats .sc-row:nth-last-child(-n+2) {
    border-bottom: none;
  }

  #finalSummaryCard .sc-icon {
    font-size: 0.9rem;
  }

  #finalSummaryCard h2#summaryTitle {
    margin-bottom: 0.3rem;
  }

  .sc-row-bonus {
    margin-top: 0.25rem;
    padding: 0.35rem 0.5rem;
  }

  .sc-pill {
    flex-direction: row;
    gap: 0.35rem;
    padding: 0.35rem 0.5rem;
  }
}

/* ===========================================================================
   CHARACTER STATS & UPGRADE (shop character tab)

   Tier pill + level pips + LV counter on one header row, then one row per stat:
   a coloured icon chip, the label, and a bar. The faint ghost behind each fill
   is that character's own maximum, so the gap between fill and ghost reads as
   "this is what upgrading buys you".
   =========================================================================== */
.wd-stats {
  width: 100%;
  max-width: 23rem;
  margin: 0 auto 0.6rem;
  padding: 0.7rem 0.85rem;
  border-radius: 0.9rem;
  background: linear-gradient(180deg, #16264a 0%, #0d1a33 100%);
  border: 2px solid #2f6ad0;
  box-shadow:
    0 0 1.1rem -0.3rem rgba(59, 130, 246, 0.8),
    0 0.5rem 1rem rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
  pointer-events: none;
}

.wd-stat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}

/* Solid pill, white text — a label, not an outline. */
.wd-tier {
  padding: 0.22rem 0.7rem;
  border-radius: 0.45rem;
  background: linear-gradient(180deg, #2f7ae5 0%, #1f5cc0 100%);
  border: 1px solid rgba(255, 255, 255, 0.35);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 900;
  letter-spacing: 1.2px;
  white-space: nowrap;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
}

/* Rarity still reads through the pill's fill rather than a coloured outline. */
.wd-tier-common { background: linear-gradient(180deg, #38bdf8 0%, #0284c7 100%); }
.wd-tier-rare { background: linear-gradient(180deg, #4ade80 0%, #16a34a 100%); }
.wd-tier-epic { background: linear-gradient(180deg, #c084fc 0%, #7e22ce 100%); }
.wd-tier-legendary { background: linear-gradient(180deg, #fbbf24 0%, #d97706 100%); }
.wd-tier-mythic { background: linear-gradient(180deg, #fb7185 0%, #be123c 100%); }

.wd-pips {
  display: flex;
  gap: 0.3rem;
}

.wd-pip {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: #46536b;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
}

.wd-pip.on {
  background: linear-gradient(180deg, #ffd75e 0%, #f0a91c 100%);
  box-shadow: 0 0 0.4rem rgba(255, 201, 58, 0.9);
}

.wd-lvl {
  color: #fff;
  font-size: 0.68rem;
  font-weight: 900;
  letter-spacing: 0.6px;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}

/* icon chip | label | bar */
.wd-stat {
  display: grid;
  grid-template-columns: 1.55rem 4.2rem 1fr;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.42rem;
}

.wd-stat-icon {
  width: 1.55rem;
  height: 1.55rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.4rem;
  font-size: 0.8rem;
  line-height: 1;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.wd-stat-icon.pw { background: linear-gradient(180deg, #fb923c 0%, #e0431f 100%); }
.wd-stat-icon.tm { background: linear-gradient(180deg, #4cc3f5 0%, #1274c4 100%); }

.wd-stat-label {
  color: #fff;
  font-size: 0.66rem;
  font-weight: 900;
  letter-spacing: 1px;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7);
}

.wd-stat-track {
  position: relative;
  height: 0.62rem;
  border-radius: 999px;
  background: #5b6577;
  overflow: hidden;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.55);
}

/* This character's ceiling at level 5 — the headroom upgrading buys. */
.wd-stat-ghost {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.16);
}

.wd-stat-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  border-radius: 999px;
  transition: width 0.25s ease;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.wd-stat-fill.pw { background: linear-gradient(90deg, #ff9d3c 0%, #ef3b26 100%); }
.wd-stat-fill.tm { background: linear-gradient(90deg, #6fd0f7 0%, #1f7fd4 100%); }

/* ---- UPGRADE ----
   Deliberately the SAME BUTTON as SELECT/UNLOCK above it — identical width,
   padding, radius, type size, border weight and 3D bottom edge — differing only
   in colour. They sit directly on top of one another, so any difference in
   geometry reads as a mistake rather than a distinction.

   Every metric below is copied from .wd-action (search "--wd-step"); keep them
   in step if that block is ever retuned. */
.wd-upgrade {
  --wd-step: #0a2a5e;
  width: 100%;
  max-width: 22rem;
  margin: 0.55rem auto 0;
  padding: 0.95rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  border-radius: 0.9rem;
  border: 2px solid #7db4ff;
  background: linear-gradient(180deg, #2f6ad0 0%, #163a7a 100%);
  color: #fff;
  font-family: inherit;
  font-size: clamp(1.05rem, 4vw, 1.35rem);
  font-weight: 900;
  letter-spacing: 2px;
  text-transform: uppercase;
  cursor: pointer;
  pointer-events: auto;
  text-shadow: 0 2px 3px rgba(0, 0, 0, 0.5);
  /* `normal`, matching .wd-action's default strut, is what makes this button the
     same HEIGHT as SELECT above it. Pinning a number here instead made it 2-4px
     shorter, and made the height wobble between the UPGRADE and MAX LEVEL states
     as their inline content changed. The strut now sets the line box in every
     state, so nothing the label contains can move it. */
  line-height: normal;
  transition: transform .08s, box-shadow .08s, filter .15s ease;
  box-shadow:
    0 0.35rem 0 var(--wd-step),
    0 0.6rem 1.2rem rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

.wd-upgrade:not(:disabled):hover {
  filter: brightness(1.08);
}

/* Presses down by exactly its edge depth, like the raised .wd-action states. */
.wd-upgrade:not(:disabled):active {
  transform: translateY(0.3rem);
  box-shadow: 0 0 0 var(--wd-step), 0 0.2rem 0.5rem rgba(0, 0, 0, 0.5);
}

.wd-up-icon {
  color: #6cf08a;
  font-size: 1.15em;
  line-height: 1;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
}

.wd-up-cost {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.32);
  font-size: 0.85em;
  /* Kept under the button's strut so the pill can never grow the line box. */
  line-height: 1;
  color: #ffd75e;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* Unaffordable: still pressable, so the tap can explain the price. */
.wd-upgrade.short {
  --wd-step: #2b3444;
  border-color: #7b8ba3;
  background: linear-gradient(180deg, #4a566d 0%, #2a3446 100%);
}

.wd-upgrade.short .wd-up-icon {
  color: #b6c2d4;
  filter: none;
}

/* MAX LEVEL is a STATE, not an action — so it drops its bottom edge and sits
   INTO the surface, exactly as .wd-action.selected does. That is what makes
   "nothing left to press here" read without a disabled grey. */
.wd-upgrade.maxed {
  border-color: #ffc93a;
  background: linear-gradient(180deg, #4a3a12 0%, #241b06 100%);
  color: #ffd75e;
  cursor: default;
  transform: none;
  box-shadow:
    inset 0 0.2rem 0.5rem rgba(0, 0, 0, 0.55),
    0 0 1rem rgba(255, 201, 58, 0.35);
}

.wd-upgrade.maxed .wd-up-icon {
  color: #ffd75e;
  filter: drop-shadow(0 0 0.4rem rgba(255, 201, 58, 0.9));
}

/* ===========================================================================
   CHARACTER DOCK — stats + actions as ONE bottom-aligned card

   Previously three stacked blocks (stats card, price row, SELECT, then UPGRADE
   below it) which together ate most of the screen and hid the character the
   player is there to look at. They are now a single card pinned to the bottom
   edge: the stats panel loses its own frame and becomes the card's upper half,
   and SELECT / UPGRADE sit SIDE BY SIDE on one row instead of stacking. That
   removes a full button's worth of height plus three sets of card padding.
   =========================================================================== */
.wd-dock {
  position: relative;
  flex-shrink: 0;
  width: 100%;
  /* Wider than the old 23rem stats card: the two buttons now share one row, and
     at 23rem "UPGRADE 🪙 2400" overflowed its half of it. */
  max-width: 30rem;
  margin: 0 auto;
  padding: 0.7rem 0.75rem 0.75rem;
  border-radius: 1.1rem 1.1rem 0 0;
  background: linear-gradient(180deg, #16264a 0%, #0d1a33 100%);
  border: 2px solid #2f6ad0;
  border-bottom: none;
  box-shadow:
    0 0 1.1rem -0.3rem rgba(59, 130, 246, 0.8),
    0 -0.4rem 1.2rem rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

/* The stats block is now the dock's upper half, so it drops every bit of its
   own card chrome — a frame inside a frame is the thing this change removes. */
.wd-dock .wd-stats {
  max-width: none;
  margin: 0 0 0.65rem;
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

/* Price tag. Pinned to the card's top edge as an overlay so showing/hiding it
   cannot change the dock's height — which is what the old visibility:hidden
   placeholder row existed to prevent, at the cost of permanent dead space. */
.wd-dock .wd-reqs {
  position: absolute;
  top: 0;
  right: 0.9rem;
  transform: translateY(-55%);
  margin: 0;
  gap: 0.5rem;
  z-index: 2;
}

.wd-dock .wd-req {
  padding: 0.3rem 0.75rem;
  gap: 0.35rem;
  border-radius: 999px;
  background: linear-gradient(180deg, #1f3765 0%, #101f3d 100%);
  border: 2px solid #2f6ad0;
  box-shadow: 0 0.25rem 0.6rem rgba(0, 0, 0, 0.5);
}

.wd-dock .wd-req-icon { font-size: 0.9rem; }
.wd-dock .wd-req-val { font-size: 0.9rem; font-weight: 900; }

/* One row, two buttons. SELECT is the primary action so it takes the larger
   share; UPGRADE is a secondary spend and sits beside it, not beneath it. */
.wd-dock-actions {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
}

/* Both buttons keep their own colours and 3D edges from the blocks above —
   only their geometry changes, so they can share a row without either one
   looking like a shrunken version of the other. */
#shopCharacterSelect .wd-dock-actions .wd-action,
#shopCharacterSelect .wd-dock-actions .wd-upgrade,
.wd-dock-actions .wd-action,
.wd-dock-actions .wd-upgrade {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  max-width: none;
  margin: 0;
  padding: 0.8rem 0.5rem;
  font-size: clamp(0.78rem, 3.1vw, 0.95rem);
  letter-spacing: 0.5px;
  gap: 0.4rem;
  white-space: nowrap;
}

/* SELECT / SELECTED / UNLOCK is what the player came to press, so it takes the
   larger share of the row. Scoped through the id as well, because the sizing
   rule above has to outrank #shopCharacterSelect .wd-action and would otherwise
   win this property back. */
#shopCharacterSelect .wd-dock-actions .wd-action,
.wd-dock-actions .wd-action { flex-grow: 1.35; }

.wd-dock-actions .wd-up-cost {
  padding: 0.15rem 0.45rem;
  font-size: 0.9em;
}

/* Locked characters hide UPGRADE entirely (see renderUpgrade), so the action
   button simply spans the row on its own. */
