Suspend now pauses the timer instead of letting it run out:
- 'Suspend & Leave' sends suspended=true with time_left to backend
- On resume, backend re-anchors started_at to now with held time_left
- Closing tab without suspending continues to run the timer (unchanged)
Timer-expired auto-submits are marked with expired=1 and excluded from:
- Attempt history (GET /attempts/history)
- Dashboard stats (quiz count, total attempts, average score)
- Attempt list (GET /attempts)
- DDL: ALTER TABLE quiz_attempts ADD COLUMN expired INTEGER DEFAULT 0
Course-quiz decoupling is preserved — these changes only touch
non-course quizzes (Quiz.course_id IS NULL).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>