- useMorseAudio: wraps MorseAudioEngine, exposes currentSignal for FlashArea
- useProgress: fetches /api/progress + /api/mnemonics, recordAnswer with local stats
- FlashArea: gold flash on signal, height varies dot/dash
- ScoreBar: level name, score, streak display
- PracticeCard: play button, answer input, correct/incorrect feedback with mnemonic
- GamePage: full practice loop, level-up modal at streak%10, anti-repeat history
- Fix: incorrect attempt recorded only once per challenge (no stat inflation on retries)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- MnemonicBuilder: 12 NOVICE_LETTERS, real-time validatePhrase, save/skip
- OnboardingPage: wraps builder, passes token and onComplete
- App.tsx: post-auth fetch /api/mnemonics, route to onboarding if <12 saved
- Fix: validatePhrase called with letter (not Morse pattern) as second arg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- useAuth: session token from URL/localStorage, /api/auth/me validation, logout
- LoginPage: email form with POST /api/auth/request, error handling
- App.tsx: use useAuth hook, LoginPage, 'sent' confirmation route
- Fix: auto-route to game covers both 'login' and 'sent' routes when authenticated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use pendingCancel flag (consumed once) to suppress stop-before-play while
allowing subsequent play() calls to proceed normally. Add afterEach timer
cleanup, re-play test, and behavioral assertion for off().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>