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.
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.
- /api/stats: add verifyCsrf middleware (defense-in-depth; no-op for GETs)
- VerifyEmailPage: useRef guard to prevent React StrictMode double-fire of
the single-use verify token in dev
- router.tsx: route-level code splitting via React.lazy + Suspense; initial
bundle drops from 397 KB to 224 KB with per-route chunks (0.3–14 KB each)
- e2e: wait for verify-email completion before login; bump Account-menu
timeout to handle Vite cold-chunk compile
Applied ui-ux-pro-max design system recommendations:
- Tailwind theme: study purple primary + correct green accent
- Inter + Plus Jakarta Sans typography
- Glassmorphic surfaces with soft shadows and mesh background
- Real 3D card flip with spring physics + answer feedback flash
- Gradient stat cards, progress bar, animated done screen with score ring
- Polished Layout, Dashboard, Admin, AdminLesson, CardTable, ImportDialog, PracticeSetup, Practice, PracticeDone
- E2E smoke updated for new accessible names