:root {
  --mono: 'JetBrains Mono', monospace;
  --font-xs: clamp(10px, 1.8vw, 12px);
  --font-s: clamp(12px, 2.5vw, 16px);
  --font-m: clamp(14px, 3vw, 20px);
  --font-l: clamp(18px, 3.5vw, 24px);
  --font-xl: clamp(24px, 5vw, 36px);
  --font-xxl: clamp(32px, 7vw, 48px);
  --letter-spacing-label: 0.5px;
  --gap-xs: clamp(3px, 0.25vw, 6px);
  --gap-s: clamp(6px, 0.5vw, 10px);
  --gap-m: clamp(10px, 0.8vw, 16px);
  --gap-l: clamp(20px, 2vw, 32px);
  --gap-xl: clamp(32px, 3vw, 48px);
  --menu-bottom-bar-space: clamp(64px, 6vw, 88px); /* reserved height of .menu-bottom-bar (tabs + Start), which overlays every tab's bottom edge */
  /* What .menu-card reserves at its bottom = bar height + (loading ? #loading-strip height : 0). The strip
     is a second absolute overlay stacked ABOVE the bar, so the card must clear both while it shows; both
     tokens are kept equal to the overlays' true rendered heights by _syncBottomBarSpace() (ui.js). Default
     to the bar-only reservation for the first paint before JS measures. */
  --menu-bottom-reserve: var(--menu-bottom-bar-space);
  --size-icon-s: clamp(20px, 1.4vw, 26px);
  --size-icon-m: clamp(20px, 1.6vw, 28px);
  --radius-s: 0;
  --radius-m: 0;
  --radius-l: 0;
  --transition-fast: 0.15s;
  --opacity-disabled: 0.3;
  --border-subtle: rgba(255, 255, 255, 0.1);
  --border-dim: rgba(255, 255, 255, 0.15);
  --border-mid: rgba(255, 255, 255, 0.25);
  --surface-1: rgba(255, 255, 255, 0.03);
  --shadow-card: 0 0 40px rgba(0, 0, 0, 0.6);
  --content-max-width: 40em;

  /* Chinese pigment palette */
  --cinnabar: #b5453a;                    /* 朱砂 zhūshā — hutong doors, lanterns */
  --cinnabar-dim: rgba(181, 69, 58, 0.12);
  --cinnabar-mid: rgba(181, 69, 58, 0.3);
  --cinnabar-bright: rgba(181, 69, 58, 0.5);
  --cinnabar-hover: rgba(181, 69, 58, 0.65);
  --gold-ochre: #c8a45a;                  /* 石黄 shíhuáng — imperial mineral gold */
  --gold-ochre-dim: rgba(200, 164, 90, 0.12);
  --gold-ochre-mid: rgba(200, 164, 90, 0.3);
  --qing: #52837b;                         /* 青 qīng — celadon, temple roofs, jade */
  --qing-dim: rgba(82, 131, 123, 0.12);
  --qing-mid: rgba(82, 131, 123, 0.3);
  --qing-bright: rgba(72, 165, 150, 0.7);
  --qing-hover: rgba(72, 165, 150, 0.85);
  --qing-select-bg: rgba(72, 165, 150, 0.3);
  --qing-select-border: rgba(72, 165, 150, 0.6);
  --ink-black: rgba(22, 18, 14, 0.94);    /* 墨 mò — warm ink on xuan paper */
  --ink-black-solid: #1a1614;              /* opaque fallback for native controls (e.g. <option>) that can't render translucent --ink-black */

  /* Text — warm rice paper tones */
  --text-primary: #ede6d8;                /* 宣纸白 — rice paper white */
  --text-secondary: #d0c8b8;
  --text-muted: #a8a090;
  --text-dim: #787068;
  --text-on-accent: #fff;

  /* Semantic aliases */
  --bg-card: var(--ink-black);
  --accent: var(--cinnabar);
  --accent-dim: var(--cinnabar-dim);
  --accent-mid: var(--cinnabar-mid);
  --accent-bright: var(--cinnabar-bright);
}

@font-face {
  font-family: 'JetBrains Mono';
  src: url('./fonts/JetBrainsMono[wght].ttf') format('truetype');
  font-style: normal;
}

@font-face {
  font-family: 'JetBrains Mono';
  src: url('./fonts/JetBrainsMono-Italic[wght].ttf') format('truetype');
  font-style: italic;
}

@font-face {
  font-family: 'Special Gothic';
  src: url('./fonts/SpecialGothic-VariableFont_wdth,wght.ttf') format('truetype');
}

@font-face {
  font-family: 'Special Gothic Condensed One';
  src: url('./fonts/SpecialGothicCondensedOne-Regular.ttf') format('truetype');
}

@font-face {
  font-family: 'Special Gothic Expanded One';
  src: url('./fonts/SpecialGothicExpandedOne-Regular.ttf') format('truetype');
}

@font-face {
  font-family: 'Noto Sans SC subset';
  src: url('./fonts/NotoSansSC-subset.ttf') format('truetype');
}

/* ========================================
   Base Styles
   ======================================== */

html {
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  margin: 0;
  overflow: hidden;
  overscroll-behavior: none;
  background: #111;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  touch-action: manipulation;
  -webkit-touch-callout: none;
  /*
  -webkit-font-smoothing: none;
  -moz-osx-font-smoothing: unset;
  text-rendering: optimizeSpeed;
  */
  font-family: var(--mono);
  font-size: var(--font-s);
}

/* Boot overlay — covers the brief gap before the Colour/Tuning turntable screen is ready. No progress
   messaging: nothing is gating on textures/level load anymore by the time this is visible. */
#boot-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gap-l);
  background: #111;
}

.boot-overlay-title {
  font-size: var(--font-xl);
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--text-primary);
}

.boot-overlay-spinner {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 3px solid var(--border-mid);
  border-top-color: var(--cinnabar);
  animation: boot-overlay-spin 0.8s linear infinite;
}

.boot-overlay-status {
  font-size: var(--font-s);
  color: var(--text-muted);
  letter-spacing: 1px;
}

@keyframes boot-overlay-spin {
  to { transform: rotate(360deg); }
}

button, input, select {
  font-family: var(--mono);
  font-size: var(--font-s);
}

canvas {
  display: block;
  touch-action: none;
}

/* ========================================
   Debug display
   ======================================== */

#debug-container {
  position: absolute;
  top: calc(100vh - 100px);
  height: 80px;
  right: 20px;
  font-size: var(--font-xs);
  color: rgba(255,255,255,0.6);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  width: 200px;
}

#debug {
  display: none;
}

/* ========================================
   Main Menu (tabbed)
   ======================================== */

/* Shared full-screen overlay shell — every top-level modal/menu surface (main menu, dialogs,
   race/delivery results, the map view) hides via display:none and covers the viewport when shown.
   z-index is set per-consumer below since layering order matters (dialogs sit above the menu, the
   map view sits below it). */
#main-menu,
#race-results,
#delivery-results,
#dialog-overlay,
#map-view {
  display: none;
  position: fixed;
  inset: 0;
}

#main-menu {
  z-index: 9999;
}

.pause-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.4);
}

/* Main-menu backdrop is opaque by default (startup / main menu) so nothing on the paused game canvas
   bleeds through the menu tabs — in particular the turntable cat, which is reparented back into the
   game scene on leaving Colour/Tuning and used to ghost through at the old 40% alpha. Only a genuine
   mid-game pause (.paused-in-game, set in showMainMenu) dims back to translucent so you can still see
   your paused run behind the menu. Scoped to #main-menu so the dialog/results overlays keep their own
   translucent dim. */
#main-menu .pause-overlay {
  background: var(--ink-black-solid);
}

#main-menu.paused-in-game .pause-overlay {
  background: rgba(0, 0, 0, 0.4);
}

/* Shared "card surface" — dark ink background, no border, ambient shadow. Every popup/panel
   surface in the menu system (centered dialogs and the full-screen menu panel alike) uses this
   same base look. */
.pause-card,
.dialog-card,
.menu-card {
  background: var(--bg-card);
  border: none;
  box-shadow: var(--shadow-card);
}

/* Shared "centered popup" shape — .menu-card is a full-screen panel instead (see below), so it
   doesn't join this block. */
.pause-card,
.dialog-card {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: var(--gap-l);
  width: 90vw;
}

.pause-card {
  display: flex;
  flex-direction: column;
  gap: var(--gap-m);
  min-width: 280px;
  max-width: clamp(320px, 30vw, 440px);
}

/* Full-screen panel — every tab (Colour/Tuning/Play/Scores/Settings) shares this shell. No more
   small centered card; Play/Scores/Settings have nothing to reveal behind them, so the panel takes
   the whole viewport. Only Colour/Tuning narrow to a docked-right column (see
   #main-menu.tab-active-turntable below) so the spinning cat shows + is draggable in the space
   that opens up on the left. */
.menu-card {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--gap-s);
  /* Bottom padding reserves space for .menu-bottom-bar, which is a sibling overlaid on top of this
     card's bottom edge (not nested in it) so it can span the full screen width on every tab. Extra
     breathing room on top/sides (--gap-xl) reads fine on a spacious desktop viewport but eats too
     much of a small phone screen — .is-mobile below tightens it back to --gap-s/--gap-l. */
  padding: var(--gap-xl) var(--gap-xl) var(--menu-bottom-reserve) var(--gap-xl);
  overflow-y: auto;
}

.is-mobile .menu-card {
  padding: var(--gap-s) var(--gap-l) var(--menu-bottom-reserve) var(--gap-l);
}

/* Colour/Tuning only: narrow to a docked-right column so the turntable shows in the space to the left. */
#main-menu.tab-active-turntable .menu-card {
  left: auto;
  right: 0;
  width: clamp(320px, 38vw, 460px);
}

/* Bottom bar: tab strip + the persistent Start/Resume action, one row, spanning the full screen width
   on every tab (a sibling of .menu-card, overlaid above it — not nested, so .menu-card's width doesn't
   constrain it). Tabs double as Colour/Tuning's "back" — there's no dedicated back button, switching to
   any other tab exits it. */
.menu-bottom-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  /* Real height is pinned to the SAME constant #loading-strip offsets itself by (bottom:
     var(--menu-bottom-bar-space)), so the strip sits flush on the bar's top edge with no sliver
     of page background between them. Without this the reservation and the bar's content-driven
     height are computed independently and drift apart. box-sizing so the padding is included. */
  min-height: var(--menu-bottom-bar-space);
  box-sizing: border-box;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-s);
  padding: var(--gap-s) var(--gap-l) var(--gap-m);
  background: var(--bg-card);
  box-shadow: 0 -10px 16px -10px rgba(0, 0, 0, 0.6);
}

/* Stays clickable even while Colour/Tuning make #main-menu click-through for the turntable. */
#main-menu.tab-active-turntable .menu-bottom-bar {
  pointer-events: auto;
}

/* Boot loading strip — a full-width sibling sitting immediately above .menu-bottom-bar. Hidden
   (display:none, not just invisible) except while #main-menu carries .is-loading, so it never
   permanently grows the reserved bottom-bar space (--menu-bottom-bar-space is sized for
   .menu-bottom-bar alone, via .menu-card's bottom padding). See showMainMenu()/showLoadingReady()
   in ui.js for where the class is set/cleared. */
#loading-strip {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--menu-bottom-bar-space);
  z-index: 3;
  padding: var(--gap-s) var(--gap-l);
  background: var(--bg-card);
  /* Purely informational (bar + status text, no controls) — must never intercept taps on the card
     content it overlays. The card also reserves its height via --menu-bottom-reserve, but this guards
     the frame between the strip appearing and _syncBottomBarSpace() re-measuring. */
  pointer-events: none;
}

#main-menu.is-loading #loading-strip {
  display: block;
}

.menu-tabs {
  display: flex;
  gap: var(--gap-l);
  border-bottom: none;
}

.menu-tab {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--gap-s);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  padding: var(--gap-s);
  cursor: pointer;
  font-size: var(--font-s);
  letter-spacing: 1px;
  touch-action: manipulation;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.menu-tab-icon {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
}

/* Mobile: icon-only, so 5 tabs + Start/Resume still fit one row (the accessible name comes from
   the button's aria-label, set alongside the now-hidden label in index.html, so nothing is lost
   for assistive tech). */
.is-mobile .menu-tab-label {
  display: none;
}

/* Mobile menu text runs slightly smaller than desktop — redeclare the --font-* custom properties
   scoped to #main-menu only (not :root/.is-mobile globally) so the game HUD, which reads the same
   tokens, is unaffected. Every element inside #main-menu that already uses var(--font-*) (settings
   items, customize sliders, tab labels, etc.) picks these up via normal custom-property cascade.
   ~12% smaller than the :root clamp() ranges, same shape/units. */
.is-mobile #main-menu {
  --font-xs: clamp(9px, 1.6vw, 11px);
  --font-s: clamp(11px, 2.2vw, 14px);
  --font-m: clamp(12px, 2.6vw, 18px);
  --font-l: clamp(16px, 3vw, 21px);
  --font-xl: clamp(21px, 4.4vw, 32px);
  --font-xxl: clamp(28px, 6.2vw, 42px);
}

.menu-tab.active {
  color: var(--text-primary);
  border-bottom-color: var(--cinnabar);
}

.menu-tab:hover {
  color: var(--cinnabar);
}

.menu-tab:focus-visible {
  color: var(--cinnabar);
  outline: 1px solid var(--cinnabar-mid);
  outline-offset: -2px;
}

.menu-tab:disabled {
  opacity: var(--opacity-disabled);
  cursor: default;
  pointer-events: none;
}

.tab-panel {
  display: none;
}

.tab-panel.active {
  display: flex;
  flex-direction: column;
  gap: var(--gap-s);
}

/* Side-by-side items within tabs */
.tab-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap-s);
}

.tab-row-3 {
  grid-template-columns: 1fr 1fr 1fr;
}

.tab-row-4 {
  grid-template-columns: repeat(4, 1fr);
}

/* Start/Resume live in .menu-bottom-bar now (which owns the sticky/background/shadow treatment). */
.menu-actions {
  display: flex;
  gap: var(--gap-s);
  align-items: stretch;
}

/* Natural-width, right-aligned buttons (not stretched). Colour carries the hierarchy — Resume keeps
   the cinnabar treatment, Start/Restart is demoted — so they don't need to differ in size. */
.menu-actions #start-btn,
.menu-actions #resume-btn { flex: 0 0 auto; }

/* Icon+label buttons: desktop shows the text label (icons hidden), mobile shows a compact icon-only
   button (label hidden) so 6 icon-tabs + Start + Resume all fit the one no-wrap bottom-bar row on a
   phone — previously the two text buttons pushed Resume off the right edge. Mirrors how .menu-tab
   collapses its label to an icon on mobile. */
.menu-actions .pause-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-s);
}

.menu-actions .btn-icon {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  display: none;
}

.is-mobile .menu-actions .btn-label {
  display: none;
}

.is-mobile .menu-actions .pause-primary {
  padding: var(--gap-s);
}

.is-mobile .menu-actions #resume-btn .btn-icon,
.is-mobile .menu-actions #start-btn .btn-icon-start {
  display: block;
}

/* Start toggles to "Restart" when the current settings match the running game (pause-primary--destructive):
   swap the play glyph for a restart glyph so the icon-only mobile button still reads correctly. */
.is-mobile .menu-actions #start-btn.pause-primary--destructive .btn-icon-start {
  display: none;
}

.is-mobile .menu-actions #start-btn.pause-primary--destructive .btn-icon-restart {
  display: block;
}

#in-game-actions {
  display: none;
  flex-direction: column;
  gap: var(--gap-s);
}

/* ========================================
   Dialog Overlay
   ======================================== */

#dialog-overlay {
  z-index: 10001;
}

.dialog-card {
  border-radius: var(--radius-m);
  min-width: 260px;
  max-width: clamp(360px, 30vw, 480px);
  text-align: center;
}

#dialog-message {
  font-size: var(--font-s);
  color: var(--text-secondary);
  margin-bottom: clamp(12px, 1vw, 20px);
  line-height: 1.5;
}

#dialog-actions {
  display: flex;
  gap: var(--gap-s);
}

.mode-info {
  display: none;
  font-size: var(--font-s);
  color: var(--text-secondary);
  line-height: 1.7;
  padding: var(--gap-xs) 0;
  min-height: 2.4em;
}

.pause-primary {
  padding: clamp(12px, 1vw, 18px) var(--gap-l);
  font-size: var(--font-s);
  background: var(--cinnabar-bright);
  color: var(--text-primary);
  border: 1px solid rgba(181, 69, 58, 0.7);
  border-radius: var(--radius-m);
  cursor: pointer;
  touch-action: manipulation;
  text-align: center;
  letter-spacing: var(--letter-spacing-label);
  transition: background var(--transition-fast), opacity var(--transition-fast);
}

.pause-primary:disabled {
  opacity: var(--opacity-disabled);
  cursor: default;
  pointer-events: none;
}

/* "Locked" reads as disabled but stays clickable — a native :disabled button emits no click event,
   so it can't surface a "still loading" toast. Keep pointer-events on; the click handler no-ops+toasts. */
#start-btn.is-locked {
  opacity: var(--opacity-disabled);
  cursor: default;
}
#start-btn.is-locked:hover {
  background: var(--cinnabar-bright);   /* cancel the hover brighten so it never reads as active */
}

.pause-primary:hover {
  background: var(--cinnabar-hover);
}

.pause-primary:focus-visible {
  background: var(--cinnabar-hover);
  outline: 1px solid var(--cinnabar);
  outline-offset: 2px;
}

/* Restart: bright qing with white text to distinguish from cinnabar Start */
#start-btn.pause-primary--destructive {
  background: var(--qing-bright);
  border-color: rgba(72, 165, 150, 0.8);
  color: var(--text-on-accent);
}

#start-btn.pause-primary--destructive:hover {
  background: var(--qing-hover);
}

#start-btn.pause-primary--destructive:focus-visible {
  background: var(--qing-hover);
  outline-color: rgba(72, 165, 150, 0.9);
}

/* While a run is in progress, Resume is the primary action — the Start/Restart button steps back to
   a quiet secondary so it doesn't compete (and a destructive Restart isn't visually tempting). */
#start-btn.action-demoted,
#start-btn.action-demoted.pause-primary--destructive {
  background: rgba(255, 255, 255, 0.07);
  border-color: var(--border-subtle);
  color: var(--text-muted);
}

#start-btn.action-demoted:hover,
#start-btn.action-demoted.pause-primary--destructive:hover {
  background: var(--qing-dim);
  color: var(--text-primary);
}

.pause-secondary {
  display: flex;
  gap: var(--gap-s);
  justify-content: flex-end;
}

.menu-button-secondary {
  width: auto;
  padding: clamp(7px, 0.55vw, 12px) var(--gap-m);
  font-size: var(--font-s);
  background: rgba(255, 255, 255, 0.07);
  color: var(--text-muted);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-m);
  cursor: pointer;
  touch-action: manipulation;
  text-align: center;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.menu-button-secondary:hover {
  background: var(--qing-dim);
  color: var(--text-primary);
}

.menu-button-secondary:focus-visible {
  background: var(--qing-dim);
  color: var(--text-primary);
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

/* ========================================
   Settings Menu
   ======================================== */

/* Settings and scores content lives inside tab panels now */

.stat-row {
  display: flex;
  justify-content: space-between;
  padding: var(--gap-xs) 0;
}

.stat-label {
  color: var(--text-muted);
  font-size: var(--font-s);
}

.stat-value {
  color: var(--qing);
  font-size: var(--font-s);
  font-weight: bold;
}

/* ========================================
   Map View
   ======================================== */

#map-view {
  z-index: 9998;
  pointer-events: none; /* Keep this none so canvas below can receive events */
}

#map-view.active {
  display: block;
  /* Don't set pointer-events: auto here - we want the canvas to receive events */
}

#map-controls {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  pointer-events: auto;
}

#map-help {
  padding: var(--gap-m) clamp(16px, 1.2vw, 28px);
  font-size: var(--font-s);
  background: rgba(0, 0, 0, 0.85);
  color: var(--text-primary);
  border: none;
  text-align: center;
  white-space: nowrap;
}

/* ============================================================================
   COLOUR / TUNING TABS — the turntable renders on the game canvas BEHIND #main-menu; while either
   is active, #main-menu goes click-through (pause-overlay hidden, pointer-events:none) except for
   the docked .menu-card panel and the auto-spin button, so the left region shows + the model is
   draggable. Every other tab keeps #main-menu's normal opaque/dimmed behaviour.
   ============================================================================ */
#main-menu.tab-active-turntable {
  pointer-events: none;
}

#main-menu.tab-active-turntable .pause-overlay {
  display: none;
}

#main-menu.tab-active-turntable .menu-card {
  pointer-events: auto;
}

#customize-autorotate-btn {
  display: none;
  position: absolute;
  /* Lifted clear of .menu-bottom-bar, which overlays the very bottom of the screen. */
  bottom: calc(var(--menu-bottom-bar-space) + var(--gap-l));
  left: var(--gap-l);
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  pointer-events: auto;
  border-radius: 50%;
  cursor: pointer;
  touch-action: manipulation;
  color: var(--text-primary);
  background: var(--ink-black);
  border: 1px solid var(--border-mid);
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

#customize-autorotate-btn svg {
  width: 20px;
  height: 20px;
}

#main-menu.tab-active-turntable #customize-autorotate-btn {
  display: flex;
}

#customize-autorotate-btn:hover,
#customize-autorotate-btn:focus-visible {
  border-color: var(--qing-bright);
  outline: none;
}

#customize-autorotate-btn.is-off {
  color: var(--text-muted);
  border-style: dashed;
  opacity: 0.55;
}

/* Border-box scoped to the menu card: its width is 100% (mobile) / a clamp (desktop) PLUS padding,
   and the stylesheet has no global box-sizing reset — without this the panel overflows the viewport
   and every right-edge element (chart axis labels, wrapped note text) is clipped off-screen. */
.menu-card,
.menu-card * {
  box-sizing: border-box;
}

/* Colour panel: two columns. Sliders, toggles, colour sliders and section headers all span the full
   width via the shared .settings-item flow (see below) — no collapsible wells, every control is
   visible at once. */
.settings-grid.customize-colour-grid {
  grid-template-columns: 1fr 1fr;
}

/* Sliders + toggles + colour sliders: label on its own line, control below — two lines per box. */
.customize-colour-grid > .settings-item {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: clamp(2px, 0.3vw, 4px);
  padding: var(--gap-s) clamp(8px, 0.6vw, 12px);
}
.customize-colour-grid > .settings-item .settings-label { margin-bottom: 0; }
.customize-colour-grid > .settings-item .slider { margin-bottom: 0; width: 100%; }
.customize-colour-grid > .settings-item .settings-checkbox {
  margin-top: 0;
  width: var(--size-icon-s);
  height: var(--size-icon-s);
}


/* Tuning panel — sliders (right-docked column), each axis a label + unit readout (.settings-value) +
   the shared .slider. The rider hint + the honest diagram for the touched axis live on the diagram
   STAGE in the left region (.tuning-diagram-stage below), not on each slider. */
.customize-tuning {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap-s);
  align-content: start;
}

/* The reset button is one column wide like Colour's Randomize (see .customize-action-btn) and the
   axis sliders pair up two-per-row. */
.customize-tuning-axis {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--surface-1);
  padding: clamp(8px, 0.6vw, 14px);
  border-radius: var(--radius-m);
  /* Transparent by default so the active border below doesn't shift layout; the whole card is a
     click target that promotes its axis to the diagram stage. */
  border: 1px solid transparent;
  cursor: pointer;
}

.customize-tuning-axis.is-active {
  border-color: var(--qing-bright);
  background: rgba(72, 165, 150, 0.07);
}

/* Tuning diagram stage — the honest readout for the axis you're touching, shown ONLY on the Tuning tab.
   Sits over the left region (desktop) / above the sheet (mobile), on a near-opaque ink backdrop so the
   diagram reads while the dimmed turntable sits behind it. Non-interactive: you drive it from the
   sliders, so pointer-events stay off and a drag still spins the model behind. Drawn by tuning-diagrams.js. */
.tuning-diagram-stage {
  display: none;
  position: absolute;
  top: var(--gap-xl);
  left: var(--gap-xl);
  right: calc(clamp(320px, 38vw, 460px) + var(--gap-l));
  bottom: calc(var(--menu-bottom-bar-space) + var(--gap-l));
  z-index: 2;
  pointer-events: none;
  flex-direction: column;
  gap: var(--gap-xs);
  padding: var(--gap-l);
  /* Opaque, not translucent: the diagram is the subject on this tab, so a clean engineering panel
     reads better than the turntable bleeding through sparse diagrams' empty areas. */
  background: var(--ink-black-solid);
  border: 1px solid var(--border-subtle);
  box-sizing: border-box;
}

#main-menu.tab-active-tuning .tuning-diagram-stage {
  display: flex;
}

/* The model is behind the diagram on this tab, so the auto-spin control is pointless here. */
#main-menu.tab-active-tuning #customize-autorotate-btn {
  display: none;
}

.tds-title {
  font-size: var(--font-m);
  font-weight: 600;
  color: var(--text-primary);
}

.tds-hint {
  margin: 0;
  font-size: var(--font-s);
  color: var(--text-muted);
  line-height: 1.4;
  max-width: 62ch;
}

#tds-canvas {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
}

/* Mobile: the panel is a bottom sheet (46vh), so the stage fills the area above it. */
.is-mobile #main-menu.tab-active-tuning .tuning-diagram-stage {
  top: var(--gap-l);
  left: var(--gap-l);
  right: var(--gap-l);
  bottom: calc(46vh + var(--gap-s));
}

.customize-tuning-reset {
  margin-top: var(--gap-s);
  padding: var(--gap-s);
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--text-dim);
  border-radius: var(--radius-l);
  font: inherit;
  font-size: var(--font-s);
  cursor: pointer;
}

.customize-tuning-reset:hover,
.customize-tuning-reset:focus-visible {
  border-color: var(--qing-bright);
  color: var(--text-on-accent);
}

/* A standalone one-column action button (Colour's Randomize, Tuning's Reset to stock) — sized like
   any other grid cell rather than spanning the full row, sitting by itself via .customize-grid-gap. */
.customize-action-btn {
  width: 100%;
}

/* Tuning's Reset sits half-width in the now single-column tuning grid — as if it were the left cell of a
   two-column row. Targeted by ID: the .customize-tuning-reset class is shared with Colour's Randomize,
   which lives in a two-column grid and must stay a full cell wide. */
#cz-tune-reset {
  width: calc(50% - var(--gap-s) / 2);
  justify-self: start;
}

/* Empty row-breaker (no label) that gives a standalone item (the scooter colour pair, or an action
   button) a bit of space above it and forces it onto its own row, without a text section header. */
.customize-grid-gap {
  grid-column: 1 / -1;
  height: 0;
  margin-top: var(--gap-m);
}

/* Mobile Colour/Tuning only: the panel becomes a bottom sheet so the turntable fills the area above it
   (the JS framing centres the model + aims lower so it sits in that upper area). Play/Scores/Settings
   have nothing to reveal, so they stay full-screen on mobile too. Auto-spin moves to the top-right so
   it doesn't sit behind the sheet. Tight top/side padding for the small sheet, but the bottom keeps the
   shared --menu-bottom-reserve like every other card so its controls clear the bottom-bar + loading
   strip overlays (which sit over the sheet's bottom edge). */
.is-mobile #main-menu.tab-active-turntable .menu-card {
  top: auto;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 42vh;
  padding: clamp(12px, 3vw, 18px) clamp(12px, 3vw, 18px) var(--menu-bottom-reserve);
}

.is-mobile #customize-autorotate-btn {
  top: var(--gap-l);
  bottom: auto;
  right: var(--gap-l);
  left: auto;
}

.settings-title {
  margin: 0 0 var(--gap-m) 0;
  font-size: var(--font-m);
  font-weight: normal;
  color: var(--text-secondary);
  text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.9);
}

/* Level dropdown sits beside the difficulty chips on the Play tab — match their font size. */
#level-select {
  font-size: var(--font-s);
}

/* Settings page: allow vertical scroll but block pinch-zoom. iOS Safari ignores the viewport's
   user-scalable=no, so the pinch gesture has to be killed at the element level here. */
.tab-panel[data-tab="settings"] {
  touch-action: pan-y;
}

/* Grid layout for main settings */
.settings-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--gap-s);
}

.settings-item {
  background: var(--surface-1);
  padding: clamp(8px, 0.6vw, 14px);
  border: none;
  border-radius: var(--radius-m);
}

.settings-label {
  display: block;
  font-size: var(--font-s);
  color: var(--text-secondary);
  margin-bottom: var(--gap-xs);
  letter-spacing: var(--letter-spacing-label);
}

.settings-name {
  display: inline-block;
  min-width: 8em;
}

.settings-value {
  color: var(--qing);
}

.settings-select {
  width: 100%;
  padding: var(--gap-s) clamp(24px, 2vw, 28px) var(--gap-s) clamp(7px, 0.55vw, 12px);
  background: rgba(0, 0, 0, 0.3);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-s);
  font-family: var(--mono);
  font-size: var(--font-xs);
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23787068'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right clamp(7px, 0.6vw, 10px) center;
  cursor: pointer;
  transition: border-color var(--transition-fast), background-color var(--transition-fast);
}

.settings-select:hover {
  background-color: rgba(0, 0, 0, 0.45);
  border-color: var(--border-dim);
}

.settings-select:focus-visible {
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

.settings-select option {
  background: var(--ink-black-solid);
  font-family: var(--mono);
}

/* Radio button groups styled as horizontal button strips */
.radio-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-xs);
}

.radio-option {
  display: inline-flex;
  align-items: center;
  padding: clamp(5px, 0.4vw, 8px) clamp(7px, 0.55vw, 11px);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-muted);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  font-family: var(--mono);
  font-size: var(--font-s);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
  user-select: none;
  -webkit-user-select: none;
}

.radio-option input[type="radio"] {
  display: none;
}

.radio-option:hover {
  background: var(--qing-dim);
  color: var(--text-primary);
}

.radio-option:has(input:checked) {
  background: var(--qing-select-bg);
  color: var(--text-on-accent);
  border-color: var(--qing-select-border);
}

.radio-option:has(input:focus-visible) {
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

.radio-option-disabled {
  opacity: var(--opacity-disabled);
  cursor: not-allowed;
  pointer-events: none;
}

/* Visual run-setup pickers — sky-gradient time tiles + Lucide-icon weather tiles. Both reuse the
   hidden-radio + :has(input:checked) selection pattern (same as .radio-option). */
.sky-tile-group,
.icon-tile-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-xs);
}

/* Time: a left→right day arc. The tile IS the sky colour (flat, derived from the game's per-time sky
   settings in environment.js); the label overlays a small dark scrim so it stays legible on any
   colour. Random is a neutral tile with a shuffle glyph. */
.sky-tile {
  position: relative;
  flex: 0 0 auto;
  width: 74px;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  color: var(--text-on-accent);
  cursor: pointer;
  transition: border-color var(--transition-fast), filter var(--transition-fast);
}

.sky-tile input[type="radio"] { display: none; }

.sky-morning   { background: #88ccee; }
.sky-afternoon { background: #2288cc; }
.sky-evening   { background: #ee7755; }
.sky-night     { background: #223366; }
/* Random: the four skies as hard-edged flat bands (morning→night) — "any time", same colour language. */
.sky-random    { background: linear-gradient(90deg, #88ccee 0 25%, #2288cc 25% 50%, #ee7755 50% 75%, #223366 75%); }

.sky-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 3px 2px;
  background: rgba(0, 0, 0, 0.5);
  font-family: var(--mono);
  font-size: var(--font-xs);
  letter-spacing: 0.3px;
  line-height: 1.15;
}

.sky-label small { font-size: 0.85em; color: rgba(255, 255, 255, 0.7); }

.sky-tile:hover { filter: brightness(1.12); }

.sky-tile:has(input:checked) {
  border-color: var(--qing-bright);
}

/* The tile body is the sky colour (can't tint it for selection), so the label bar carries the
   selected state — keeps the border the same 1px weight as every other tile/chip. */
.sky-tile:has(input:checked) .sky-label {
  background: var(--qing-hover);
}

.sky-tile:has(input:focus-visible) {
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

/* Weather: icon tiles (clear / rain / random). */
.icon-tile {
  flex: 0 0 auto;
  min-width: 72px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: var(--gap-s);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}

.icon-tile input[type="radio"] { display: none; }

.icon-tile-glyph {
  width: 22px;
  height: 22px;
}

.icon-tile-label {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--mono);
  font-size: var(--font-xs);
  letter-spacing: 0.3px;
}

.icon-tile-label small { font-size: 0.85em; color: var(--text-dim); }

.icon-tile:hover { background: var(--qing-dim); color: var(--text-primary); }

.icon-tile:has(input:checked) {
  border-color: var(--qing-select-border);
  background: var(--qing-select-bg);
  color: var(--text-on-accent);
}

.icon-tile:has(input:checked) .icon-tile-label small { color: rgba(255, 255, 255, 0.7); }

.icon-tile:has(input:focus-visible) {
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

/* Swatch colour picker — a wrapping button grid (replaces the old range-slider picker). The cat base
   picker is only 4 swatches, so a slider was overkill; swatches also let the eu/phaeo split read at a
   glance (see .color-swatch-divider). */
.color-swatches {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap-xs);
}

.color-swatch {
  width: var(--size-icon-m);
  height: var(--size-icon-m);
  padding: 0;
  border-radius: 50%;
  border: 2px solid var(--border-mid);
  cursor: pointer;
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}

.color-swatch:hover { transform: scale(1.12); }

.color-swatch.is-selected {
  border-color: var(--text-on-accent);
  box-shadow: 0 0 0 2px var(--qing-select-border);
}

.color-swatch:focus-visible {
  outline: 2px solid var(--qing-select-border);
  outline-offset: 2px;
}

/* Eumelanin↔phaeomelanin boundary in the base picker — a thin vertical rule so the "black series" and
   the single "red series" swatch read as two pigment families. */
.color-swatch-divider {
  width: 1px;
  align-self: stretch;
  min-height: var(--size-icon-m);
  margin: 0 var(--gap-xs);
  background: var(--border-mid);
}


/* Collapsible details */
.settings-details {
  grid-column: 1 / -1;
  margin-top: var(--gap-m);
  background: var(--surface-1);
  border: none;
  border-radius: var(--radius-m);
}

.settings-summary {
  padding: var(--gap-s);
  cursor: pointer;
  font-size: var(--font-s);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-label);
  user-select: none;
}

.settings-summary:hover {
  background: var(--qing-dim);
  color: var(--qing);
}

.settings-details[open] .settings-summary {
  margin-bottom: var(--gap-s);
}

.settings-details .settings-item {
  margin: var(--gap-s);
  background: rgba(0, 0, 0, 0.3);
}

/* Responsive grid: 2 cols on medium, 1 on small */
@media (max-width: 640px) {
  .settings-grid {
    grid-template-columns: 1fr 1fr;
  }
  .tab-row-3 {
    grid-template-columns: 1fr 1fr;
  }
  /* Collapse the Game-tab two-column layout on narrow viewports too, not just
     device-detected mobile — a narrow desktop window cramps the columns the same
     way. Mirrors the .is-mobile .two-col rules below. */
  .two-col {
    grid-template-columns: 1fr;
    gap: var(--gap-m);
  }
  .two-col .col + .col {
    border-left: none;
    padding-left: 0;
    border-top: 1px solid var(--border-subtle);
    padding-top: var(--gap-m);
  }
}

@media (max-width: 420px) {
  .settings-grid {
    grid-template-columns: 1fr;
  }
  .tab-row,
  .tab-row-3 {
    grid-template-columns: 1fr;
  }
}

/* TASK #544: on a wide DESKTOP viewport, lay each Settings drawer's controls out in two columns —
   there's horizontal room the single stack wasted. The grid goes on the .settings-section-body wrapper
   (built by buildSettingsTab), NOT on <details> itself: browsers wrap a <details>' non-summary children
   in one ::details-content box, so gridding <details> would drop every item into a single track. The
   <summary> stays a full-width block sibling above the grid. Gated on both min-width (so a narrow window
   stays single-column) and body:not(.is-mobile) (so touch devices never split, even in landscape). Only
   the Settings tab is targeted — the Play tab's Scores collapsible shares .settings-details but must
   stay full-width. */
@media (min-width: 768px) {
  body:not(.is-mobile) .tab-panel[data-tab="settings"] .settings-details[open] > .settings-section-body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
  }
}

/* Consistent form controls */
.settings-checkbox {
  display: block;
  width: clamp(22px, 1.6vw, 30px);
  height: clamp(22px, 1.6vw, 30px);
  accent-color: var(--qing);
  cursor: pointer;
  touch-action: manipulation;
  margin-top: var(--gap-xs);
}

.settings-checkbox:focus-visible {
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

.slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  /* Taller than the visible track (below) on purpose — a bigger invisible hit area around the
     thin line makes the whole control easier to grab, not just a cosmetic track thickness. */
  height: clamp(20px, 1.5vw, 28px);
  margin-bottom: var(--gap-s);
  cursor: pointer;
  accent-color: var(--qing);
  touch-action: manipulation;
  background: transparent;
  outline: none;
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: var(--size-icon-m);
  height: var(--size-icon-m);
  border-radius: 50%;
  background: var(--qing);
  border: none;
  cursor: pointer;
  margin-top: clamp(-9px, -0.6vw, -6px);
}

.slider::-moz-range-thumb {
  width: var(--size-icon-m);
  height: var(--size-icon-m);
  border-radius: 50%;
  background: var(--qing);
  border: none;
  cursor: pointer;
}

.slider::-webkit-slider-runnable-track {
  height: var(--gap-s);
  background: rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-s);
}

.slider::-moz-range-track {
  height: var(--gap-s);
  background: rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-s);
}

.slider:focus-visible {
  outline: 1px solid var(--qing-mid);
  outline-offset: 2px;
}

/* ========================================
   Controls Tab — Keyboard & Touch Reference
   ======================================== */

.keys-title {
  font-size: var(--font-m);
  color: var(--text-secondary);
  letter-spacing: var(--letter-spacing-label);
}

.keys-grid {
  display: flex;
  flex-direction: column;
  gap: var(--gap-s);
  max-width: var(--content-max-width);
}

.keys-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-m);
}

.keys-keys {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  width: 25%;
}

.keys-desc {
  flex: 1;
  color: var(--text-muted);
  font-size: var(--font-s);
}

kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: clamp(22px, 3vw, 30px);
  height: clamp(22px, 3vw, 30px);
  padding: 0 4px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border-dim);
  border-bottom: 2px solid var(--border-mid);
  border-radius: var(--radius-s);
  font-family: var(--mono);
  font-size: var(--font-xs);
  color: var(--text-secondary);
  line-height: 1;
}

/* "or" between key alternatives (e.g. [W] or [↑]). */
.kbd-or {
  font-size: var(--font-xs);
  color: var(--text-dim);
}

/* Mode pills */

/* A vertical stack, not a wrapping grid — mode is the Game tab's primary decision (see .two-col
   below), so each option gets a full-width row instead of competing for space in a tight grid. */
.mode-pill-group {
  display: flex;
  flex-direction: column;
  gap: var(--gap-xs);
}

.mode-pill {
  cursor: pointer;
  padding: var(--gap-m);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-m);
  font-family: var(--mono);
  font-size: var(--font-m);
  color: var(--text-dim);
  transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
  user-select: none;
}

.mode-pill input[type="radio"] {
  display: none;
}

.mode-pill:hover {
  border-color: var(--border-dim);
  color: var(--text-muted);
}

/* Mode pills are config (like the difficulty/time/weather pickers), so they share the teal selected
   state — cinnabar is reserved for navigation + commit (tabs, Start/Resume). */
.mode-pill.active {
  border-color: var(--qing-select-border);
  color: var(--text-on-accent);
  background: var(--qing-select-bg);
}

.mode-pill-disabled {
  opacity: var(--opacity-disabled);
  pointer-events: none;
}

/* Game tab layout: Mode (the primary decision) on the left, World (level/difficulty/time/weather,
   secondary) on the right — two columns instead of one long stack. Collapses to a single column on
   mobile (below), same pattern every other two-column area in this menu already uses. */
.section-heading {
  font-size: var(--font-xs);
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 var(--gap-s);
  padding-bottom: var(--gap-xs);
  border-bottom: 1px solid var(--border-subtle);
}

.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap-l);
  margin-bottom: var(--gap-m);
}

.two-col .col + .col {
  border-left: 1px solid var(--border-subtle);
  padding-left: var(--gap-l);
}

.two-col .col > *:not(:last-child) {
  margin-bottom: var(--gap-s);
}

.is-mobile .two-col {
  grid-template-columns: 1fr;
  gap: var(--gap-m);
}

.is-mobile .two-col .col + .col {
  border-left: none;
  padding-left: 0;
  border-top: 1px solid var(--border-subtle);
  padding-top: var(--gap-m);
}

/* Loading progress (visual bar/status — lives inside #loading-strip, the persistent bottom-bar
   strip in the Main Menu section above; not tied to any one tab). */

#loading-bar-container {
  height: 8px;
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
}

#loading-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--cinnabar);
  transition: width 0.3s ease;
}

#loading-status {
  font-size: var(--font-xs);
  color: var(--text-dim);
  min-height: 1em;
  letter-spacing: 0.3px;
}

/* ========================================
   Developer Tools
   ======================================== */

.dev-tools-button {
  position: fixed;
  top: 10px;
  right: 10px;
  padding: 8px 16px;
  background: rgba(0, 0, 0, 0.75);
  color: white;
  border: 2px solid rgba(255, 255, 255, 0.75);
  font-size: var(--font-s);
  font-weight: bold;
  cursor: pointer;
  z-index: 9000;
  text-transform: uppercase;
  letter-spacing: 1px;
  transition: background 0.2s;
}

.dev-tools-button:hover {
  background: rgba(255, 255, 255, 0.25);
}

.dev-tools-panel {
  display: none;
  position: fixed;
  top: 60px;
  right: 10px;
  width: 500px;
  max-height: 80vh;
  background: rgba(20, 20, 30, 0.9);
  border: 2px solid rgba(255, 255, 255, 0.5);
  padding: 0;
  z-index: 9000;
  font-size: var(--font-xs);
  color: white;
  overflow-y: auto;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}

.dev-tools-container {
  padding: 0 12px;
}

.dev-tool-section {
  margin-top: 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border-subtle);
}

.dev-tool-section:last-child {
  border-bottom: none;
}

.dev-tool-title {
  margin: 0 0 6px 0;
  font-size: var(--font-xs);
  color: white;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.dev-tool-content {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.dev-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
}

.dev-label {
  flex: 0 0 120px;
  font-size: var(--font-xs);
  color: #aaa;
  text-transform: uppercase;
}

.dev-select {
  flex: 1;
  padding: 4px 8px;
  background: rgba(0, 0, 0, 0.5);
  color: white;
  border: 1px solid #666;
  font-size: var(--font-xs);
}

.dev-select option {
  background: #000;
}

.dev-color {
  width: 50px;
  height: 28px;
  border: 1px solid #666;
  cursor: pointer;
  background: transparent;
}

.dev-hex {
  font-size: var(--font-xs);
  color: #aaa;
}

.dev-slider {
  flex: 1;
  height: 6px;
  cursor: pointer;
  accent-color: #ff8800;
}

.dev-value {
  flex: 0 0 50px;
  text-align: right;
  font-size: var(--font-xs);
  color: #fff;
}

.dev-button {
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.1);
  color: white;
  border: 1px solid #666;
  cursor: pointer;
  font-size: var(--font-xs);
  text-transform: uppercase;
  transition: background 0.2s;
}

/* BikeTuningTool — editable value box, subsystem headers, and the precedence note. */
.dev-number {
  flex: 0 0 72px;
  min-width: 0;
  text-align: right;
  font-size: var(--font-xs);
  color: #fff;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid #555;
  padding: 2px 4px;
}

.dev-subhead {
  margin: 10px 0 2px;
  font-size: var(--font-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #ff8800;
  border-bottom: 1px solid rgba(255, 136, 0, 0.3);
}

.dev-note {
  margin: 4px 0 6px;
  font-size: var(--font-xs);
  line-height: 1.4;
  color: #9aa;
}

.dev-button:hover {
  background: rgba(255, 255, 255, 0.2);
}

.dev-tools-close {
  width: 100%;
  padding: 10px;
  background: rgba(255, 100, 0, 0.3);
  color: white;
  border: none;
  border-top: 1px solid rgba(255, 150, 0, 0.5);
  cursor: pointer;
  font-size: var(--font-s);
  text-transform: uppercase;
  letter-spacing: 1px;
  transition: background 0.2s;
}

.dev-tools-close:hover {
  background: rgba(255, 120, 0, 0.4);
}

/* ========================================
   Race Overlays
   ======================================== */

#race-results,
#delivery-results {
  z-index: 9999;
}

.race-results-card {
  min-width: 320px;
  max-width: 420px;
}

#race-results-name-row {
  margin-top: 12px;
}

.race-results-name-label {
  color: var(--qing);
}

.race-results-name-inputs {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

.race-results-name-input {
  box-sizing: border-box;
  flex: 2;
}

.race-results-save-btn {
  flex: 1;
  padding: 6px;
}

#race-results-scoreboard,
#delivery-results-scoreboard {
  margin-top: 12px;
}

.race-results-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

.race-results-action-1 {
  flex: 1;
}

.race-results-action-2 {
  flex: 2;
}

.race-stats {
  font-size: var(--font-s);
  color: var(--text-secondary);
}

/* Inline Play-tab scores collapsible: pad the board + clear button in from the surface edges (the
   summary is already padded), matching the breathing room the standalone tab used to have. */
.scores-details[open] {
  padding-bottom: var(--gap-s);
}

.scores-details #scores-board,
.scores-details .pause-secondary {
  padding-left: var(--gap-s);
  padding-right: var(--gap-s);
}

.scores-details .pause-secondary {
  margin-top: var(--gap-s);
}

/* Empty-state line for the inline Play-tab scores board (no routes / no scores yet). */
.scores-empty {
  color: var(--text-dim);
  font-size: var(--font-xs);
  text-align: center;
  padding: var(--gap-s);
}

.scoreboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-s);
}

.scoreboard-table th {
  color: var(--accent);
  font-weight: normal;
  font-size: var(--font-s);
  letter-spacing: var(--letter-spacing-label);
  padding: var(--gap-xs) clamp(4px, 0.35vw, 8px) var(--gap-s);
  border-bottom: none;
  text-align: left;
}

.scoreboard-table td {
  padding: clamp(2px, 0.2vw, 5px) clamp(4px, 0.35vw, 8px);
  color: var(--text-secondary);
}

.scoreboard-table tr:nth-child(even) td {
  background: var(--surface-1);
}

.scoreboard-table tr.current-entry td {
  color: var(--qing);
  font-weight: bold;
}

.scoreboard-table .rank {
  color: var(--text-muted);
  width: 24px;
}

.scoreboard-table .score {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Mobile responsive */
@media (max-width: 768px) {
  .dev-tools-panel {
    width: calc(100% - 20px);
    right: 10px;
    max-height: 70vh;
  }

  .dev-label {
    flex: 0 0 100px;
    font-size: var(--font-s);
  }
}

/* ========================================
   Mobile Buttons Overlay (Buttons control mode)
   ======================================== */

/* Steering = thin full-height strips down each edge, BOTH orientations, starting just below the
   top pause zone; the brake bar sits centered along the bottom between them. #mobile-buttons is a
   full-screen, click-through positioning context — only the strips/brake themselves take touches. */
#mobile-buttons {
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
  --steer-w: clamp(80px, 16vw, 160px);   /* width of each edge strip beside the brake; also the brake bar's height */
  --steer-w-upper: calc(var(--steer-w) * 1.6); /* wider steer hit-area ABOVE the brake band — the strips run full height but the brake (last in DOM) wins its own bottom band by paint order, so the extra width only bites above it */
  --pause-zone-h: calc(100% / 3);        /* strips start below this; mirrors PAUSE_ZONE_HEIGHT_RATIO (input.js) */
}

/* Layout-neutral wrapper: the strips position individually against #mobile-buttons. */
.mobile-steer-row {
  display: contents;
}

/* Translucent glyph-only controls — no resting background box, no gold, no glyph-font dependency.
   Left/right/brake all fall through to transparent; only the arrow glyph (currentColor via the
   ::before triangles) is visible at rest, at low alpha so pressed/active states read as a clear
   jump to full opacity. */
.mobile-btn {
  pointer-events: auto;
  touch-action: none;
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: color 0.1s;
  /* Prevent text-selection blue flash on tap */
  -webkit-tap-highlight-color: transparent;
  border-radius: 0;
}

.mobile-btn-left,
.mobile-btn-right {
  position: absolute;
  top: var(--pause-zone-h);
  bottom: 0;
  width: var(--steer-w-upper);
  background: transparent;
}
.mobile-btn-left  { left: 0; }
.mobile-btn-right { right: 0; }

/* Steering highlight is JS-driven (.pressed) */
.mobile-btn-left.pressed,
.mobile-btn-right.pressed {
  color: rgba(255, 255, 255, 0.5);
}

/* Steering arrows drawn as CSS triangles — iOS renders the ◀/▶ glyphs as colour emoji. */
.mobile-btn-left::before,
.mobile-btn-right::before {
  content: '';
  width: 0;
  height: 0;
  border-top: 22px solid transparent;
  border-bottom: 22px solid transparent;
}
.mobile-btn-left::before  { border-right: 30px solid currentColor; }
.mobile-btn-right::before { border-left:  30px solid currentColor; }

/* Brake bar centered along the bottom, between the two edge strips. */
.mobile-btn-brake {
  position: absolute;
  bottom: 0;
  left: var(--steer-w);
  right: var(--steer-w);
  height: var(--steer-w);
  border: none;
}

/* Brake = down arrow (CSS triangle), matching the steering arrows. */
.mobile-btn-brake::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 26px solid transparent;
  border-right: 26px solid transparent;
  border-top: 30px solid currentColor;
}

/* Brake engaged — red wash (input.js applyButtonInputs toggles .braking on the overlay). */
#mobile-buttons.braking .mobile-btn-brake,
.mobile-btn-brake:active {
  color: rgba(255, 64, 64, 0.5);
}

/* ========================================
   Transient status toast
   ======================================== */

/* Centred pill near the top. Must sit ABOVE the full-screen #main-menu (z 9999) or it's
   hidden behind whichever tab has opaque top-centre content (it only showed on the colours
   tab, whose top-centre is empty). Kept below #dialog-overlay (z 10001) so a modal dialog
   still wins. UIManager.showToast toggles .is-visible. */
#toast {
  position: fixed;
  top: clamp(16px, 4vh, 48px);
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  z-index: 10000;
  max-width: min(90vw, 420px);
  padding: 10px 18px;
  background: var(--bg-card);
  color: var(--text-primary);
  border-radius: var(--radius-m);
  font-size: var(--font-s);
  letter-spacing: var(--letter-spacing-label);
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast), transform var(--transition-fast);
}
#toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

