/* ──────────────────────────────────────────────────────────────────────
   z-index hierarchy (highest first). Keep this in sync when adding
   new fixed/absolute UI so layering stays predictable:

    100  .create-level-modal        chooser opened FROM the Library tab —
                                    must sit above the dashboard (90) so
                                    the two-card picker isn't occluded
    100  .seed-config-modal         seed-generator config (also opens
                                    from the Library tab; same reason)
    100  .confirm-modal             styled Yes/No dialog — the M2
                                    autosave-recovery + return-to-Library
                                    paths can fire while the dashboard
                                    is open, so it needs to sit above 90
     95  .lobby-config              landing panel (above legacy dashboard)
     90  .sky-sprint-dashboard      fullscreen rich panels
     60  .sky-sprint-identity       account widget (top-right)
     50  .overlay                   legacy in-canvas modal (post-run +
                                    error states)
     40  .level-history-modal       calendar / history popouts
     27  .toast-host                non-blocking notifications
     24  .landing                   unified main-menu surface
     11  #fps-counter               dev HUD chip
      9  #object-tuner              level-editor dev panel
      4  .countdown                 pre-run countdown
      3  .hud                       in-game chip strip

   Lower-numbered layers (game canvas, normal flow) are implicit.
   ─────────────────────────────────────────────────────────────────── */

:root {
  /* ── Design system tokens — single source of truth ───────────────── */

  /* Color — semantic */
  --color-surface-deep: #07203a;
  --color-surface: rgba(7, 32, 58, 0.82);
  --color-surface-elevated: rgba(20, 32, 50, 0.94);
  --color-surface-light: rgba(243, 248, 252, 0.9);
  --color-text: #f3f8fc;
  --color-text-on-light: #07203a;
  --color-text-muted: rgba(243, 248, 252, 0.66);
  --color-text-subtle: rgba(243, 248, 252, 0.5);

  --color-border: rgba(255, 255, 255, 0.18);
  --color-border-strong: rgba(255, 255, 255, 0.32);
  --color-border-light: rgba(7, 32, 58, 0.14);

  /* HUD chip background — slightly more transparent than --color-surface
     so HUD chips don't fully obscure the canvas behind them. Kept as its
     own named token because adjusting the chip strip's opacity is a
     common visual-tuning knob. */
  --color-chip-bg: rgba(7, 32, 58, 0.72);

  --color-primary: #ffb703;
  --color-primary-strong: #ffda66;
  --color-info: #4fa3ff;
  --color-good: #3ddb84;
  --color-good-soft: #6fe0a3;
  --color-warn: #f5b34c;
  --color-bad: #ff6b6b;
  --color-bad-soft: #ff9a8a;
  --color-admin: #b388ff;
  --color-admin-soft: #d6a8ff;
  --color-dev: #f5b34c;

  /* Spacing scale (matches Tailwind-ish steps) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;

  /* Border radius — three sizes + pill */
  --radius-sm: 0.4rem;
  --radius-md: 0.7rem;
  --radius-lg: 1rem;
  --radius-pill: 999px;

  /* Shadows */
  --shadow-sm: 0 2px 8px rgba(7, 32, 58, 0.18);
  --shadow-md: 0 6px 22px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 16px 42px rgba(7, 32, 58, 0.35);

  /* Typography */
  --font-ui: "Gill Sans MT", "Candara", "Trebuchet MS", system-ui, sans-serif;
  --font-mono: "SFMono-Regular", Consolas, "Courier New", monospace;

  /* Transitions */
  --motion-fast: 110ms ease;
  --motion-medium: 180ms ease;
}

/* Skip animations / transitions when the user has the Reduce Motion
   accessibility preference set globally OR has toggled the setting on
   in the dashboard. The dashboard option toggles a body-level class,
   the OS preference uses prefers-reduced-motion. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
}

body {
  font-family: "Gill Sans MT", "Candara", "Trebuchet MS", sans-serif;
  color: var(--color-text);
  background: radial-gradient(circle at 20% 20%, #1a5c7e 0%, #0a263f 55%, #071624 100%);
  overflow: hidden;
}

.game-shell {
  position: relative;
  width: 100vw;
  height: 100vh;
}

#game-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.hud {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: stretch;
  gap: 0.5rem;
  z-index: 3;
  max-width: calc(100% - 1.5rem);
  animation: hud-reveal 320ms ease-out;
}

.chip {
  min-width: 6.75rem;
  background: var(--color-chip-bg);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 0.8rem;
  padding: 0.38rem 0.58rem;
  backdrop-filter: blur(4px);
}

.chip span {
  display: block;
  font-size: 0.7rem;
  opacity: 0.85;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
}

.chip strong {
  display: block;
  margin-top: 0.15rem;
  font-size: 1.04rem;
  line-height: 1.1;
  text-align: center;
}

/* Time + Lives carry the whole race in their heads — give them the
   biggest type and a brighter chip so the eye lands there first. */
.chip-prominent {
  min-width: 7.5rem;
  background: rgba(7, 32, 58, 0.78);
  border-color: rgba(255, 222, 145, 0.45);
  box-shadow: 0 0 0 1px rgba(255, 222, 145, 0.12), 0 4px 16px rgba(0, 0, 0, 0.28);
}

.chip-prominent span {
  font-size: 0.74rem;
  opacity: 1;
  color: rgba(255, 238, 195, 0.92);
}

.chip-prominent strong {
  margin-top: 0.18rem;
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

#time-left .t-unit {
  display: inline;
  font-size: 0.6em;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0.9;
  margin: 0 0.03em;
}

.chip-progress {
  min-width: 14rem;
}

.chip-progress strong {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

/* Subtle right-aligned percentage that lives next to the height value. */
.chip-suffix {
  font-size: 0.85rem;
  opacity: 0.72;
  font-weight: 500;
}

/* Lives chip — horizontal row of color-emoji hearts. Container is
   inline-flex so the hearts never wrap and the chip auto-sizes to
   fit them. Glyphs carry their own colour (red ❤️, black 🖤);
   .is-lost just dims the black one slightly so the row still reads
   as 'occupied slots' rather than disappearing slots. */
#lives-left {
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.18rem;
  white-space: nowrap;
}
.life-heart {
  font-size: 2.6rem;
  line-height: 1;
  display: inline-block;
  transform: translateY(-1px);
  filter: drop-shadow(0 0 5px rgba(255, 61, 94, 0.4));
  transition: filter 180ms ease, opacity 180ms ease;
}
.life-heart.is-lost {
  filter: none;
  opacity: 0.7;
}

/* Practice-mode ∞ glyph: match the Time chip's numeric styling so the
   two prominent chips read as a matched pair rather than one bright
   heart-glowing outlier. `.chip span` and `.chip-prominent span` set
   uppercase / amber / wide letter-spacing on ALL descendant spans
   (built for the "LIVES" label), so we explicitly pull the numeric
   look from the parent <strong> via `inherit` on each. */
.life-heart.life-heart--infinite {
  font-size: inherit;
  line-height: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: none;
  transform: none;
  filter: none;
}

/* The HUD row is `align-items: stretch`, so the Lives tile matches the
   tallest chip's height. When we render the ∞ (smaller than the hearts
   row it replaces) it sits at the top of the tile by default; push it
   to the bottom so it visually anchors where the hearts sat. `:has()`
   scopes this to the practice-mode strong only — the hearts branch
   still lays out as inline-block spans in a row. */
#lives-left:has(.life-heart--infinite) {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
}

.progress-meter {
  margin-top: 0.34rem;
  width: 100%;
  height: 0.45rem;
  position: relative;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.24);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  width: 0%;
  border-radius: inherit;
  background: linear-gradient(90deg, #7ff4c6 0%, #ffcf66 55%, #ff9a66 100%);
  transition: width 110ms linear;
}

.progress-record-marker {
  position: absolute;
  top: -0.18rem;
  bottom: -0.18rem;
  left: 0%;
  width: 0.2rem;
  border-radius: 999px;
  background: rgba(255, 238, 170, 0.95);
  box-shadow: 0 0 0 1px rgba(7, 32, 58, 0.55), 0 0 10px rgba(255, 222, 145, 0.55);
  transform: translateX(-50%);
  display: none;
  pointer-events: none;
}

.controls {
  position: absolute;
  left: 50%;
  bottom: 0.9rem;
  transform: translateX(-50%);
  z-index: 3;
  font-size: 0.9rem;
  letter-spacing: 0.03em;
  color: rgba(243, 248, 252, 0.94);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

.countdown {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  pointer-events: none;
  font-size: clamp(3.4rem, 12vw, 6.2rem);
  font-weight: 800;
  letter-spacing: 0.03em;
  color: rgba(255, 244, 204, 0.96);
  text-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}

.countdown.hidden {
  display: none;
}

.overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: linear-gradient(145deg, rgba(7, 32, 58, 0.52), rgba(10, 38, 63, 0.68));
  transition: opacity 180ms ease;
}

.overlay.overlay-celebration {
  place-items: start center;
  padding-top: 0.85rem;
  background: linear-gradient(180deg, rgba(7, 32, 58, 0.2), rgba(7, 32, 58, 0.04) 35%, rgba(7, 32, 58, 0));
  pointer-events: none;
}

.overlay.overlay-celebration .panel {
  width: min(34rem, 94vw);
  padding: 0.72rem 1rem 0.86rem;
  background: rgba(243, 248, 252, 0.76);
  box-shadow: 0 10px 28px rgba(7, 32, 58, 0.25);
  backdrop-filter: blur(2px);
  pointer-events: auto;
}

.overlay.hidden {
  opacity: 0;
  pointer-events: none;
}

.panel {
  width: min(33rem, 94vw);
  border-radius: 1rem;
  background: var(--color-surface-light);
  border: 1px solid rgba(7, 32, 58, 0.14);
  box-shadow: var(--shadow-lg);
  padding: 1rem 1.2rem 1.2rem;
  text-align: center;
  color: var(--color-text-on-light);
  animation: panel-in 300ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
  /* Positioning context for the legacy #lobby-config customize
     popover. Kept until the form itself moves into the landing
     Profile submenu via DOM adoption (Phase 4). */
  position: relative;
}

.panel h1 {
  margin: 0 0 0.42rem;
  font-size: clamp(1.6rem, 3.1vw, 2.2rem);
  letter-spacing: 0.03em;
}

.panel p {
  margin: 0;
  font-size: 1rem;
  line-height: 1.34;
}

/* (Retired: the catch-all hide for legacy #lobby-card /
   #lobby-actions / #lobby-controls. The markup has been deleted in
   Phase 7 so the hide is no longer needed.) */

/* (Retired: #home-screen, .home-screen, .home-screen__hero,
   .home-screen__tiles, .home-tile*. The 3-tile lobby menu and
   ~115 lines of supporting CSS were deleted in favor of .landing.) */

/* (Retired: .lobby-card / .lobby-card__heading / .lobby-card__eyebrow /
   .lobby-card__date / .lobby-card__stats / .lobby-stat /
   .lobby-stat__label / .lobby-stat__value / .lobby-stat__value.is-pb /
   .lobby-stat__value.is-quota-empty / .lobby-card__note /
   .lobby-actions / .lobby-link / .lobby-controls* — ~135 lines of CSS
   for a card that hadn't rendered since the landing rebuild. The card
   showed Goal / Seed / Best today / Quota; the landing footer chip
   covers daily quota inline, the dashboard Profile tab covers PB
   history, and the seed-of-the-day is admin trivia surfaced via the
   admin panel. The lobby-actions Customize button + Controls
   reference <details> went with the lobby actions row. */

/* ─────────────────────────────────────────────────────────────────────
   Post-run summary card
   ─────────────────────────────────────────────────────────────────────
   Replaces the old single-line 'Height: 200m in 18.3s. Press Enter...'
   message with a richer screen showing rank, PB delta, and a
   leaderboard preview. Visible only when .overlay carries the
   .overlay-show-postrun class. */

.postrun-card,
.postrun-actions {
  display: none;
}

.overlay.overlay-show-postrun #postrun-card {
  display: block;
  margin: 0.85rem 0 0.5rem;
  padding: 0.85rem 1rem 0.9rem;
  border-radius: 0.75rem;
  background: rgba(7, 32, 58, 0.06);
  border: 1px solid rgba(7, 32, 58, 0.14);
  text-align: left;
}

.overlay.overlay-show-postrun #postrun-actions {
  display: flex;
  gap: 0.7rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 0.7rem;
}

.postrun-card__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid rgba(7, 32, 58, 0.12);
}

.postrun-card__eyebrow {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(7, 32, 58, 0.7);
  font-weight: 700;
}

.postrun-card__eyebrow.is-summit {
  color: #1c8a4a;
}

.postrun-card__eyebrow.is-fail {
  color: #b04a2c;
}

.postrun-card__hero {
  display: inline-flex;
  align-items: baseline;
  gap: 0.55rem;
}

.postrun-card__time {
  font-size: 1.45rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-on-light);
}

/* Delta pill next to the hero time — replaces the old in-row delta. */
.postrun-card__delta {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
  background: rgba(7, 32, 58, 0.08);
  color: rgba(7, 32, 58, 0.7);
}
.postrun-card__delta.is-pb {
  background: rgba(28, 138, 74, 0.18);
  color: #146a37;
}
.postrun-card__delta.is-slower {
  background: rgba(176, 74, 44, 0.16);
  color: #8b3919;
}

/* Flawless badge — shown next to the Deaths row when count == 0. */
.postrun-flawless {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 0.14rem 0.45rem;
  border-radius: 999px;
  background: rgba(255, 207, 102, 0.22);
  color: #8a5a06;
}

.postrun-card__rows {
  display: grid;
  gap: 0.4rem;
  margin: 0.55rem 0 0.7rem;
}

/* Placement banner — shown for CLEAN summit-completing runs once the
   /run/submit response (or /placement/today fallback) returns the
   placement block. The bright yellow band differentiates this from
   the neutral row stack so it reads as a celebration of the result,
   not as another data row. */
.postrun-placement {
  margin: 0.6rem 0 0.55rem;
  padding: 0.6rem 0.75rem;
  border-radius: 0.7rem;
  background: linear-gradient(
    180deg,
    rgba(255, 222, 122, 0.28) 0%,
    rgba(255, 183, 3, 0.12) 100%
  );
  border: 1px solid rgba(255, 183, 3, 0.55);
  text-align: left;
}

.postrun-placement[hidden] {
  display: none;
}

.postrun-placement__headline {
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--color-text-on-light, #1a2740);
  line-height: 1.3;
}

.postrun-placement__sub {
  font-size: 0.78rem;
  opacity: 0.7;
  margin-top: 0.2rem;
  color: var(--color-text-on-light, #1a2740);
}

.postrun-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: 0.6rem;
  font-size: 0.9rem;
}

.postrun-row > span:first-child {
  color: rgba(7, 32, 58, 0.62);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.postrun-row > strong {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--color-text-on-light);
}

.postrun-delta {
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  padding: 0.05rem 0.45rem;
  border-radius: 999px;
  background: rgba(7, 32, 58, 0.08);
  color: rgba(7, 32, 58, 0.72);
}

.postrun-delta.is-pb {
  background: rgba(28, 138, 74, 0.16);
  color: #1c8a4a;
}

.postrun-delta.is-slower {
  background: rgba(176, 74, 44, 0.14);
  color: #b04a2c;
}

.postrun-card__leaderboard {
  margin-top: 0.5rem;
  padding-top: 0.6rem;
  border-top: 1px dashed rgba(7, 32, 58, 0.14);
}

.postrun-leaderboard__title {
  display: block;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(7, 32, 58, 0.58);
  font-weight: 700;
  margin-bottom: 0.35rem;
}

.postrun-leaderboard__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.18rem;
  font-variant-numeric: tabular-nums;
}

.postrun-leaderboard__list li {
  display: grid;
  grid-template-columns: 2.4rem 1fr auto;
  gap: 0.5rem;
  font-size: 0.86rem;
  padding: 0.18rem 0.4rem;
  border-radius: 0.4rem;
  color: rgba(7, 32, 58, 0.86);
}

.postrun-leaderboard__list li.is-me {
  background: rgba(255, 183, 3, 0.18);
  font-weight: 700;
  color: var(--color-text-on-light);
}

/* Practice stats rows render as label/value pairs (Today's avg / 24.6s),
   not the daily's rank/name/time triple. Override the parent's 3-column
   grid so the label spans all leftover width and the value hugs right. */
.postrun-leaderboard__list li.postrun-leaderboard__row {
  grid-template-columns: 1fr auto;
}

/* Practice post-run card cleanup. The eyebrow + hero time row is
   redundant with the "Run Time" stat below, the rows slot is empty
   (Best today + Deaths are ranked-only and hidden for practice),
   and the dashed divider above the leaderboard becomes the only
   visual element in an otherwise quiet card — remove all three so
   only Sky Sprint / date subtitle / TODAY'S PRACTICE / 5 rows show. */
.postrun-card.is-practice .postrun-card__header,
.postrun-card.is-practice .postrun-card__rows,
.postrun-card.is-practice .postrun-leaderboard__title {
  display: none;
}
.postrun-card.is-practice .postrun-card__leaderboard {
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}

/* Practice stats numbers + labels — 75% larger than the default
   (~0.86rem → 1.5rem) so the result reads at a glance instead of
   blending into the surrounding chrome. */
.postrun-card.is-practice .postrun-leaderboard__list li.postrun-leaderboard__row {
  font-size: 1.5rem;
  padding: 0.32rem 0.55rem;
}

.postrun-leaderboard__list li.postrun-leaderboard__loading,
.postrun-leaderboard__list li.postrun-leaderboard__empty {
  display: block;
  font-style: italic;
  color: rgba(7, 32, 58, 0.5);
  text-align: center;
}

.postrun-secondary {
  appearance: none;
  margin: 0;
  padding: 0.4rem 0.95rem;
  border-radius: 999px;
  border: 1px solid rgba(7, 32, 58, 0.22);
  background: transparent;
  color: var(--color-text-on-light);
  font: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 110ms ease, transform 110ms ease;
}

.postrun-secondary:hover {
  background: rgba(7, 32, 58, 0.08);
  transform: translateY(-1px);
}

/* Customize sub-form (hidden until 'Customize character' is clicked).
   Two gates: only render when (1) the overlay is in lobby mode, AND
   (2) the form's [hidden] attribute is not set. The [hidden] attribute
   is the click-toggle; the parent class is the lobby-vs-postrun gate. */

.lobby-config {
  display: none;
  gap: 0.45rem;
  text-align: left;
  margin-top: 0.6rem;
  padding-top: 0.7rem;
  border-top: 1px solid rgba(7, 32, 58, 0.15);
}

/* Quick-customize modal — when #lobby-config is shown via the landing
   pencil button, display it as a small floating panel anchored to the
   left edge of the viewport so it doesn't overlap the character canvas.
   Vertically centred via translateY(-50%); horizontally pinned to
   left: 1.5rem. z-index 95 sits above .landing (24) and the legacy
   dashboard (90).
   The .landing__submenu-body .lobby-config rule later in this file has
   equal specificity but comes after, so it still wins for the legacy
   adopted-into-submenu path (defensive — that path is unreachable
   from the landing as of PR #5). */
.lobby-config:not([hidden]) {
  display: grid;
  position: fixed;
  top: 50%;
  left: 1.5rem;
  transform: translateY(-50%);
  z-index: 95;
  background: var(--color-surface-light);
  color: var(--color-text-on-light);
  padding: 1.6rem 1.6rem 1.2rem;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  min-width: 18rem;
  max-width: min(20rem, calc(100vw - 3rem));
  border-top: 0;
  margin-top: 0;
}

/* (Retired: the .overlay.overlay-show-lobby .lobby-config side-
   popover rules. The customize form now renders inline inside the
   landing Profile submenu, not as an overlay side-popover.) */

.lobby-config__close {
  position: absolute;
  top: 0.4rem;
  right: 0.5rem;
  width: 1.6rem;
  height: 1.6rem;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: rgba(7, 32, 58, 0.6);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

.lobby-config__close:hover {
  background: rgba(7, 32, 58, 0.08);
  color: rgba(7, 32, 58, 0.9);
}

.lobby-config__heading {
  margin: 0 0 0.1rem;
  font-size: 0.84rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(7, 32, 58, 0.78);
  font-weight: 700;
}

.lobby-config label {
  display: grid;
  gap: 0.18rem;
  font-size: 0.78rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: rgba(7, 32, 58, 0.86);
}

.lobby-config .dev-toggle {
  display: flex;
  align-items: center;
  gap: 0.42rem;
  text-transform: none;
  font-size: 0.82rem;
  letter-spacing: 0;
}

.lobby-config input[type="text"],
.lobby-config select {
  width: 100%;
  border-radius: 0.45rem;
  border: 1px solid rgba(7, 32, 58, 0.2);
  padding: 0.34rem 0.42rem;
  background: #f9fcff;
  color: var(--color-text-on-light);
  font-size: 0.86rem;
}

/* 6×6 character-colour grid replacing the old hex-code <select>.
   Each swatch is a small square with the colour as its background;
   the currently-selected one gets a darker ring + slight scale.
   Selectors are deliberately scoped under .character-color-grid so
   they out-specificity the broader '.panel button' rule that
   styles the lobby's Start Run button (gold gradient, 999px radius,
   9rem min-width) — without this, every swatch inherits that style
   and renders as a yellow circle. */

.character-color-grid {
  display: grid;
  /* Fixed swatch size keeps the grid compact regardless of where it
     renders (inline below the lobby OR in the side popover). 22px
     squares with a 4px gap gives a 6-column grid that's about 152px
     wide — fits the 16rem popover and stays readable on the inline
     fallback too. */
  grid-template-columns: repeat(6, 22px);
  justify-content: center;
  gap: 4px;
  padding: 0.32rem;
  border-radius: var(--radius-md);
  border: 1px solid rgba(7, 32, 58, 0.18);
  background: rgba(7, 32, 58, 0.04);
  margin-top: 0.18rem;
}

.character-color-grid .character-color-swatch {
  appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  /* Reset .panel button's font + sizing so the swatch is purely the
     coloured square it claims to be. */
  min-width: 0;
  font: inherit;
  width: 22px;
  height: 22px;
  border-radius: 5px;
  background: var(--swatch);
  cursor: pointer;
  transition: transform var(--motion-fast),
              box-shadow var(--motion-fast),
              outline-offset var(--motion-fast);
  outline: 0 solid transparent;
  outline-offset: 0;
  position: relative;
}

.character-color-grid .character-color-swatch:hover {
  /* Reset .panel button's translateY(-1px) scale(1.01) hover so we
     get the swatch-specific scale instead of the button's. */
  transform: scale(1.08);
  box-shadow: 0 2px 8px rgba(7, 32, 58, 0.3);
  filter: none;
}

.character-color-grid .character-color-swatch:active {
  /* .panel button:active has translateY(0) scale(0.99); make the
     swatch press feel proportional. */
  transform: scale(1.02);
}

.character-color-grid .character-color-swatch:focus-visible {
  outline: 2px solid var(--color-info);
  outline-offset: 2px;
}

.character-color-grid .character-color-swatch.is-selected {
  outline: 3px solid var(--color-text-on-light);
  outline-offset: 2px;
  transform: scale(1.05);
  box-shadow: 0 3px 10px rgba(7, 32, 58, 0.4);
}

/* A small ✓ badge on the selected swatch so colour-blind users have a
   non-colour cue. Drawn with ::after rather than an extra DOM node. */
.character-color-grid .character-color-swatch.is-selected::after {
  content: "✓";
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 0.7rem;
  line-height: 1;
  color: rgba(0, 0, 0, 0.78);
  text-shadow: 0 1px 2px rgba(255, 255, 255, 0.5);
  pointer-events: none;
}

.panel button {
  margin-top: 1rem;
  padding: 0.56rem 1.2rem;
  min-width: 9rem;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(110deg, var(--color-primary) 0%, var(--color-primary-strong) 85%);
  color: #202738;
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: transform 110ms ease, filter 110ms ease;
}

.panel button:hover {
  transform: translateY(-1px) scale(1.01);
  filter: saturate(1.06);
}

.panel button:active {
  transform: translateY(0) scale(0.99);
}

.panel button:disabled {
  cursor: wait;
  opacity: 0.62;
  filter: saturate(0.75);
  transform: none;
}

@keyframes panel-in {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes hud-reveal {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------------------------------------------------------------------------
 * Sky Sprint dashboard — Profile / Leaderboard / Account
 * Injected by sky-sprint-api.js. Styled here so designers can tweak it
 * without touching the adapter.
 * ---------------------------------------------------------------------------*/

/* ─────────────────────────────────────────────────────────────────────
   Identity widget (top-right)
   ─────────────────────────────────────────────────────────────────────
   Single button that doubles as the menu opener and the role indicator.
   Signed-out: shows 'Sign in' with a placeholder dot avatar.
   Signed-in:  shows initials avatar + username + role badge if admin.
   Click → opens the dashboard. */

.sky-sprint-identity {
  position: fixed;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.4rem 0.7rem 0.4rem 0.5rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(7, 32, 58, 0.82);
  color: #f3f8fc;
  font-family: inherit;
  font-size: 0.84rem;
  letter-spacing: 0.02em;
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: transform 120ms ease, background 120ms ease,
              border-color 120ms ease;
}

.sky-sprint-identity:hover {
  background: rgba(7, 32, 58, 0.94);
  transform: translateY(-1px);
}

.sky-sprint-identity:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.sky-sprint-identity__avatar {
  flex: 0 0 auto;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.18);
  font-variant: small-caps;
}

.sky-sprint-identity.is-authed .sky-sprint-identity__avatar {
  background: linear-gradient(135deg, rgba(255, 183, 3, 0.85), rgba(255, 218, 102, 0.7));
  color: #0d1d33;
  border-color: rgba(255, 183, 3, 0.6);
}

.sky-sprint-identity.is-admin .sky-sprint-identity__avatar {
  background: linear-gradient(135deg, #b388ff, #d6a8ff);
  color: #1f0a3a;
  border-color: rgba(179, 136, 255, 0.7);
}

.sky-sprint-identity__main {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.04rem;
  line-height: 1;
}

.sky-sprint-identity__name {
  font-weight: 600;
  font-size: 0.84rem;
}

.sky-sprint-identity__role {
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 700;
  padding: 0.05rem 0.32rem;
  border-radius: 999px;
  background: rgba(179, 136, 255, 0.22);
  color: #d6a8ff;
  border: 1px solid rgba(179, 136, 255, 0.4);
}

/* Tiny green indicator dot that appears in the corner of the avatar
   when the API client has a live access token. Replaces the old
   'API Connected · ...' pill at bottom-left. */
.sky-sprint-identity::before {
  content: "";
  position: absolute;
  top: 0.35rem;
  right: 0.45rem;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.25);
  box-shadow: 0 0 0 1px rgba(7, 32, 58, 0.6);
  transition: background 160ms ease;
}

.sky-sprint-identity.is-online::before {
  background: #4ade80;
}

.sky-sprint-dashboard {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: none;
  background: linear-gradient(160deg, rgba(7, 32, 58, 0.78), rgba(10, 38, 63, 0.92));
  backdrop-filter: blur(6px);
  color: var(--color-text);
  padding: 1.2rem;
}

.sky-sprint-dashboard.is-open {
  display: block;
}

/* The dashboard "header" used to be a banner spanning the full
   width (tabs left, dev-pill + Close right). Tabs are retired and
   the new look is a chromeless surface with a floating Close in
   the top-right corner. Strip the banner styling and float the
   header so it sits over the body content without a divider line. */
.sky-sprint-dashboard__header {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
  padding: 0;
  border: 0;
}

.sky-sprint-dashboard__tabs {
  display: flex;
  gap: 0.4rem;
}

.sky-sprint-dashboard__tab {
  padding: 0.38rem 0.9rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.sky-sprint-dashboard__tab.is-active {
  background: var(--color-primary);
  color: #202738;
  border-color: var(--color-primary);
}

.sky-sprint-dashboard__header-actions {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.sky-sprint-dashboard__close {
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  color: inherit;
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 1;
  cursor: pointer;
  padding: 0.42rem 0.85rem 0.42rem 0.6rem;
  transition: background var(--motion-fast), transform var(--motion-fast),
              border-color var(--motion-fast);
}

.sky-sprint-dashboard__close:hover {
  background: rgba(255, 255, 255, 0.16);
  border-color: var(--color-border-strong);
  transform: translateY(-1px);
}

.sky-sprint-dashboard__close > span[aria-hidden="true"] {
  font-size: 1.05rem;
  line-height: 1;
}

.sky-sprint-dashboard__body {
  overflow: auto;
  max-width: 52rem;
  width: 100%;
  /* Reserve room at the top so the floating Close button (top:
     0.75rem) doesn't visually crash into the first card. Width is
     centered horizontally. */
  margin: 1.5rem auto 0;
  /* Body has the full dashboard height available now that the
     header is absolutely positioned and out of normal flow. */
  height: calc(100% - 1.5rem);
}

.sky-sprint-dashboard__view {
  display: none;
}

.sky-sprint-dashboard__view.is-active {
  display: block;
}

.sky-sprint-card {
  background: rgba(243, 248, 252, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 0.9rem;
  padding: 0.9rem 1rem;
  margin-bottom: 0.8rem;
}

/* Session-expired banner — appears once above the Account tab's
   sign-in form when the player got auto-redirected after their
   refresh token expired. Distinct amber accent so it reads as an
   informational status rather than a regular content card. */
.sky-sprint-card.sky-sprint-session-expired {
  background: rgba(255, 183, 3, 0.16);
  border-color: rgba(255, 183, 3, 0.55);
  color: rgba(255, 234, 178, 0.95);
  font-size: 0.86rem;
  line-height: 1.4;
}

.sky-sprint-card.sky-sprint-session-expired strong {
  display: block;
  margin-bottom: 0.15rem;
  color: rgb(255, 222, 122);
}

.sky-sprint-card h2 {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-primary);
}

.sky-sprint-card dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.3rem 0.8rem;
  margin: 0;
  font-size: 0.9rem;
}

.sky-sprint-card dt {
  opacity: 0.72;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.74rem;
}

.sky-sprint-card dd {
  margin: 0;
  word-break: break-all;
}

.sky-sprint-leaderboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.sky-sprint-leaderboard-table th,
.sky-sprint-leaderboard-table td {
  padding: 0.38rem 0.55rem;
  text-align: left;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.sky-sprint-leaderboard-table th {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 0.72rem;
  opacity: 0.72;
}

.sky-sprint-leaderboard-table tr.is-me {
  background: rgba(255, 183, 3, 0.12);
}

/* Leaderboard username -> public-profile click-through. Styled as a
   text link so it reads like data, not a chunky button — but still
   a real <button> for keyboard accessibility. */
.sky-sprint-username-link {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 2px;
}

.sky-sprint-username-link:hover {
  text-decoration: underline solid;
  color: var(--color-info, #4fa3ff);
}

.sky-sprint-username-link:focus-visible {
  outline: 2px solid var(--color-info, #4fa3ff);
  outline-offset: 2px;
  border-radius: 2px;
}

.sky-sprint-segmented {
  display: inline-flex;
  gap: 0.4rem;
}

.sky-sprint-seg {
  appearance: none;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(7, 32, 58, 0.5);
  color: inherit;
  font: inherit;
  font-size: 0.82rem;
  padding: 0.28rem 0.7rem;
  border-radius: 999px;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
}

.sky-sprint-seg:hover {
  border-color: rgba(255, 255, 255, 0.4);
}

.sky-sprint-seg.is-active {
  background: rgba(255, 183, 3, 0.18);
  border-color: rgba(255, 183, 3, 0.55);
}

/* B8 Track 3 — disabled scope buttons (All teams / Your team are
   blocked on B6 Groups). Greyed out, no hover effect, no pointer.
   The browser-native [disabled] attribute also handles input — we
   add visual cues on top. */
.sky-sprint-seg.is-disabled,
.sky-sprint-seg[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
  background: transparent;
}

.sky-sprint-seg.is-disabled:hover,
.sky-sprint-seg[disabled]:hover {
  background: transparent;
  border-color: var(--color-border);
}

.sky-sprint-segmented--scope {
  margin-top: 0;
}

/* ── B9 Track 1 — Leaderboard chrome polish ───────────────────────────
   Bigger heading, connected segmented controls, and a collapsible
   snapshot-meta disclosure. All segmented styling is scoped to
   .sky-sprint-segctl (added only to the leaderboard's category / mode /
   scope toggle groups) so the shared .sky-sprint-seg look used elsewhere
   — e.g. the Levels library "All / Mine" toggle — is left untouched. */

/* Confident heading — overrides the quiet global .sky-sprint-card h2 for
   the leaderboard surface only, with a short gradient accent bar. */
.sky-sprint-leaderboard-card h2 {
  display: inline-flex;
  flex-direction: column;
  gap: 0.32rem;
  margin: 0 0 0.7rem;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-primary-strong);
}

.sky-sprint-leaderboard-card h2::after {
  content: "";
  width: 2.4rem;
  height: 3px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--color-primary), var(--color-primary-strong));
}

/* Connected segmented control — a pill "track" wrapping its buttons,
   with the active button rendered as a filled amber thumb. */
.sky-sprint-segctl {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  margin: 0 0.5rem 0.7rem 0;
  padding: 0.2rem;
  background: rgba(7, 32, 58, 0.55);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  vertical-align: middle;
}

.sky-sprint-segctl .sky-sprint-seg {
  border: 0;
  background: transparent;
  border-radius: var(--radius-pill);
  padding: 0.32rem 0.9rem;
  font-size: 0.82rem;
  color: var(--color-text-muted);
  transition: color var(--motion-fast), background var(--motion-fast);
}

.sky-sprint-segctl .sky-sprint-seg:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: transparent;
  color: var(--color-text);
}

.sky-sprint-segctl .sky-sprint-seg.is-active {
  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-strong));
  color: var(--color-surface-deep);
  font-weight: 700;
  border-color: transparent;
  box-shadow: var(--shadow-sm);
}

.sky-sprint-segctl .sky-sprint-seg.is-disabled,
.sky-sprint-segctl .sky-sprint-seg[disabled] {
  background: transparent;
  color: var(--color-text-subtle);
  opacity: 0.5;
}

.sky-sprint-segctl .sky-sprint-seg.is-disabled:hover,
.sky-sprint-segctl .sky-sprint-seg[disabled]:hover {
  background: transparent;
  color: var(--color-text-subtle);
}

/* Always-on context line under the toggles. */
.sky-sprint-lb-subtitle {
  margin: 0 0 0.55rem;
  font-size: 0.82rem;
  opacity: 0.72;
}

/* Snapshot id + created timestamp — collapsed by default behind a
   small uppercase disclosure with a rotating caret. */
.sky-sprint-lb-snapmeta {
  margin: 0 0 0.7rem;
  color: var(--color-text-muted);
}

.sky-sprint-lb-snapmeta__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  width: max-content;
  padding: 0.1rem 0;
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0.7;
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition: opacity var(--motion-fast);
}

.sky-sprint-lb-snapmeta__toggle:hover {
  opacity: 1;
}

.sky-sprint-lb-snapmeta__toggle::-webkit-details-marker {
  display: none;
}

.sky-sprint-lb-snapmeta__toggle::before {
  content: "\25B8"; /* ▸ */
  font-size: 0.62rem;
  transition: transform var(--motion-fast);
}

.sky-sprint-lb-snapmeta[open] .sky-sprint-lb-snapmeta__toggle::before {
  transform: rotate(90deg);
}

.sky-sprint-lb-snapmeta__body {
  margin-top: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  opacity: 0.7;
}

/* Friendly empty state — a summit illustration + inviting copy + a CTA
   that launches today's run, in place of the old bare placeholder row. */
.sky-sprint-lb-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  padding: 1.7rem 1rem 1.3rem;
  text-align: center;
}

.sky-sprint-lb-empty__art {
  margin-bottom: 0.25rem;
}

.sky-sprint-lb-empty__title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-text);
}

.sky-sprint-lb-empty__sub {
  margin: 0;
  max-width: 24rem;
  font-size: 0.86rem;
  opacity: 0.7;
}

.sky-sprint-lb-empty__cta {
  appearance: none;
  margin-top: 0.65rem;
  padding: 0.5rem 1.4rem;
  border: 0;
  border-radius: var(--radius-pill);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-surface-deep);
  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-strong));
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: transform var(--motion-fast), box-shadow var(--motion-fast),
    filter var(--motion-fast);
}

.sky-sprint-lb-empty__cta:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
  filter: brightness(1.04);
}

.sky-sprint-lb-empty__cta:active {
  transform: translateY(0);
}

.sky-sprint-lb-empty__cta:focus-visible {
  outline: 2px solid var(--color-info);
  outline-offset: 2px;
}

/* ── B9 Track 1 — populated-row polish ────────────────────────────────
   Alternating banding, row hover, a stronger "you" row, and metallic
   top-3 rank medals. The .is-me rules sit AFTER the banding rule (equal
   specificity) so the "you" highlight wins on even rows too. */
.sky-sprint-leaderboard-table tbody tr {
  transition: background var(--motion-fast);
}

.sky-sprint-leaderboard-table tbody tr:nth-child(even) {
  background: rgba(255, 255, 255, 0.03);
}

.sky-sprint-leaderboard-table tbody tr:hover {
  background: rgba(255, 255, 255, 0.07);
}

.sky-sprint-leaderboard-table tbody tr.is-me {
  background: rgba(255, 183, 3, 0.16);
}

.sky-sprint-leaderboard-table tbody tr.is-me:hover {
  background: rgba(255, 183, 3, 0.22);
}

/* Left accent bar on the "you" row, drawn on the first cell so it reads
   as a continuous marker down the row's leading edge. */
.sky-sprint-leaderboard-table tbody tr.is-me td:first-child {
  box-shadow: inset 3px 0 0 var(--color-primary);
}

/* Metallic rank medals for the top 3. */
.sky-sprint-rank-cell {
  white-space: nowrap;
}

.sky-sprint-rank-medal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-surface-deep);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.5), var(--shadow-sm);
}

.sky-sprint-rank-medal--1 {
  background: linear-gradient(145deg, #ffe08a, #f5b301); /* gold */
}

.sky-sprint-rank-medal--2 {
  background: linear-gradient(145deg, #eef2f6, #aeb8c4); /* silver */
}

.sky-sprint-rank-medal--3 {
  background: linear-gradient(145deg, #f0b486, #c77b41); /* bronze */
}

/* ── B9 Track 2 — Levels (Library) chrome ─────────────────────────────
   Heading lift to match the leaderboard, plus the filters bar de-inlined
   into real CSS and made sticky. The search/tag/difficulty inputs become
   chip filters in a later PR; the container, source toggle (segctl) and
   refresh styling carry over. */
.sky-sprint-library-card h2 {
  display: inline-flex;
  flex-direction: column;
  gap: 0.32rem;
  margin: 0 0 0.7rem;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-primary-strong);
}

.sky-sprint-library-card h2::after {
  content: "";
  width: 2.4rem;
  height: 3px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--color-primary), var(--color-primary-strong));
}

.sky-sprint-library-subtitle {
  margin: 0 0 0.6rem;
  font-size: 0.82rem;
  opacity: 0.72;
}

/* Sticky filter bar — sticks to the top of the scrolling dashboard body
   so the filters stay reachable while browsing a long catalogue. */
.sky-sprint-library-filters {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.65rem;
  padding: 0.6rem 0.7rem;
  background: rgba(7, 32, 58, 0.62);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  backdrop-filter: blur(4px);
}

.sky-sprint-library-source.sky-sprint-segctl {
  margin: 0;
  align-self: flex-start;
}

/* B9 Track 2 (PR 3) — chip-based filters. A label + a wrap-row of chips
   per facet (difficulty, tags); the source toggle + search input sit
   above. The Refresh button self-aligns to the start. */
.sky-sprint-filter-group {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.sky-sprint-filter-group__label {
  flex: 0 0 auto;
  min-width: 4.5rem;
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.6;
}

.sky-sprint-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.sky-sprint-chip {
  appearance: none;
  font: inherit;
  font-size: 0.76rem;
  padding: 0.22rem 0.7rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border);
  background: rgba(7, 32, 58, 0.5);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--motion-fast), background var(--motion-fast),
    border-color var(--motion-fast);
}

.sky-sprint-chip:hover {
  border-color: var(--color-border-strong);
  color: var(--color-text);
}

.sky-sprint-chip.is-active {
  background: linear-gradient(135deg, var(--color-primary), var(--color-primary-strong));
  border-color: transparent;
  color: var(--color-surface-deep);
  font-weight: 700;
}

.sky-sprint-chip:focus-visible {
  outline: 2px solid var(--color-info);
  outline-offset: 2px;
}

.sky-sprint-field--search {
  max-width: 24rem;
}

/* Friendly empty / no-results state for the library results region. */
.sky-sprint-lib-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 1.8rem 1rem 1.4rem;
  text-align: center;
}

.sky-sprint-lib-empty__art {
  margin-bottom: 0.2rem;
}

.sky-sprint-lib-empty__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-text);
}

.sky-sprint-lib-empty__sub {
  margin: 0;
  max-width: 26rem;
  font-size: 0.84rem;
  opacity: 0.7;
}

.sky-sprint-lib-empty__cta {
  appearance: none;
  margin-top: 0.6rem;
  padding: 0.45rem 1.2rem;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  color: inherit;
  background: rgba(255, 255, 255, 0.06);
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast);
}

.sky-sprint-lib-empty__cta:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: var(--color-primary);
}

.sky-sprint-lib-empty__cta:focus-visible {
  outline: 2px solid var(--color-info);
  outline-offset: 2px;
}

.sky-sprint-field {
  display: grid;
  gap: 0.18rem;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.72;
}

.sky-sprint-field__input {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.32rem 0.42rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: rgba(7, 32, 58, 0.5);
  color: inherit;
}

.sky-sprint-field__input:focus-visible {
  outline: 2px solid var(--color-info);
  outline-offset: 1px;
}

.sky-sprint-lib-refresh {
  align-self: flex-start;
  font: inherit;
  font-size: 0.82rem;
  padding: 0.42rem 0.85rem;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(79, 163, 255, 0.45);
  background: rgba(79, 163, 255, 0.18);
  color: inherit;
  cursor: pointer;
  transition: background var(--motion-fast);
}

.sky-sprint-lib-refresh:hover {
  background: rgba(79, 163, 255, 0.28);
}

/* ── B9 Track 2 (PR 2) — card grid + per-level cards ──────────────────
   Default view is a responsive card grid; "list" keeps the table. Each
   card carries a deterministic peak motif (no real geometry exists
   client-side) tinted by difficulty band. */
.sky-sprint-library-view {
  margin: 0 0 0.7rem;
}

.sky-sprint-level-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 0.7rem;
}

.sky-sprint-level-card {
  display: flex;
  flex-direction: column;
  text-align: left;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: rgba(243, 248, 252, 0.04);
  color: inherit;
  transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}

.sky-sprint-level-card:hover {
  border-color: var(--color-border-strong);
  box-shadow: var(--shadow-md);
}

/* B13 A2 — card actions row (Info / Play / Edit). */
.sky-sprint-level-card__actions {
  display: flex;
  gap: 0.3rem;
  margin-top: auto;
  padding: 0.4rem 0.5rem 0.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.sky-sprint-level-card__act {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: rgba(7, 32, 58, 0.5);
  color: var(--color-text);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast),
    opacity var(--motion-fast);
}

.sky-sprint-level-card__act:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--color-border-strong);
}

.sky-sprint-level-card__act:focus-visible {
  outline: 2px solid var(--color-info);
  outline-offset: 2px;
}

.sky-sprint-level-card__act[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

/* B13 A3 — admin lifecycle panel inside the (light-themed) level info
   modal. Dark-on-light to read against the modal card. */
.sky-sprint-detail-admin {
  margin-top: 1rem;
  padding-top: 0.8rem;
  border-top: 1px solid rgba(7, 32, 58, 0.14);
}

.sky-sprint-detail-admin__badges {
  margin-bottom: 0.5rem;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.6;
}

.sky-sprint-detail-admin__lock {
  display: flex;
  gap: 0.4rem;
  margin-bottom: 0.5rem;
}

.sky-sprint-detail-admin__date {
  font: inherit;
  font-size: 0.82rem;
  padding: 0.3rem 0.4rem;
  border: 1px solid rgba(7, 32, 58, 0.25);
  border-radius: 0.4rem;
  background: #fff;
  color: inherit;
}

.sky-sprint-detail-admin__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.sky-sprint-detail-admin__btn {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.32rem 0.7rem;
  border: 1px solid rgba(7, 32, 58, 0.25);
  border-radius: 0.45rem;
  background: rgba(7, 32, 58, 0.05);
  color: inherit;
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast);
}

.sky-sprint-detail-admin__btn:hover {
  background: rgba(7, 32, 58, 0.12);
  border-color: rgba(7, 32, 58, 0.4);
}

.sky-sprint-detail-admin__btn--danger {
  color: #b3261e;
  border-color: rgba(179, 38, 30, 0.4);
}

.sky-sprint-detail-admin__btn--danger:hover {
  background: rgba(179, 38, 30, 0.1);
}

.sky-sprint-detail-admin .sky-sprint-admin-status-msg {
  margin: 0.5rem 0 0;
  min-height: 1rem;
  font-size: 0.78rem;
}

.sky-sprint-detail-admin .sky-sprint-admin-status-msg.is-good {
  color: #1a7f37;
}

.sky-sprint-detail-admin .sky-sprint-admin-status-msg.is-error {
  color: #b3261e;
}

.sky-sprint-level-card__art {
  position: relative;
  display: block;
  height: 76px;
  overflow: hidden;
  background: linear-gradient(160deg, rgba(26, 92, 126, 0.45), rgba(7, 32, 58, 0.6));
}

.sky-sprint-level-card__motif {
  display: block;
  width: 100%;
  height: 100%;
}

.sky-sprint-level-card__pin {
  position: absolute;
  top: 5px;
  right: 7px;
  font-size: 0.8rem;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
}

/* B21 M3 refinement — seed-origin dice pill in the top-left of the
   card art. Doesn't collide with the pin (top-right) or the
   difficulty band (bottom-left). */
.sky-sprint-level-card__origin {
  position: absolute;
  top: 5px;
  left: 7px;
  padding: 0.05rem 0.35rem;
  font-size: 0.7rem;
  border-radius: 999px;
  background: rgba(255, 218, 102, 0.24);
  border: 1px solid rgba(255, 218, 102, 0.55);
  color: rgba(255, 234, 178, 1);
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
  cursor: help;
}

.sky-sprint-level-card__diff {
  position: absolute;
  bottom: 6px;
  left: 7px;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-pill);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--diff-tint, var(--color-text));
  background: rgba(7, 32, 58, 0.72);
  border: 1px solid var(--diff-tint, var(--color-border));
}

.sky-sprint-level-card__body {
  display: flex;
  flex-direction: column;
  gap: 0.18rem;
  padding: 0.5rem 0.6rem 0.6rem;
}

.sky-sprint-level-card__title {
  font-size: 0.92rem;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sky-sprint-level-card__author {
  font-size: 0.72rem;
  opacity: 0.62;
}

.sky-sprint-level-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem;
  margin: 0.1rem 0;
  min-height: 0.2rem;
}

.sky-sprint-level-card__tag {
  font-size: 0.66rem;
  padding: 0.06rem 0.32rem;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.08);
}

.sky-sprint-level-card__meta {
  font-size: 0.68rem;
  opacity: 0.6;
  font-variant-numeric: tabular-nums;
}

/* Auth switcher — wraps the sign-in + create-account cards so only
   one is shown at a time. Centered in the available width with a
   card just wider than the input fields it contains. */
.sky-sprint-auth-switch {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  margin: 1.5rem 0;
}

.sky-sprint-auth-card {
  /* Just wide enough to host the inputs (max-width: 22rem on the
     form) plus the card's own padding. */
  width: 100%;
  max-width: 26rem;
  text-align: center;
}

/* Show only the card whose data-auth-card matches the parent switch's
   data-auth-mode. Generalised past signin/register so the "forgot"
   card (B18 T1) — and any future auth surface — participates without
   extending the selector list. */
.sky-sprint-auth-switch[data-auth-mode="signin"]
  .sky-sprint-auth-card:not([data-auth-card="signin"]),
.sky-sprint-auth-switch[data-auth-mode="register"]
  .sky-sprint-auth-card:not([data-auth-card="register"]),
.sky-sprint-auth-switch[data-auth-mode="forgot"]
  .sky-sprint-auth-card:not([data-auth-card="forgot"]) {
  display: none;
}

.sky-sprint-auth-card h2 {
  text-align: center;
}

.sky-sprint-auth-form {
  display: grid;
  gap: 0.55rem;
  max-width: 22rem;
  /* Centered within the auth card so labels + inputs sit in the
     visual middle even when the card has a bit of extra padding. */
  margin: 0 auto;
  text-align: left;
}

.sky-sprint-auth-form label {
  display: grid;
  gap: 0.22rem;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.82;
}

.sky-sprint-auth-form input {
  width: 100%;
  padding: 0.42rem 0.55rem;
  border-radius: 0.45rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(7, 32, 58, 0.5);
  color: inherit;
  font-size: 0.9rem;
}

.sky-sprint-auth-form button {
  padding: 0.55rem;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(110deg, var(--color-primary) 0%, var(--color-primary-strong) 85%);
  color: #202738;
  font-weight: 700;
  cursor: pointer;
  font-size: 0.9rem;
}

.sky-sprint-auth-form .secondary {
  background: transparent;
  color: var(--color-text);
  border: 1px solid rgba(255, 255, 255, 0.3);
}

.sky-sprint-auth-form__error {
  min-height: 1.1rem;
  font-size: 0.8rem;
  color: #ff9a8a;
}

.sky-sprint-empty {
  padding: 1rem;
  text-align: center;
  opacity: 0.7;
  font-style: italic;
}

/* Settings tab — checkbox / slider rows + a controls reference list. */

.sky-sprint-setting {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.42rem 0;
  cursor: pointer;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.sky-sprint-setting:first-of-type {
  border-top: 0;
}

.sky-sprint-setting input[type="checkbox"] {
  margin-top: 0.18rem;
  flex: 0 0 auto;
}

.sky-sprint-setting > span {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  flex: 1 1 auto;
}

.sky-sprint-setting strong {
  font-size: 0.92rem;
  font-weight: 600;
}

.sky-sprint-setting small {
  font-size: 0.76rem;
  opacity: 0.66;
  line-height: 1.32;
}

.sky-sprint-setting--slider {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0.6rem 0.9rem;
}

.sky-sprint-setting--slider > span {
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
}

.sky-sprint-setting--slider input[type="range"] {
  grid-column: 1 / -1;
  width: 100%;
}

.sky-sprint-controls-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.32rem;
  font-size: 0.86rem;
}

.sky-sprint-controls-list kbd {
  display: inline-block;
  margin-right: 0.18rem;
  padding: 0.08rem 0.42rem;
  border-radius: 0.32rem;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(7, 32, 58, 0.4);
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
}

/* ── B9 T4 — Settings polish ──────────────────────────────────────────
   The Settings cards inherit the base .sky-sprint-card frame but layer
   the leaderboard-card visual identity on top: bigger confident
   heading with a small gradient accent bar, an inline monochrome icon
   anchoring each section, tighter row hover state, a value chip for
   the SFX slider, and full-width "action card" CTAs for Report a bug
   / Send feedback. Scoped to .sky-sprint-settings-card so other
   dashboard cards keep their quieter look. */

.sky-sprint-settings-card {
  padding: 1.05rem 1.1rem 0.85rem;
}

.sky-sprint-settings-card__head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0 0 0.75rem;
}

.sky-sprint-settings-card__icon {
  flex: 0 0 auto;
  color: var(--color-primary-strong);
  opacity: 0.92;
}

/* Confident heading — mirrors .sky-sprint-leaderboard-card h2. The
   ::after accent bar is anchored to the head row so it sits directly
   under the icon+title pair regardless of card padding. */
.sky-sprint-settings-card h2 {
  margin: 0;
  font-size: 1.28rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-primary-strong);
  text-transform: none;
  line-height: 1.15;
}

.sky-sprint-settings-card__head::after {
  content: "";
  flex: 0 0 auto;
  margin-left: auto;
  width: 2.4rem;
  height: 3px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--color-primary), var(--color-primary-strong));
  opacity: 0.85;
}

.sky-sprint-settings-card__note {
  margin: 0 0 0.6rem;
  font-size: 0.82rem;
  line-height: 1.4;
  opacity: 0.78;
}

.sky-sprint-settings-card__note:last-child {
  margin-bottom: 0;
}

.sky-sprint-settings-card__note--faint {
  opacity: 0.55;
  font-size: 0.76rem;
}

/* Slider value chip — small pill on the right, filled with the same
   accent gradient so the readout ties visually to the accent bar. */
.sky-sprint-settings-card__value {
  display: inline-block;
  padding: 0.08rem 0.5rem;
  border-radius: var(--radius-pill);
  background: rgba(255, 183, 3, 0.14);
  color: rgb(255, 222, 122);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  opacity: 1;
}

/* Row hover — subtle brightening so rows feel interactive without
   competing with the section heading. Only applied on cards that host
   settings labels (not the action cards). */
.sky-sprint-settings-card .sky-sprint-setting {
  border-radius: 0.5rem;
  padding: 0.5rem 0.55rem;
  margin: 0 -0.55rem;
  transition: background 120ms ease;
}

.sky-sprint-settings-card .sky-sprint-setting:hover {
  background: rgba(255, 255, 255, 0.04);
}

.sky-sprint-settings-card .sky-sprint-setting + .sky-sprint-setting {
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.sky-sprint-settings-card .sky-sprint-setting {
  border-top: 0;
}

/* Controls list — grouping the key chips inside a fixed-width slot on
   the left so the labels line up in a clean column. */
.sky-sprint-controls-list li {
  display: grid;
  grid-template-columns: minmax(9rem, auto) 1fr;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.28rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.sky-sprint-controls-list li:first-child {
  border-top: 0;
}

.sky-sprint-controls-list__keys {
  display: inline-flex;
  align-items: center;
  gap: 0.22rem;
  flex-wrap: wrap;
}

.sky-sprint-controls-list__or {
  opacity: 0.55;
  font-size: 0.74rem;
  letter-spacing: 0.02em;
}

/* Action cards — Report / Feedback. Same frame + heading identity as
   the settings cards, but the note gets a little more room and the
   CTA is a full-width, accent-outlined link that mirrors the
   leaderboard empty-state CTA vibe without the filled gradient (this
   is a secondary action, not a hero). */
.sky-sprint-settings-card--action .sky-sprint-settings-card__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.35rem;
  padding: 0.5rem 1.1rem;
  border: 1px solid rgba(255, 183, 3, 0.55);
  border-radius: var(--radius-pill);
  background: rgba(255, 183, 3, 0.08);
  color: rgb(255, 222, 122);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
}

.sky-sprint-settings-card--action .sky-sprint-settings-card__cta:hover {
  background: rgba(255, 183, 3, 0.16);
  border-color: rgba(255, 183, 3, 0.75);
  transform: translateY(-1px);
}

.sky-sprint-settings-card--action .sky-sprint-settings-card__cta:focus-visible {
  outline: 2px solid rgba(255, 183, 3, 0.6);
  outline-offset: 2px;
}

/* ─────────────────────────────────────────────────────────────────────
   Admin work UI — daily lock workflow + library + reset.
   No longer a separate tab; embedded inside the Dev tab via
   _renderAdminContent. Styles below are reused as-is. */

.sky-sprint-admin-row {
  display: flex;
  align-items: end;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0.5rem 0;
}

.sky-sprint-admin-row label {
  display: grid;
  gap: 0.18rem;
  flex: 1 1 auto;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.78;
}

.sky-sprint-admin-row input[type="date"] {
  font-family: inherit;
  font-size: 0.92rem;
  padding: 0.4rem 0.5rem;
  border-radius: 0.45rem;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(7, 32, 58, 0.5);
  color: var(--color-text);
  color-scheme: dark;
}

.sky-sprint-admin-status {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.32rem 0.85rem;
  margin: 0.3rem 0 0;
  font-size: 0.88rem;
}

.sky-sprint-admin-status dt {
  opacity: 0.66;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.74rem;
  align-self: center;
}

.sky-sprint-admin-status dd {
  margin: 0;
  word-break: break-all;
}

.sky-sprint-admin-status-msg {
  margin: 0.55rem 0 0;
  min-height: 1.1rem;
  font-size: 0.82rem;
  opacity: 0.86;
}

.sky-sprint-admin-status-msg.is-warn {
  color: #f5b34c;
}

.sky-sprint-admin-status-msg.is-error {
  color: #ff9a8a;
}

.sky-sprint-admin-status-msg.is-good {
  color: #6fe0a3;
}

.sky-sprint-pill {
  display: inline-block;
  padding: 0.12rem 0.6rem;
  border-radius: 999px;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 700;
}

.sky-sprint-pill.is-good {
  background: rgba(61, 219, 132, 0.18);
  color: #6fe0a3;
  border: 1px solid rgba(61, 219, 132, 0.35);
}

.sky-sprint-pill.is-bad {
  background: rgba(255, 107, 107, 0.18);
  color: #ff9a8a;
  border: 1px solid rgba(255, 107, 107, 0.35);
}

.sky-sprint-pill.is-muted {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.16);
}

.sky-sprint-admin-btn-primary,
.sky-sprint-admin-btn-secondary {
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  padding: 0.45rem 1rem;
  border-radius: 999px;
  cursor: pointer;
  transition: transform 110ms ease, filter 110ms ease, background 110ms ease;
}

.sky-sprint-admin-btn-primary {
  border: 0;
  background: linear-gradient(110deg, var(--color-primary) 0%, var(--color-primary-strong) 85%);
  color: #202738;
}

.sky-sprint-admin-btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: saturate(0.5);
}

.sky-sprint-admin-btn-primary:not(:disabled):hover {
  transform: translateY(-1px);
}

.sky-sprint-admin-btn-secondary {
  background: transparent;
  color: var(--color-text);
  border: 1px solid rgba(255, 255, 255, 0.28);
}

.sky-sprint-admin-btn-secondary:hover {
  background: rgba(255, 255, 255, 0.08);
}

.sky-sprint-history-table code {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  background: rgba(255, 255, 255, 0.08);
  padding: 0.05rem 0.32rem;
  border-radius: var(--radius-sm);
}

.sky-sprint-history-table small {
  font-size: 0.72rem;
}

/* Admin level library — saved-snapshot table actions. */

.sky-sprint-library-table .sky-sprint-pill {
  margin-right: 0.18rem;
}

.sky-sprint-library-actions {
  white-space: nowrap;
  display: flex;
  gap: 0.32rem;
}

.sky-sprint-library-actions button {
  font: inherit;
  font-size: 0.82rem;
  padding: 0.28rem 0.55rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: rgba(255, 255, 255, 0.06);
  color: inherit;
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast);
}

.sky-sprint-library-actions button:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: var(--color-border-strong);
}

.sky-sprint-library-actions button[data-act="lock"] {
  background: rgba(255, 183, 3, 0.16);
  border-color: rgba(255, 183, 3, 0.35);
  color: var(--color-primary-strong);
}

.sky-sprint-library-actions button[data-act="lock"]:hover {
  background: rgba(255, 183, 3, 0.28);
}

.sky-sprint-library-actions button[data-act="delete"]:hover {
  background: rgba(255, 107, 107, 0.18);
  border-color: rgba(255, 107, 107, 0.35);
  color: var(--color-bad-soft);
}

/* Versions sub-row beneath a library entry. Hidden by default,
   toggled by the '▾ vN' button. */

.sky-sprint-library-versions {
  background: rgba(7, 32, 58, 0.06);
  padding: 0.45rem 0.7rem !important;
}

.sky-sprint-library-versions__loading {
  font-size: 0.82rem;
  opacity: 0.6;
  font-style: italic;
}

.sky-sprint-library-versions__list {
  display: grid;
  gap: 0.32rem;
}

.sky-sprint-library-version {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.42rem 0.6rem;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 0.86rem;
}

.sky-sprint-library-version strong {
  font-variant-numeric: tabular-nums;
  margin-right: 0.42rem;
}

/* Library row name layout — star + name button + status pill on one line. */

.sky-sprint-lib-name {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
}

.sky-sprint-lib-name-btn {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: left;
  color: inherit;
  cursor: pointer;
}

.sky-sprint-lib-name-btn:hover strong {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.sky-sprint-lib-star {
  font-size: 1.05rem;
  color: rgba(255, 255, 255, 0.32);
  cursor: pointer;
  user-select: none;
  transition: color var(--motion-fast), transform var(--motion-fast);
}

.sky-sprint-lib-star:hover {
  color: var(--color-warn);
  transform: scale(1.18);
}

.sky-sprint-lib-star.is-pinned {
  color: var(--color-warn);
}

.sky-sprint-lib-row.is-pinned {
  background: rgba(245, 179, 76, 0.05);
}

/* Detail panel sub-row beneath a library row. */

.sky-sprint-library-detail {
  background: rgba(7, 32, 58, 0.08);
  padding: 0.85rem 1rem !important;
}

.sky-sprint-library-detail__loading {
  font-size: 0.86rem;
  opacity: 0.6;
  font-style: italic;
}

.sky-sprint-library-detail__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: 1.2rem;
}

.sky-sprint-library-detail__col h3 {
  margin: 0 0 0.45rem;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.66);
}

.sky-sprint-library-detail__col textarea {
  width: 100%;
  font: inherit;
  font-size: 0.86rem;
  line-height: 1.45;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: rgba(7, 32, 58, 0.5);
  color: inherit;
  resize: vertical;
  min-height: 6rem;
  font-family: var(--font-ui);
}

.sky-sprint-library-detail__col select {
  font: inherit;
  font-size: 0.84rem;
  padding: 0.32rem 0.55rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: rgba(7, 32, 58, 0.5);
  color: inherit;
}

/* Filter row spacing — the new richer filter bar uses .sky-sprint-admin-row
   with a `.sky-sprint-lib-filters` accent. Slightly tighter spacing so
   four+ inputs fit comfortably. */
.sky-sprint-lib-filters label {
  font-size: 0.74rem;
}
.sky-sprint-lib-filters input,
.sky-sprint-lib-filters select {
  font-size: 0.84rem;
}

/* Admin 'Danger zone' — destructive reset buttons. */

.sky-sprint-card--danger {
  border-color: rgba(255, 107, 107, 0.4);
  background: rgba(255, 107, 107, 0.05);
}

.sky-sprint-card--danger h2 {
  color: var(--color-bad-soft);
}

.sky-sprint-admin-btn-danger {
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  padding: 0.45rem 1rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 1px solid rgba(255, 107, 107, 0.45);
  background: rgba(255, 107, 107, 0.18);
  color: var(--color-bad-soft);
  transition: background var(--motion-fast), transform var(--motion-fast);
}

.sky-sprint-admin-btn-danger:hover {
  background: rgba(255, 107, 107, 0.32);
  transform: translateY(-1px);
}

.sky-sprint-admin-btn-danger--soft {
  background: transparent;
  border-color: rgba(255, 107, 107, 0.3);
  color: rgba(255, 154, 138, 0.78);
}

.sky-sprint-admin-btn-danger--soft:hover {
  background: rgba(255, 107, 107, 0.12);
  border-color: rgba(255, 107, 107, 0.5);
}

/* ─────────────────────────────────────────────────────────────────────
   First-play onboarding overlay (game.js maybeShowOnboarding) */

.onboarding-card {
  width: min(28rem, 92vw);
  padding: var(--space-5) var(--space-5) var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-surface-light);
  color: var(--color-text-on-light);
  box-shadow: var(--shadow-lg);
  text-align: center;
  position: relative;
  font-family: var(--font-ui);
  animation: panel-in 280ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
}

.onboarding-skip {
  position: absolute;
  top: var(--space-2);
  right: var(--space-3);
  background: transparent;
  border: 0;
  color: rgba(7, 32, 58, 0.55);
  font: inherit;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  padding: var(--space-1);
}

.onboarding-skip:hover {
  color: var(--color-text-on-light);
}

.onboarding-eyebrow {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(7, 32, 58, 0.6);
  font-weight: 700;
  margin-bottom: var(--space-2);
}

.onboarding-title {
  margin: 0 0 var(--space-3);
  font-size: clamp(1.4rem, 4vw, 1.85rem);
  letter-spacing: 0.02em;
}

.onboarding-body {
  margin: 0;
  font-size: 0.96rem;
  line-height: 1.45;
  color: rgba(7, 32, 58, 0.84);
}

.onboarding-body strong {
  display: inline-block;
  margin: 0 0.05rem;
  padding: 0.05rem 0.42rem;
  border-radius: var(--radius-sm);
  background: rgba(7, 32, 58, 0.08);
  font-weight: 700;
  font-family: var(--font-mono);
  font-size: 0.84em;
}

.onboarding-progress {
  display: flex;
  justify-content: center;
  gap: 0.42rem;
  margin: var(--space-4) 0 var(--space-3);
}

.onboarding-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: rgba(7, 32, 58, 0.18);
  transition: background var(--motion-fast);
}

.onboarding-dot.is-current {
  background: var(--color-primary);
}

.onboarding-actions {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
}

.onboarding-back,
.onboarding-next {
  flex: 1 1 auto;
  font: inherit;
  font-size: 0.92rem;
  font-weight: 700;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: transform var(--motion-fast), filter var(--motion-fast),
              background var(--motion-fast);
}

.onboarding-back {
  background: transparent;
  color: rgba(7, 32, 58, 0.78);
  border: 1px solid rgba(7, 32, 58, 0.22);
}

.onboarding-back:disabled {
  opacity: 0.32;
  cursor: not-allowed;
}

.onboarding-back:hover:not(:disabled) {
  background: rgba(7, 32, 58, 0.06);
}

.onboarding-next {
  border: 0;
  background: linear-gradient(110deg, var(--color-primary) 0%, var(--color-primary-strong) 85%);
  color: #202738;
}

.onboarding-next:hover {
  transform: translateY(-1px);
  filter: saturate(1.06);
}

/* Dev tab gating + action buttons. */

.sky-sprint-dashboard__tab.is-dev-only {
  display: none;
}

.sky-sprint-dashboard.is-dev .sky-sprint-dashboard__tab.is-dev-only {
  display: inline-block;
  background: rgba(245, 179, 76, 0.16);
  border-color: rgba(245, 179, 76, 0.4);
  color: #f5b34c;
}

.sky-sprint-dev-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.55rem;
  margin-top: 0.4rem;
}

/* Audio editor (Dev tab) — grouped per-SFX rows. */
.sky-sprint-sfx-list { display: flex; flex-direction: column; gap: 0.85rem; margin-top: 0.4rem; }
.sky-sprint-sfx-group__title {
  margin: 0 0 0.3rem;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.72;
}
.sky-sprint-sfx-table { width: 100%; border-collapse: collapse; }
.sky-sprint-sfx-table tr {
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.sky-sprint-sfx-table tr:last-child { border-bottom: 0; }
.sky-sprint-sfx-table td {
  padding: 0.4rem 0.45rem;
  vertical-align: middle;
}
.sky-sprint-sfx-meta {
  min-width: 11rem;
}
.sky-sprint-sfx-meta strong {
  display: block;
  font-weight: 600;
  font-size: 0.86rem;
}
.sky-sprint-sfx-meta small {
  display: block;
  margin-top: 0.1rem;
  opacity: 0.62;
  font-size: 0.72rem;
}
.sky-sprint-sfx-slider {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 11rem;
}
.sky-sprint-sfx-slider input[type="range"] {
  flex: 1;
  accent-color: #ffe19a;
}
.sky-sprint-sfx-slider span {
  font: 600 11px/1 "Consolas", monospace;
  opacity: 0.72;
  min-width: 2.6rem;
  text-align: right;
}
.sky-sprint-sfx-mute label {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  font-size: 0.78rem;
  cursor: pointer;
  white-space: nowrap;
}
.sky-sprint-sfx-test { text-align: right; white-space: nowrap; }
.sky-sprint-sfx-test button { font-size: 0.74rem; padding: 0.22rem 0.55rem; }
/* When a row is muted, dim its meta + slider so the eye lands on the
   un-muted rows. The ▶ Test button stays bright since it bypasses. */
.sky-sprint-sfx-row.is-muted .sky-sprint-sfx-meta,
.sky-sprint-sfx-row.is-muted .sky-sprint-sfx-slider {
  opacity: 0.42;
}
.sky-sprint-sfx-footer {
  margin-top: 0.7rem;
  padding-top: 0.55rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  justify-content: flex-end;
}

.sky-sprint-dev-btn {
  font: inherit;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.55rem 0.7rem;
  border-radius: 0.55rem;
  border: 1px solid rgba(79, 163, 255, 0.4);
  background: rgba(79, 163, 255, 0.12);
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  font-size: 0.85rem;
  transition: background 110ms ease, transform 110ms ease;
}

.sky-sprint-dev-btn:hover:not(:disabled) {
  background: rgba(79, 163, 255, 0.22);
  transform: translateY(-1px);
}

.sky-sprint-dev-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.sky-sprint-dev-btn > span {
  display: flex;
  flex-direction: column;
  gap: 0.05rem;
  flex: 1 1 auto;
  line-height: 1.2;
}

.sky-sprint-dev-btn strong {
  font-size: 0.88rem;
  font-weight: 600;
}

.sky-sprint-dev-btn small {
  font-size: 0.74rem;
  opacity: 0.66;
  letter-spacing: 0.02em;
}

.sky-sprint-dev-btn--single {
  grid-column: 1 / -1;
}

/* ───────────────────────────────────────────────────────────────────
   Top bar — retired. The persistent .top-bar that lived here was
   replaced by the unified landing panel (.landing). All Play /
   Levels / Leaderboard / Profile / Settings entry points moved
   into landing-panel.js + landing-submenu.js. Markup, CSS, and the
   _topBarController controller have all been deleted. See
   index.api.html (no <nav class="top-bar">), game.js (no
   _topBarController), and the retirement commit for full context.
   ─────────────────────────────────────────────────────────────────── */

/* B8 Track 1 follow-up (2026-06-07): retire the legacy floating
   "Sign in" / identity widget. It sits at z-index 60 (set in the
   .sky-sprint-identity rule above) which was covering the new
   top-bar Profile/Settings icons in the top-right corner. The new
   top bar's Profile button is now the canonical sign-in entry —
   when clicked while signed-out it opens the dashboard on the
   Account tab where the existing sign-in form lives (wired in
   game.js). The button stays in the DOM so existing JS references
   (game.js's identity-widget controller, _updateMenuToggleLabel,
   etc.) keep working without null-checks. */
.sky-sprint-identity,
#sky-sprint-menu-toggle {
  display: none !important;
}

/* (Retired: .overlay-show-lobby + .overlay--has-message marker
   classes. The empty-lobby-state hide rule and the error-message
   force-unhide rules existed because the legacy lobby card was
   stripped down so much that the .overlay would render as an
   empty white pill. With the legacy lobby-card markup gone and
   no caller passing { showLobby: true } to showOverlay, both
   classes are dead. Error overlays now render via the .overlay
   directly with title/message/button visible.) */

/* ───────────────────────────────────────────────────────────────────
   B8 Track 4 — Profile split-screen layout.

   Replaces the legacy Profile-tab content with a two-column layout:
     - Left column: content for the currently-selected sub-menu
     - Right column: sub-menu navigation buttons (Character / Achievements
       / Team / Account / Log-out / Admin)

   The shell module owns layout markup + sub-menu state; this CSS owns
   the visual. Active button gets the same soft amber treatment as the
   top-bar active highlight (consistent active-element vocabulary
   across the UI).
   ─────────────────────────────────────────────────────────────────── */

/* B9 Track 3 (P1) — profile hero header (above the split). */
.profile-hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.profile-hero__id {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  min-width: 0;
}

.profile-hero__avatar {
  flex: 0 0 auto;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.35));
}

.profile-hero__idtext {
  min-width: 0;
}

.profile-hero__name {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--color-primary-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.profile-hero__since {
  margin: 0.1rem 0 0;
  font-size: 0.8rem;
  opacity: 0.66;
}

.profile-hero__stats {
  display: flex;
  gap: 1.4rem;
  flex: 0 0 auto;
}

.profile-hero__stat {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.profile-hero__stat-num {
  font-size: 1.35rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

.profile-hero__stat-label {
  font-size: 0.64rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.6;
}

/* B9 Track 3 (P3) — Profile → Character: live preview + a bigger swatch
   grid in the profile context (the compact 22px grid stays for the lobby
   popover; the ID selector scopes the override to the profile instance). */
.profile-character-editor {
  display: flex;
  gap: 1.1rem;
  align-items: flex-start;
  flex-wrap: wrap;
}

.profile-character-preview {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  border-radius: var(--radius-md);
  background: linear-gradient(160deg, rgba(26, 92, 126, 0.35), rgba(7, 32, 58, 0.5));
  border: 1px solid var(--color-border);
}

.profile-character-preview .profile-hero__avatar {
  width: 72px;
  height: 72px;
}

.profile-character-editor__body {
  flex: 1 1 14rem;
  min-width: 0;
}

#profile-character-color-grid.character-color-grid {
  grid-template-columns: repeat(6, 34px);
  gap: 6px;
  justify-content: start;
}

#profile-character-color-grid .character-color-swatch {
  width: 34px;
  height: 34px;
  border-radius: 7px;
}

.profile-split {
  display: grid;
  grid-template-columns: 1fr 14rem;
  gap: var(--space-4);
  align-items: start;
  min-height: 32rem;
}

.profile-split__content {
  min-width: 0; /* lets long cards wrap */
}

.profile-submenu {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  position: sticky;
  top: 0;
}

.profile-submenu__btn {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font: inherit;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  text-align: left;
  transition: background var(--motion-fast), border-color var(--motion-fast),
              color var(--motion-fast);
}

.profile-submenu__btn:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--color-border-strong);
}

.profile-submenu__btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.profile-submenu__btn.is-active {
  background: rgba(255, 183, 3, 0.12);
  border-color: rgba(255, 183, 3, 0.42);
  color: var(--color-primary-strong);
}

.profile-submenu__btn.is-disabled {
  opacity: 0.42;
  cursor: not-allowed;
  pointer-events: none; /* aria-disabled is the auth signal */
}

.profile-submenu__icon {
  font-size: 1.05rem;
  line-height: 1;
}

.profile-submenu__label {
  white-space: nowrap;
}

.profile-submenu__placeholder p {
  margin: 0;
  color: var(--color-text-muted);
}

/* Mobile narrowing — stack vertically below ~720px. Sub-menu becomes
   a horizontal row at the top. */
@media (max-width: 720px) {
  .profile-split {
    grid-template-columns: 1fr;
  }
  .profile-submenu {
    flex-direction: row;
    overflow-x: auto;
    position: relative;
  }
  .profile-submenu__btn {
    flex-shrink: 0;
  }
}

/* Retired: legacy dashboard tab strip. Landing-submenu owns
   navigation now and the dashboard is accessed via "Open full
   view" CTAs from each submenu. The tab markup stays in the DOM
   so existing JS references continue to work — just hidden. */
.sky-sprint-dashboard__tabs {
  display: none !important;
}

/* B8 Track 2 — Level History calendar modal. */
.level-history-modal {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}

.level-history-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.66);
  backdrop-filter: blur(4px);
}

.level-history-modal__panel {
  position: relative;
  z-index: 1;
  max-width: 56rem;
  width: 100%;
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-5);
  color: var(--color-text);
}

.level-history-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}

.level-history-modal__header h2 {
  margin: 0;
  font-size: 1.4rem;
}

.level-history-modal__close {
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  width: 2.2rem;
  height: 2.2rem;
  color: inherit;
  cursor: pointer;
  font-size: 1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.level-history-modal__close:hover {
  background: rgba(255, 255, 255, 0.08);
}

.level-history-modal__sub {
  margin: 0 0 var(--space-4);
  font-size: .88rem;
  opacity: .74;
}

.level-history-modal__week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-2);
}

.level-history__cell {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-2);
  background: rgba(0, 0, 0, 0.18);
  min-height: 8.4rem;
  display: flex;
  flex-direction: column;
  gap: .35rem;
}

.level-history__cell.is-today {
  border-color: rgba(255, 183, 3, 0.42);
  background: rgba(255, 183, 3, 0.06);
}

.level-history__cell header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: .82rem;
  opacity: .82;
}

.level-history__today {
  font-size: .68rem;
  background: rgba(255, 183, 3, 0.22);
  color: var(--color-primary-strong);
  padding: 0 .35rem;
  border-radius: var(--radius-pill);
  letter-spacing: .04em;
  text-transform: uppercase;
}

.level-history__empty {
  margin: auto 0;
  text-align: center;
  font-size: 1.2rem;
  opacity: .42;
}

.level-history__placement {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  font-size: .82rem;
}

.level-history__rank {
  font-size: 1.1rem;
  font-weight: 700;
}

.level-history__pct {
  opacity: .76;
}

.level-history__time {
  opacity: .66;
  font-family: var(--font-mono);
}

.level-history__day-of {
  margin-top: .25rem;
  align-self: flex-start;
  font-size: .68rem;
  background: rgba(63, 219, 132, 0.16);
  color: var(--color-good-soft);
  padding: 0 .4rem;
  border-radius: var(--radius-pill);
  letter-spacing: .04em;
}

.level-history-modal__signed-out,
.level-history-modal__loading,
.level-history-modal__error {
  grid-column: 1 / -1;
  text-align: center;
  padding: var(--space-4);
  opacity: .8;
}

.level-history-modal__error {
  color: var(--color-bad-soft);
}

/* Narrow viewports: stack as 3-per-row to keep cells readable. */
@media (max-width: 720px) {
  .level-history-modal__week {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* B8 Track 4 slice 5 — Profile → Achievements pane styles. */
.profile-achievements__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}

.profile-achievements__stat {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--color-border);
}

.profile-achievements__stat-label {
  font-size: .76rem;
  opacity: .72;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.profile-achievements__stat strong {
  font-size: 1.1rem;
}

.profile-achievements__badges {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.profile-achievements__badge {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--color-border);
  opacity: 0.62;
  transition: opacity var(--motion-fast);
}

.profile-achievements__badge.is-earned {
  opacity: 1;
  border-color: rgba(255, 183, 3, 0.42);
  background: rgba(255, 183, 3, 0.06);
}

.profile-achievements__badge-icon {
  font-size: 1.8rem;
  line-height: 1;
  flex-shrink: 0;
}

.profile-achievements__badge-body {
  flex: 1;
  min-width: 0;
}

.profile-achievements__badge-name {
  font-weight: 600;
  font-size: .94rem;
}

.profile-achievements__badge-desc {
  font-size: .8rem;
  opacity: .76;
  margin-top: .15rem;
}

.profile-achievements__badge-earned {
  font-size: .72rem;
  opacity: .68;
  margin-top: .35rem;
  color: var(--color-primary-strong);
}

@media (max-width: 640px) {
  .profile-achievements__stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* B8 Track 4 — Profile → Admin sub-menu pane styles. */
.profile-admin__hint {
  margin: .55rem 0 0;
  font-size: .76rem;
  opacity: .58;
  font-style: italic;
}

.profile-admin__danger {
  border-color: rgba(255, 107, 107, 0.32) !important;
  background: linear-gradient(rgba(255, 107, 107, 0.04), rgba(255, 107, 107, 0.08));
}

.profile-admin__date {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-2) 0;
  font-size: .92rem;
}

.profile-admin__date input[type="date"] {
  padding: .35rem .55rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: rgba(0, 0, 0, 0.25);
  color: inherit;
  font: inherit;
}

.profile-admin__danger-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.profile-admin__danger-msg {
  margin: var(--space-3) 0 0;
  font-size: .88rem;
  min-height: 1.2em;
}

.profile-admin__danger-msg.is-good {
  color: var(--color-good);
}

.profile-admin__danger-msg.is-error {
  color: var(--color-bad);
}

/* B8 / B3 — PRACTICE HUD chip. Sits alongside the existing
   time/lives/height/level chips during a practice run. Amber tint
   to match the "this isn't a ranked run" affordance language used
   elsewhere in the UI. Hidden via [hidden] when not in practice;
   game.js toggles the attribute via _syncPracticeHudChip(). */
.chip-practice {
  background: rgba(255, 183, 3, 0.16);
  border: 1px solid rgba(255, 183, 3, 0.42);
  color: var(--color-primary-strong);
}

/* ───────────────────────────────────────────────────────────────────
   LANDING — unified main-menu surface.

   Replaces the legacy top-bar + scattered overlays. Visible whenever
   the player is in the lobby state (not actively running, not in
   countdown, not in post-run). The layout is:

     [ title ]
     [ character | menu ]
     [    quota strip   ]

   Sub-menu states (Profile / Settings / etc.) live in slice 5 — they
   transition the right column to a sub-panel.

   Hidden by default via [hidden]. The landing controller toggles
   visibility based on gameState.
   ─────────────────────────────────────────────────────────────────── */
.landing {
  position: fixed;
  inset: 0;
  z-index: 24;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: clamp(1rem, 4vh, 3rem) clamp(1rem, 5vw, 3rem);
  /* Fully opaque — nothing from the game canvas should show through
     when the main menu is up. The previous 0.96/0.92 alpha let the
     animated background bleed through. */
  background: linear-gradient(160deg,
    rgb(7, 32, 58),
    rgb(10, 38, 63));
  color: var(--color-text);
  font-family: var(--font-ui);
}

.landing[hidden] {
  display: none;
}

/* When the landing is visible, defensively hide the game canvas so
   even if the panel becomes semi-transparent (dev tweak, mid-refactor)
   the animated background can't peek through. Paired with a game-loop
   skip in game.js's gameLoop that stops draw() from running while the
   landing panel is up. */
body.is-landing-visible #game-canvas {
  visibility: hidden;
}

.landing__header {
  text-align: center;
  margin-bottom: clamp(1rem, 3vh, 2rem);
}

.landing__title {
  margin: 0;
  font-size: clamp(2rem, 5vw, 2.8rem);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.06;
}

.landing__body {
  display: grid;
  /* auto auto = each column hugs its content (no more reserved
     half-width "islands" with dead space in the middle). The
     pair is then centered as a unit. */
  grid-template-columns: auto auto;
  justify-content: center;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: center;
  max-width: 64rem;
  width: 100%;
  margin: 0 auto;
}

.landing__character {
  position: relative;          /* anchor for the quick-customize pencil */
  display: flex;
  justify-content: center;
  align-items: center;
  width: 22rem;
  grid-column: 1;
  grid-row: 1;
}

.landing__character-canvas {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  filter: drop-shadow(0 10px 28px rgba(7, 32, 58, 0.45));
}

/* Quick-customize pencil — diagonally offset from the character's
   bottom-left. Grey-ish, semi-transparent; brightens on hover so the
   affordance is discoverable without competing with the character art.
   Position spec: ratio 1.5 (down) : 0.75 (left) where 1 unit = the
   cap-height of the "S" in "Sky Sprint" title (~1.7rem given the title
   clamps at 2-2.8rem with ~0.7 cap-height). Adjust via these two values
   if the title font-size changes meaningfully. */
.landing__character-edit {
  position: absolute;
  bottom: 0.85rem;
  left: 2.1rem;
  width: 2rem;
  height: 2rem;
  padding: 0.3rem;
  border: 0;
  border-radius: 999px;
  background: rgba(180, 200, 220, 0.14);
  color: rgba(220, 230, 240, 0.55);
  cursor: pointer;
  transition: background var(--motion-fast), color var(--motion-fast),
              transform var(--motion-fast);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
.landing__character-edit svg {
  width: 100%;
  height: 100%;
  display: block;
}
.landing__character-edit:hover,
.landing__character-edit:focus-visible {
  background: rgba(200, 215, 230, 0.32);
  color: rgba(245, 250, 255, 0.92);
  transform: scale(1.08);
  outline: none;
}

.landing__menu {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
  max-width: 22rem;
  grid-column: 2;
  grid-row: 1;
}

.landing__menu-divider {
  height: 1px;
  background: var(--color-border);
  margin: 0.35rem 0;
}

.landing__menu-item {
  display: grid;
  grid-template-columns: 3rem 1fr;
  grid-template-areas: "icon label";
  column-gap: 0.65rem;
  align-items: center;
  text-align: left;
  /* Tighter padding so the panel hugs the (now-larger) text +
     icon instead of leaving big gaps. The sub-line is hidden so
     the row height is single-line. */
  padding: 0.45rem 0.85rem;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font: inherit;
  cursor: pointer;
  transition: background var(--motion-fast), border-color var(--motion-fast),
              transform var(--motion-fast);
}

.landing__menu-item:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--color-border-strong);
  transform: translateX(2px);
}

/* Focus indicator used for arrow-key nav — matched on both :focus
   and :focus-visible so it shows regardless of whether the browser
   flags the focus as "keyboard" (programmatic focus from show() is
   :focus but not :focus-visible in most engines). Combines the
   amber outline with the same background bump + translateX as hover,
   so a highlighted menu item reads as clearly interactive. */
.landing__menu-item:focus,
.landing__menu-item:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--color-border-strong);
  transform: translateX(2px);
}

/* Primary CTA — matches the default .landing__menu-item exactly
   in its resting state (same background, border, and text colour).
   Playtest feedback asked for zero visual dominance when not
   interacted with; the amber lives only on the ▶ icon so the
   button still reads as "this is the default action". Hover and
   focus fall through to the shared .landing__menu-item rules
   above so all menu items animate identically. */
.landing__menu-item--primary { /* intentionally empty — see above */ }

.landing__menu-item--primary .landing__menu-item-icon {
  color: var(--color-primary-strong, #ffb703);
}

.landing__menu-item--secondary {
  background: rgba(79, 163, 255, 0.08);
  border-color: rgba(79, 163, 255, 0.36);
}

.landing__menu-item--secondary:hover {
  background: rgba(79, 163, 255, 0.14);
}

.landing__menu-item-icon {
  grid-area: icon;
  /* +25% from 1.4rem baseline (rounded). */
  font-size: 1.75rem;
  line-height: 1;
  justify-self: center;
  align-self: center;
}

.landing__menu-item-label {
  grid-area: label;
  font-weight: 700;
  /* +25% from 1rem baseline. */
  font-size: 1.25rem;
  letter-spacing: 0.01em;
}

/* Sub-line ("Today's challenge", "Random seed · won't count")
   suppressed — kept in the markup for accessibility but hidden
   from view to give the icon + label more breathing room. */
.landing__menu-item-sub {
  display: none;
}

.landing__footer {
  text-align: center;
  font-size: 0.86rem;
  opacity: 0.78;
  margin-top: clamp(1rem, 3vh, 2rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
}

/* Skip-tutorial link — a low-key underlined text button that sits
   below the quota strip while tutorial-status === "unstarted".
   Deliberately un-primary — the amber Play Tutorial button above
   is where we want the eye to go. */
.landing__skip-tutorial {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  font-size: 0.82rem;
  padding: 0.25rem 0.5rem;
  opacity: 0.65;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: opacity var(--motion-fast);
}

.landing__skip-tutorial:hover,
.landing__skip-tutorial:focus-visible {
  opacity: 1;
  outline: none;
}

/* ------------------------------------------------------------------
   Tutorial hint chip (B11).
   Fixed top-strip guidance chip surfaced by _syncActiveHint() in
   game.js when the player enters an unused hint trigger during the
   tutorial run. Never captures pointer events; fades via opacity
   transition so JS doesn't have to tick per-frame animation.
------------------------------------------------------------------ */

.tutorial-hint-chip {
  position: fixed;
  /* Nudged ~20vh lower than the previous top-of-viewport docking so the
     chip sits closer to eye-level while playing rather than in the HUD
     strip. Roughly "1/5th lower on the page" per the readability tune. */
  top: calc(0.75rem + 3.6rem + 20vh);
  left: 50%;
  transform: translateX(-50%);
  z-index: 22;
  display: inline-flex;
  align-items: center;
  gap: 1.5rem;
  padding: 1.1rem 2.2rem;
  border-radius: var(--radius-pill, 999px);
  background: rgba(7, 32, 58, 0.82);
  color: #f3f8fc;
  border: 1px solid rgba(255, 183, 3, 0.55);
  box-shadow: 0 4px 22px rgba(0, 0, 0, 0.35),
              0 0 0 3px rgba(255, 183, 3, 0.14);
  /* Doubled from 0.94rem so sign copy is legible from a normal play
     distance. Padding + gap above scale in the same ratio so the pill
     grows uniformly, not just the type. */
  font-size: 1.88rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
  max-width: min(92vw, 900px);
  text-align: center;
}

.tutorial-hint-chip--visible {
  opacity: 1;
}

.tutorial-hint-chip__text {
  line-height: 1.15;
  /* Respect authored \n in the hint text so multi-line chips render
     as multiple lines rather than collapsing to a single wrapped
     paragraph. `pre-wrap` also preserves regular word-wrapping so
     long single-line text still breaks naturally. */
  white-space: pre-wrap;
}

.tutorial-hint-chip__keys {
  display: inline-flex;
  gap: 0.28rem;
}

.tutorial-hint-chip__keys[hidden] {
  display: none;
}

.tutorial-hint-chip__keys kbd {
  display: inline-block;
  padding: 0.16rem 0.84rem;
  border-radius: 0.5rem;
  border: 1px solid rgba(255, 255, 255, 0.28);
  background: rgba(7, 32, 58, 0.55);
  font-family: "SFMono-Regular", Consolas, monospace;
  /* Scaled up to match the doubled chip type; the kbd chips keep their
     ~0.83× relative ratio against the surrounding sentence. */
  font-size: 1.56rem;
  letter-spacing: 0.02em;
}

.landing__quota {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.85rem;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
}

.landing__quota[data-state="empty"] {
  background: rgba(214, 60, 60, 0.14);
  border-color: rgba(214, 60, 60, 0.5);
  color: #ffd0d0;
}

.landing__quota[data-state="anonymous"] {
  /* 27.5% larger than the default chip (1.5 × 0.85 after the -15%
     trim). Still reads as a primary CTA without dominating. */
  font-size: 1.275em;
  padding: 0.51rem 1.084rem;
  cursor: pointer;
  font-weight: 600;
  opacity: 0.92;
  transition: background var(--motion-fast), border-color var(--motion-fast),
              transform var(--motion-fast);
}

.landing__quota[data-state="anonymous"]:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: var(--color-border-strong);
  transform: translateY(-1px);
}

.landing__quota[data-state="anonymous"]:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

.landing__quota[data-state="signed-out"] a,
.landing__quota[data-state="signed-out"] button {
  color: var(--color-primary-strong);
  background: transparent;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  font: inherit;
  padding: 0;
}

/* Narrow viewports — stack vertically so the menu doesn't squish */
@media (max-width: 720px) {
  .landing__body {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
  .landing__character {
    max-width: 16rem;
  }
}

/* Landing sub-menu panel — replaces the character column when a
   player picks Leaderboard / Levels / Profile / Settings from the
   landing menu. The character slot is hidden in-place (rather than
   removed from layout) so the grid columns stay stable. */
.landing__character.is-hidden {
  visibility: hidden;
  pointer-events: none;
}

.landing__submenu {
  grid-column: 1;              /* same column as the character canvas */
  grid-row: 1;
  width: 100%;
  max-width: 24rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  animation: landing-submenu-in 240ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
  min-height: 280px;
}

.landing__submenu[hidden] {
  display: none;
}

@keyframes landing-submenu-in {
  from { transform: translateX(-12px); opacity: 0; }
  to   { transform: translateX(0);     opacity: 1; }
}

.landing__submenu-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}

.landing__submenu-back {
  appearance: none;
  font: inherit;
  font-weight: 600;
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  padding: 0.35rem 0.85rem;
  cursor: pointer;
  transition: background var(--motion-fast), transform var(--motion-fast);
}

.landing__submenu-back:hover {
  background: rgba(255, 255, 255, 0.08);
  transform: translateX(-2px);
}

.landing__submenu-back:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

.landing__submenu-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.landing__submenu-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
  align-items: stretch;
}

.landing__submenu-message {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.5;
  opacity: 0.85;
}

/* Adopted #lobby-config inside the Profile submenu — the legacy
   form is absolute-positioned as a side-popover relative to .panel
   when in its home. Re-flow it as a normal block inside the
   submenu body, drop the popover chrome, and hide the internal
   close button (the submenu Back button is the explicit close). */
.landing__submenu-body .lobby-config {
  position: static;
  inset: auto;
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
  border-radius: 0;
}

.landing__submenu-body .lobby-config__close {
  display: none;
}

.landing__submenu-body .lobby-config__heading {
  /* The submenu header already names the section; the inner
     heading would be redundant noise. */
  display: none;
}

.landing__submenu-cta {
  appearance: none;
  font: inherit;
  font-weight: 700;
  align-self: flex-start;
  padding: 0.6rem 1.2rem;
  background: var(--color-primary);
  color: var(--color-text-on-light);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: background var(--motion-fast), transform var(--motion-fast),
              box-shadow var(--motion-medium);
}

.landing__submenu-cta:hover {
  background: var(--color-primary-strong);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

.landing__submenu-cta:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

/* B3 Track 5 — Practice run badge + history filter toggle.
   The badge replaces the raw BLEMISHED_PRACTICE status string in
   the Recent Runs table; the filter toggle hides practice rows
   when the player wants to focus on ranked attempts. */
.sky-sprint-tag {
  display: inline-block;
  padding: 0.12rem 0.55rem;
  border-radius: var(--radius-pill);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.03em;
}

.sky-sprint-tag--practice {
  background: rgba(79, 163, 255, 0.18);
  border: 1px solid rgba(79, 163, 255, 0.45);
  color: rgba(213, 235, 255, 0.95);
}

.sky-sprint-history-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.55rem;
  font-size: 0.82rem;
  opacity: 0.82;
  cursor: pointer;
}

.sky-sprint-history-filter input {
  margin: 0;
  cursor: pointer;
}

.sky-sprint-leaderboard-table tr.is-practice {
  opacity: 0.82;
}

/* Confirm modal — styled Yes/No dialog matching the landing design.
   Built lazily by frontend/src/runtime/confirm-modal.js. Used for
   the signed-out Daily prompt and any future confirmations that
   want consistent visual language. */
.confirm-modal {
  position: fixed;
  inset: 0;
  /* 100 sits above the dashboard (90). Historically 55 was enough
     because the confirm-modal only opened inside the editor with the
     dashboard closed, but the B21 M2 autosave-recovery + Library-tab
     return path can open a confirm-modal while the dashboard is
     visible. Bumping the whole class avoids adding a second variant. */
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.confirm-modal[hidden] {
  display: none;
}

.confirm-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.66);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.confirm-modal__card {
  position: relative;
  z-index: 1;
  max-width: 28rem;
  width: 100%;
  padding: var(--space-5);
  text-align: center;
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
  animation: confirm-modal-in 220ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
}

@keyframes confirm-modal-in {
  from { transform: translateY(8px); opacity: 0; }
  to   { transform: translateY(0);   opacity: 1; }
}

.confirm-modal--warn .confirm-modal__title {
  color: var(--color-warn);
}

.confirm-modal__title {
  margin: 0 0 var(--space-3);
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.confirm-modal__message {
  margin: 0 0 var(--space-4);
  font-size: 0.94rem;
  line-height: 1.4;
  opacity: 0.86;
}

.confirm-modal__actions {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
  flex-wrap: wrap;
}

.confirm-modal__yes,
.confirm-modal__middle,
.confirm-modal__no {
  appearance: none;
  font: inherit;
  font-weight: 700;
  padding: 0.65rem 1.4rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 0;
  transition: background var(--motion-fast), transform var(--motion-fast),
              box-shadow var(--motion-medium);
}

.confirm-modal__yes {
  background: var(--color-primary);
  color: var(--color-text-on-light);
  box-shadow: var(--shadow-sm);
}

.confirm-modal__yes:hover {
  background: var(--color-primary-strong);
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

/* Middle button — used for destructive "Discard" style choices in
   3-button dialogs. Muted red so it reads as "loss-of-work" without
   competing with the primary Save (yes) button. Hidden by default;
   the JS unhides when middleLabel is provided. */
.confirm-modal__middle {
  background: transparent;
  color: rgba(255, 132, 132, 0.9);
  border: 1px solid rgba(255, 132, 132, 0.55);
}

.confirm-modal__middle:hover {
  background: rgba(255, 132, 132, 0.12);
  border-color: rgba(255, 132, 132, 0.8);
}

.confirm-modal__no {
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
}

.confirm-modal__no:hover {
  background: rgba(255, 255, 255, 0.08);
}

.confirm-modal__yes:focus-visible,
.confirm-modal__middle:focus-visible,
.confirm-modal__no:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

/* ------------------------------------------------------------------
   Create Level chooser modal (B21 M2).
   Two-card picker opened from the Library tab's "+ Create Level"
   button. Reuses the confirm-modal backdrop styling but the body is
   a two-column card grid (Custom / Seeded).
   ------------------------------------------------------------------ */

.create-level-modal {
  position: fixed;
  inset: 0;
  /* 100 sits above the dashboard (90) so the chooser opened FROM
     the Library tab isn't occluded by its own parent panel.
     confirm-modal at 55 doesn't have this problem because it's
     opened from edit mode with the dashboard already closed. */
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.create-level-modal[hidden] {
  display: none;
}

.create-level-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.66);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.create-level-modal__card {
  position: relative;
  z-index: 1;
  max-width: 44rem;
  width: 100%;
  padding: var(--space-5);
  text-align: center;
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
  animation: confirm-modal-in 220ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
}

.create-level-modal__title {
  margin: 0 0 var(--space-2);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.create-level-modal__intro {
  margin: 0 auto var(--space-4);
  max-width: 34rem;
  font-size: 0.94rem;
  line-height: 1.45;
  opacity: 0.82;
}

.create-level-modal__cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

@media (max-width: 640px) {
  .create-level-modal__cards {
    grid-template-columns: 1fr;
  }
}

.create-level-modal__choice {
  appearance: none;
  font: inherit;
  cursor: pointer;
  text-align: left;
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  color: var(--color-text);
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  gap: var(--space-2);
  transition: background var(--motion-fast), border-color var(--motion-fast),
              transform var(--motion-fast), box-shadow var(--motion-medium);
}

.create-level-modal__choice:hover:not(.is-disabled):not([disabled]) {
  background: var(--color-surface-elevated);
  border-color: var(--color-primary-strong);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.create-level-modal__choice:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

.create-level-modal__choice.is-disabled,
.create-level-modal__choice[disabled] {
  cursor: not-allowed;
  opacity: 0.55;
}

.create-level-modal__choice-icon {
  font-size: 2rem;
  line-height: 1;
}

.create-level-modal__choice-title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-primary-strong);
}

.create-level-modal__choice-body {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.45;
  opacity: 0.82;
}

.create-level-modal__choice-cta {
  align-self: end;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--color-primary);
  letter-spacing: 0.01em;
}

.create-level-modal__actions {
  display: flex;
  gap: var(--space-2);
  justify-content: center;
}

.create-level-modal__cancel {
  appearance: none;
  font: inherit;
  font-weight: 700;
  padding: 0.55rem 1.2rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  transition: background var(--motion-fast);
}

.create-level-modal__cancel:hover {
  background: rgba(255, 255, 255, 0.08);
}

.create-level-modal__cancel:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

/* Library header — "+ Create Level" CTA (B21 M2, admin-only). Lives
   above the filter chip row, right-aligned. */
.sky-sprint-library-header {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-bottom: var(--space-3);
}

.sky-sprint-library-create-btn {
  appearance: none;
  font: inherit;
  font-weight: 700;
  padding: 0.55rem 1.15rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 0;
  background: linear-gradient(110deg, var(--color-primary) 0%, var(--color-primary-strong) 85%);
  color: var(--color-text-on-light);
  box-shadow: var(--shadow-sm);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  transition: transform var(--motion-fast), box-shadow var(--motion-medium);
}

.sky-sprint-library-create-btn:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

.sky-sprint-library-create-btn:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

.sky-sprint-library-create-btn__icon {
  font-size: 1.05rem;
  line-height: 1;
}

/* ------------------------------------------------------------------
   Editor top strip (B21 M1c) — thin, top-left, holds the "✏ Edit
   mode" badge + readouts (dirty pill, altitude, grid, sel count).
   Replaces the old centered top EDIT MODE bar (whose action buttons
   moved to the bottom action dock).
   ------------------------------------------------------------------ */

.editor-top-strip {
  position: fixed;
  top: 0.6rem;
  left: 0.6rem;
  z-index: 12;
  display: none;
  align-items: center;
  gap: 0.55rem;
  padding: 0.35rem 0.65rem;
  background: rgba(20, 32, 50, 0.92);
  border: 1px solid rgba(79, 163, 255, 0.42);
  border-radius: 999px;
  color: #f3f8fc;
  font: 600 12px/1.15 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  transition: border-color 160ms ease, background 160ms ease;
}

.editor-top-strip--dirty {
  border-color: rgba(255, 196, 84, 0.85);
  background: rgba(38, 26, 12, 0.94);
}

.editor-top-strip__badge {
  font-weight: 700;
  color: rgba(126, 178, 232, 1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 11px;
}

.editor-top-strip--dirty .editor-top-strip__badge {
  color: rgba(255, 196, 84, 1);
}

.editor-top-strip__pill,
.editor-top-strip__readout {
  font-weight: 600;
  font-size: 11px;
  opacity: 0.86;
  color: rgba(210, 224, 240, 1);
}

.editor-top-strip__pill {
  color: rgba(255, 196, 84, 1);
  font-weight: 700;
}

/* ------------------------------------------------------------------
   Editor bottom action dock (B21 M1a) — center-bottom floating dock
   with icon + label + hotkey chip buttons. Replaces the top EDIT
   MODE bar's action buttons. Wraps to two rows on narrow viewports.
   ------------------------------------------------------------------ */

.editor-action-dock {
  position: fixed;
  left: 50%;
  bottom: 1.1rem;
  transform: translateX(-50%);
  z-index: 12;
  display: none;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.45rem 0.55rem;
  max-width: min(96vw, 68rem);
  background: rgba(14, 22, 38, 0.92);
  border: 1px solid rgba(79, 163, 255, 0.35);
  border-radius: 14px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}

.editor-dock-btn {
  appearance: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.7rem;
  border-radius: 10px;
  border: 1px solid rgba(126, 178, 232, 0.24);
  background: rgba(24, 36, 56, 0.88);
  color: #f3f8fc;
  font: 700 12px/1.1 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0.02em;
  transition: background 140ms ease, border-color 140ms ease,
              transform 140ms ease, box-shadow 220ms ease;
}

.editor-dock-btn:hover {
  background: rgba(35, 52, 82, 0.95);
  border-color: rgba(126, 178, 232, 0.55);
  transform: translateY(-1px);
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.4);
}

.editor-dock-btn:focus-visible {
  outline: 2px solid rgba(255, 218, 102, 0.9);
  outline-offset: 2px;
}

.editor-dock-btn__icon {
  font-size: 14px;
  line-height: 1;
}

.editor-dock-btn__label {
  font-weight: 700;
}

.editor-dock-btn__hotkey {
  font: 600 10px/1 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  letter-spacing: 0.02em;
  padding: 0.15rem 0.35rem;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.32);
  color: rgba(210, 224, 240, 0.72);
  border: 1px solid rgba(126, 178, 232, 0.18);
}

.editor-dock-btn--primary {
  background: linear-gradient(110deg, rgba(255, 183, 3, 0.98) 0%, rgba(255, 218, 102, 0.98) 85%);
  color: #0d1d33;
  border-color: rgba(255, 218, 102, 0.9);
}

.editor-dock-btn--primary:hover {
  background: linear-gradient(110deg, rgba(255, 218, 102, 1) 0%, rgba(255, 236, 168, 1) 85%);
  border-color: rgba(255, 236, 168, 1);
}

.editor-dock-btn--primary .editor-dock-btn__hotkey {
  background: rgba(0, 0, 0, 0.18);
  color: rgba(13, 29, 51, 0.72);
  border-color: rgba(13, 29, 51, 0.28);
}

.editor-dock-btn--exit {
  border-color: rgba(255, 132, 132, 0.35);
  color: rgba(255, 200, 200, 1);
}

.editor-dock-btn--exit:hover {
  background: rgba(74, 30, 30, 0.9);
  border-color: rgba(255, 132, 132, 0.7);
}

@media (max-width: 720px) {
  .editor-dock-btn__label {
    display: none;
  }
  .editor-dock-btn__hotkey {
    display: none;
  }
}

/* ------------------------------------------------------------------
   Editor left rail (B21 M1d) — blade-style accordion holding
   Place / Level Config / Object Defaults / Selection. Docked to
   the left edge of the viewport, always visible in edit mode.
   The palette DOM is relocated inside the PLACE blade at edit-mode
   entry (see _editorEnsureLeftRail).
   ------------------------------------------------------------------ */

.editor-left-rail {
  position: fixed;
  top: 3.4rem;                 /* below the top strip */
  bottom: 6.5rem;              /* above the bottom action dock */
  left: 0.7rem;
  /* Bumped from 16rem → 18rem so long palette labels (e.g. "Sticky wall
     · stickyWall", "Spawn point · spawn") fit on a single row without
     wrapping. Toggle collapse with the `\` hotkey. */
  width: 18rem;
  display: none;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0;
  z-index: 12;
  overflow-y: auto;
  overflow-x: hidden;
  color: #f3f8fc;
  font: 12px/1.3 system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* Collapsed state — the rail is hidden but its DOM stays so re-open
   restores expanded/collapsed blade state exactly. Toggle with `\`. */
.editor-left-rail[data-collapsed="true"] {
  display: none !important;
}

.editor-left-rail::-webkit-scrollbar {
  width: 6px;
}
.editor-left-rail::-webkit-scrollbar-thumb {
  background: rgba(126, 178, 232, 0.28);
  border-radius: 3px;
}

.editor-blade {
  background: rgba(20, 32, 50, 0.94);
  border: 1px solid rgba(79, 163, 255, 0.32);
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  flex-shrink: 0;
}

.editor-blade__header {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.7rem;
  background: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  text-align: left;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #8eb4d6;
  transition: background 140ms ease, color 140ms ease;
}

.editor-blade__header:hover {
  background: rgba(35, 52, 82, 0.5);
  color: #cfe5ff;
}

.editor-blade__header:focus-visible {
  outline: 2px solid rgba(255, 218, 102, 0.9);
  outline-offset: -2px;
}

.editor-blade__caret {
  display: inline-block;
  width: 1rem;
  font-size: 10px;
  opacity: 0.72;
}

.editor-blade__title {
  font-weight: 700;
}

.editor-blade__body {
  padding: 0.55rem 0.7rem 0.75rem;
  border-top: 1px solid rgba(126, 178, 232, 0.14);
  background: rgba(14, 22, 38, 0.55);
}

.editor-blade__group-title {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.62;
  margin-bottom: 0.35rem;
}

.editor-blade__slider {
  margin-bottom: 0.55rem;
}

.editor-blade__slider:last-of-type {
  margin-bottom: 0.3rem;
}

.editor-blade__slider-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.18rem;
  font-size: 12px;
}

.editor-blade__slider-value {
  font: 700 11.5px/1 ui-monospace, Consolas, monospace;
  color: rgba(255, 218, 102, 0.95);
}

.editor-blade__slider input[type="range"] {
  width: 100%;
  accent-color: #ffe19a;
}

.editor-blade__hint {
  margin: 0.4rem 0 0;
  font-size: 11px;
  opacity: 0.72;
  line-height: 1.4;
}

.editor-blade__empty {
  font-size: 12px;
  opacity: 0.7;
  padding: 0.15rem 0;
}

.editor-blade__selection-count {
  font-weight: 700;
  font-size: 12.5px;
  color: rgba(255, 218, 102, 0.95);
  margin-bottom: 0.25rem;
}

.editor-blade__selection-kinds {
  font: 600 11.5px/1.35 ui-monospace, Consolas, monospace;
  opacity: 0.82;
  margin-bottom: 0.15rem;
  word-break: break-word;
}

.editor-blade__cta {
  appearance: none;
  font: inherit;
  font-weight: 700;
  padding: 0.5rem 0.9rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 218, 102, 0.55);
  background: rgba(255, 218, 102, 0.14);
  color: rgba(255, 234, 178, 1);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease,
              transform 140ms ease;
  margin-top: 0.35rem;
}

.editor-blade__cta:hover {
  background: rgba(255, 218, 102, 0.28);
  border-color: rgba(255, 236, 168, 0.85);
  transform: translateY(-1px);
}

.editor-blade__cta:focus-visible {
  outline: 2px solid rgba(255, 218, 102, 0.95);
  outline-offset: 2px;
}

/* When the palette is relocated into the PLACE blade, its inline
   position styles are overridden by _editorEnsureLeftRail. This
   selector strips the leftover legacy panel chrome so the palette's
   contents blend with the blade. */
.editor-blade__body #editor-palette {
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/* ------------------------------------------------------------------
   Seed Config modal (B21 M3) — opened from the chooser's Seed
   Generator card. Similar visual language to .create-level-modal
   with a bespoke card body that holds the seed input + sliders.
   Sits above the dashboard (100) for the same reason as the
   chooser.
   ------------------------------------------------------------------ */

.seed-config-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.seed-config-modal[hidden] {
  display: none;
}

.seed-config-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.66);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.seed-config-modal__card {
  position: relative;
  z-index: 1;
  max-width: 32rem;
  width: 100%;
  padding: var(--space-5);
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
  animation: confirm-modal-in 220ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
  max-height: 92vh;
  overflow-y: auto;
}

.seed-config-modal__title {
  margin: 0 0 var(--space-2);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-align: center;
}

.seed-config-modal__intro {
  margin: 0 auto var(--space-4);
  max-width: 26rem;
  font-size: 0.9rem;
  line-height: 1.45;
  opacity: 0.82;
  text-align: center;
}

.seed-config-modal__seed {
  margin-bottom: var(--space-4);
}

.seed-config-modal__seed-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-primary-strong);
  margin-bottom: 0.35rem;
}

.seed-config-modal__seed-row {
  display: flex;
  gap: 0.5rem;
}

.seed-config-modal__seed-row input {
  flex: 1;
  appearance: none;
  padding: 0.55rem 0.7rem;
  background: rgba(7, 32, 58, 0.55);
  border: 1px solid rgba(126, 178, 232, 0.35);
  border-radius: 0.5rem;
  color: var(--color-text);
  font: 700 14px/1.2 ui-monospace, Consolas, monospace;
  letter-spacing: 0.02em;
}

.seed-config-modal__seed-row input:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 2px;
}

.seed-config-modal__roll {
  appearance: none;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  background: rgba(255, 218, 102, 0.14);
  border: 1px solid rgba(255, 218, 102, 0.55);
  color: rgba(255, 234, 178, 1);
  transition: background 140ms ease, transform 140ms ease;
}

.seed-config-modal__roll:hover {
  background: rgba(255, 218, 102, 0.28);
  transform: translateY(-1px);
}

.seed-config-modal__group {
  border: 1px solid rgba(126, 178, 232, 0.22);
  border-radius: 0.6rem;
  padding: 0.65rem 0.8rem 0.8rem;
  margin: 0 0 var(--space-4);
  background: rgba(14, 22, 38, 0.45);
}

.seed-config-modal__group-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0 0.4rem;
  color: rgba(126, 178, 232, 1);
}

.seed-config-modal__slider {
  margin-bottom: 0.55rem;
}

.seed-config-modal__slider:last-child {
  margin-bottom: 0;
}

.seed-config-modal__slider-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-size: 12.5px;
  margin-bottom: 0.15rem;
}

.seed-config-modal__slider-value {
  font: 700 12px/1 ui-monospace, Consolas, monospace;
  color: rgba(255, 218, 102, 0.95);
}

.seed-config-modal__slider input[type="range"] {
  width: 100%;
  accent-color: #ffe19a;
}

.seed-config-modal__advanced {
  margin-bottom: var(--space-4);
  padding: 0.4rem 0.7rem;
  border: 1px dashed rgba(126, 178, 232, 0.24);
  border-radius: 0.5rem;
  background: rgba(14, 22, 38, 0.35);
  font-size: 12px;
}

.seed-config-modal__advanced summary {
  cursor: pointer;
  padding: 0.25rem 0;
  font-weight: 700;
  color: rgba(126, 178, 232, 1);
  letter-spacing: 0.02em;
}

.seed-config-modal__note {
  margin: 0.35rem 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  opacity: 0.78;
}

.seed-config-modal__actions {
  display: flex;
  gap: var(--space-2);
  justify-content: flex-end;
}

.seed-config-modal__cancel,
.seed-config-modal__generate {
  appearance: none;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  padding: 0.55rem 1.2rem;
  border-radius: var(--radius-pill);
  transition: background 140ms ease, transform 140ms ease,
              box-shadow 220ms ease;
}

.seed-config-modal__cancel {
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
}

.seed-config-modal__cancel:hover {
  background: rgba(255, 255, 255, 0.08);
}

.seed-config-modal__generate {
  background: linear-gradient(110deg, var(--color-primary) 0%, var(--color-primary-strong) 85%);
  color: var(--color-text-on-light);
  border: 0;
  box-shadow: var(--shadow-sm);
}

.seed-config-modal__generate:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

.seed-config-modal__cancel:focus-visible,
.seed-config-modal__generate:focus-visible,
.seed-config-modal__roll:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 2px;
}

/* Top-strip seed badge (B21 M3) — right-side pill on the editor
   top strip showing the current seed value when in seeded mode. */
.editor-top-strip__seed-badge {
  font: 700 11px/1 ui-monospace, Consolas, monospace;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: rgba(255, 218, 102, 0.14);
  color: rgba(255, 234, 178, 1);
  border: 1px solid rgba(255, 218, 102, 0.4);
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 140ms ease;
}

.editor-top-strip__seed-badge:hover {
  background: rgba(255, 218, 102, 0.28);
}

/* ------------------------------------------------------------------
   Right-rail Seed panel (B21 M3 refinement).
   Docked below the minimap. Only rendered in seeded mode. Holds
   Reroll / Regenerate / Reset to pristine actions + a compact
   summary of the active seed value + config params.
   ------------------------------------------------------------------ */

.editor-seed-panel {
  position: fixed;
  top: 15.5rem;                /* below the docked minimap */
  right: 0.7rem;
  width: 10rem;
  z-index: 12;
  padding: 0.6rem 0.7rem;
  background: rgba(20, 32, 50, 0.94);
  border: 1px solid rgba(255, 218, 102, 0.42);
  border-radius: 0.6rem;
  color: #f3f8fc;
  font: 12px/1.35 system-ui, -apple-system, "Segoe UI", sans-serif;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}

.editor-seed-panel[hidden] {
  display: none;
}

.editor-seed-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  margin-bottom: 0.4rem;
}

.editor-seed-panel__title {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.68;
}

.editor-seed-panel__value {
  font: 700 11px/1 ui-monospace, Consolas, monospace;
  color: rgba(255, 234, 178, 1);
}

.editor-seed-panel__summary {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.1rem 0.45rem;
  margin: 0 0 0.55rem;
  padding: 0.35rem 0;
  border-top: 1px solid rgba(126, 178, 232, 0.14);
  border-bottom: 1px solid rgba(126, 178, 232, 0.14);
  font-size: 11px;
}

.editor-seed-panel__summary:empty {
  display: none;
}

.editor-seed-panel__summary dt {
  opacity: 0.6;
}

.editor-seed-panel__summary dd {
  margin: 0;
  font-weight: 600;
  color: rgba(210, 224, 240, 1);
  font-variant-numeric: tabular-nums;
}

.editor-seed-panel__actions {
  display: grid;
  gap: 0.3rem;
}

.editor-seed-panel__btn {
  appearance: none;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  font-size: 11.5px;
  padding: 0.4rem 0.5rem;
  border-radius: 0.4rem;
  background: rgba(24, 36, 56, 0.88);
  border: 1px solid rgba(126, 178, 232, 0.28);
  color: #f3f8fc;
  transition: background 140ms ease, border-color 140ms ease;
  text-align: left;
}

.editor-seed-panel__btn:hover:not([disabled]) {
  background: rgba(35, 52, 82, 0.95);
  border-color: rgba(255, 218, 102, 0.5);
}

.editor-seed-panel__btn:focus-visible {
  outline: 2px solid rgba(255, 218, 102, 0.9);
  outline-offset: 2px;
}

.editor-seed-panel__btn[disabled] {
  cursor: not-allowed;
  opacity: 0.42;
}

/* ------------------------------------------------------------------
   Level Config blade — World shape group stubs (B21 M3 refinement).
   Ships the group with disabled controls so the IA is stable when
   M4 (horizontal goal) + M5 (per-level width) land.
   ------------------------------------------------------------------ */

.editor-blade__stub-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.32rem 0.15rem;
  border-radius: 0.3rem;
  font-size: 12px;
  opacity: 0.62;
  cursor: not-allowed;
}

.editor-blade__stub-row + .editor-blade__stub-row {
  border-top: 1px dashed rgba(126, 178, 232, 0.14);
}

.editor-blade__stub-label {
  color: rgba(210, 224, 240, 1);
  font-weight: 600;
}

.editor-blade__stub-pill {
  font: 700 10px/1 ui-monospace, Consolas, monospace;
  padding: 0.15rem 0.4rem;
  border-radius: 999px;
  background: rgba(126, 178, 232, 0.14);
  color: rgba(126, 178, 232, 1);
  border: 1px solid rgba(126, 178, 232, 0.28);
  letter-spacing: 0.02em;
}

/* ------------------------------------------------------------------
   First-launch tutorial modal (B11 v2).
   Sticky "Play Tutorial" vs "Skip to menu" gate. Fired once, then
   never again — the tutorial-status store persists the choice.
   Styling mirrors .confirm-modal but the play button carries the
   amber primary accent (matches the landing's Play Tutorial CTA
   the modal replaces).
------------------------------------------------------------------ */
.tutorial-launch-modal {
  position: fixed;
  inset: 0;
  z-index: 58;
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.tutorial-launch-modal[hidden] {
  display: none;
}

.tutorial-launch-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.tutorial-launch-modal__card {
  position: relative;
  z-index: 1;
  max-width: 30rem;
  width: 100%;
  padding: var(--space-5);
  text-align: center;
  background: var(--color-surface-elevated);
  border: 1px solid rgba(255, 183, 3, 0.55);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg),
              0 0 0 3px rgba(255, 183, 3, 0.14);
  color: var(--color-text);
  animation: confirm-modal-in 260ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
}

.tutorial-launch-modal__eyebrow {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-primary-strong, #ffb703);
  opacity: 0.9;
  margin-bottom: var(--space-2);
}

.tutorial-launch-modal__title {
  margin: 0 0 var(--space-3);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.tutorial-launch-modal__message {
  margin: 0 0 var(--space-4);
  font-size: 0.94rem;
  line-height: 1.45;
  opacity: 0.86;
}

.tutorial-launch-modal__actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
}

.tutorial-launch-modal__play,
.tutorial-launch-modal__skip {
  appearance: none;
  font: inherit;
  font-weight: 700;
  padding: 0.75rem 1.6rem;
  border-radius: var(--radius-pill);
  cursor: pointer;
  border: 0;
  transition: background var(--motion-fast), transform var(--motion-fast),
              box-shadow var(--motion-medium);
}

.tutorial-launch-modal__play {
  background: rgba(255, 183, 3, 0.9);
  color: #0b1a2d;
  box-shadow: 0 4px 18px rgba(255, 183, 3, 0.25);
}

.tutorial-launch-modal__play:hover {
  background: #ffb703;
  transform: translateY(-1px);
}

.tutorial-launch-modal__skip {
  background: transparent;
  color: var(--color-text);
  border: 1px solid var(--color-border-strong);
  opacity: 0.85;
}

.tutorial-launch-modal__skip:hover {
  background: rgba(255, 255, 255, 0.08);
  opacity: 1;
}

.tutorial-launch-modal__play:focus-visible,
.tutorial-launch-modal__skip:focus-visible {
  outline: 2px solid rgba(255, 183, 3, 0.9);
  outline-offset: 3px;
}

/* Pause menu — full-screen overlay shown mid-run via Esc. Centered
   card with a vertical button stack: Resume (primary) / Restart Level
   (secondary) / Quit to Main Menu (secondary). Sits above .confirm-modal
   (55) so a pause is always topmost. */
.pause-menu {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.pause-menu[hidden] {
  display: none;
}

.pause-menu__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.pause-menu__card {
  position: relative;
  z-index: 1;
  min-width: 22rem;
  max-width: 28rem;
  padding: var(--space-5) var(--space-5) var(--space-4);
  text-align: center;
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  color: var(--color-text);
  animation: confirm-modal-in 220ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
}

.pause-menu__title {
  margin: 0 0 var(--space-4);
  font-size: 1.6rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

.pause-menu__actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: stretch;
}

.pause-menu__actions button {
  appearance: none;
  font: inherit;
  font-weight: 700;
  padding: 0.7rem 1.2rem;
  border-radius: var(--radius-md);
  cursor: pointer;
  border: 1px solid var(--color-border-strong);
  background: transparent;
  color: var(--color-text);
  transition: background var(--motion-fast), transform var(--motion-fast),
              box-shadow var(--motion-medium);
}

.pause-menu__actions button:hover {
  background: rgba(255, 255, 255, 0.08);
}

.pause-menu__actions button:focus-visible {
  outline: 2px solid var(--color-primary-strong);
  outline-offset: 3px;
}

.pause-menu__actions button:disabled {
  opacity: 0.38;
  cursor: not-allowed;
  /* Suppress hover lift / colour shift on disabled buttons. */
  transform: none !important;
  box-shadow: none !important;
}

.pause-menu__actions button:disabled:hover {
  background: transparent;
}

.pause-menu__resume {
  background: var(--color-primary) !important;
  color: var(--color-text-on-light) !important;
  border-color: transparent !important;
  box-shadow: var(--shadow-sm);
}

.pause-menu__resume:hover {
  background: var(--color-primary-strong) !important;
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}

/* Toast notifications — non-obstructive top-centre messages.
   Used for run-start errors (quota exhausted, daily not published,
   etc) where the legacy showOverlay() pattern obstructed the whole
   screen and blocked the OK button.

   The host fills the top strip but is pointer-events:none so the
   top bar + game canvas behind it stay clickable. Individual
   .toast children re-enable pointer-events so the close button is
   actually clickable. */
.toast-host {
  position: fixed;
  /* Aligned with the HUD chips + identity widget (0.75rem from
     the viewport top) now that the top bar is retired. The
     previous 4.4rem offset was clearing the persistent top bar. */
  top: 0.75rem;
  left: 0;
  right: 0;
  z-index: 27;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  pointer-events: none;
  padding: 0 var(--space-3);
}

.toast {
  pointer-events: auto;
  max-width: min(36rem, 92vw);
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  color: var(--color-text);
  font-family: var(--font-ui);
  animation: toast-in 220ms cubic-bezier(0.24, 0.92, 0.43, 0.97);
}

@keyframes toast-in {
  from { transform: translateY(-12px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.toast.is-leaving {
  animation: toast-out 180ms ease forwards;
}

@keyframes toast-out {
  to { transform: translateY(-8px); opacity: 0; }
}

.toast--warn {
  border-color: rgba(245, 179, 76, 0.6);
}

.toast--error {
  border-color: rgba(255, 107, 107, 0.6);
}

.toast__icon {
  font-size: 1.4rem;
  line-height: 1.2;
  flex-shrink: 0;
}

.toast__body {
  flex: 1;
  min-width: 0;
}

.toast__title {
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: 0.01em;
  margin: 0 0 0.18rem;
}

.toast__message {
  margin: 0;
  font-size: 0.9rem;
  opacity: 0.86;
  line-height: 1.36;
}

.toast__close {
  background: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.25rem 0.45rem;
  border-radius: var(--radius-sm);
  opacity: 0.72;
  transition: opacity var(--motion-fast), background var(--motion-fast);
  flex-shrink: 0;
}

.toast__close:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.08);
}

.toast__close:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  opacity: 1;
}

/* DEV mode HUD chip — visible while playerProfile.developerMode is on.
   Stronger purple tint (admin/dev affordance color) to clearly signal
   "this run won't count toward the leaderboard." Pairs with the
   dev-mode confirm dialog at run start so dev-flagged runs are never
   silent. Toggled by game.js's _syncDevModeHudChip() each frame. */
.chip-dev {
  background: rgba(179, 136, 255, 0.16);
  border: 1px solid rgba(179, 136, 255, 0.5);
  color: var(--color-admin-soft);
}

/* (Retired: .home-screen__tiles display:none + .home-screen
   grid-template overrides. The legacy home-screen lobby has been
   deleted in favor of .landing.) */

/* Art-overhaul Phase 3 — boot splash. Fullscreen overlay shown while
   the asset loader preloads sprite PNGs before the first game frame.
   z-index: 1000 — above every other surface (the previous max was 90).
   Reduce-motion friendly: no animations. Neutral design — branded
   variant (logo) is a later cosmetic pass. */
.sky-sprint-boot-splash {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 20% 20%, #1a5c7e 0%, #0a263f 55%, #071624 100%);
  color: var(--color-text);
  font-family: var(--font-ui);
}
.sky-sprint-boot-splash .boot-splash__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-6);
}
.sky-sprint-boot-splash .boot-splash__title {
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}
.sky-sprint-boot-splash .boot-splash__bar {
  width: 260px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}
.sky-sprint-boot-splash .boot-splash__fill {
  width: 0%;
  height: 100%;
  background: var(--color-primary);
  transition: width var(--motion-medium);
}
.sky-sprint-boot-splash .boot-splash__pct {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* Art-overhaul Phase 6 — character swatch sprite thumbnail. Injected
   by SkySprintVariants.decorateSwatchGrid when a variant's idle.0
   sprite has loaded. Fills the 22×22 swatch button, overriding the
   solid --swatch background. Pointer-events disabled so the parent
   button still catches clicks. No-op when no variant sprites exist. */
.character-color-grid .character-color-swatch__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
  pointer-events: none;
  image-rendering: auto;
}
.character-color-grid .character-color-swatch {
  position: relative;
}
