- Filter (key/strings/fret window) collapsed behind Filter button with
active-count badge; opens the unified TargetNoteControls panel
- Display key signature moved into the filter panel (all modes)
- Instrument + Tuning selects moved into Settings modal (Instrument
section); removed from footer
- Settings modal restructured into collapsible sections: Instrument,
Appearance, Audio, Microphone
- Phrase setup panel: removed cross-reference hint texts
- PracticeFilter is now the single source of truth: removed mirrored
keyTonic/keyMode/fretWindow fields from PhraseSettings
- Dark mode: neutral gray with slight blue tint (was warm brown)
- color-scheme per theme so native controls (selects, checkboxes, number
inputs, sliders) render dark-native instead of black-on-black.
- Themed .control-select text color; new .control-input class replaces
hardcoded white-on-black inline input styles.
- Tuner widget stays mounted when enabled: idle state (dimmed, needle parked,
'—') instead of appearing/disappearing. Fully theme-var based (was
hardcoded white — invisible in light mode).
- Remove CSS transitions on per-frame values (tuner needle, level meter):
transitions interrupted 60x/sec are left stuck by WebKit/Safari; the
display EMA from the detection loop replaces them.
- VexFlow notation: resolve CSS var() to concrete colors (SVG attributes
can't resolve var(), silently fell back to black) and override VexFlow's
hardcoded #444 ledger-line default; re-render on theme changes
(useLayoutEffect so children read the updated attribute in time).
- Themed switch toggles (were hardcoded #ddd / white — glowed in dark mode).
- Fixed-height pitch readout so the layout stops jumping when a note starts
or stops sounding.
- Split SettingsContext into context/Provider/hook files (fast-refresh);
memoized provider functions; typed webkitAudioContext; prefer-const;
removed 'as any' casts. ESLint now passes clean.