/*
 * Stars menu system.
 * Adapted from ZENITH UI Kit by cotara_gold (CC BY 4.0).
 * https://cotara-gold.itch.io/zenith-premium-minimalist-html5-ui-kit
 */

:root {
  --stars-bg: #06060e;
  --stars-accent: #e6efff;
  --stars-active: #aac8ff;
  --stars-dim: rgba(230, 239, 255, 0.4);
  --stars-border: rgba(230, 239, 255, 0.15);
  --stars-font: 'Inter', sans-serif;
  --stars-ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
}

#titleScreen {
  position: fixed;
  inset: 0;
  background-color: #0a0d1a;
  z-index: 1000;
  user-select: none;
  font-family: var(--stars-font);
  color: var(--stars-accent);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: opacity 0.8s var(--stars-ease-out-expo);
  overflow: hidden;
}

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

/* The title's orbit (title-orbit.js): a planet and our star going round it,
   behind the type. Sits under the stage; never takes input. */
#titleOrbit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: transparent;
  pointer-events: none;
  cursor: default;
}

/* Stage wraps the visible title layout — used for the full-stage "tear" glitch.
   Kept as a plain positioning container (no flex) so absolute-positioned children
   like .title-telemetry aren't affected by flex layout quirks. */
.title-stage {
  position: absolute;
  inset: 0;
}

@keyframes stars-fade-in {
  0% { opacity: 0; transform: translateY(15px); }
  100% { opacity: 1; transform: translateY(0); }
}

.title-content {
  position: absolute;
  top: 0;
  bottom: 0;
  left: max(15vw, 3rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  animation: stars-fade-in 1.8s var(--stars-ease-out-expo) forwards;
}

.title-header {
  margin-bottom: 5rem;
}

.title-main {
  font-size: clamp(3.5rem, 6vw, 5rem);
  font-weight: 200;
  letter-spacing: 0.15em;
  color: rgba(233, 237, 247, 0);
  line-height: 1;
  margin: 0;
  position: relative;
  display: inline-block;
  transition: letter-spacing 900ms cubic-bezier(0.22, 1, 0.36, 1),
              color 900ms ease;
}

/* Final reveal state uses the original wordmark tracking (0.25em) rather
   than the design artifact's 0.35em — David wants original dimensions. */
.title-main.wordmark-ready {
  letter-spacing: 0.25em;
  color: rgba(233, 237, 247, 1);
}

/* Compression state — subtle vertical squash, scanlines overlay visible.
   JS owns transform/scanline animation timing; CSS supplies the look. */
.title-main.wordmark-compressing {
  transition: transform 55ms linear, filter 55ms linear;
}

.title-main-body {
  display: inline-block;
  position: relative;
}

.title-letter {
  display: inline-block;
}

/* Chroma split overlays — JS toggles .chroma-on; pseudo-content reads from
   data-text so bit-rot letter swaps are mirrored into the chroma layers. */
.title-main-body::before,
.title-main-body::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  letter-spacing: inherit;
  font-weight: inherit;
  opacity: 0;
  mix-blend-mode: screen;
  pointer-events: none;
  white-space: nowrap;
  transition: opacity 40ms linear;
}
.title-main-body::before {
  color: rgba(255, 64, 64, 0.65);
  transform: translateX(var(--chroma-red-x, -2px));
}
.title-main-body::after {
  color: rgba(64, 240, 255, 0.65);
  transform: translateX(var(--chroma-cyan-x, 2px));
}
.title-main-body.chroma-on::before,
.title-main-body.chroma-on::after {
  opacity: 1;
}

/* Scanlines overlay — JS toggles .scanlines-on when compressing. The lines
   drift upward via background-position animation while active. */
.title-scanlines {
  position: absolute;
  inset: -6% -2%;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0,
    rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, 0.72) 2px,
    rgba(0, 0, 0, 0.72) 3px
  );
  background-size: 100% 3px;
  opacity: 0;
  transition: opacity 80ms linear;
  mix-blend-mode: multiply;
}
.title-main-body.scanlines-on .title-scanlines {
  opacity: 0.95;
  animation: scanline-drift 520ms linear infinite;
}

@keyframes scanline-drift {
  from { background-position-y: 0; }
  to { background-position-y: -3px; }
}

/* Telemetry — bottom-right cluster: timestamp + signal-interpretation stamp underneath. */
.title-telemetry {
  position: absolute;
  bottom: 24px;
  right: 28px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  font-family: var(--r-mono, 'JetBrains Mono', monospace);
  pointer-events: none;
  z-index: 3;
  opacity: 0;
  animation: stars-fade-in 1.4s 0.8s var(--stars-ease-out-expo) forwards;
}

.telemetry-ts {
  font-size: 8px;
  letter-spacing: 0.22em;
  font-weight: 300;
  color: rgba(255, 255, 255, 0.22);
  font-variant-numeric: tabular-nums;
}

/* Per-character model: each glyph span carries its own inline color (alpha).
   No CSS color transition — transitions are intentionally binary (snap), so
   the flicker reads as signal noise rather than a deliberate fade effect. */
.telemetry-stamp {
  font-size: 5px;
  letter-spacing: 0.18em;
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.telemetry-stamp-char {
  display: inline-block;
  margin-right: 0.25em;
}

.telemetry-stamp-char:last-child {
  margin-right: 0;
}

/* Tear effect — each clipped stage clone gets a .title-stage-clip-top / -bottom. */
.title-stage.tearing .title-stage-original {
  visibility: hidden;
}
.title-stage-clone {
  position: absolute;
  inset: 0;
  pointer-events: none;
  will-change: transform, clip-path;
}
.title-tear-seam {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(180, 220, 255, 0.35), rgba(255, 255, 255, 0.08));
}

.title-nav {
  display: flex;
  flex-direction: column;
  width: max-content;
}

.title-menu-item {
  background: none;
  border: none;
  color: var(--stars-accent);
  font-family: var(--stars-font);
  font-size: 1.25rem;
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.4;
  padding: 0.75rem 0;
  line-height: 1;
  cursor: pointer;
  text-align: left;
  transition: opacity 0.4s ease, color 0.4s ease;
}

.title-menu-item span {
  display: inline-block;
  transition: transform 0.4s var(--stars-ease-out-expo);
}

.title-menu-item:hover:not(:disabled),
.title-menu-item:focus:not(:disabled) {
  opacity: 1;
  color: var(--stars-active);
  outline: none;
}

.title-menu-item:hover:not(:disabled) span,
.title-menu-item:focus:not(:disabled) span {
  transform: translateX(8px);
}

.title-menu-item:disabled {
  cursor: default;
  opacity: 0.2;
}

/* Shown only to a visitor with nothing but a touchscreen (game.js): the game
   can be opened on a phone but not played on one. One plain sentence under
   the menu; it does not block anything. */
.title-notice {
  margin: 2.2rem 0 0;
  max-width: 21rem;
  color: var(--stars-accent);
  font-family: var(--stars-font);
  font-size: 0.82rem;
  font-weight: 300;
  letter-spacing: 0.1em;
  line-height: 1.7;
  opacity: 0.8;
}
.title-notice[hidden] { display: none; }

/* ============================================================
 * Settings modal — RECEIVER spec
 * Tokens — PANEL #0d1120, AMBER #e8c879 (audio/active),
 *          BLUE #4da8ff (gamepad-related). Inter for display, JetBrains
 *          Mono for metadata/labels/key caps.
 * ============================================================ */

:root {
  --r-panel: #0d1120;
  --r-border: rgba(255, 255, 255, 0.08);
  --r-text: rgba(233, 237, 247, 0.92);
  --r-text-dim: rgba(255, 255, 255, 0.45);
  --r-text-muted: rgba(255, 255, 255, 0.32);
  --r-text-faint: rgba(255, 255, 255, 0.18);
  --r-blue: #4da8ff;
  --r-blue-soft: rgba(77, 168, 255, 0.14);
  --r-amber: #e8c879;
  --r-amber-soft: rgba(232, 200, 121, 0.7);
  --r-mono: 'JetBrains Mono', monospace;
}

#settingsModal {
  position: fixed;
  inset: 0;
  background: rgba(6, 8, 15, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  font-family: var(--stars-font);
  color: var(--r-text);
  user-select: none;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s var(--stars-ease-out-expo);
}

#settingsModal.open {
  opacity: 1;
  pointer-events: auto;
}

.settings-dialog {
  width: 880px;
  max-width: 95vw;
  /* Height budget. #settingsModal centres this with flex and does not scroll, so
     without a ceiling a tall pane overflows off BOTH edges with no way to reach
     it — the header and the tab bar go off-screen and you cannot switch tabs.
     Measured 2026-07-26: the Calibration pane made the dialog 1051px, already
     clipping 38px at the top on a 1912x947 viewport. Header and tabs stay put;
     only the pane body scrolls. */
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background-color: var(--r-panel);
  border: 1px solid var(--r-border);
  box-shadow: 0 20px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.02);
  transform: translateY(15px);
  transition: transform 0.5s var(--stars-ease-out-expo);
}

#settingsModal.open .settings-dialog {
  transform: translateY(0);
}

.settings-header {
  padding: 22px 28px 18px;
  border-bottom: 1px solid var(--r-border);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex: 0 0 auto;   /* pinned — never squeezed by a tall pane */
}

.settings-header-title {
  font-family: var(--stars-font);
  font-size: 14px;
  font-weight: 300;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--r-text);
}

.settings-header-sub {
  font-family: var(--r-mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--r-text-faint);
  margin-top: 6px;
}

.settings-header-stamp {
  font-family: var(--r-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.28);
}

.settings-header-stamp-char {
  display: inline-block;
  margin-right: 0.32em;
}
.settings-header-stamp-char:last-child {
  margin-right: 0;
}

/* Tabs */

.settings-tabs {
  display: flex;
  border-bottom: 1px solid var(--r-border);
  flex: 0 0 auto;   /* pinned — the tab bar must always be reachable */
}
.settings-tab {
  flex: 1;
  background: transparent;
  border: none;
  padding: 16px 18px;
  cursor: pointer;
  text-align: center;
  border-top: 1px solid transparent;
  border-right: 1px solid var(--r-border);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.settings-tab:last-child { border-right: none; }
.settings-tab:hover,
.settings-tab:focus { outline: none; }
.settings-tab .settings-tab-label {
  display: block;
  font-family: var(--r-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--r-text-dim);
  transition: color 0.2s ease;
}
.settings-tab .settings-tab-sub {
  display: block;
  font-family: var(--r-mono);
  font-size: 8.5px;
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--r-text-faint);
  margin-top: 4px;
}
.settings-tab.is-active {
  border-top-color: var(--r-amber-soft);
  background: rgba(232, 200, 121, 0.04);
}
.settings-tab.is-active .settings-tab-label { color: var(--r-amber); }
.settings-tab:hover:not(.is-active) .settings-tab-label { color: var(--r-text); }
.settings-tab:focus {
  outline: none;
  background: rgba(232, 200, 121, 0.08);
  border-top-color: var(--r-amber);
}
.settings-tab:focus .settings-tab-label { color: var(--r-amber); }

/* Content */

.settings-content {
  padding: 20px 32px;
  /* The only scrolling region. min-height:0 is required or the flex item refuses
     to shrink below its content height and the overflow escapes the dialog. The
     320px floor that used to live here moved to the pane, so short panes still
     hold their shape without blocking this box from shrinking. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Slim scrollbar, matched to the chapter-grid treatment. */
.settings-content::-webkit-scrollbar { width: 4px; }
.settings-content::-webkit-scrollbar-track { background: transparent; }
.settings-content::-webkit-scrollbar-thumb {
  background: var(--r-border);
  border-radius: 2px;
}
.settings-content::-webkit-scrollbar-thumb:hover { background: var(--stars-dim); }

.settings-tab-pane { display: none; }
.settings-tab-pane.is-active { display: block; min-height: 320px; }

.settings-section-label {
  font-family: var(--r-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--r-text-muted);
  margin-bottom: 14px;
}
.settings-section-label--top { margin-top: 18px; }

/* Row */

.settings-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 0;
  border-bottom: 1px solid var(--r-border);
}
.settings-row[hidden] { display: none; }
.settings-row[data-row="aimStick"],
.settings-row[data-row="stickSens"],
.settings-row[data-row="vibration"] { padding: 14px 0; }

.settings-row-label {
  flex: 0 0 200px;
  min-width: 200px;
}
.settings-row-name {
  font-family: var(--stars-font);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--r-text);
  transition: color 0.15s ease;
}
.settings-row:focus-within .settings-row-name {
  color: var(--r-amber);
}
.settings-row-hint {
  font-family: var(--r-mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--r-text-faint);
  margin-top: 4px;
}
.settings-row-control {
  flex: 1;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

/* Calibration — faint-star brightness target preview */
.settings-calib {
  margin-bottom: 22px;
}
.settings-calib canvas {
  position: static; /* override the global game-canvas absolute positioning */
  display: block;
  width: 100%;
  max-width: 560px;
  /* 28/13 rather than 7/3: the single merged target carries the starfield, the
     colour reference planet and the black-level ramp, so it needs a little more
     height than the star band alone did. Still far shorter than the two stacked
     previews it replaced. */
  aspect-ratio: 28 / 13;
  height: auto;
  margin: 0 auto;
  background: #06060f;
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 8px;
  cursor: auto;
}
.settings-calib-caption {
  font-family: var(--r-mono);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--r-text-faint);
  text-align: center;
  margin-top: 10px;
}
.settings-calib-caption b {
  color: var(--r-text-muted);
  font-weight: 400;
}
/* Caption sitting with a slider group rather than under the canvas: left-aligned
   so it joins the row rhythm, and spaced below rather than above. */
.settings-calib-caption--section {
  text-align: left;
  margin: 0 0 14px;
}

/* Slider — track, fill, ticks, square handle, value readout */

.r-slider {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  max-width: 340px;
  position: relative;
}
.r-slider-track {
  flex: 1;
  height: 24px;
  position: relative;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.r-slider-track-bg {
  position: absolute;
  left: 0; right: 0;
  top: calc(50% - 1px);
  height: 2px;
  background: rgba(255, 255, 255, 0.10);
}
.r-slider-track-fill {
  position: absolute;
  left: 0;
  top: calc(50% - 1px);
  height: 2px;
  background: var(--r-amber-soft);
  transition: background 0.2s ease;
}
.r-slider[data-accent="blue"] .r-slider-track-fill { background: rgba(77, 168, 255, 0.55); }
.r-slider.is-active .r-slider-track-fill { background: var(--r-amber); }
.r-slider[data-accent="blue"].is-active .r-slider-track-fill { background: var(--r-blue); }

.r-slider-tick {
  position: absolute;
  top: calc(50% - 3px);
  width: 1px;
  height: 6px;
  background: rgba(255, 255, 255, 0.10);
  pointer-events: none;
}
.r-slider-handle {
  position: absolute;
  top: calc(50% - 7px);
  width: 14px;
  height: 14px;
  margin-left: -7px;
  background: #e6ebf5;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transition: box-shadow 0.2s ease, background 0.2s ease;
  /* Grabbable in its own right. This used to be pointer-events:none so clicks
     fell through to the track, which works only while the handle is fully
     inside the track — at 0% or 100% the -7px offset puts half the handle past
     the track edge, where nothing was listening. Saturation defaults to 100%,
     so its handle shipped with half of it dead to the mouse. makeSlider binds
     the same drag handler to the handle as to the track. */
  pointer-events: auto;
}
.r-slider.is-active .r-slider-handle {
  background: #fff;
  box-shadow: 0 0 0 2px rgba(232, 200, 121, 0.25), 0 0 12px rgba(232, 200, 121, 0.45);
}
.r-slider[data-accent="blue"].is-active .r-slider-handle {
  box-shadow: 0 0 0 2px rgba(77, 168, 255, 0.25), 0 0 12px rgba(77, 168, 255, 0.45);
}
.r-slider-value {
  font-family: var(--r-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--r-amber-soft);
  min-width: 44px;
  text-align: right;
  cursor: text;
  padding: 2px 4px;
}
.r-slider[data-accent="blue"] .r-slider-value { color: rgba(77, 168, 255, 0.7); }
.r-slider-value-input {
  width: 44px;
  padding: 2px 4px;
  text-align: right;
  font-family: var(--r-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--r-amber-soft);
  color: #fff;
  outline: none;
}
.r-slider[data-accent="blue"] .r-slider-value-input {
  border-color: rgba(77, 168, 255, 0.7);
}

/* Toggle */

.r-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  user-select: none;
}
.r-toggle-box {
  position: relative;
  width: 38px;
  height: 20px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: transparent;
  transition: all 0.25s ease;
}
.r-toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 15px;
  height: 14px;
  background: rgba(255, 255, 255, 0.6);
  transition: left 0.2s ease, background 0.2s ease;
}
.r-toggle-label {
  margin-left: 12px;
  font-family: var(--r-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--r-text-dim);
  transition: color 0.2s ease;
}
.r-toggle.is-on .r-toggle-box {
  border-color: var(--r-blue);
  background: var(--r-blue-soft);
}
.r-toggle.is-on .r-toggle-thumb {
  left: 19px;
  background: var(--r-blue);
}
.r-toggle.is-on .r-toggle-label { color: var(--r-blue); }
.r-toggle:focus {
  outline: none;
}
.r-toggle:focus .r-toggle-box {
  box-shadow: 0 0 0 1px var(--r-amber-soft), 0 0 8px rgba(232, 200, 121, 0.35);
}

/* Segmented */

.r-segmented {
  display: inline-flex;
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.r-seg {
  background: transparent;
  border: none;
  padding: 8px 16px;
  cursor: pointer;
  font-family: var(--r-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--r-text-dim);
  transition: background 0.2s ease, color 0.2s ease;
}
.r-seg + .r-seg {
  border-left: 1px solid rgba(255, 255, 255, 0.10);
}
.r-seg:hover { color: var(--r-text); outline: none; }
.r-seg:focus {
  outline: none;
  background: rgba(232, 200, 121, 0.08);
  color: var(--r-amber);
}
.r-seg.is-active {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
}

/* Bindings — header row + grid of action rows */

.settings-bindings-header {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
  gap: 16px;
  padding-bottom: 8px;
}
.settings-bindings-col {
  font-family: var(--r-mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--r-text-faint);
}

.settings-bindings { display: flex; flex-direction: column; }
.settings-bindings .settings-row { padding: 10px 0; }
.settings-bindings .settings-row-control {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
  justify-content: flex-start;
  width: 100%;
}
.settings-bindings .settings-row-control > div {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Key caps — filled / empty / listening */
.r-cap {
  min-width: 36px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.04);
  font-family: var(--r-mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: #fff;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.r-cap:hover,
.r-cap:focus {
  border-color: var(--r-amber-soft);
  background: rgba(232, 200, 121, 0.04);
  color: var(--r-amber);
  outline: none;
}
.r-cap.is-empty {
  border-style: dashed;
  border-color: var(--r-text-faint);
  background: transparent;
  color: var(--r-text-faint);
  font-size: 12px;
  padding: 0 8px;
}
.r-cap.is-empty:hover {
  border-style: dashed;
  border-color: var(--r-amber-soft);
  background: rgba(232, 200, 121, 0.04);
  color: var(--r-amber);
}
.r-cap.is-listening {
  border-style: solid;
  border-color: var(--r-amber);
  background: rgba(232, 200, 121, 0.10);
  color: var(--r-amber);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 0 12px;
  animation: rebindPulse 1.4s ease-in-out infinite;
}
@keyframes rebindPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* Conflict tooltip — small inline note above the listening cap */
.r-cap-conflict {
  position: absolute;
  margin-top: -36px;
  padding: 4px 8px;
  font-family: var(--r-mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(255, 150, 120);
  background: rgba(40, 12, 12, 0.85);
  border: 1px solid rgba(255, 150, 120, 0.35);
  pointer-events: none;
  white-space: nowrap;
}

/* Access placeholder */

.settings-access-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-height: 280px;
  text-align: center;
}
.settings-access-placeholder-stamp {
  font-family: var(--r-mono);
  font-size: 22px;
  color: var(--r-text-faint);
  letter-spacing: 0.2em;
}
.settings-access-placeholder-text {
  font-family: var(--stars-font);
  font-weight: 300;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--r-text-dim);
}
.settings-access-placeholder-hint {
  font-family: var(--r-mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--r-text-faint);
}

/* Footer */

.settings-footer {
  padding: 16px 28px;
  border-top: 1px solid var(--r-border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
.settings-footer-right {
  display: flex;
  align-items: center;
  gap: 18px;
}
.settings-esc-hint {
  font-family: var(--r-mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--r-text-faint);
}
.r-reset {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--r-mono);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--r-text-dim);
  cursor: pointer;
  transition: color 0.2s ease;
}
.r-reset:hover {
  color: var(--r-text);
  text-decoration: underline;
  text-underline-offset: 4px;
  outline: none;
}
.r-close {
  background: transparent;
  color: var(--r-text);
  border: 1px solid rgba(255, 255, 255, 0.4);
  padding: 10px 24px;
  font-family: var(--r-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  cursor: pointer;
  transition: all 0.2s ease;
}
.r-close:hover,
.r-close:focus {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  border-color: #fff;
  outline: none;
}
.r-reset:focus {
  outline: none;
  color: var(--r-amber);
}

/* ============================================================
 * Pause menu
 * ============================================================ */

#pauseModal {
  position: fixed;
  inset: 0;
  background: rgba(6, 6, 14, 0.65);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  padding-left: max(15vw, 3rem);
  z-index: 1500;
  font-family: var(--stars-font);
  color: var(--stars-accent);
  user-select: none;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s var(--stars-ease-out-expo);
}

#pauseModal.open {
  opacity: 1;
  pointer-events: auto;
}

.pause-content {
  display: flex;
  flex-direction: column;
  transform: translateY(15px);
  transition: transform 0.5s var(--stars-ease-out-expo);
}

#pauseModal.open .pause-content {
  transform: translateY(0);
}

.pause-header {
  margin-bottom: 2.5rem;
}

.pause-title {
  font-size: 2rem;
  font-weight: 200;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  margin: 0;
  line-height: 1;
}

.pause-nav {
  display: flex;
  flex-direction: column;
  width: max-content;
  min-width: 220px;
}

.pause-divider {
  border-top: 1px solid var(--stars-border);
  margin: 0.75rem 0;
  width: 120px;
  opacity: 0.6;
}

/* ============================================================
 * Credits modal — content-only variant of the settings dialog
 * ============================================================ */

#creditsModal,
#guideModal {
  position: fixed;
  inset: 0;
  background: rgba(6, 6, 14, 0.75);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  font-family: var(--stars-font);
  color: var(--stars-accent);
  user-select: none;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s var(--stars-ease-out-expo);
}

#creditsModal.open,
#guideModal.open {
  opacity: 1;
  pointer-events: auto;
}

.credits-dialog {
  width: 640px;
  max-width: 95vw;
  background-color: var(--stars-bg);
  border: 1px solid var(--stars-border);
  transform: translateY(15px);
  transition: transform 0.5s var(--stars-ease-out-expo);
  /* Height budget, same reasoning as .settings-dialog. Found by
     surfaceAuditSizes() 2026-07-26: at 1366x768 the Guide put its title 6px
     above the viewport and its close button 6px below (30px at 1280x720), with
     no scroll container — so on a common laptop the how-to-play screen could
     not be dismissed with the mouse. Header and footer stay pinned; only the
     body scrolls. */
  max-height: 88vh;
  display: flex;
  flex-direction: column;
}

#creditsModal.open .credits-dialog,
#guideModal.open .credits-dialog {
  transform: translateY(0);
}

.credits-header {
  padding: 1.5rem 2rem;
  border-bottom: 1px solid var(--stars-border);
  flex: 0 0 auto;   /* pinned — stays reachable when the body scrolls */
}

.credits-header-title {
  font-size: 0.85rem;
  font-weight: 300;
  letter-spacing: 0.3em;
  text-transform: uppercase;
}

.credits-content {
  padding: 2.5rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 2rem;
  /* The only scrolling region. min-height:0 is required or this flex item
     refuses to shrink below its content and the overflow escapes the dialog. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.credits-content::-webkit-scrollbar { width: 4px; }
.credits-content::-webkit-scrollbar-track { background: transparent; }
.credits-content::-webkit-scrollbar-thumb { background: var(--stars-border); border-radius: 2px; }
.credits-content::-webkit-scrollbar-thumb:hover { background: var(--stars-dim); }

.credits-section {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.credits-section-title {
  font-size: 0.65rem;
  color: var(--stars-dim);
  letter-spacing: 0.25em;
  text-transform: uppercase;
}

.credits-line {
  font-size: 0.85rem;
  font-weight: 300;
  letter-spacing: 0.06em;
}

.credits-link {
  color: var(--stars-accent);
  text-decoration: none;
  border-bottom: 1px solid var(--stars-border);
  transition: color 0.3s ease, border-color 0.3s ease;
}

.credits-link:hover,
.credits-link:focus {
  color: var(--stars-active);
  border-color: var(--stars-active);
  outline: none;
}

.credits-license {
  font-size: 0.7rem;
  color: var(--stars-dim);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  margin-left: 0.5rem;
}
/* a licence tag that links to the licence it names */
a.credits-license { text-decoration: none; transition: color 0.3s ease; }
a.credits-license:hover,
a.credits-license:focus { color: var(--stars-active); outline: none; }

.credits-footer {
  padding: 1.5rem 2rem;
  border-top: 1px solid var(--stars-border);
  display: flex;
  justify-content: flex-end;
  flex: 0 0 auto;   /* pinned — stays reachable when the body scrolls */
}

/* ============================================================
 * Chapter select
 * ============================================================ */

#chapterSelect {
  position: fixed;
  inset: 0;
  background-color: var(--stars-bg);
  z-index: 1100;
  font-family: var(--stars-font);
  color: var(--stars-accent);
  user-select: none;
  display: flex;
  flex-direction: column;
  opacity: 1;
  transition: opacity 0.6s var(--stars-ease-out-expo);
  overflow: hidden;
}

#chapterSelect.chapter-hidden {
  opacity: 0;
  pointer-events: none;
}

.chapter-select-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2.5rem max(5vw, 3rem) 1.5rem;
  border-bottom: 1px solid var(--stars-border);
}

.chapter-select-title {
  font-size: 1.25rem;
  font-weight: 200;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  margin: 0;
  line-height: 1;
}

.chapter-back-btn {
  background: none;
  border: none;
  color: var(--stars-accent);
  font-family: var(--stars-font);
  font-size: 0.75rem;
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.5;
  cursor: pointer;
  padding: 0.5rem 0.75rem;
  transition: opacity 0.3s ease, color 0.3s ease;
}

.chapter-back-btn:hover,
.chapter-back-btn:focus {
  opacity: 1;
  color: var(--stars-active);
  outline: none;
}

.chapter-grid {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 1.25rem;
  padding: 3rem max(12vw, 5rem);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  scroll-behavior: smooth;
  flex: 1;
}
/* Two consistent lines: the campaign (top) and the secondary strip (below).
   Same-size cards, same behaviour; the secondary just holds the Étude door
   and the soloists. Tighter vertical padding so both bands fit one screen. */
.chapter-grid { padding-top: 1.5rem; padding-bottom: 1.5rem; }

/* soloists: rankless — the "you" amber, no par/tier/difficulty chrome */
.ms-card--soloist .ms-card__face { border-color: rgba(255, 179, 71, 0.4); }
.ms-card--soloist:focus .ms-card__face,
.ms-card--soloist.is-selected .ms-card__face { border-color: rgba(255, 191, 92, 0.95); }
.ms-card--soloist .ms-chrome-top__tier,
.ms-card--soloist .ms-chrome-top__index,
.ms-card--soloist .ms-chrome-meta { display: none; }

/* Slim horizontal scrollbar, matched to Stars palette */
.chapter-grid::-webkit-scrollbar { height: 4px; }
.chapter-grid::-webkit-scrollbar-track { background: transparent; }
.chapter-grid::-webkit-scrollbar-thumb {
  background: var(--stars-border);
  border-radius: 2px;
}
.chapter-grid::-webkit-scrollbar-thumb:hover { background: var(--stars-dim); }

.chapter-card {
  flex: 0 0 200px;
  background: transparent;
  border: 1px solid var(--stars-border);
  color: var(--stars-accent);
  cursor: pointer;
  padding: 1rem 1.1rem;
  font-family: var(--stars-font);
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-height: 280px;
  scroll-snap-align: center;
  transition: border-color 0.35s var(--stars-ease-out-expo),
              transform 0.35s var(--stars-ease-out-expo),
              background-color 0.35s ease;
}

/* Reserved space for future map symbol / thumbnail / logo — takes the
 * upper portion of the card, pushing the text block toward the bottom. */
.chapter-card__icon {
  flex: 1 1 auto;
  min-height: 100px;
}

.chapter-card:hover:not(:disabled),
.chapter-card:focus:not(:disabled) {
  border-color: var(--stars-active);
  transform: translateY(-3px);
  background-color: rgba(170, 200, 255, 0.03);
  outline: none;
}

.chapter-card:disabled {
  cursor: default;
  opacity: 0.3;
}

.chapter-card__index {
  font-size: 0.7rem;
  letter-spacing: 0.35em;
  color: var(--stars-dim);
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}

.chapter-card__name {
  font-size: 1.05rem;
  font-weight: 300;
  line-height: 1.2;
  letter-spacing: 0.04em;
  color: var(--stars-accent);
}

.chapter-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  font-size: 0.65rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--stars-dim);
  margin-top: auto;
}

.chapter-card__meta-item {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
}

.chapter-card__meta-value {
  color: var(--stars-accent);
  font-variant-numeric: tabular-nums;
}

.chapter-card__shards {
  color: var(--stars-active);
  opacity: 0.75;
}

.chapter-status {
  padding: 0.75rem max(5vw, 3rem) 1.25rem;
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--stars-dim);
  min-height: 1.5rem;
  text-align: right;   /* out of the way of the soloist/étude left column */
}

/* ============================================================
 * HUD hide utility — applied to HUD header and legend when
 * "Show HUD" is off (default).
 * ============================================================ */

.hud--hidden {
  display: none !important;
}

/* ============================================================
   i18n — non-Latin locale gate (body class set by i18n.js).
   Wide tracking + uppercase are Latin display conventions; they
   are meaningless or harmful for CJK and connected scripts, so
   strip them wholesale when a non-Latin locale is active.
   ============================================================ */
body.locale-nonlatin,
body.locale-nonlatin *:not(.i18n-latin):not(.i18n-latin *) {
  letter-spacing: normal !important;
  text-transform: none !important;
}

/* Language cycler (settings) — ‹ name › ; scales to the full locale set. */
.r-cycler { display: flex; align-items: center; gap: 4px; }
.r-cyc-arrow {
  background: none; border: 1px solid var(--r-border); color: var(--r-text-dim);
  border-radius: 5px; width: 26px; height: 26px; cursor: pointer;
  font-size: 14px; line-height: 1; padding: 0;
}
.r-cyc-arrow:hover { color: var(--r-text); border-color: rgba(255, 255, 255, 0.25); }
.r-cyc-label {
  min-width: 128px; text-align: center; color: var(--r-text);
  font-size: 12.5px; letter-spacing: 0.4px;
}

/* How-to-play guide visuals */
.guide-row { display: flex; align-items: center; gap: 14px; margin: 7px 0; }
.guide-row .credits-line { margin: 0; }
.guide-shard {
  flex: 0 0 auto; width: 32px; height: 32px;
  filter: drop-shadow(0 0 5px rgba(170, 200, 255, 0.45));
}
.guide-card {
  flex: 0 0 auto; width: 42px; height: 56px;
  border-radius: 5px; position: relative;
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: 4px;
  font-family: var(--r-mono, 'JetBrains Mono', monospace);
  font-size: 7.5px; letter-spacing: 1.2px;
  color: rgba(255, 255, 255, 0.55);
}
.guide-card svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.guide-card--par  { background: #090b1c; border: 1px solid rgba(220, 20, 60, 0.55); }
.guide-card--time { background: #0a0d1a; border: 1px solid rgba(170, 200, 255, 0.4); }

/* Tall window — the Arranged hub's phone capture, or a narrow browser. The
   type moves to the top so the planet has the lower half to itself (the same
   threshold title-orbit.js uses to place it). */
@media (max-aspect-ratio: 10/11) {
  .title-content {
    justify-content: flex-start;
    top: 12vh;
    left: 10vw;
  }
  .title-header {
    margin-bottom: 2.6rem;
  }
}
