- Page scroll container is overflow-x-hidden so the interface stays fixed; long
content can no longer push the page wider than the viewport (tree was ~50% cut off).
- CardTable wrapped in overflow-x-auto with a min-width so only the table scrolls
horizontally on small screens.
- Sublesson and lesson-tree rows get min-w-0 so truncate works in flex; long names
now ellipsize instead of overflowing. Tree drag handle + hover actions hidden on
mobile (were unusable via touch anyway), freeing width for the name.
- Lesson detail title wraps and scales down on mobile.
Registration now rolls back the just-created user (token cascades) and returns a
clear 502 EMAIL_SEND_FAILED if the verification email can't be sent, instead of a
500 leaving an unverifiable orphan account. resend-verification and
forgot-password swallow mail failures (log + still return generic 200) so a broken
mailer can't break the flow or leak account existence. Adds regression tests.
The in-session progress bar reads session.cardsShown/cardsCorrect/cardsIncorrect,
but the session store's answer() never refreshed the session object — the backend
tracked the counters but the client kept the stale start values (all 0), so the bar
appeared frozen. answer() now mirrors the backend's increment locally; end() still
replaces with authoritative server totals. Adds an E2E regression test.
Two bugs surfaced by Excel import on a lesson with a lesson_path column:
1. resolveLesson created lessons without owner_id, so after the ownership
model (sub-project B) they never appeared in getLessonTree — import
reported success but nothing was visible.
2. lesson_path was resolved at the root; cards landed in new root lessons
instead of under the lesson the import was started from.
Now: auto-created lessons get ownerId + visibility 'private'; lesson_path is
resolved relative to the started lesson (each segment a sublesson). Also drop
the stale eager card_progress insert (progress is per-user and lazy since B).
Add e2e/ux.spec.ts covering the ⌘K search palette, lesson detail page,
stats sections, and the legacy /admin → /lessons redirect.
Also fixes two issues uncovered by running the full suite:
- Skip auth rate limiter in e2e by running the backend with NODE_ENV=test
(registration limit of 5 was tripping later tests).
- Render the card table for lesson owners even when the lesson has zero
cards, so the first card can be added from the detail page.