:root {
  color-scheme: light;
  --ink: #221b16;
  --ink-soft: #57483b;
  --paper: #f6f1e8;
  --paper-strong: #efe7da;
  --surface: rgba(255, 251, 244, 0.82);
  --surface-solid: #fffaf2;
  --border: rgba(62, 45, 30, 0.2);
  --border-strong: rgba(62, 45, 30, 0.36);
  --accent: #8c3b2f;
  --accent-strong: #6f1f16;
  --gold: #b27a37;
  --in-tune: #6f8f52;
  --in-tune-strong: #4f6a38;
  --tune-gray: #9c9186;
  --shadow-soft: 0 10px 30px rgba(34, 27, 22, 0.1);
  --shadow-strong: 0 18px 45px rgba(34, 27, 22, 0.18);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: Inter, "Segoe UI", Roboto, system-ui, sans-serif;
  color: var(--ink);
  line-height: 1.55;
  background:
    radial-gradient(circle at 85% 8%, rgba(178, 122, 55, 0.16), transparent 35%),
    radial-gradient(circle at 12% 22%, rgba(140, 59, 47, 0.1), transparent 34%),
    repeating-linear-gradient(
      180deg,
      rgba(57, 43, 31, 0.035) 0,
      rgba(57, 43, 31, 0.035) 1px,
      transparent 1px,
      transparent 36px
    ),
    linear-gradient(180deg, #fcf7ef 0%, #f3eadb 100%);
}

.page {
  max-width: 1100px;
  margin: 0 auto;
  padding: 2.6rem 1.1rem 3.4rem;
  display: grid;
  gap: 1.35rem;
}

.hero {
  text-align: center;
  position: relative;
  padding: 1.4rem 0.7rem 0.1rem;
}

.hero::before,
.hero::after {
  content: "";
  position: absolute;
  left: 50%;
  width: min(520px, 78vw);
  height: 1px;
  transform: translateX(-50%);
  background: linear-gradient(90deg, transparent, rgba(62, 45, 30, 0.46), transparent);
}

.hero::before {
  top: 0.3rem;
}

.hero::after {
  bottom: -0.2rem;
}

.eyebrow {
  margin: 0;
  font-family: "Marck Script", "Brush Script MT", cursive;
  font-size: clamp(1.2rem, 2.6vw, 1.7rem);
  color: var(--accent);
}

.hero h1 {
  margin: 0.2rem 0 0.55rem;
  font-family: "Cormorant Garamond", Georgia, serif;
  font-size: clamp(2.4rem, 5.2vw, 4rem);
  line-height: 1.05;
  letter-spacing: 0.02em;
  color: #2d221b;
}

.tuner {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-soft);
  border-radius: 1.05rem;
  padding: 1.2rem;
  display: grid;
  gap: 1rem;
  min-width: 0;
}

/* ---- sticky "live" area: the current reading, visual, and Start/Stop
   control stay pinned at the top of the viewport as the settings below
   scroll past, so the tuning is always visible while adjusting them.
   Bleeds into the card's own padding via negative margins, then restores
   it, so the opaque background covers edge-to-edge behind the sticky
   bar rather than leaving the card's side padding transparent. ---- */
.tuner-live {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  gap: 1rem;
  margin: -1.2rem -1.2rem 0;
  padding: 1.2rem 1.2rem 0.9rem;
  background: var(--surface-solid);
  border-radius: 1.05rem 1.05rem 0 0;
  border-bottom: 1px solid var(--border);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---- shared note readout, common to all three visual modes. Only
   present while the tuner is running (see updateReadout) — stopped, it
   collapses so the live area stays compact. ---- */
.note-readout {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 0.15rem;
  /* Reserves the same vertical band a detected note (name + cents/Hz line)
     takes up, so the "no signal" message centers within that band (via
     align-content above) instead of sitting flush at the top. */
  min-height: clamp(4.8rem, 13vw, 6.2rem);
  text-align: center;
}

/* .note-readout sets display:grid, which would otherwise beat the [hidden]
   attribute's UA display:none. */
.note-readout[hidden] {
  display: none;
}

.note-name {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 700;
  font-size: clamp(3.2rem, 9vw, 4.4rem);
  line-height: 1;
  letter-spacing: -0.02em;
  /* Gray the further out of tune, easing toward the normal brown as the
     reading approaches the green "in tune" zone (see .tuner.in-tune below,
     which wins outright once inside that zone). --tune-mix is set in JS. */
  color: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), var(--accent-strong));
  transition: color 160ms ease;
}

.tuner.in-tune .note-name {
  color: var(--in-tune-strong);
}

/* The "no signal" message is a full sentence, not a note name, so it
   drops to a small wrapping size instead of the giant note-name font
   meant for two or three characters. */
.note-name.is-no-signal {
  font-size: clamp(1rem, 3.4vw, 1.25rem);
  font-weight: 600;
  letter-spacing: 0;
  line-height: 1.35;
  white-space: normal;
  max-width: 22rem;
}

/* Outranks .tuner.in-tune .note-name (green) — the "no signal" state
   carries .in-tune too (see updateReadout), since idling at the reference
   pitch is visually treated like sitting in tune, but the text itself
   should still read as an alert, not a green "all good". A studio
   "ON AIR"-style vivid red rather than the muted wood/brass accent. */
.tuner.in-tune .note-name.is-no-signal {
  color: #e2261c;
  text-shadow: 0 0 12px rgba(226, 38, 28, 0.4);
}

.note-meta {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

.note-meta.is-empty {
  display: none;
}

.cents-value {
  font-weight: 700;
  min-width: 3.4ch;
}

.tuner.in-tune .cents-value {
  color: var(--in-tune-strong);
}

/* ---- visual stage: the Strobe/Needle/Meter toggle sits directly above
   the device, close enough to read as attached to it, with the device
   itself centered on the stage below. ---- */
.visual-stage {
  position: relative;
  min-height: 268px;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 0.25rem;
  padding-top: 0.2rem;
  min-width: 0;
}

.visual-devices {
  position: relative;
  display: grid;
  justify-items: center;
  align-items: center;
  min-width: 0;
}

.visual-element {
  grid-area: 1 / 1;
  width: min(240px, 74vw);
  max-width: 100%;
  min-width: 0;
}

.strobe.visual-element {
  width: min(340px, 88vw);
}

.tuner[data-visual-mode="strobe"] .visual-stage {
  min-height: 240px;
}

.tuner[data-visual-mode="needle"] .visual-stage {
  min-height: 268px;
}

.tuner[data-visual-mode="meter"] .visual-stage {
  min-height: 320px;
}

.strobe-device,
.needle-device {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

.device-case,
.device-case-circle,
.strobe-case {
  filter: drop-shadow(0 2px 5px rgba(61, 31, 15, 0.14));
}

/* ---- strobe wheel — inspired by the Peterson StroboStomp HD: five fine,
   dense concentric rings (not chunky spokes) rotating at graduated speeds,
   all freezing in unison the instant the note is in tune. Rotation is
   applied via the SVG transform attribute (not CSS transform-origin),
   matching the metronome pendulum's approach for reliable cross-browser
   rotation. Ring geometry is generated once in script.js; only the color/
   glow language lives here. ---- */
.strobe-window-rim {
  fill: none;
  stroke: rgba(255, 255, 255, 0.12);
  stroke-width: 1;
}

.strobe-vignette,
.strobe-glass-highlight {
  pointer-events: none;
}

.strobe-ring {
  filter: drop-shadow(0 0 1.4px rgba(133, 180, 224, 0.55));
}

/* Blue rather than the old beige/brass — sits closer on the wheel to the
   green in-tune color it settles into, so freezing reads as a shift in
   motion rather than a jarring color swap. */
.strobe-ring path {
  fill: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), #a8cdf0);
  stroke: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), #a8cdf0);
  stroke-width: 0.5;
}

.strobe-ring-alt path {
  fill: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), #4f7cb8);
  stroke: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), #4f7cb8);
}

.strobe-hub {
  fill: #f6f1e8;
}

.strobe-hub-shine {
  fill: rgba(255, 255, 255, 0.75);
}

.tuner.in-tune .strobe-ring path {
  fill: var(--in-tune);
  stroke: var(--in-tune);
}

.tuner.in-tune .strobe-ring {
  filter: drop-shadow(0 0 2.5px rgba(111, 143, 82, 0.85));
}

/* ---- needle gauge: same case/window language as the strobe device and the
   metronome pendulum, with a needle rotating around a base pivot. ---- */
.needle-flat-label,
.needle-sharp-label {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-size: 20px;
  fill: rgba(246, 241, 232, 0.5);
}

.needle-flat-label {
  text-anchor: middle;
}

.needle-sharp-label {
  text-anchor: middle;
}

#needleScaleTicks line {
  stroke: rgba(246, 241, 232, 0.32);
  stroke-width: 1.5;
  stroke-linecap: round;
}

#needleScaleTicks line.major {
  stroke: rgba(246, 241, 232, 0.55);
  stroke-width: 2.25;
}

#needleInTuneArc path {
  stroke: var(--in-tune);
  stroke-width: 5;
  stroke-linecap: round;
  fill: none;
  opacity: 0.55;
}

.needle-pointer {
  fill: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), var(--gold));
  transition: fill 160ms ease;
}

.tuner.in-tune .needle-pointer {
  fill: var(--in-tune);
}

.needle-hub {
  fill: #f6f1e8;
  stroke: rgba(61, 31, 15, 0.3);
  stroke-width: 1;
}

#needleSwing {
  transition: none;
}

/* ---- LED / variance meter: a vertical ladder (flat at the bottom, sharp
   at the top) that fills from the tuned center outward, plus a precise
   sliding indicator on top. ---- */
.led-meter.visual-element {
  justify-content: center;
  width: auto;
}

.led-meter.visual-element:not([hidden]) {
  display: flex;
}

/* Positioned so the track alone (not the track+labels row as a unit) is
   what .led-meter's justify-content:center centers — the labels are taken
   out of flow (see .led-scale-labels) so they don't pull the bar off-center. */
.led-meter-device {
  position: relative;
  height: min(280px, 40vh);
}

/* Golden bezel around the bar, matching the wood/brass case gradient used
   on the strobe and needle devices (tunerCaseGradient) rather than the
   plain dark border it had before. */
.led-case {
  height: 100%;
  padding: 4px;
  border-radius: 1rem;
  background: linear-gradient(145deg, #caa06a, #8a5a30);
  box-shadow: 0 2px 5px rgba(61, 31, 15, 0.14);
}

.led-track {
  position: relative;
  width: 132px;
  height: 100%;
  border-radius: 0.7rem;
  background: linear-gradient(180deg, #241b14, #171310);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.led-segments {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  justify-content: space-between;
  padding: 10px 0;
}

.led-dot {
  width: 16px;
  height: 5px;
  border-radius: 999px;
  background: rgba(246, 241, 232, 0.14);
  transition: background 90ms ease, box-shadow 90ms ease;
}

.led-dot.center {
  width: 24px;
  height: 6px;
}

.led-dot.lit {
  background: var(--gold);
  box-shadow: 0 0 6px rgba(178, 122, 55, 0.7);
}

.led-dot.lit.center {
  background: var(--in-tune);
  box-shadow: 0 0 9px rgba(111, 143, 82, 0.85);
}

.led-dot.lit.edge {
  background: var(--accent);
  box-shadow: 0 0 6px rgba(140, 59, 47, 0.7);
}

.led-indicator {
  position: absolute;
  left: 4px;
  right: 4px;
  height: 2px;
  bottom: 50%;
  background: #fffdf8;
  border-radius: 999px;
  box-shadow: 0 0 8px rgba(255, 253, 248, 0.85);
  transform: translateY(50%);
  transition: opacity 160ms ease;
  opacity: 0;
}

.led-indicator.visible {
  opacity: 1;
}

.led-scale-labels {
  position: absolute;
  left: 100%;
  top: 0;
  margin-left: 0.7rem;
  display: flex;
  flex-direction: column-reverse;
  justify-content: space-between;
  height: 100%;
  padding: 0.2rem 0;
  color: var(--ink-soft);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.led-scale-labels span:nth-child(2) {
  text-transform: uppercase;
  font-size: 0.68rem;
}

.tuner.in-tune .led-scale-labels span:nth-child(2) {
  color: var(--in-tune-strong);
  font-weight: 700;
}

.display-toggle {
  display: flex;
  justify-content: center;
  gap: 0.4rem;
}

/* ---- style-toggle: a single sliding switch (not two separate buttons)
   between two named states, styled like the tuner's own dark recessed
   track with a golden case-colored thumb. ---- */
.style-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  cursor: pointer;
}

.style-toggle-label {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink-soft);
  transition: color 120ms ease;
}

.style-toggle-label.is-active {
  color: var(--accent-strong);
  font-weight: 700;
}

.style-toggle-switch {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 24px;
  flex: 0 0 auto;
}

.style-toggle-switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}

.style-toggle-track {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, #241b14, #171310);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

.style-toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: linear-gradient(145deg, #caa06a, #8a5a30);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  transition: transform 150ms ease;
}

.style-toggle-switch input:checked ~ .style-toggle-track .style-toggle-thumb {
  transform: translateX(20px);
}

.style-toggle-switch input:focus-visible ~ .style-toggle-track {
  outline: 2px solid rgba(163, 86, 34, 0.9);
  outline-offset: 2px;
}

/* ---- power switch: the Start/Stop control, styled as a vintage-gear
   slide switch — a dark recessed track with a golden case-colored thumb
   that slides across, a "Start Tuner" legend printed above it and the
   current position (OFF / ON) called out below, the ON state lit in the
   same live red the tuner uses elsewhere. Sits at the very top of the
   sticky live area, above the Strobe/Needle/Meter display options. ---- */
.power-switch {
  display: grid;
  justify-items: center;
  gap: 0.32rem;
}

.power-switch-legend {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-strong);
}

.power-toggle {
  position: relative;
  width: 66px;
  height: 30px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  flex: 0 0 auto;
}

.power-toggle-track {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, #241b14, #171310);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.4);
  transition: background 160ms ease, border-color 160ms ease;
  pointer-events: none;
}

.power-toggle-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: linear-gradient(145deg, #caa06a, #8a5a30);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  transition: transform 160ms ease;
}

.power-toggle[aria-pressed="true"] .power-toggle-thumb {
  transform: translateX(36px);
}

.power-toggle[aria-pressed="true"] .power-toggle-track {
  background: linear-gradient(180deg, #7c1f18, #4f1310);
  border-color: rgba(156, 38, 32, 0.7);
}

.power-toggle:focus-visible .power-toggle-track {
  outline: 2px solid rgba(163, 86, 34, 0.9);
  outline-offset: 2px;
}

.power-switch-state {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  min-width: 4ch;
  text-align: center;
}

.power-toggle[aria-pressed="true"] ~ .power-switch-state {
  color: #e2261c;
  text-shadow: 0 0 10px rgba(226, 38, 28, 0.4);
}

.tap-status {
  text-align: center;
  min-height: 1.1em;
}

/* ---- tuning statement: a plain-language readout of the current Standard
   + Key + reference pitch, kept in sync from updateTuningStatement() —
   distinct from .hint's muted gray since this is live status, not a
   one-time explanation. Sits right after .visual-stage as a caption/legend
   for the device, so the negative margin claws back most of .tuner-live's
   1rem grid gap to read as "attached" to the display rather than floating
   in the middle of the stack. ---- */
.tuning-statement {
  margin: -0.75rem 0 0;
  text-align: center;
  color: var(--accent-strong);
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.01em;
}

/* ---- settings: Input Monitor, Reference Pitch, Tuning Standard and Test
   Tone. A single centered column by default; on wide screens (where two
   full-width panels plus the gap actually fit) they pair up two-across. ---- */
.settings-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
}

.settings-row .control-block {
  margin: 0 auto;
}

@media (min-width: 900px) {
  .settings-row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
  }

  .settings-row .control-block {
    flex: 1 1 24rem;
    max-width: 26rem;
    margin: 0;
  }
}

.panel-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.panel-title {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
  /* Pushes whatever follows (a panel-value, the collapse toggle, or both)
     to the right edge of the header, rather than spreading all header
     children apart evenly. */
  margin-right: auto;
}

/* ---- collapsible frames: every panel-header carries a toggle; clicking
   it hides everything in the .control-block except the header itself,
   collapsing the frame down to a title bar. ---- */
.collapse-toggle {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: none;
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
  border-radius: 0.5rem;
}

.collapse-toggle:hover {
  background: var(--paper-strong);
  color: var(--ink);
}

.collapse-icon {
  display: inline-block;
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
  transition: transform 150ms ease;
}

.collapse-toggle[aria-expanded="false"] .collapse-icon {
  transform: rotate(-90deg);
}

.control-block.is-collapsed > *:not(.panel-header) {
  display: none;
}

.panel-value {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 700;
  font-size: 1.3rem;
  color: var(--accent-strong);
}

.slider-title {
  display: block;
  color: var(--ink-soft);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  margin-top: 0.2rem;
}

.slider-title:first-of-type {
  margin-top: 0;
}

.fine-freq-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.6rem;
}

/* Label above a full-width select (not side by side) — the tuning
   standard names run long ("Just Intonation (Major)"), and a select box
   narrower than its selected option's text gets silently clipped by the
   browser, so the row needs the select to have the row's *whole* width
   available rather than sharing it with an inline label. */
.select-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.3rem;
  /* Grid items (this is one, inside .control-block's grid) default to
     min-width:auto same as flex items — without this, a long option (e.g.
     "Just Intonation (Major)") forces the whole row past the card's edge
     instead of letting the select inside it shrink to fit. */
  min-width: 0;
}

.select-row[hidden] {
  display: none;
}

.select-row-label {
  color: var(--ink-soft);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.select-row select {
  /* Flex items default to min-width:auto, which floors their shrink at the
     content's own intrinsic width — without this, a long option (e.g.
     "Just Intonation (Major)") overflows the panel instead of shrinking
     to fit. */
  min-width: 0;
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: 0.65rem;
  background: #fff9f1;
  color: var(--ink);
  padding: 0.35rem 0.5rem;
  cursor: pointer;
}

.select-row select:hover {
  background: #fff3e3;
}

.fine-freq-value {
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  font-size: 0.85rem;
  white-space: nowrap;
}

.fine-freq-value input[type="number"] {
  width: 3.2ch;
  border: none;
  background: none;
  padding: 0;
  text-align: right;
  font: inherit;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent-strong);
  -moz-appearance: textfield;
  appearance: textfield;
}

.fine-freq-value input[type="number"]::-webkit-inner-spin-button,
.fine-freq-value input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.panel-slider {
  width: 100%;
}

/* ---- fine-tuning slider: a center tick marking 0¢, so there's a visible
   target to aim for (or double-click near) when resetting. ---- */
.cents-slider-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.cents-center-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 2px;
  height: 12px;
  background: var(--border-strong);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* ---- reference pitch presets: plain text options (no button chrome) —
   the frequency in the page's regular serif, the tuning-standard name in
   the same cursive script as the "BeatConfused Studio" eyebrow, so picking
   a standard feels like reading a label rather than pressing a button. ---- */
.preset-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: 1.3rem;
  row-gap: 0.5rem;
}

.preset-option {
  position: relative;
  display: block;
  cursor: pointer;
}

.preset-option input {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.preset-option span {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.05rem;
  line-height: 1.15;
  text-align: center;
  border-bottom: 1.5px solid transparent;
  padding-bottom: 0.15rem;
  transition: border-color 120ms ease;
}

.preset-freq {
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink);
  transition: color 120ms ease;
}

.preset-name {
  font-family: "Marck Script", "Brush Script MT", cursive;
  font-size: 1.05rem;
  color: var(--ink-soft);
  letter-spacing: 0.01em;
  transition: color 120ms ease;
}

/* Presets with a nickname (e.g. Verdi's "Scientific" pitch) show it on its
   own italic line below the plain-set primary name, rather than joined by
   a comma on one line. */
.preset-name-primary {
  font-style: normal;
}

.preset-name-secondary {
  font-style: italic;
  margin-top: 0.05rem;
}

.preset-option:hover span {
  border-color: var(--border);
}

.preset-option input:checked + span {
  border-color: var(--accent-strong);
}

.preset-option input:checked + span .preset-freq,
.preset-option input:checked + span .preset-name {
  color: var(--accent-strong);
}

.preset-option input:focus-visible + span {
  outline: 2px solid rgba(163, 86, 34, 0.9);
  outline-offset: 3px;
  border-radius: 0.2rem;
}

/* ---- variance bar: how far the test tone sits from its nearest note, and
   how much room remains before the next one — a single lean fill, not a
   segmented ladder, so it reads at a glance. ---- */
.variance-block {
  display: grid;
  gap: 0.3rem;
}

.variance-track {
  position: relative;
  height: 6px;
  border-radius: 999px;
  background: var(--paper-strong);
  border: 1px solid var(--border);
  overflow: hidden;
}

.variance-track::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  background: var(--border-strong);
}

.variance-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  background: color-mix(in srgb, var(--tune-gray) calc(var(--tune-mix, 0) * 1%), var(--accent));
  border-radius: 999px;
  transition: background 160ms ease;
}

.variance-fill.in-tune {
  background: var(--in-tune);
}

.variance-labels {
  display: flex;
  justify-content: space-between;
  color: var(--ink-soft);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

.variance-labels span:nth-child(2) {
  color: var(--accent-strong);
  font-weight: 700;
}

/* ---- test tone controls ---- */
.test-tone-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 0.75rem;
  margin-top: 0.1rem;
  min-width: 0;
}

.test-tone-controls .small-btn {
  flex: 0 0 auto;
}

.test-tone-controls .small-btn[aria-pressed="true"] {
  background: linear-gradient(140deg, #b67a35, #8d5524);
  border-color: rgba(115, 68, 29, 0.7);
  color: #fffdf7;
}

.volume-control {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 1 auto;
  min-width: 0;
}

.volume-label {
  flex: 0 0 auto;
  font-size: 0.78rem;
  color: var(--ink-soft);
}

.volume-control input[type="range"] {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  max-width: 76px;
}

.test-tone-block .hint {
  text-align: center;
}

/* ---- Input Monitor: microphone gain boost + level meter. ---- */
.signal-block .hint {
  text-align: center;
}

.signal-meters-row {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 2.2rem;
  padding-top: 0.2rem;
}

.vertical-control {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.vertical-control-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.vertical-control-value {
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--accent-strong);
  /* Fixed (not min-) width, sized for the longest reading ("-60.0 dB"),
     so the level text changing length can't re-center the flex row and
     nudge the gain slider sideways. */
  width: 4.6rem;
  text-align: center;
  white-space: nowrap;
}

/* The classic rotated-range-input technique for a vertical slider: the
   input keeps its normal horizontal layout box, centered then rotated
   -90deg so dragging up (visually) still means dragging toward the max
   end (originally the right) — the natural "higher fader = more gain" feel. */
.vertical-slider-wrap {
  position: relative;
  width: 44px;
  height: 160px;
}

.vertical-slider-wrap .vertical-slider {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 160px;
  height: 32px;
  margin: 0;
  transform: translate(-50%, -50%) rotate(-90deg);
  accent-color: var(--accent);
}

/* Same golden-case-over-dark-track language as the vertical Meter visual,
   just without segments — a single continuous fill rising from the
   bottom, colored gold at safe levels and warming toward the accent red
   as it nears clipping. */
.level-meter-case {
  width: 44px;
  height: 160px;
  padding: 4px;
  border-radius: 1rem;
  background: linear-gradient(145deg, #caa06a, #8a5a30);
  box-shadow: 0 2px 5px rgba(61, 31, 15, 0.14);
}

.level-meter-track {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 0.7rem;
  background: linear-gradient(180deg, #241b14, #171310);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.level-meter-fill {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0%;
  background: color-mix(in srgb, var(--accent) calc(var(--level-mix, 0) * 1%), var(--gold));
  transition: height 90ms linear;
}

/* A separate full-width frame below the settings row — spans the same
   total width as that row whether its three blocks sit on one line or
   have wrapped onto their own, since it isn't one of those flex items. */
.control-block.spectrum-analyzer-block {
  max-width: none;
  margin: 0;
}

.spectrum-block {
  display: grid;
  gap: 0.35rem;
}

/* Same golden-case-over-dark-window bezel as the strobe/needle/meter
   devices, so the spectrum reads as one more instrument display rather
   than a bare canvas. */
.spectrum-case {
  padding: 4px;
  border-radius: 0.85rem;
  background: linear-gradient(145deg, #caa06a, #8a5a30);
  box-shadow: 0 2px 5px rgba(61, 31, 15, 0.14);
}

.spectrum-canvas {
  width: 100%;
  height: 140px;
  display: block;
  border-radius: 0.65rem;
  background: linear-gradient(180deg, #241b14, #171310);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.35);
}

.spectrum-labels {
  display: flex;
  justify-content: space-between;
  color: var(--ink-soft);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
}

/* ---- Frequency Table: a full-width frame like the spectrum analyzer,
   holding a long (88-row) reference table in its own scroll container so
   it doesn't stretch the page when expanded. ---- */
.control-block.freq-table-block {
  max-width: none;
  margin: 0;
}

.freq-table-controls {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 1.6rem;
}

.freq-table-controls .select-row {
  flex: 1 1 12rem;
  max-width: 20rem;
}

.freq-table-scroll {
  max-height: 22rem;
  overflow: auto;
  border-radius: 0.65rem;
  border: 1px solid var(--border);
}

.freq-table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  white-space: nowrap;
}

.freq-table th,
.freq-table td {
  padding: 0.35rem 0.65rem;
  text-align: right;
}

.freq-table thead th {
  position: sticky;
  top: 0;
  background: var(--surface-solid);
  border-bottom: 1px solid var(--border-strong);
  z-index: 1;
}

.freq-table-note-col,
.freq-table td:first-child {
  text-align: left;
  position: sticky;
  left: 0;
  background: var(--surface-solid);
}

.freq-table-note-col {
  z-index: 2;
}

/* display:flex directly on a <th> breaks the table's column layout in
   some browsers (the cell stops negotiating width with its siblings, so
   every header ends up rendered on top of the first, sticky column).
   Block-stacking the two spans instead keeps the cell a normal table
   cell while still showing the frequency and standard name on their own
   lines. */
.freq-table thead th:not(.freq-table-note-col) {
  line-height: 1.15;
}

.freq-table-col-freq {
  display: block;
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--accent-strong);
}

/* The standard's nickname (e.g. Verdi's "Scientific" pitch) sits on its
   own italic line below the plain-set primary name — same split as the
   Reference Pitch presets — so the combined string doesn't force each
   column wider than the numbers underneath it need. */
.freq-table-col-name-primary {
  display: block;
  font-family: "Marck Script", "Brush Script MT", cursive;
  font-size: 0.9rem;
  font-style: normal;
  color: var(--ink-soft);
}

.freq-table-col-name-secondary {
  display: block;
  font-family: "Marck Script", "Brush Script MT", cursive;
  font-size: 0.9rem;
  font-style: italic;
  color: var(--ink-soft);
}

.freq-table tbody tr:nth-child(even) {
  background: rgba(62, 45, 30, 0.04);
}

/* ---- shared primitives (buttons, pills, inputs) — same language as the
   metronome ---- */
.toggle-btn {
  width: min(170px, 100%);
  justify-self: center;
  border: 1px solid rgba(123, 71, 32, 0.55);
  border-radius: 999px;
  padding: 0.35rem 1rem;
  background: linear-gradient(145deg, #b57a38, #885123);
  color: #fffdf8;
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.05em;
  cursor: pointer;
  box-shadow: 0 3px 9px rgba(82, 47, 20, 0.2);
}

.toggle-btn:hover {
  filter: brightness(1.04);
}

/* Switches from the case's warm brown/gold to a clear red once listening
   starts — the same "live" red used for No Signal — rather than just a
   darker shade of the same gradient, so it's unmistakable the mic is on. */
.toggle-btn[aria-pressed="true"] {
  background: linear-gradient(145deg, #d1453a, #9c2620);
  border-color: rgba(156, 38, 32, 0.65);
  box-shadow: 0 3px 10px rgba(156, 38, 32, 0.35);
}

.control-block {
  background: var(--surface-solid);
  border: 1px solid var(--border);
  border-radius: 0.85rem;
  padding: 0.9rem;
  display: grid;
  gap: 0.7rem;
  align-content: start;
  max-width: 26rem;
  margin: 0 auto;
  width: 100%;
}

label,
legend {
  color: var(--ink-soft);
  font-size: 0.95rem;
}

input,
select,
button {
  font: inherit;
}

input[type="number"] {
  border-radius: 0.65rem;
}

input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}

.small-btn {
  border: 1px solid var(--border-strong);
  background: #fff9f1;
  color: var(--ink);
  border-radius: 0.65rem;
  padding: 0.35rem 0.7rem;
  cursor: pointer;
}

.small-btn:hover {
  background: #fff3e3;
}

.pill-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.pill {
  position: relative;
  display: block;
  cursor: pointer;
}

.pill input {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.pill span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 38px;
  padding: 0 0.7rem;
  border-radius: 0.65rem;
  border: 1px solid var(--border-strong);
  background: #fffdf9;
  color: #2f2218;
  font-size: 0.85rem;
  font-weight: 600;
  transition: transform 100ms ease, border-color 100ms ease, background 100ms ease, color 100ms ease;
}

.pill input:checked + span {
  background: linear-gradient(140deg, #b67a35, #8d5524);
  border-color: rgba(115, 68, 29, 0.7);
  color: #fffdf7;
  transform: translateY(-1px);
}

.pill input:focus-visible + span,
.toggle-btn:focus-visible,
.small-btn:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 2px solid rgba(163, 86, 34, 0.9);
  outline-offset: 2px;
}

.pill-sm span {
  min-height: 32px;
  min-width: 32px;
  padding: 0 0.55rem;
  font-size: 0.78rem;
}

.hint {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.82rem;
}

.hint.is-error {
  color: var(--accent-strong);
}

.footer {
  text-align: center;
  color: var(--ink-soft);
  font-family: "Cormorant Garamond", Georgia, serif;
  font-size: 1.22rem;
}

.footer-links,
.footer-contact {
  margin: 0.3rem 0 0;
  font-family: Inter, "Segoe UI", Roboto, system-ui, sans-serif;
  font-size: 0.85rem;
}

.footer-links a,
.footer-contact a {
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.05rem;
  transition: color 120ms ease, border-color 120ms ease;
}

.footer-links a:hover,
.footer-contact a:hover {
  color: var(--accent);
  border-color: var(--accent);
}

@media (max-width: 720px) {
  .page {
    padding-top: 2rem;
  }

  .tuner {
    padding: 1rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
