@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@400;600;800&display=swap');

:root {
  --color-bg: #f3f2f2;
  --color-surface: #eae9e9;
  --color-text: #201e1d;
  --color-accent: #ec3013;
  --color-accent-700: #ae1800;
  --color-divider: color-mix(in srgb, #201e1d 40%, transparent);

  --color-neutral-300: #d7d3d3;
  --color-neutral-400: #bab6b6;
  --color-neutral-500: #9b9797;
  --color-neutral-600: #7d7979;
  --color-neutral-700: #605d5d;

  /* Piano key colors (see DEVELOPMENT_PLAN.md §v2.5) - deliberately NOT overridden in the dark
     theme below. A white key has to stay light and a black key has to stay dark regardless of
     the app's overall theme, the same way a real keyboard doesn't repaint itself - reusing
     --color-bg/--color-text (which flip between themes) here would invert white and black keys
     in dark mode, since --color-bg becomes dark and --color-text becomes light. */
  --key-white-bg: #f3f2f2;
  --key-black-bg: #201e1d;
  --key-divider: color-mix(in srgb, #201e1d 40%, transparent);
  --key-label-color: #7d7979;
  --key-label-active-color: #f3f2f2;

  /* Modal/sheet backdrop scrim (see DEVELOPMENT_PLAN.md §v2.5) - also intentionally not
     overridden in the dark theme below. A backdrop has to always dim the page toward black,
     regardless of theme; mixing in --color-text (which becomes light in dark mode) would wash
     the page lighter instead of dimming it, the opposite of a scrim's job. */
  --scrim: color-mix(in srgb, #201e1d 45%, transparent);

  --font-heading: "Archivo", system-ui, sans-serif;
  --font-body: "Archivo", system-ui, sans-serif;
}

/* Dark mode (see DEVELOPMENT_PLAN.md §v2.4 v2.5) - every component already consumes these as
   var(), including the SVG fill/stroke rules gauge.js/staff.js render, so overriding the full
   token set here is the only CSS needed. The one non-CSS piece is staff.js's INK_COLOR, which
   recolors the clef PNGs on a canvas and can't read a custom property - see its own comment.
   --key-* tokens are intentionally absent here - see their definition in :root above. */
:root[data-theme="dark"] {
  --color-bg: #16151a;
  --color-surface: #201f24;
  --color-text: #f0eeed;
  --color-accent: #ec3013;
  --color-accent-700: #ff5c3f;
  --color-divider: color-mix(in srgb, #f0eeed 40%, transparent);

  --color-neutral-300: #3a3838;
  --color-neutral-400: #4d4a4a;
  --color-neutral-500: #6b6767;
  --color-neutral-600: #9b9797;
  --color-neutral-700: #bdb9b8;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 15px;
  line-height: 1.5;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 800;
  margin: 0;
}

/* — buttons — */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 14px;
  color: var(--color-text);
  background: transparent;
  border: 1px solid var(--color-divider);
  border-radius: 0;
  padding: 10px 20px;
}

.btn-primary {
  background: var(--color-accent);
  color: var(--color-bg);
  border-color: var(--color-accent);
}

.btn-primary:hover:not(:disabled) {
  background: var(--color-accent-700);
  border-color: var(--color-accent-700);
}

.btn-secondary:hover:not(:disabled) {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-icon {
  width: 36px;
  height: 36px;
  padding: 0;
}

.btn-block {
  width: 100%;
}

/* Visually hidden but still announced by screen readers - used on the segmented controls'
   <legend> so "Practice Mode"/"Difficulty" survive as an accessible group name (PR #12
   review) without reintroducing a visible label the redesign deliberately dropped. */
.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;
}

/* — segmented control (Practice Mode / Difficulty) — */
.seg {
  display: inline-flex;
  border: 1px solid var(--color-divider);
  padding: 0;
  margin: 0;
}

.seg-block {
  display: flex;
  width: 100%;
}

.seg-opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 14px;
  font-size: 13px;
  cursor: pointer;
}

.seg-block .seg-opt {
  flex: 1;
}

.seg-opt + .seg-opt {
  border-left: 1px solid var(--color-divider);
}

.seg-opt input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.seg-opt:has(input:checked) {
  background: var(--color-accent);
  color: var(--color-bg);
}

.seg-opt:not(:has(input:checked)):hover {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
}

.seg:has(input:disabled) {
  opacity: 0.45;
}

.seg[hidden] {
  display: none;
}

.seg-opt:has(input:disabled) {
  cursor: not-allowed;
}

/* — dot toggle (Auto-play / Auto-advance) — */
.radio {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 13px;
  color: var(--color-neutral-700);
}

.radio input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}

.radio .dot {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 50%;
  border: 1.5px solid var(--color-divider);
}

.radio input:checked + .dot {
  border-color: var(--color-accent);
  background: var(--color-accent);
  box-shadow: inset 0 0 0 3px var(--color-bg);
}

.radio:has(input:disabled) {
  opacity: 0.45;
  cursor: not-allowed;
}

/* — native selects (Voice Type / Low / High) — */
select {
  -webkit-appearance: none;
  appearance: none;
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 14px;
  padding: 6px 10px;
  border-radius: 0;
  min-height: 36px;
}

/* ══════════════════ Onboarding (first-run mic-permission intro) ══════════════════ */
#onboarding-screen {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--color-bg);
  z-index: 100;
}

#onboarding-screen[hidden] {
  display: none;
}

.onboarding-card {
  width: min(560px, 100%);
  padding: 48px 40px;
  border: 2px solid var(--color-divider);
}

.onboarding-kicker {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent);
}

.onboarding-title {
  font-size: 44px;
  line-height: 0.95;
  margin-top: 10px;
}

.onboarding-body {
  font-size: 16px;
  color: var(--color-neutral-700);
  margin: 18px 0 0;
  line-height: 1.5;
}

.onboarding-mic-note {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 28px 0;
  padding: 18px;
  border: 2px solid var(--color-divider);
  background: var(--color-surface);
}

.onboarding-mic-title {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 15px;
}

.onboarding-mic-detail {
  font-size: 13px;
  color: var(--color-neutral-700);
}

.onboarding-error {
  font-size: 13px;
  color: var(--color-accent-700);
  margin: -8px 0 20px;
}

.onboarding-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

/* ══════════════════ Custom sequence tutorial popup (§v2.4) ══════════════════ */
.modal-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--scrim);
  z-index: 110;
}

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

.modal-card {
  width: min(420px, 100%);
  padding: 28px 24px;
  background: var(--color-bg);
  border: 2px solid var(--color-divider);
}

/* Wider variant for the About/Tutorial modal (§v2.6), which has substantially more content
   than the Custom-sequence tutorial popup this component was originally built for. */
.modal-card-large {
  width: min(560px, 100%);
}

.modal-title {
  font-size: 20px;
  margin-bottom: 14px;
}

/* "Updated for vX.X" banner (§v2.7) - shown above the About/Tutorial modal's scrollable body
   whenever it's re-shown for a returning visitor after its content has changed, so it's clear
   why the modal appeared again unprompted. See main.js's showAboutTutorial(). */
.about-tutorial-update-banner {
  margin: -4px 0 14px;
  padding: 6px 10px;
  background: color-mix(in srgb, var(--color-accent) 16%, var(--color-bg));
  border: 1px solid var(--color-accent);
  color: var(--color-accent-700);
  font-size: 12px;
  font-weight: 600;
}

/* Scrolls the About/Tutorial modal's body internally instead of letting the card grow past
   the viewport on shorter screens - the title and "Got it" button stay put outside this. */
.modal-scroll {
  max-height: 55vh;
  overflow-y: auto;
  margin-bottom: 18px;
  padding-right: 4px;
}

.modal-scroll p {
  font-size: 13px;
  color: var(--color-neutral-700);
  line-height: 1.6;
  margin: 0 0 12px;
}

.modal-scroll .kicker {
  display: block;
  margin: 18px 0 6px;
}

.modal-list {
  margin: 0 0 18px;
  padding-left: 20px;
  font-size: 13px;
  color: var(--color-neutral-700);
  line-height: 1.6;
}

.modal-list li + li {
  margin-top: 8px;
}

.modal-card .radio {
  margin-bottom: 18px;
}

/* ══════════════════ App shell ══════════════════ */
#app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

#app-shell[hidden] {
  display: none;
}

.app-nav {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 16px 24px;
  border-bottom: 2px solid var(--color-divider);
  flex-wrap: wrap;
}

.app-brand {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 18px;
  letter-spacing: -0.01em;
}

.app-nav-mode {
  margin-left: auto;
}

.app-body {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 320px;
  position: relative;
}

.settings-backdrop {
  display: none;
}

.app-footer {
  border-top: 2px solid var(--color-divider);
  padding: 14px 24px;
  font-size: 11px;
  color: var(--color-neutral-600);
}

.app-footer-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.app-footer-feedback-link {
  color: var(--color-accent);
  text-decoration: none;
}

.app-footer-feedback-link:hover,
.app-footer-feedback-link:focus-visible {
  text-decoration: underline;
}

/* Resets native <button> chrome so "About" (a button, not a link, since it opens a modal
   rather than navigating) can share .app-footer-feedback-link's link-like appearance (§v2.6). */
.app-footer-link-button {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

.app-footer-legal {
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--color-divider);
  font-size: 10px;
  line-height: 1.5;
}

.kicker {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent);
}

/* — practice stage — */
.app-stage {
  padding: 28px 32px 40px;
  display: flex;
  flex-direction: column;
  gap: 28px;
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}

.stage-target {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
}

.target-note-name {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 84px;
  line-height: 0.9;
  margin: 6px 0 0;
}

.target-note-freq {
  font-size: 14px;
  color: var(--color-neutral-600);
  margin: 6px 0 0;
}

/* Holds the staff and its two captions (off-canvas warning, windowing notice) as one column, so
   the captions stay under the staff instead of under the target note name - .stage-target is a
   flex row, and before §v2.10's user testing the captions were siblings of it and started at the
   row's left edge. The width/flex sizing that used to live on .target-staff moves here. */
.target-staff-column {
  width: 260px;
  flex: none;
}

.target-staff {
  width: 100%;
}

.stage-gauge {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.detected-frequency {
  font-size: 1.1rem;
  font-weight: 600;
  margin: 0.5rem 0 0;
  text-align: center;
}

.mic-mute-indicator {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-neutral-600);
  margin: 0.35rem 0 0;
  text-align: center;
}

.stage-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding-top: 20px;
  border-top: 2px solid var(--color-divider);
}

.controls-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.controls-toggles {
  margin-left: auto;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

.stage-keyboard .kicker {
  display: block;
  margin-bottom: 8px;
}

/* Positive top margin, not the -16px these carried while they were siblings of .stage-target
   (where it cancelled .app-stage's 28px row gap) - inside .target-staff-column there's no gap to
   cancel, so a negative margin would ride up over the staff artwork. */
.staff-off-canvas-warning {
  font-size: 12px;
  color: var(--color-accent-700);
  margin: 6px 0 0;
}

/* Informational, not a warning (§v2.10) - deliberately the muted text color the keyboard's own
   range label uses rather than the accent color above, so a scrolling sequence doesn't read as
   an error condition. */
.staff-window-notice {
  font-size: 12px;
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
  margin: 6px 0 0;
}

.keyboard-range-label {
  font-size: 12px;
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
  margin: 8px 0 0;
}

/* — custom sequence source/clef controls (§v2.4) — */
.stage-phrase-source {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
}

.stage-phrase-source[hidden] {
  display: none;
}

/* — staff — */
.staff-svg {
  width: 100%;
  display: block;
}

.staff-ledger-line {
  stroke: var(--color-text);
  stroke-width: 1.5;
}

.staff-notehead {
  fill: var(--color-text);
}

.staff-sharp {
  fill: var(--color-text);
  font-size: 20px;
}

/* Flat glyph for transposed written names (§v2.7) - its own rule rather than sharing
   .staff-sharp because ♭ and ♯ don't share optical metrics: the flat's bowl is smaller and sits
   above its baseline, so it needs slightly more size to read at the same weight (its y nudge
   lives in staff.js's FLAT_Y_OFFSET, alongside the sharp's). */
.staff-flat {
  fill: var(--color-text);
  font-size: 24px;
}

.staff-notehead-matched {
  fill: var(--color-accent);
}

.staff-notehead-current {
  fill: var(--color-text);
}

.staff-notehead-upcoming {
  fill: var(--color-neutral-400);
}

/* — gauge — */
.gauge-svg {
  width: 100%;
  max-width: 420px;
  display: block;
  margin: 0 auto;
}

.gauge-arc {
  fill: none;
  stroke: var(--color-neutral-300);
  stroke-width: 8;
}

.gauge-zone {
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 8;
}

.gauge-tick {
  stroke: var(--color-neutral-500);
  stroke-width: 2;
}

.gauge-tick-center {
  stroke: var(--color-text);
  stroke-width: 3;
}

.gauge-needle {
  transition: transform 60ms ease-out;
}

.gauge-needle-line {
  stroke: var(--color-accent);
  stroke-width: 4;
  stroke-linecap: round;
}

.gauge-needle-pivot {
  fill: var(--color-text);
}

.gauge-status {
  margin: 0.5rem 0 0;
  text-align: center;
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 20px;
  color: var(--color-neutral-600);
}

.gauge-status-in-tune {
  color: var(--color-accent);
}

.gauge-status-off {
  color: var(--color-text);
}

.gauge-status-matched {
  color: var(--color-accent);
}

/* — keyboard — */
.keyboard-scroll {
  overflow: hidden;
}

.keyboard {
  position: relative;
  width: 100%;
  border: 2px solid var(--color-divider);
}

.key {
  position: absolute;
  top: 0;
  box-sizing: border-box;
}

.key-white {
  background: var(--key-white-bg);
  border-right: 1px solid var(--key-divider);
  z-index: 1;
}

.key-white:last-child {
  border-right: 0;
}

.key-black {
  background: var(--key-black-bg);
  z-index: 2;
}

.key-white.key-active {
  background: var(--color-accent);
}

.key-black.key-active {
  background: var(--color-accent-700);
}

/* Marks every other note of the current phrase/sequence (see DEVELOPMENT_PLAN.md §v2.4 PR #22
   review), so its shape is visible on the keyboard alongside the current target's full
   key-active color. A muted tint of the accent color, not the accent itself, keeps the
   current-target key visually distinct at a glance. */
.key-white.key-in-phrase {
  background: color-mix(in srgb, var(--color-accent) 25%, var(--key-white-bg));
}

.key-black.key-in-phrase {
  background: color-mix(in srgb, var(--color-accent) 45%, var(--key-black-bg));
}

/* C-note landmark labels (see DEVELOPMENT_PLAN.md §v2.4) - always on, every mode, so a custom
   sequence's notes can be picked precisely across a multi-octave range. */
.key-label {
  position: absolute;
  bottom: 6px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 10px;
  font-weight: 600;
  color: var(--key-label-color);
  pointer-events: none;
  user-select: none;
}

.key-white.key-active .key-label {
  color: var(--key-label-active-color);
}

/* Dims and disables the keyboard while a tone is playing or the mic is listening, during
   custom-sequence building (§v2.4) - editing and active practice are mutually exclusive. */
#keyboard-mount.keyboard-locked {
  opacity: 0.5;
  pointer-events: none;
}

/* — settings drawer (desktop: static column; mobile: overlay sheet) — */
.settings-drawer {
  border-left: 2px solid var(--color-divider);
  background: var(--color-surface);
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  overflow-y: auto;
}

.settings-close {
  display: none;
}

.drawer-section h4 {
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 14px;
}

.drawer-divider {
  height: 2px;
  background: var(--color-divider);
}

.difficulty-caption {
  font-size: 12px;
  color: var(--color-neutral-600);
  margin: 8px 0 0;
}

.instrument-row,
.voice-type-row,
.range-selector-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

/* Without this, the class's own `display: flex` above beats the browser's default
   `[hidden] { display: none }` UA rule (same specificity, author rule loads later) - so
   toggling the `hidden` attribute (see rangeSelector.js's handleInstrumentChange) would do
   nothing visible. Same fix already applied to .modal-overlay for the same reason. */
.voice-type-row[hidden] {
  display: none;
}

.range-selector-row {
  margin-bottom: 0;
}

.instrument-row select,
.voice-type-row select,
.range-selector-row select {
  flex: 1;
  min-width: 90px;
}

.instrument-row label,
.voice-type-row label,
.range-selector-row label {
  color: var(--color-neutral-700);
  font-size: 0.85rem;
}

.stats-list {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
  font-size: 14px;
}

.stats-list li {
  display: flex;
  justify-content: space-between;
  padding: 7px 0;
  border-bottom: 1px solid var(--color-divider);
}

.stats-list li:last-child {
  border-bottom: none;
}

.stats-label {
  color: var(--color-neutral-700);
}

.stats-value {
  font-weight: 800;
}

.stats-last-session {
  color: var(--color-neutral-600);
  font-size: 0.85rem;
  margin: 0 0 14px;
}

/* — mobile layout — */
@media (max-width: 880px) {
  .app-body {
    display: block;
  }

  .app-nav-mode {
    margin-left: 0;
    order: 3;
    flex-basis: 100%;
  }

  .app-nav-mode .seg {
    width: 100%;
  }

  .app-nav-mode .seg .seg-opt {
    flex: 1;
  }

  #settings-toggle {
    margin-left: auto;
  }

  .app-stage {
    padding: 20px 20px 32px;
  }

  .target-note-name {
    font-size: 52px;
  }

  .target-staff-column {
    width: 45%;
  }

  .controls-toggles {
    margin-left: 0;
    flex-basis: 100%;
    justify-content: center;
  }

  .app-footer {
    text-align: center;
  }

  .app-footer-row {
    flex-direction: column;
  }

  .settings-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(320px, 88vw);
    border-left: 2px solid var(--color-divider);
    transform: translateX(100%);
    transition: transform 0.25s ease-out;
    z-index: 101;
  }

  .settings-close {
    display: block;
    align-self: flex-end;
    background: transparent;
    border: 0;
    font-size: 22px;
    line-height: 1;
    color: var(--color-text);
    cursor: pointer;
    padding: 0;
  }

  body.settings-open .settings-drawer {
    transform: translateX(0);
  }

  body.settings-open .settings-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: var(--scrim);
    z-index: 99;
  }
}

@media (min-width: 881px) {
  #settings-toggle {
    display: none;
  }
}
