migrate interval comparison, fix feedback sequence
Created by: jedmund
Stacked on top of #27.
Summary
- Third exercise migrated to the shared architecture. Dual listen (Play A / Play B) inline in the exercise; answers rendered via `PickerWidget` with `["A", "B"]` using its two-choice HStack path. Dynamic per-prompt title ("Which is the Major 3rd?") since the target interval changes each prompt.
- Routed through `PageContainer.intervalComparisonLayout`, `PlaygroundScreen` (moved from `keyboardTools` to `exerciseTools`), and `ExerciseStateRegistry` scored-state restoration.
- Bug fix: `ExerciseState.recordResult` used to both record and advance in one go, which meant the final prompt's feedback was skipped (ScoringOverlay flipped to completion before the user saw it) and any exercise reading the current prompt during feedback saw the next one. Split into `recordResult` + `advance`; IntervalClassifier and IntervalTrainer updated to call `state.advance()` on Next.
Test plan
-
Standalone Interval Comparison: Exercises grid → tap → listen to both A and B → answer → see feedback with matching title, semitone comparison, correct/incorrect dot. Next → new prompt with different target, first progress dot reflects result. -
Complete all 8 prompts; verify final prompt's feedback renders (no skip), completion fits card. -
Lesson Interval Classifier/Trainer: final prompt's feedback should now render before completion (previously skipped). -
Lesson Interval Comparison (music-theory 3-ex3 / 4-ex3 / 5-ex3): context text above card, progress survives navigation, attempt recorded on completion.