/* EMBERWALL — full-screen overlays: the menus, world/level select, settings, the run summary and
   every other screen shown in .ui-layer-screens. (The title-screen vista/logotype rail and the
   Forge/Proving Ground screens land in this file too, as their steps of the batch add them.)
   Sibling of styles.css (tokens + shared chrome) — load styles.css first, this after, per the
   four-stylesheet split recorded in index.html and docs/decisions.md. */

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

.ui-layer-screens { inset: 0; pointer-events: none; z-index: 8; }
.ui-screens { position: absolute; inset: 0; }

/* Two scrim weights (D14/§1.5): modal weight silences the board entirely for screens where the
   game has stopped; vista weight (added below) only darkens enough to read the rail, leaving the
   attract skirmish and other live boards visible through the clear side — a later step's screens
   opt into it via .is-vista, but the rule lives here now so nothing restyles twice. */
.ui-screen {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  box-sizing: border-box;
  pointer-events: auto;
  background: radial-gradient(120% 120% at 50% 0%,
    color-mix(in srgb, var(--ink) 76%, transparent),
    color-mix(in srgb, var(--ink) 88%, transparent));
  backdrop-filter: blur(3px);
}

.ui-screen.is-vista {
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--ink) 82%, transparent) 0%,
    color-mix(in srgb, var(--ink) 55%, transparent) 34%,
    color-mix(in srgb, var(--ink) 18%, transparent) 62%,
    color-mix(in srgb, var(--ink) 40%, transparent) 100%);
  backdrop-filter: none;
}

.ui-screen[hidden] { display: none; }

/* D90: one blocking, accessible first-run spotlight above both the board and ordinary screens. */
.ui-layer-tutorial {
  position: absolute;
  inset: 0;
  z-index: 18;
  pointer-events: none;
}

.ui-tutorial {
  position: fixed;
  inset: 0;
  pointer-events: auto;
  color: var(--text);
}

.ui-tutorial[hidden], .ui-tutorial.is-suspended { display: none; }

.ui-tutorial-blocker {
  position: absolute;
  inset: 0;
  background: rgba(7, 6, 10, 0.72);
  backdrop-filter: blur(2px);
}

.ui-tutorial-spot {
  position: absolute;
  left: 0;
  top: 0;
  display: none;
  padding: 0;
  min-width: 20px;
  min-height: 20px;
  border: 2px solid var(--gold);
  border-radius: 10px;
  background: rgba(255, 220, 126, 0.08);
  box-shadow: 0 0 0 9999px rgba(7, 6, 10, 0.76), 0 0 0 5px rgba(255, 220, 126, 0.18),
    0 0 32px rgba(255, 190, 80, 0.65);
  cursor: pointer;
  animation: tutorial-pulse 1.45s ease-in-out infinite;
  z-index: 1;
}

.ui-tutorial.is-targeted .ui-tutorial-blocker { background: transparent; backdrop-filter: none; }
.ui-tutorial.is-targeted .ui-tutorial-spot { display: block; }

.ui-tutorial-card {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: max(22px, calc(var(--safe-b) + 14px));
  width: min(520px, calc(100vw - 28px));
  padding: 20px 22px;
  transform: translateX(-50%);
  box-sizing: border-box;
  border: 1px solid var(--line-hot);
  border-top: 3px solid var(--gold);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--panel) 96%, black);
  box-shadow: 0 20px 70px rgba(0, 0, 0, 0.72);
}

.ui-tutorial-card.is-top {
  top: max(22px, calc(var(--safe-t) + 14px));
  bottom: auto;
}

.ui-tutorial.is-choice .ui-tutorial-card {
  top: 50%;
  bottom: auto;
  transform: translate(-50%, -50%);
}

.ui-tutorial-eyebrow {
  display: block;
  margin-bottom: 7px;
  font-family: var(--mono);
  color: var(--gold);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.ui-tutorial-title { margin-bottom: 8px !important; font-size: clamp(20px, 3vw, 28px) !important; }
.ui-tutorial-copy { margin: 0 !important; color: var(--dim); font-size: 14px; line-height: 1.55; }
.ui-tutorial-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 17px; }
.ui-tutorial-count { margin-right: auto; color: var(--faint); font-family: var(--mono); font-size: 11px; }
.ui-tutorial-primary { border-color: var(--gold) !important; background: rgba(255, 198, 92, 0.16) !important; font-weight: 700; }
.ui-tutorial-secondary { border-color: transparent !important; background: transparent !important; color: var(--dim) !important; }
.ui-tutorial.is-waiting .ui-tutorial-card { pointer-events: none; }
.ui-tutorial.is-waiting .ui-tutorial-blocker,
.ui-tutorial.is-observe .ui-tutorial-blocker { background: rgba(7, 6, 10, 0.14); backdrop-filter: none; }

@keyframes tutorial-pulse {
  0%, 100% { border-color: rgba(255, 220, 126, 0.82); }
  50% { border-color: #fff3c4; box-shadow: 0 0 0 9999px rgba(7, 6, 10, 0.76),
    0 0 0 8px rgba(255, 220, 126, 0.12), 0 0 40px rgba(255, 190, 80, 0.78); }
}

@keyframes tutorial-nudge {
  0%, 100% { margin-left: 0; }
  30% { margin-left: -7px; }
  65% { margin-left: 7px; }
}

.ui-tutorial-card.is-nudge { animation: tutorial-nudge 180ms ease-out; }

@media (max-width: 640px) {
  .ui-tutorial-card { padding: 16px; bottom: max(12px, var(--safe-b)); }
  .ui-tutorial-card.is-top { top: max(12px, var(--safe-t)); }
  .ui-tutorial-actions { flex-wrap: wrap; }
  .ui-tutorial-count { width: 100%; }
  .ui-tutorial-secondary, .ui-tutorial-primary { flex: 1 1 auto; }
}

.ui-panel {
  width: min(680px, 100%);
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 26px 30px calc(26px + var(--safe-b));
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-sizing: border-box;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6);
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

/* The shared small header (continue-run dialog, end screens) gets the logotype's gradient fill,
   statically — no breathing, so it never competes with the big rail wordmark below (§5.1). */
.ui-title {
  font-size: clamp(28px, 6vw, 44px);
  letter-spacing: 0.22em;
  margin-bottom: 4px;
  background-image: linear-gradient(180deg,
    #fff3cf 0%, #ffd98a 22%, #ffb454 40%, #ff7a1a 58%, #8a4a26 74%, #4a3a40 88%, #3a3038 100%);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}
.ui-tagline, .ui-screen-flavour { color: var(--dim); }
.ui-screen-head { margin-bottom: 16px; }
/* Screen titles stop shouting: words, not signage — "The Forge", "Choose a holding". */
.ui-screen-title { color: var(--accent); letter-spacing: 0.1em; font-weight: 700; margin-bottom: 4px; }
.ui-screen-sub { color: var(--dim); margin: 0; }

/* Multiplayer hub and pre-match lobby. These controls intentionally use the same button and
   panel language as the rest of the game, while giving tower/map choices enough visual weight to
   be read at a glance. */
.versus-choice-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.versus-mode-card { min-height: 150px; align-items: flex-start; justify-content: center; gap: 7px; padding: 20px; border: 1px solid var(--line-strong); border-radius: var(--radius); background: linear-gradient(145deg, var(--panel-soft), var(--panel)); text-align: left; }
.versus-mode-card-live { border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.versus-mode-card-live { animation: versus-card-arrive 360ms var(--ease) both; }
.versus-mode-card-live:nth-child(2) { animation-delay: 70ms; }
.versus-mode-card-live:hover:not(:disabled) { transform: translateY(-3px); border-color: var(--accent); box-shadow: 0 10px 34px var(--glow); }
@keyframes versus-card-arrive { from { opacity: 0; transform: translateY(10px); } }
.versus-mode-card.is-disabled { opacity: .46; cursor: not-allowed; }
.versus-mode-card strong { display: block; color: var(--text); font-size: 18px; }
.versus-card-kicker, .versus-tower-choice-type { color: var(--accent); font: 10px var(--mono); letter-spacing: .14em; text-transform: uppercase; }
.versus-card-copy { color: var(--dim); font-size: 13px; line-height: 1.45; }
.versus-lobby-note, .versus-queue-status, .versus-invite-help { color: var(--dim); }
.versus-field { display: flex; align-items: center; gap: 12px; margin: 0 0 14px; }
.versus-label { min-width: 150px; color: var(--faint); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; }
.versus-map-select, .versus-friend-search { min-height: var(--tap); min-width: 0; padding: 9px 12px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--sunken); color: var(--text); }
.versus-map-select { flex: 1; }
.versus-lobby-card, .versus-rules { margin-top: 14px; padding: 15px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel-soft); }
.versus-rules h3 { margin: 0 0 7px; color: var(--accent); }
.versus-rules p { margin: 7px 0 0; color: var(--dim); font-size: 13px; }
.versus-section-title { margin-bottom: 5px; color: var(--text); font-size: 12px; letter-spacing: .15em; text-transform: uppercase; }
.versus-loadout-count { margin: 0 0 14px; color: var(--dim); font-size: 12px; }
.versus-tower-type-group + .versus-tower-type-group { margin-top: 18px; }
.versus-tower-type-title { margin: 0 0 8px; padding-left: 8px; border-left: 3px solid var(--tower-accent); color: var(--dim); font-size: 11px; letter-spacing: .13em; text-transform: uppercase; }
.versus-tower-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 8px; }
.versus-tower-choice { min-height: 128px; align-items: center; justify-content: center; gap: 2px; padding: 9px 7px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: rgba(12, 11, 16, .28); text-align: center; }
.versus-tower-choice::before, .versus-map-choice::before { content: ''; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--tower-accent, var(--accent)); opacity: .55; }
.versus-tower-choice, .versus-map-choice { position: relative; overflow: hidden; }
.versus-tower-choice:hover:not(:disabled), .versus-map-choice:hover:not(:disabled) { border-color: var(--tower-accent, var(--accent)); transform: translateY(-1px); }
.versus-tower-choice.is-selected, .versus-map-choice.is-selected, .versus-random-map.is-selected { border-color: var(--tower-accent, var(--accent)); background: color-mix(in srgb, var(--tower-accent, var(--accent)) 16%, var(--panel)); box-shadow: inset 0 0 0 1px var(--tower-accent, var(--accent)); }
.versus-tower-choice:disabled { opacity: .35; cursor: not-allowed; }
.versus-tower-icon { width: 56px; height: 56px; border-radius: 9px; background: var(--sunken); }
.versus-tower-choice-name, .versus-map-choice-name { max-width: 100%; overflow: hidden; color: var(--text); font-size: 12px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.versus-friend-search-row { display: flex; gap: 8px; margin-bottom: 10px; }
.versus-friend-search-row .versus-friend-search { flex: 1; }
.versus-friend-results, .versus-friend-list { display: grid; gap: 8px; }
.versus-friend-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 44px; padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel-soft); }
.versus-friend-divider { margin: 18px 0 8px; color: var(--accent); font: 11px var(--mono); letter-spacing: .13em; text-transform: uppercase; }
.versus-invite-token { display: block; margin-top: 8px; padding: 8px; overflow-wrap: anywhere; color: var(--gold); background: var(--sunken); }
.versus-prematch-banner { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-bottom: 10px; padding: 13px 15px; border: 1px solid var(--line-hot); border-radius: var(--radius); background: linear-gradient(100deg, var(--tint), var(--panel-soft)); }
.versus-prematch-phase { margin: 0; color: var(--accent); }
.versus-prematch-timer { min-width: 2ch; color: var(--gold); font: 700 25px var(--mono); text-align: right; }
.versus-prematch-timer.is-urgent { color: var(--bad); animation: versus-timer-pulse .7s ease-in-out infinite alternate; }
@keyframes versus-timer-pulse { to { transform: scale(1.12); } }
.versus-prematch-content { display: grid; gap: 15px; }
.versus-prematch-towers { max-height: 53vh; overflow-y: auto; padding: 3px; scrollbar-color: var(--line-hot) transparent; }
.versus-prematch-towers .versus-tower-grid,
.versus-prematch-towers.versus-tower-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 11px; }
.versus-prematch-towers .versus-tower-choice,
.versus-prematch-towers.versus-tower-grid .versus-tower-choice { min-height: 190px; gap: 5px; padding: 12px 9px; }
.versus-prematch-towers .versus-tower-icon,
.versus-prematch-towers.versus-tower-grid .versus-tower-icon { border: 1px solid color-mix(in srgb, var(--tower-accent, var(--accent)) 40%, transparent); box-shadow: 0 8px 22px rgba(0,0,0,.28); }
.versus-prematch-players { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.versus-prematch-player { display: flex; flex-direction: column; gap: 3px; padding: 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel-soft); }
.versus-prematch-player.is-you { border-color: var(--line-hot); }
.versus-prematch-player span { color: var(--faint); font-size: 12px; }
.versus-map-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; max-height: 54vh; overflow-y: auto; padding: 2px; }
.versus-map-choice { min-width: 0; align-items: stretch; gap: 6px; padding: 7px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel-soft); text-align: left; }
.versus-map-choice .me-thumb { display: block; width: 100%; height: auto; border-radius: 6px; }
.versus-random-map { justify-self: start; margin-top: 2px; border-color: var(--line-strong); }
.versus-boost-burst { position: absolute; top: 68px; left: 50%; z-index: 4; width: 1px; height: 1px; pointer-events: none; }
.versus-boost-burst.is-own { left: 150%; }
.versus-boost-spark { position: absolute; width: 6px; height: 6px; border-radius: 50%; background: var(--gold); box-shadow: 0 0 12px currentColor; opacity: 0; }
.versus-boost-burst.is-defense .versus-boost-spark { color: #7fd9ff; background: #7fd9ff; }
.versus-boost-burst.is-assault .versus-boost-spark { color: var(--ember); background: var(--ember); }
.versus-boost-burst.is-active .versus-boost-spark { animation: versus-boost-spark 820ms ease-out both; }
@keyframes versus-boost-spark {
  0% { opacity: 0; transform: rotate(var(--spark-angle)) translateX(3px) scale(.5); }
  18% { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--spark-angle)) translateX(var(--spark-distance)) scale(.15); }
}
.versus-result.is-visible { animation: versus-result-in 520ms var(--ease) both; }
.versus-result.is-loss { color: var(--bad); text-shadow: 0 0 24px var(--bad); }
@keyframes versus-result-in { from { opacity: 0; transform: scale(.72); } to { opacity: 1; transform: scale(1); } }
@media (max-width: 600px) {
  .versus-choice-grid, .versus-prematch-players { grid-template-columns: 1fr; }
  .versus-field { align-items: stretch; flex-direction: column; gap: 5px; }
  .versus-label { min-width: 0; }
  .versus-map-select { width: 100%; }
  .versus-prematch-towers { max-height: 48vh; }
}

/* Live Versus is a board split, not a second HUD. The real board is the right half; the left half
   is a renderer-backed opponent canvas with the same terrain scale and an explicit telegraph. */
.ui-layer-versus { overflow: hidden; }
.versus-board-labels { position: absolute; inset: 0; pointer-events: none !important; }
.versus-own-board-label { position: absolute; top: 10px; left: 50%; right: 10px; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; gap: 7px; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--ink) 80%, transparent); text-align: right; }
.versus-board-embers { color: var(--ember); font: 700 11px var(--mono); }
.versus-board-economy { color: var(--gold); font: 10px var(--mono); }
.versus-own-board-label .versus-connection-status { flex-basis: 100%; text-align: right; }
.ui-layer-versus .versus-opponent { top: 0; left: 0; right: auto; width: 50%; height: 100%; padding: 10px; box-sizing: border-box; display: flex; flex-direction: column; gap: 5px; border: 0; border-right: 1px solid var(--line-hot); border-radius: 0; background: color-mix(in srgb, var(--ink) 18%, transparent); box-shadow: none; }
.ui-layer-versus .versus-opponent-title { overflow: hidden; color: var(--text); text-overflow: ellipsis; white-space: nowrap; }
.ui-layer-versus .versus-opponent-canvas { flex: 1 1 auto; width: 100%; height: 100%; min-height: 0; border-radius: var(--radius-sm); background: var(--bg-0); }
.ui-layer-versus .versus-telegraph, .ui-layer-versus .versus-boost-notice { flex: 0 0 auto; }
.ui-layer-versus .versus-result { z-index: 3; }
.versus-side-sends { display: none; flex: 0 0 auto; max-height: 44%; padding: 8px; overflow-y: auto; border-top: 1px solid var(--line-hot); background: color-mix(in srgb, var(--ink) 88%, transparent); }
.versus-side-sends:not([hidden]) { display: block; }
.versus-side-sends .versus-section-title { margin-top: 0; }
.versus-side-sends .versus-sends { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); overflow: visible; }
.versus-send { min-width: 118px; align-items: center; text-align: center; }
.versus-send-icon { width: 42px; height: 42px; border-radius: 8px; background: var(--sunken); }
.versus-send-id { max-width: 100%; overflow: hidden; color: var(--faint); font: 9px var(--mono); text-overflow: ellipsis; white-space: nowrap; }
.versus-send-label { color: var(--text); font-size: 11px; font-weight: 700; }
.versus-send-meta { color: var(--gold); font: 10px var(--mono); line-height: 1.35; }
.versus-side-sends .versus-send { min-width: 0; white-space: normal; text-align: center; }
.versus-side-sends .versus-boosts { margin-top: 8px; padding: 0; border: 0; background: transparent; }
@media (max-width: 860px) {
  .ui-layer-versus { right: 0; }
  .versus-side-sends { max-height: 42%; }
}

.ui-btn { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-height: var(--tap); padding: 10px 18px; }
.ui-btn-label { font-weight: 600; letter-spacing: 0.03em; }
.ui-btn-sub { font-size: 11px; color: var(--faint); }
/* Filled, not ghosted (VISUAL-DESIGN §2.2): the one place the interface raises its voice, so the
   primary action on a screen is obvious without reading it. Safe in every world — the darkest
   `--ink-on-accent` sits on is Hollow Core's `#ff3a6a`, which still clears AA at button size. */
.ui-btn-primary {
  border-color: transparent;
  background: var(--accent);
  color: var(--ink-on-accent);
  font-weight: 600;
}
.ui-btn-primary .ui-btn-sub { color: color-mix(in srgb, var(--ink-on-accent) 68%, transparent); }
.ui-btn-primary:hover:not(:disabled) {
  border-color: transparent;
  background: var(--accent);
  filter: brightness(1.06);
}
.ui-btn-danger { border-color: rgba(255, 93, 108, 0.4); color: #ffb3ba; }
.ui-btn-danger:hover:not(:disabled) { border-color: var(--bad); background: rgba(255, 93, 108, 0.12); }

.ui-menu-list { display: flex; flex-direction: column; gap: 8px; }
.ui-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.ui-difficulty { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }

/* The attract-mode demo's canvas (VISUAL-DESIGN §5.2). `body.is-vista` is the exact complement of
   `#ui.is-ingame` — render/ui.js's applyScreen toggles both at the same site, both from the 'screen'
   bus event AND from initUI's own explicit boot-time call (app.screen is 'boot' before anything has
   ever played, which already satisfies `!inGame`). Everywhere no real game is running, `#game` drops
   the map-cell inset (styles.css) and fills the whole viewport, so render/attract.js's skirmish reads
   as a background rather than a board floating in a smaller box. main.js calls `renderer.onResize()`
   on every 'screen' transition (not only into 'game'), AND once more right after ui.initUI() at boot
   — the very first onResize() inside renderer.initRenderer() necessarily runs before this class has
   been decided at all — so the canvas backing store actually catches up to this box the instant it
   changes size. render/attract.js re-fits its own camera to the demo's grid every tick it is
   visible, independent of all of the above. */
body.is-vista #game {
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

/* ------------------------------------------------------------- title-screen vista rail (D14 §5) --

   The menu's body is still `.ui-panel` (layerFor builds every screen the same way) — this class
   strips the centred-box treatment so it sits flush left over the vista scrim (`.ui-screen.is-vista`
   above) instead of floating a panel on top of it. `margin-right: auto` is what pushes a single flex
   child to the left edge of `.ui-screen`'s centring flexbox without touching that shared rule.
   src/render/menuVista.js builds everything inside it. */
.ui-vista-rail {
  width: min(440px, 44vw);
  max-width: 100%;
  padding: 8vh 40px;
  margin-right: auto;
  border: 0;
  background: none;
  box-shadow: none;
  text-align: left;
}
.ui-vista-rail .ui-tagline { max-width: 44ch; }

/* The EMBERWALL logotype: a wall of letters with fire rising behind it, glyph tops lit ember-hot
   cooling to stone at the baseline. Gradient-fill text on the system stack, zero assets. */
.ui-logo-wrap { margin-bottom: 8px; }
.ui-logo { margin: 0; font-weight: 800; font-size: clamp(56px, 10vw, 120px); letter-spacing: 0.06em; line-height: 0.95; }
.ui-logo-word {
  background-image: linear-gradient(180deg,
    #fff3cf 0%, #ffd98a 22%, #ffb454 40%, #ff7a1a 58%, #8a4a26 74%, #4a3a40 88%, #3a3038 100%);
  background-size: 100% 160%;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 18px rgba(255, 150, 50, 0.28)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.5));
  animation: logo-breathe 7s ease-in-out infinite alternate;
}
@keyframes logo-breathe {
  from { background-position: 0% 0%; }
  to { background-position: 0% 28%; }
}
.ui-logo-underline {
  display: block;
  width: 62%;
  height: 4px;
  margin-top: 6px;
  border-radius: 999px;
  background: linear-gradient(90deg, transparent, #ff7a1a 18%, #ffd98a 50%, #ff7a1a 82%, transparent);
  box-shadow: 0 0 22px rgba(255, 140, 40, 0.35);
  animation: logo-underline-pulse 4s ease-in-out infinite alternate;
}
@keyframes logo-underline-pulse {
  from { opacity: 0.75; }
  to { opacity: 1; }
}

/* The Sparks currency chip (content design §1): a pill button, not a label — clicking it opens
   the Forge, same as the rail's own Forge entry. The glyph is a font character, zero-asset, same
   idiom as the ★ star rating elsewhere in this file. */
.ui-spark-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 4px 0 20px;
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--sunken);
  font-family: var(--mono);
  color: var(--text);
  cursor: pointer;
}
.ui-spark-chip:hover { border-color: var(--line-hot); }
.ui-spark-glyph { color: var(--spark); text-shadow: 0 0 8px color-mix(in srgb, var(--spark) 45%, transparent); }
.ui-spark-count { font-variant-numeric: tabular-nums; }

.ui-worlds { display: flex; flex-direction: column; gap: 12px; max-height: 52vh; overflow-y: auto; overscroll-behavior: contain; padding-right: 4px; }
.ui-world { padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--sunken); }
.ui-world.is-locked { opacity: 0.55; }
.ui-world-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
.ui-world-name { color: var(--world-accent, var(--text)); margin: 0; }
.ui-world-twist { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--faint); margin-left: auto; }
.ui-world-desc, .ui-twist-desc { font-size: 12px; color: var(--dim); margin-bottom: 6px; }
.ui-twist-desc { color: var(--faint); }
.ui-lock { font-size: 12px; color: var(--warn); margin-bottom: 6px; }
.ui-levels { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.ui-levels-empty { font-size: 12px; color: var(--faint); }
.ui-level { align-items: flex-start; padding: 10px 12px; }
.ui-level-name { font-weight: 600; }
.ui-level.is-cleared { border-color: rgba(158, 222, 122, 0.35); }
.ui-stars { letter-spacing: 0.2em; font-size: 13px; color: var(--faint); }
/* D68: a star is the colour of the difficulty it was earned on — yellow, orange, red — and the row
   carries it as a custom property so one rule serves the level grid, the level screen and the
   victory screen alike. Gold stays the fallback for a row with no difficulty (the criteria list). */
.ui-star.is-earned { color: var(--star-color, var(--gold)); }
.ui-record { display: flex; flex-direction: column; gap: 1px; }
.ui-record-row { display: flex; align-items: center; gap: 8px; }
.ui-record-row .ui-stars { font-size: 11px; letter-spacing: 0.12em; }
.ui-record-named { gap: 6px; margin-bottom: 14px; }
.ui-record-named .ui-stars { font-size: 15px; letter-spacing: 0.18em; }
.ui-record-name { width: 64px; font-size: 12px; color: var(--dim); }
.ui-record-count { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--faint); }
.ui-record-row.is-cleared .ui-record-count { color: var(--dim); }
.ui-diff.is-active { border-color: var(--diff-color, var(--accent)); }
.ui-diff.is-active .ui-btn-label { color: var(--diff-color, var(--accent)); }

/* The level screen: the map on the left, the record and the choice on the right. */
.ui-level-body { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(240px, 1fr); gap: 18px; align-items: start; }
.ui-level-preview {
  display: flex; align-items: center; justify-content: center;
  padding: 10px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--sunken);
}
/* The library draws its thumbnails at a fixed 132x88; here the same drawing fills the panel. */
.ui-level-preview canvas.me-thumb { width: 100%; max-width: 480px; height: auto; border-radius: var(--radius-sm); image-rendering: auto; }
.ui-level-sub { margin: 0 0 8px; font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); }
.ui-level-side .ui-difficulty { flex-direction: column; margin-bottom: 0; }
.ui-level-side .ui-diff { width: 100%; align-items: flex-start; }
@media (max-width: 700px) {
  .ui-level-body { grid-template-columns: 1fr; }
}

.ui-settings { display: flex; flex-direction: column; gap: 6px; }
.ui-setting { display: flex; flex-direction: column; gap: 6px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.ui-setting-head { display: flex; align-items: baseline; gap: 10px; }
.ui-setting-label { font-weight: 600; }
.ui-setting-value { margin-left: auto; font-family: var(--mono); color: var(--accent); }
.ui-setting-help { font-size: 12px; color: var(--faint); }
.ui-slider { width: 100%; }
.ui-toggle { flex-direction: row; align-items: center; justify-content: space-between; width: 100%; }
.ui-toggle-state { font-family: var(--mono); font-size: 12px; color: var(--dim); }
.ui-speed-group { display: flex; gap: 6px; }
.ui-speed-option { min-width: 56px; font-family: var(--mono); align-items: center; }
.ui-setting-danger { border-bottom: 0; }
.ui-danger-question { color: var(--warn); font-size: 13px; }
.ui-setting-credits { gap: 4px; }
.ui-credit-line { font-size: 12px; color: var(--faint); margin: 0; }

.ui-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; margin: 14px 0; }
.ui-summary-item { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--sunken); }
.ui-summary-value { font-family: var(--mono); font-size: 20px; color: var(--accent); }
.ui-summary-label { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--faint); }
.ui-criteria { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.ui-criterion { display: flex; align-items: baseline; gap: 8px; font-size: 13px; color: var(--faint); }
.ui-criterion.is-earned { color: var(--text); }
.ui-criterion-mark { color: var(--gold); }

/* The Sparks struck by a finished run (render/summary.js). A tinted band rather than another
   .ui-summary-item, because it is the one figure on these screens that outlives the run. */
.ui-sparks-award {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 12px 0 4px;
  padding: 12px 14px;
  border: 1px solid color-mix(in srgb, var(--spark) 28%, transparent);
  border-left: 3px solid var(--spark);
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, color-mix(in srgb, var(--spark) 12%, transparent), transparent);
}
.ui-sparks-total { display: flex; align-items: baseline; gap: 8px; margin: 0; }
.ui-sparks-value { font-family: var(--mono); font-size: 26px; font-weight: 700; color: var(--spark); }
.ui-sparks-glyph { font-size: 20px; color: var(--spark); text-shadow: 0 0 10px color-mix(in srgb, var(--spark) 45%, transparent); }
.ui-sparks-breakdown { margin: 0; font-family: var(--mono); font-size: 12px; color: var(--dim); }
.ui-sparks-note { margin: 0; font-size: 12px; color: var(--faint); }

/* What a clear opened (render/summary.js). Deliberately the Sparks band's twin — same shape, same
   left rule — because they are the same kind of news: the two things a finished run leaves behind.
   The accent rather than the spark gold keeps them apart at a glance when both are on screen. */
.ui-unlock-award {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 12px 0 4px;
  padding: 12px 14px;
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--tint), transparent);
}
.ui-unlock-title { margin: 0; font-size: 15px; font-weight: 600; color: var(--accent); }
/* Three unlocks at once is possible (a world can open alongside two towers) and the victory screen
   has no room to grow, so the list scrolls rather than pushing the buttons off the bottom. */
.ui-unlock-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 232px;
  overflow-y: auto;
}
.ui-unlock-row { display: flex; align-items: flex-start; gap: 12px; }
.ui-unlock-preview {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
}
.ui-unlock-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ui-unlock-name { margin: 0; font-weight: 600; color: var(--text); }
.ui-unlock-meta { margin: 0; font-family: var(--mono); font-size: 12px; color: var(--gold); }
.ui-unlock-blurb { margin: 0; font-size: 12px; line-height: 1.45; color: var(--dim); }

/* ------------------------------------------------------------------------- the Forge (§2.7) --
   Wider than the standard .ui-panel: a SKU grid reads cramped at the summary-screen width. */
.ui-screen-forge .ui-panel { width: min(940px, 94vw); }
.ui-forge-head { display: flex; align-items: center; gap: var(--gap-lg); margin-bottom: var(--gap-lg); }
.ui-forge-head .ui-screen-title { margin: 0; }
.ui-forge-sparks { margin-left: auto; cursor: default; }
.ui-forge-sparks:hover { border-color: var(--line); }

.ui-forge-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--gap); }
.ui-forge-empty { grid-column: 1 / -1; color: var(--faint); }

.ui-forge-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
}
.ui-forge-card-head { display: flex; align-items: center; gap: 10px; }
.ui-forge-icon { width: 44px; height: 44px; flex: 0 0 44px; border-radius: var(--radius-sm); background: var(--plaque); }
.ui-forge-card-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ui-forge-card-name { font-size: 14px; font-weight: 600; }
/* The plaque rule (VISUAL-DESIGN §1.4): a damage-type colour only ever sits on the fixed dark
   plaque, never straight on a panel — the six TYPE_COLOR values stay AA-legible at any brightness. */
.ui-forge-card-type {
  align-self: flex-start;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--plaque);
  color: var(--accent);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* D58: the two upgrade paths are bought separately, so they sit side by side under their own
   headings — the same two words the in-run upgrade panel uses (shop.js exports PATH_TITLE). Laid
   out as a grid rather than stacked so the Adept rows line up across the two columns: the price
   comparison a player is actually making is between paths, not between tiers. */
.ui-forge-paths { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; align-items: start; }
.ui-forge-paths.is-single { grid-template-columns: 1fr; }
.ui-forge-path { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ui-forge-path-head {
  margin: 0;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
/* D67: the design's own price, when it has one. It gates BOTH paths, so it spans the full card
   above them rather than sitting in either column — the reading order is 'buy the design, then buy
   its tiers'. Marked with the accent edge because it is the row that makes the rest possible. */
.ui-forge-design { border-left: 2px solid var(--accent); }
.ui-forge-design.is-owned { border-left-color: var(--line); }

/* D73: the live preview (render/towerPreview.js) sits under the Forge's header and stays put while
   the grid scrolls, so a tower can be read against its own arena without losing the card that was
   clicked. Stage left, words right; the stage takes the board's own shape (the ring is square, the
   lane is wide) through the canvas's data-layout. */
.ui-forge-preview { position: sticky; top: 0; z-index: 2; margin-bottom: var(--gap); }
.ui-matchup {
  display: grid;
  grid-template-columns: minmax(200px, 320px) minmax(0, 1fr);
  gap: 12px 16px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-top: 2px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--panel);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
}
.ui-matchup.is-hidden { display: none; }
.ui-matchup-stage {
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  aspect-ratio: 1 / 1;
}
.ui-matchup-side { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ui-matchup-head { display: flex; align-items: baseline; gap: 8px; }
.ui-matchup-title { font-weight: 600; color: var(--text); }
/* The tier pair is the one thing that says WHICH version of the tower this is, so it is given the
   tower's own damage-type colour rather than left as body text. */
.ui-matchup-tier { font-family: var(--mono); font-size: 12px; margin-left: auto; }
.ui-matchup-arena {
  display: block;
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 100%;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-soft);
}
.ui-matchup-arena[data-layout="lane"] { aspect-ratio: 14 / 8; }
/* The tier strip: Base, then each tier of each path. The current one carries the accent. */
.ui-matchup-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.ui-matchup-strip-group { display: inline-flex; align-items: center; gap: 4px; }
.ui-matchup-strip-label { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--faint); margin-right: 2px; }
.ui-matchup-chip {
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--sunken);
  color: var(--dim);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
}
.ui-matchup-chip:hover { border-color: var(--line-strong); color: var(--text); }
.ui-matchup-chip.is-current { border-color: var(--accent, var(--line-strong)); color: var(--text); background: var(--plaque); }
.ui-matchup-tier-name { font-size: 13px; font-weight: 600; color: var(--text); }
.ui-matchup-tier-desc { font-size: 11px; line-height: 1.35; color: var(--dim); }
.ui-matchup-phase-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-top: 2px; }
.ui-matchup-phase { font-size: 12px; font-weight: 600; color: var(--good); }
.ui-matchup-phase.is-weak { color: var(--warn); }
.ui-matchup-phase.is-quiet { color: var(--faint); }
.ui-matchup-score { font-family: var(--mono); font-size: 11px; color: var(--dim); }
.ui-matchup-list { display: flex; flex-direction: row; flex-wrap: wrap; gap: 4px 12px; }
.ui-matchup-row { display: flex; align-items: baseline; gap: 5px; }
.ui-matchup-enemy-count { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.ui-matchup-enemy-name { font-size: 12px; color: var(--text); }
.ui-matchup-enemy-note { font-size: 11px; color: var(--warn); }
.ui-matchup-empty:empty { display: none; }
.ui-matchup-empty { font-size: 11px; color: var(--faint); }
@media (max-width: 640px) {
  /* A phone needs the menu to be a readable rail, not 44% of an already narrow viewport. The
     desktop width deliberately reveals the vista; here that rule clipped the logo and wrapped
     every action into a tall, partially hidden column. */
  .ui-vista-rail { width: min(380px, 100%); padding: clamp(20px, 6vh, 42px) clamp(18px, 6vw, 30px); }
  .ui-logo { font-size: clamp(42px, 12vw, 56px); }
  .ui-matchup { grid-template-columns: 1fr; }
  .ui-forge-preview { position: static; }
}

/* D73: a card's head is the thing to click to preview the tower; a hovered SKU previews its tier. */
.ui-forge-card-head { cursor: pointer; border-radius: var(--radius-sm); margin: -4px; padding: 4px; }
.ui-forge-card-head:hover { background: var(--sunken); }
.ui-forge-card.is-previewing { border-color: var(--accent); }
.ui-forge-card.is-previewing .ui-forge-card-head { background: var(--plaque); }

/* One column per path is narrow; below this the grid stacks rather than squeezing the prices. */
@media (max-width: 560px) {
  .ui-forge-paths { grid-template-columns: 1fr; }
}

.ui-forge-sku {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
  text-align: left;
  transition: background 220ms var(--ease), border-color 220ms var(--ease);
}
.ui-forge-sku-head { display: flex; align-items: baseline; gap: 8px; }
.ui-forge-sku-name { font-size: 13px; font-weight: 600; }
.ui-forge-sku-price { margin-left: auto; font-family: var(--mono); font-size: 12px; color: var(--spark); }
.ui-forge-sku-effect { font-size: 11px; color: var(--dim); }
.ui-forge-sku-note { font-size: 11px; color: var(--faint); }
.ui-forge-sku.is-owned { border-color: rgba(158, 222, 122, 0.45); background: rgba(158, 222, 122, 0.08); }
.ui-forge-sku.is-owned .ui-forge-sku-name::after { content: ' \2713'; color: var(--good); }
.ui-forge-sku.is-buyable { border-color: var(--line-hot); box-shadow: 0 0 14px var(--glow); }
.ui-forge-sku.is-poor { opacity: 0.55; }
.ui-forge-sku.is-requires { opacity: 0.6; border-style: dashed; }
/* The purchase flash (VISUAL-DESIGN §2.7): a 250ms sweep of --tint across the row, then it fades
   back out under the shared --ui-fade transition on the base .ui-forge-sku rule above. */
.ui-forge-sku.is-kindle { background: var(--tint); border-color: var(--line-hot); }

.ui-forge-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: var(--gap-lg);
  padding-top: 12px;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 12px;
}
.ui-forge-footer-glyph { color: var(--spark); opacity: 0.6; }

/* -------------------------------------------------------------- the Proving Ground (§2.10) --
   The briefing screen (render/provingGround.js's buildProvingGround) is a plain .ui-panel/
   .ui-screen-flavour/.ui-actions layout and needs nothing of its own below. What follows is the
   in-game control strip: a THIRD persistent DOM layer (ui.js mounts it beside hud.js's and
   shop.js's own), visible only while a sandbox game is live — it docks where the wave preview
   sits, over the bottom-right of the board, so it reads as the sandbox's own console rather than
   an ordinary panel. */

.ui-layer-sandbox { grid-column: 1 / -1; grid-row: 1 / -1; min-width: 0; pointer-events: none; }

.ui-pg-strip {
  display: none;
  flex-direction: column;
  gap: 10px;
  position: absolute;
  /* Clears the shop panel (.ui-side, grid-column 2) rather than sitting under it — the strip lives
     in the map row, not the bottombar row the wave preview it replaces actually sits in. */
  right: calc(var(--side-w) + max(14px, var(--safe-r)));
  bottom: calc(var(--bottombar-h) + max(10px, var(--safe-b)));
  width: min(420px, calc(100vw - 28px));
  max-height: 46vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-top: 2px solid var(--line-hot);
  border-radius: var(--radius);
  background: var(--panel-soft);
  backdrop-filter: blur(6px);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  opacity: 0;
  transition: opacity var(--ui-fade) var(--ease);
  pointer-events: auto;
}
.ui-pg-strip.is-visible { display: flex; opacity: 1; }
#ui:not(.is-ingame) .ui-pg-strip { opacity: 0; pointer-events: none; }

.ui-pg-title {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.ui-pg-chips { display: flex; flex-wrap: wrap; gap: 6px; max-height: 132px; overflow-y: auto; }
.ui-pg-empty { font-size: 12px; color: var(--faint); }

.ui-pg-chip {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px 3px 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--sunken);
  white-space: nowrap;
}
.ui-pg-chip:hover { border-color: var(--line-hot); }
.ui-pg-chip-dot { width: 6px; height: 6px; border-radius: 999px; flex: 0 0 auto; }
.ui-pg-chip-icon { display: block; background: var(--plaque); border-radius: 999px; }
.ui-pg-chip-name { font-size: 11px; color: var(--dim); }

.ui-pg-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.ui-pg-gates { display: flex; gap: 4px; padding: 4px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--sunken); }

.ui-pg-count { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--faint); }
.ui-pg-count-input { width: 52px; font-family: var(--mono); }

.ui-pg-hp { display: flex; align-items: center; gap: 8px; flex: 1 1 140px; min-width: 140px; }
.ui-pg-hp-label { font-family: var(--mono); font-size: 12px; color: var(--accent); white-space: nowrap; }

.ui-pg-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.ui-pg-actions .ui-btn { min-height: var(--tap); padding: 6px 12px; }

/* ------------------------------------------------------------------- enemy intro (D21) -- */
/* The first-encounter popup: a live-drawn portrait beside the auto-derived chip row (class, camo,
   immunities, traits — same .ui-chip/.ui-tag classes as ui.js's own enemy tooltip), then the
   authored `intro` sentence from data/enemies.js. Narrower than the default .ui-panel — this is a
   quick read, not a form. */
.ui-screen-enemyIntro .ui-panel { width: min(420px, 100%); }

.ui-intro-layout {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 14px;
}
.ui-intro-portrait {
  flex: 0 0 auto;
  width: 96px;
  height: 96px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--sunken);
}
.ui-intro-chips { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 6px; flex: 1 1 auto; }
.ui-intro-text { margin: 0 0 4px; }

/* D43: the world-rule popup. Shares the intro shell with the enemy sighting; these are only the
   two parts that popup does not have — the counter-play line, given the accent so the answer to
   the rule reads apart from the rule itself, and the plain list of numbers worth knowing. */
.ui-twist-counter { color: var(--accent); }
.ui-twist-points {
  margin: 8px 0 4px;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  color: var(--dim);
}
.ui-twist-points li { margin: 0; }

/* Leaderboards (D52). The one screen whose content comes from the network, so it is built to look
   correct while empty and while offline — those are ordinary states here, not errors. */
.lb-sub {
  margin: 14px 0 6px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}
.lb-picker { display: flex; flex-wrap: wrap; gap: 6px; }
.lb-picker .ui-btn { flex: 0 0 auto; padding: 6px 12px; font-size: 12px; }
.lb-select {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
  color: var(--ink-bright, #e8e0d0);
  font: inherit;
  font-size: 13px;
}
.lb-board { margin-top: 12px; min-height: 120px; }
.lb-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.lb-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  background: var(--sunken);
  font-size: 13px;
}
/* The player's own row, so they can find themselves without reading every name. */
.lb-row.is-mine { background: color-mix(in srgb, var(--accent) 18%, var(--sunken)); }
.lb-rank {
  flex: 0 0 2em;
  font-family: var(--mono);
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.lb-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-value { flex: 0 0 auto; font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--gold); }
.lb-empty { padding: 18px 4px; }
.lb-note { margin: 4px 0; font-size: 12px; color: var(--dim); line-height: 1.45; }
.lb-error { margin: 4px 0; font-size: 12px; color: var(--bad); min-height: 1em; }
.lb-submit {
  margin-top: 16px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--sunken);
}
.lb-input {
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-1);
  color: var(--ink-bright, #e8e0d0);
  font: inherit;
  font-size: 14px;
}

/* The picker's selected state. `.ui-btn` has no active variant of its own — the HUD's speed and
   difficulty pills carry theirs in styles-game.css, and this screen is not part of the HUD — so it
   is stated here rather than borrowed from a rule that could move. */
.lb-picker .ui-btn.is-active {
  border-color: var(--line-hot);
  background: color-mix(in srgb, var(--accent) 22%, var(--sunken));
  color: var(--ink-on-accent, #241b12);
  font-weight: 600;
}
