:root {
  --paper: #FBFAF7;
  --ink: #1A1A1A;
  --gold: #B08D3C;
  --muted: #9A958C;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }

body {
  font-family: 'Cormorant Garamond', Georgia, serif;
  background: radial-gradient(120% 80% at 50% 0%, #FFFFFF 0%, var(--paper) 58%, #F4F1EA 100%);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

#app {
  width: min(92vw, 560px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
}

header { position: fixed; top: 1.4rem; left: 1.7rem; }
header h1 {
  font-size: .95rem;
  letter-spacing: .42em;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--muted);
}

main { display: flex; flex-direction: column; align-items: center; gap: 1.2rem; }

#staff {
  background: #fff;
  border: 1px solid rgba(26, 26, 26, .06);
  border-radius: 6px;
  padding: .5rem 1.1rem;
  box-shadow: 0 18px 50px -28px rgba(26, 26, 26, .35),
              0 2px 0 rgba(255, 255, 255, .6) inset;
}
#staff svg { display: block; }
#staff.shake { animation: staffShake 400ms ease; }
@keyframes staffShake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-8px); }
  30% { transform: translateX(7px); }
  45% { transform: translateX(-5px); }
  60% { transform: translateX(4px); }
  75% { transform: translateX(-2px); }
}

.prompt {
  font-size: 1.5rem;
  font-style: italic;
  color: var(--muted);
  letter-spacing: .02em;
  height: 1.6em;
}
.prompt.correct { color: var(--gold); font-style: normal; }

.played {
  font-size: 1.15rem;
  letter-spacing: .14em;
  color: var(--muted);
  min-height: 1.5em;
  transition: color .15s ease, transform .15s ease;
}
.played .note-name { color: var(--ink); font-weight: 500; }
.played.hit .note-name { color: var(--gold); }
.played.miss .note-name { color: #B4543F; }

/* ---- Streak counter (top-right) ---- */
.streak {
  position: fixed;
  top: 1.3rem;
  right: 1.7rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1;
}
.streak-num {
  font-size: 2.4rem;
  font-weight: 600;
  color: var(--ink);
  transition: transform .18s cubic-bezier(.34, 1.56, .64, 1), color .18s ease;
}
.streak-num.bump { transform: scale(1.35); color: var(--gold); }
.streak-label {
  font-size: .7rem;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: .25rem;
}

.status {
  position: fixed;
  bottom: 1.3rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 1rem;
  color: var(--muted);
  letter-spacing: .05em;
  text-align: center;
  max-width: 80vw;
}
.octave {
  position: fixed;
  bottom: 1.3rem;
  right: 1.7rem;
  font-size: .95rem;
  color: var(--muted);
  letter-spacing: .03em;
}
.focus {
  position: fixed;
  bottom: 1.3rem;
  left: 1.7rem;
  font-size: .95rem;
  color: var(--muted);
  letter-spacing: .05em;
}
.focus .focus-note { color: var(--gold); font-weight: 500; }

/* ---- Tabs (top-center segmented control) --------------------------------
   One row, one concept per tab. Five of them choose what the trainer drills;
   the last one swaps the trainer out for the Song Builder. They look alike
   because they are peers — the separators, not a second row, carry the
   difference. */
.tabs {
  position: fixed;
  top: 1.3rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  display: inline-flex;
  background: rgba(255, 255, 255, .7);
  border: 1px solid rgba(26, 26, 26, .1);
  border-radius: 999px;
  padding: .25rem;
  gap: .15rem;
  box-shadow: 0 6px 18px -12px rgba(26, 26, 26, .4);
}
.tab-btn {
  font-family: inherit;
  font-size: .95rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: none;
  border-radius: 999px;
  padding: .35rem 1.1rem;
  cursor: pointer;
  white-space: nowrap;
  transition: color .2s ease, background .2s ease;
}
.tab-btn:hover { color: var(--ink); }
.tab-btn.is-active {
  color: var(--paper);
  background: var(--ink);
}
.tab-sep {
  width: 1px;
  align-self: stretch;
  margin: .25rem .15rem;
  background: rgba(26, 26, 26, .12);
}

/* Seven tabs make a wide row, and it is centred between two things pinned to
   the corners: the CLEF wordmark on the left, the streak counter on the right.
   Below roughly 950px the bar reaches both. It gives up letter-spacing and
   padding before it gives up either corner, because a tab that is merely tighter
   still reads, and a tab sitting under the streak counter does not. */
@media (max-width: 1000px) {
  .tab-btn { font-size: .85rem; letter-spacing: .07em; padding: .35rem .6rem; }
  .tab-sep { margin: .25rem .05rem; }
}

/* ---- Note animations (applied to the VexFlow SVG note group) ---- */
.vf-stavenote.pop {
  transform-box: fill-box;
  transform-origin: center;
  animation: notePop 380ms cubic-bezier(.34, 1.56, .64, 1), noteGlow 420ms ease;
}
.vf-stavenote.nudge {
  transform-box: fill-box;
  transform-origin: center;
  animation: noteNudge 320ms ease;
}
@keyframes notePop {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.32); }
  62%  { transform: scale(.97); }
  100% { transform: scale(1); }
}
@keyframes noteGlow {
  0%, 100% { filter: none; }
  40%      { filter: drop-shadow(0 0 7px rgba(176, 141, 60, .85)); }
}
@keyframes noteNudge {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-3px); }
  40% { transform: translateX(3px); }
  60% { transform: translateX(-2px); }
  80% { transform: translateX(2px); }
}

/* ---- Start overlay ---- */
.overlay {
  position: fixed;
  inset: 0;
  background: rgba(251, 250, 247, .86);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity .5s ease;
  z-index: 10;
}
.overlay.hidden { opacity: 0; pointer-events: none; }
.overlay-card { text-align: center; max-width: 30rem; padding: 2rem; }
.overlay-card h2 { font-size: 2.6rem; font-weight: 600; margin-bottom: .6rem; }
.overlay-card p { font-size: 1.2rem; color: var(--muted); font-style: italic; margin-bottom: 1.4rem; }
.overlay-card .hint { font-size: 1rem; margin-top: 1.3rem; margin-bottom: 0; }

button#start {
  font-family: inherit;
  font-size: 1.15rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink);
  border: none;
  border-radius: 999px;
  padding: .7rem 2.4rem;
  cursor: pointer;
  transition: transform .15s ease, background .2s ease;
}
button#start:hover { background: var(--gold); transform: translateY(-1px); }

/* ---- Song Builder --------------------------------------------------------
   The trainer is a 560px card; four measures are not. This view widens the
   container and everything below assumes that extra room. */

#app.wide { width: min(96vw, 1180px); }

/* The trainer is one card and sits centred. The builder is a tall document, so
   the page has to start at the top and scroll instead of centring something
   that no longer fits — and it needs clearance under the fixed tab bar. */
body:has(#app.wide) {
  align-items: flex-start;
  padding: 4.6rem 0 2.5rem;
}

.songbuilder { width: 100%; display: flex; flex-direction: column; gap: .75rem; }

.sb-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .45rem 1.1rem;
  padding: .6rem .85rem;
  background: #fff;
  border: 1px solid rgba(26, 26, 26, .07);
  border-radius: 6px;
  box-shadow: 0 12px 34px -26px rgba(26, 26, 26, .4);
}
.sb-group { display: flex; align-items: center; gap: .35rem; }
.sb-group-end { margin-left: auto; }

.sb-label {
  font-size: .7rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); margin-right: .15rem;
}

.sb-btn, .sb-dur {
  font-family: inherit; font-size: .92rem;
  padding: .22rem .6rem;
  background: transparent; color: var(--ink);
  border: 1px solid rgba(26, 26, 26, .16); border-radius: 4px;
  cursor: pointer; transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.sb-btn:hover:not(:disabled), .sb-dur:hover { border-color: var(--gold); }
.sb-btn:disabled { opacity: .38; cursor: default; }
.sb-btn.is-active, .sb-dur.is-active {
  background: var(--gold); border-color: var(--gold); color: #fff;
}
.sb-sharp { font-size: 1.05rem; line-height: 1; }

/* Note-value glyphs are drawn paths, not text, so they inherit the button's
   colour and invert with it when the tool is active. */
.sb-dur { display: inline-flex; align-items: center; justify-content: center; padding: .18rem .5rem; }
.sb-dur svg { display: block; color: inherit; }
.sb-durations { display: inline-flex; gap: .25rem; }
.sb-cancel { color: var(--muted); }

.sb-num, .sb-select {
  font-family: inherit; font-size: .92rem;
  padding: .18rem .3rem; width: 3.4rem;
  background: #fff; color: var(--ink);
  border: 1px solid rgba(26, 26, 26, .16); border-radius: 4px;
}
.sb-select { width: auto; }
.sb-slash { color: var(--muted); }

.sb-modes { display: flex; gap: .55rem; font-size: .85rem; color: var(--muted); }
.sb-modes label { display: flex; align-items: center; gap: .22rem; cursor: pointer; }

.sb-status { font-size: .78rem; letter-spacing: .1em; color: var(--muted); min-width: 7rem; }
.sb-status.is-offline, .sb-status.is-error { color: #B4543F; }

.sb-hint { font-size: .9rem; font-style: italic; color: var(--muted); min-height: 1.3em; }
.sb-keys { font-size: .78rem; color: var(--muted); text-align: center; }
.sb-keys strong { font-weight: 600; color: var(--ink); }

/* ---- Rows --------------------------------------------------------------- */

.sb-rows { display: flex; flex-direction: column; gap: .6rem; }

.sb-row {
  position: relative;            /* the overlay positions against this */
  background: #fff;
  border: 1px solid rgba(26, 26, 26, .06);
  border-radius: 6px;
  overflow-x: auto;              /* narrow windows scroll; they never reflow */
  box-shadow: 0 14px 40px -30px rgba(26, 26, 26, .35);
}
/* Never CSS-scale this: hit testing maps client coordinates 1:1 into VexFlow's
   space, and a scaled SVG would silently offset every click. */
.sb-score svg { display: block; }

.sb-overlay { position: absolute; inset: 0; pointer-events: none; }

/* ---- Placement hints ---------------------------------------------------- */

.sb-bar { stroke: #CDCAC2; stroke-width: 1; stroke-dasharray: 3 4; }
.sb-bar.is-off { stroke-opacity: .3; }

.sb-ghost-head { fill: #9A958C; fill-opacity: .5; }
.sb-ghost-ledger { stroke: #9A958C; stroke-opacity: .5; stroke-width: 1; }
.sb-ghost { transition: transform 60ms ease-out; }   /* the "magnetic" feel */
.sb-ghost.is-illegal { display: none; }

.sb-wrong-head { fill: #B4543F; }

.sb-loop-mark { stroke: #3E7C4A; stroke-width: 3; stroke-linecap: round; }

.sb-clear { pointer-events: auto; cursor: pointer; opacity: 0; transition: opacity .15s ease; }
.sb-row:hover .sb-clear { opacity: .5; }
.sb-clear:hover { opacity: 1; }
.sb-clear circle { fill: #fff; stroke: rgba(26, 26, 26, .18); }
.sb-clear text { font-family: inherit; font-size: 13px; fill: #B4543F; }

/* ---- Practice highlighting ----------------------------------------------
   VexFlow writes fill/stroke as presentation attributes, and a stylesheet rule
   outranks those — so a state change here is one classList call and no redraw.
   Each rule colours the accidental too: setStyle in VexFlow 4 does not reach a
   note's Accidental modifier, so colouring only the head leaves a black sharp
   sitting next to a green notehead, which reads as a bug. */

.sb-note.is-cursor  path { fill: #1A1A1A; stroke: #1A1A1A; }
.sb-note.is-correct path { fill: #3E7C4A; stroke: #3E7C4A; }
.sb-note.is-wrong   path { fill: #B4543F; stroke: #B4543F; }
.sb-note.is-sounding path { fill: #B08D3C; stroke: #B08D3C; }

/* ---- Add a row ----------------------------------------------------------
   Invisible until the empty space below the last row is hovered. */

.sb-addrow { height: 3.4rem; display: flex; align-items: center; justify-content: center; }
.sb-addrow button {
  font-family: inherit; font-size: .9rem; font-style: italic;
  color: var(--muted); background: transparent;
  border: 1px dashed rgba(26, 26, 26, .2); border-radius: 5px;
  padding: .35rem 1.1rem; cursor: pointer;
  opacity: 0; transition: opacity .18s ease, border-color .18s ease, color .18s ease;
}
.sb-addrow:hover button, .sb-addrow button:focus-visible { opacity: 1; }
.sb-addrow button:hover { color: var(--gold); border-color: var(--gold); }

/* ---- Pitch ---------------------------------------------------------------
   An ear drill in a reading trainer, so it borrows the trainer's proportions
   rather than the Song Builder's: the same 560px card, centred, with the play
   button standing where the staff would be. The one thing it must never do is
   put the answer on screen before it is earned. */

.pitch {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.15rem;
}

.pitch-settings { display: flex; flex-wrap: wrap; justify-content: center; gap: .4rem 1.6rem; }
.pitch-group { display: flex; align-items: center; gap: .5rem; }
.pitch-label {
  font-size: .7rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted);
}

/* A quieter echo of the tab bar above: same pill, less contrast, because these
   choose how the drill behaves rather than which drill you are in. */
.pitch-seg {
  display: inline-flex; gap: .1rem; padding: .16rem;
  background: rgba(255, 255, 255, .7);
  border: 1px solid rgba(26, 26, 26, .1);
  border-radius: 999px;
}
.pitch-seg-btn {
  font-family: inherit; font-size: .82rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); background: transparent;
  border: none; border-radius: 999px;
  padding: .24rem .85rem; cursor: pointer;
  transition: color .2s ease, background .2s ease;
}
.pitch-seg-btn:hover { color: var(--ink); }
.pitch-seg-btn.is-active { color: var(--paper); background: var(--ink); }

/* The play button stands where the staff stands in the reading drills — it is
   the thing being read here, so it gets that weight and that shadow. */
.pitch-play {
  font-family: inherit; font-size: 1.15rem;
  letter-spacing: .16em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: .7rem;
  color: var(--paper); background: var(--ink);
  border: none; border-radius: 999px;
  padding: .8rem 2.2rem; cursor: pointer;
  box-shadow: 0 18px 44px -26px rgba(26, 26, 26, .8);
  transition: transform .15s ease, background .2s ease, box-shadow .2s ease;
}
.pitch-play:hover:not(:disabled) { background: var(--gold); transform: translateY(-1px); }
.pitch-play:disabled { opacity: .4; cursor: default; box-shadow: none; }
.pitch-play-glyph { font-size: .8em; line-height: 1; }
/* Once the answer is out, replaying is a smaller act than asking was. */
.pitch-play.is-done { background: transparent; color: var(--ink); box-shadow: none; }
.pitch-play.is-done { border: 1px solid rgba(26, 26, 26, .18); }

.pitch .prompt { font-size: 1.35rem; text-align: center; }
.pitch .prompt.missed { color: #B4543F; font-style: normal; }
.pitch-name { font-style: normal; font-weight: 600; letter-spacing: .06em; }
.pitch .prompt.correct .pitch-name { color: var(--gold); }

.pitch-answer { display: flex; flex-direction: column; align-items: center; gap: .8rem; }

.pitch-choices, .pitch-octaves {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .45rem;
  max-width: 26rem;
}

/* All twelve go in two rows of six rather than being left to wrap into a ragged
   7 and 5. Two reasons, and the second is the real one: the split falls on the
   tritone, so each row is half an octave; and a grid keeps every pitch class in
   the SAME place every round, which is what lets you stop reading the buttons
   and start reaching for them. */
.pitch-choices.is-wide {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  width: 25rem;
  max-width: 100%;
}

.pitch-choice {
  font-family: inherit; font-size: 1.15rem;
  min-width: 3.1rem;
  color: var(--ink); background: #fff;
  border: 1px solid rgba(26, 26, 26, .16); border-radius: 5px;
  padding: .45rem .6rem; cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background .15s ease, opacity .15s ease;
}
.pitch-choice:hover:not(:disabled) { border-color: var(--gold); color: var(--gold); }
.pitch-choice.is-picked { background: var(--ink); border-color: var(--ink); color: var(--paper); }

/* A refused answer is struck through where it stands rather than removed. The
   row must not reflow between attempts — the next button you reach for would
   move out from under the pointer — and knowing what you have already ruled out
   is most of what the second and third attempts are for. */
.pitch-choice.is-wrong {
  color: var(--muted); border-color: rgba(26, 26, 26, .08);
  text-decoration: line-through; opacity: .55;
}
.pitch-choice.is-answer:disabled {
  color: var(--gold); border-color: var(--gold);
  background: rgba(176, 141, 60, .1); opacity: 1;
  text-decoration: none;
}
.pitch-choice:disabled { cursor: default; }

.pitch-octaves { gap: .45rem; }
.pitch-octave { min-width: 2.6rem; }

.pitch-piano-hint {
  font-size: 1.05rem; font-style: italic; color: var(--muted);
  text-align: center; min-height: 3.2em; display: flex; align-items: center;
}

.pitch-foot { display: flex; align-items: center; gap: .9rem; }
.pitch-attempts {
  font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); min-width: 7.5rem;
}
.pitch-dots { font-size: .9rem; letter-spacing: .18em; color: var(--ink); }
.pitch-attempts.is-low .pitch-dots { color: #B4543F; }

.pitch-btn {
  font-family: inherit; font-size: .88rem;
  letter-spacing: .1em;
  color: var(--ink); background: transparent;
  border: 1px solid rgba(26, 26, 26, .16); border-radius: 4px;
  padding: .28rem .85rem; cursor: pointer;
  transition: border-color .15s ease, color .15s ease;
}
.pitch-btn:hover:not(:disabled) { border-color: var(--gold); color: var(--gold); }
.pitch-btn:disabled { opacity: .38; cursor: default; }

/* ---- Hiding ------------------------------------------------------------
   The `hidden` attribute is only a UA rule of `display: none`, so any authored
   display beats it — and several things above set one, so hiding them from JS
   did nothing at all until this existed.

   Last in the file *and* !important, which is belt and braces on purpose:
   `#app` sets display with a higher specificity than an attribute selector can
   reach, so order alone would not always be enough. */
[hidden] { display: none !important; }
