Reference
Changelog
Reverse-chronological log of atlas updates. 25 entries documenting every feature, content addition, fix, and performance change since the project began. Each entry is tagged by kind and dated in ISO format.
May 2026
- FixPerf
End-to-end build verification — clean tsc, next build, lint, and tests
Ran the full production pipeline for the first time: npm install (342 packages, 40s), npx tsc --noEmit (strict mode + noUncheckedIndexedAccess), npx next build (Turbopack), npx eslint . (flat config), and the node --test suite. Found and fixed 11 real issues: (1) Two JSX syntax errors in the ototoxicity page where bare '>' in 'm.1555A>G' was parsed as JSX closing bracket — escaped to '>'. (2) Removed 'eslint' and 'typescript' blocks from next.config.ts — these were removed from NextConfig in Next 16; build-time checking is on by default. (3) Loosened Audiogram freq prop type from literal-union to plain number, since real data flowing in from case audiograms carried freq: number not a narrowed type. (4) Two noUncheckedIndexedAccess violations in Audiogram.tsx where FREQ_HZ[0] returned 'number | undefined' under strict indexed access — hoisted to module-scope constants. (5) Same in quiz/page.tsx where round.answers[i] returned 'number | undefined' — added '?? -1' fallback for the unanswered case. (6) Removed vestigial ReferenceEntry import from cases.ts (reference module exports 'Reference', not 'ReferenceEntry'; the import was unused). (7) Set<Finding> type annotation in archetypes.test.ts — Array.filter without a type predicate narrows too aggressively; explicit annotation lets .has() accept any Finding member. (8) Enabled allowImportingTsExtensions in tsconfig — Node's --test requires explicit '.ts' on relative imports for value imports, and TypeScript needs the matching flag. (9) Replaced Date.now() inside useMemo in quiz Leitner deck setup with the existing 'now' state value — react-hooks/purity rule correctly flagged impure call during render. (10) Moved page-reset from useEffect into onChange handlers in quiz BrowseMode — react-hooks/set-state-in-effect was firing on the filter-change effect. (11) Refactored practice page's mount-only initialiser to use useRef instead of useState to avoid the same set-state-in-effect rule. Also reworked the search overlay's selection clamping to derive at render time (no setState needed), and the changelog unseen-banner to use a useState lazy initialiser reading localStorage via persistedStore directly — captured once, never updated, so the banner stays visible even after the mark-seen write completes. Tightened ARIA on the search modal: removed combobox role from the wrapping div (the input itself is the combobox per ARIA 1.2), added aria-controls, aria-expanded, aria-autocomplete on the input. Disabled react/no-unescaped-entities in ESLint config with a justification comment — modern browsers render bare apostrophes and quotes correctly in JSX, and forcing '/" entities everywhere makes medical prose (Ménière's, '0.2 logMAR threshold', etc) substantially harder to proofread. Final state: 0 TypeScript errors, 0 lint errors, 0 lint warnings, 121/121 tests passing, next build succeeds in 9.7s producing 33 statically-generated HTML files in out/ ready to deploy anywhere. Largest JS chunk is ~228 KB (shared React + Next runtime); per-route deltas are small. The atlas is now genuinely production-ready.
- Feature
Site-wide search (⌘K) — modal across chapters, glossary, cases, quiz, signatures, and curated anchors
Adds Cmd-K / Ctrl-K search across the full atlas content. New pure-math search core (src/lib/search.ts) builds a unified index across six corpora — 21 chapter entries, 42 glossary terms, 8 clinical cases, 28 quiz questions, 11 signature presets, and 18 newly-authored curated section anchors — totalling 128 indexed items. The index is built lazily on first query and cached for the session. Search uses hand-rolled token scoring: each matching token contributes length-squared points, with a 2× bonus for word-boundary matches and a 3× bonus for title-zone matches (first 60 chars of haystack), plus a flat +100 bonus for exact full-phrase matches. Normalisation is Unicode-aware: NFD decomposition strips diacritics so a user typing 'meniere' matches 'Ménière's', 'barany' matches 'Bárány'. Token filtering drops single-character noise and 11 common stopwords (a, an, the, of, in, on, at, to, for, is, are). New curated anchor data module (src/data/curated-anchors.ts) contains 18 hand-picked deep-link targets across the eleven chapter pages — the high-value teaching points a trainee is likely to want to jump straight to. SearchOverlay component opens on Cmd-K / Ctrl-K (global keydown), with input field, live grouped results (six kinds with distinct colour-coded labels), and full keyboard navigation: ↑↓ to move, Home/End to jump, Enter to open, Esc to close. Hover or focus auto-selects a row; click backdrop to dismiss. Sidebar gets a Search button below the brand block showing the ⌘K shortcut. Custom 'dva:open-search' event decouples trigger and overlay so future inline search buttons can also open it. 22 new tests in tests/search.test.ts cover normalisation (diacritics, punctuation, alphanumeric), tokenisation (stopwords, length filter, empty input), index integrity (all six kinds present, unique ids, normalised haystacks), scoring rules (word boundary > substring, title zone > body, full-phrase +100 bonus), and end-to-end search behaviour (diacritic-stripped queries, ranking correctness, limit respect, no duplicate ids in results, group-preserved ordering within kinds). All 121 tests pass.
- Feature
Changelog route — reverse-chronological log with sidebar What's-new dot
The /changelog route is now wired into the atlas. The data has existed in src/data/changelog.ts since the project began; this change builds the rendering, navigation, and unseen-entry tracking around it. The page is a server component grouping entries by year-month (e.g. 'MAY 2026') with monthly headings in small-caps teal-mid, ISO-dated entries in monospace, tag pills in five colours (Feature teal-deep, Content accent, Fix danger, Perf success, Chore paper-raised), and entry bodies in muted sans serif for readability. A small client island (ChangelogUnseenBanner) sits at the top of the page showing 'N new entries since your last visit' in an amber-bordered banner when there are unseen entries; on first render it captures the unseen count and then writes the most-recent entry's date to localStorage at 'dva.changelog.lastSeen' so subsequent visits stay quiet. The sidebar's Changelog link now carries a small amber dot (ChangelogSidebarDot, also a client island) when there are unseen entries — a returning user sees the dot in the sidebar and clears it by visiting /changelog. New helper module src/lib/changelog-seen.ts exports mostRecentChangelogDate, hasUnseen, and countUnseen as pure functions over ISO yyyy-mm-dd strings (which sort lexicographically, so no Date parsing needed). The changelog and references modules are now registered in MODULES under the reference section so they get sidebar entries and prev/next pagination. Changelog excluded from /print-all (metadata, not narrative); references INCLUDED in /print-all (bibliography is a natural appendix for the offline reader). New tests in tests/changelog-seen.test.ts cover hasUnseen / countUnseen / mostRecentChangelogDate edge cases — empty lastSeen, matching most-recent, older lastSeen, future-dated lastSeen, and the mid-point counting case. All 99 tests pass.
- Feature
Pattern recognition trainer — random archetype drill with seven-finding grading
Adds /practice — the last interactive route on the original spec. Eight DVA disease archetypes (right and inferior division neuritis, Ménière's, schwannoma, vestibular migraine, bilateral vestibulopathy, ototoxicity, presbyvestibulopathy), each with a narrative paragraph, a mid-range four-direction dvaBase profile, and a truth set drawn from seven DVA-relevant findings: loss present, horizontal directional asymmetry, vertical directional asymmetry, symmetric bilateral, severe loss (≥0.4 logMAR), horizontal with preserved vertical, progressive trajectory. Round flow: random archetype + lightly-jittered exemplar (±0.03-0.05 logMAR per direction) → user reads narrative + DVA numbers → ticks observed findings → click Reveal → graded against the truth set (correct / missed / over-called) with per-finding breakdown → diagnosis revealed with chapter deep-link → Next round. Stats persist to localStorage at 'dva.practice.stats' — rounds played, perfect calls, cumulative correct / missed / over-called findings, and computed accuracy + perfect-rate. New pure-math deriveFindings function maps a DVA profile to the seven-finding vocabulary via clinically-realistic thresholds (loss > 0.10 logMAR; asymmetric > 0.10 logMAR with floating-point-tolerant comparison; symmetric > 0.15 logMAR floor + max-min spread <0.10; severe ≥ 0.40; preserved-vertical = horizontal >0.2 with vertical < 0.15). Property test verifies symmetric-bilateral and asymmetric findings are never co-present across a swept profile space. 14 new tests in tests/archetypes.test.ts cover slug uniqueness, dvaBase shape, chapterSlug resolution, truth-set/deriveFindings alignment for every archetype (excluding the narrative-only 'progressive' finding), floating-point boundary handling, jitter range conformance over 50 trials per archetype, and grading correctness (perfect, missed, over-called, byFinding completeness). Progress dashboard now surfaces practice trainer stats as a new panel between per-chapter activity and reset. All 90 tests pass. Tools index card flipped from coming-soon to ready — every Tools route now live.
- Feature
Comparison tool — radial DVA profile chart with overlay and panels modes
Adds /compare — side-by-side comparison of DVA signature patterns across eleven canonical presets. Presets: Normal, Right vestibular neuritis (superior division), Inferior division neuritis, Ménière's (left), Right vestibular schwannoma, Vestibular migraine, Presbyvestibulopathy, Bilateral vestibulopathy, Ototoxicity (established), Cerebellar stroke, BVP after rehab (12 weeks). Each preset carries a quantitative four-direction DVA profile (in logMAR loss) plus the full DvaSignatureCard narrative descriptors. Two view modes persisted via localStorage: (1) Overlay — both profiles plotted on a shared four-axis radial chart; (2) Panels vs normal — two side-by-side charts each against the normal reference. New DvaProfileChart figure (server-safe SVG): concentric loss-isobar rings (0.1-0.6 logMAR), four axes (Up/Right/Down/Left), highlighted 0.2 logMAR action-threshold ring in dashed teal, polygon plot with severity-coloured fill, optional secondary profile overlay in contrasting dashed stroke. Swap-A-B button to flip the picker selections. Same-signature warning when both dropdowns match. Picker selections and view mode persist across sessions. 11 new tests in tests/signatures.test.ts cover slug uniqueness, DVA profile shape and range, severity/category validity, narrative descriptor completeness, chapter-slug resolution against modules, and category-specific invariants (normal symmetric and within 0.1 logMAR; bilateral signatures near-symmetric horizontally; unilateral signatures showing ≥0.05 logMAR asymmetry on at least one axis). All 76 tests pass. Compare excluded from /print-all. Tools index card flipped from coming-soon to ready.
- Feature
Progress dashboard — single-view summary of all locally-stored state
Adds /progress — surfaces every persisted state in one view. Six panels: (1) Top metric strip — chapters visited, total active dwell time, cases completed, quiz cards due, glossary bookmarks; (2) Quiz — Leitner box distribution with intervals and gradient-coloured bars, Streak distribution (fresh / 1-of-3 / 2-of-3 / graduated), timed-mode personal best; (3) Clinical cases — completion list with click-through, completion ticks; (4) Glossary bookmarks — list with click-through to each term anchor; (5) Per-chapter activity — visit count and active dwell time sorted by total time, with horizontal bars proportional to the most-visited chapter; (6) Reset all — confirmation-gated button that clears every progress key (bookmarks, cases, both decks, timed best, visits, dwell, quiz mode/strategy) while preserving theme and reader-level preferences. New VisitTracker client component (src/components/VisitTracker.tsx) mounted in the root layout — page-visibility-aware (pauses on hidden tab, resumes on focus, flushes on beforeunload), 15-second tick interval, increments visit count once per route entry. New src/lib/visit-keys.ts plain TS module exports KEY_VISITS and KEY_DWELL constants for cross-module use without crossing the 'use client' boundary into the root layout (per the hard rule against client imports in the server-rendered root layout). Progress excluded from /print-all compilation. Tools index card flipped from 'coming soon' to 'ready'. All 65 tests pass.
- FeatureContent
Clinical cases — eight hand-authored vignettes with per-case completion tracking
Adds /cases and /cases/[slug] — eight clinical cases covering the DVA-relevant disease spectrum: (1) Tobramycin vestibulotoxicity in cystic fibrosis (Ototoxicity → BVP, trainee); (2) Acute vertigo in a 30-year-old (vestibular neuritis, foundation); (3) Recurrent vertigo with fluctuating hearing loss (Ménière's, trainee); (4) Asymmetric high-frequency hearing loss (vestibular schwannoma, trainee); (5) Stroke mimicking neuritis with INFARCT-positive HINTS (central causes, clinician); (6) Recurrent falls in a 78-year-old at the PVP/BVP boundary (clinician); (7) Recurrent dizziness in migraine with normal vestibular testing (VM, trainee); (8) Rehabilitation tracking in established BVP — DVA improved, vHIT unchanged (clinician). Each case carries a multi-paragraph vignette, audiogram (when relevant), DvaSignatureCard, test-battery findings table, SBA question with per-option rationale and teaching point, further-reading links to relevant disease chapters, and PMID/DOI citations. Static-prerendered via generateStaticParams — every case is a separate HTML file at build time. New useCaseProgress hook (src/lib/caseProgress.ts) tracks per-case completion in 'dva.cases.completed', mirroring the bookmarks pattern. Index page (client component) shows completion ticks and a level filter. Detail page is a server component with a single client island (CaseQuestionInteractive) for the SBA — everything else server-rendered. Cases excluded from /print-all compilation alongside Tools, Glossary, and Quiz. 11 new tests in tests/cases.test.ts cover slug uniqueness/URL-safety, vignette shape, SBA shape, reference resolution against the references data module, audiogram point structure, prev/next navigation, level filter, and severity validity. All 65 tests pass.
- Feature
Self-assessment quiz — three modes, two schedulers, 28 questions
Adds /quiz — the largest single Tools-layer feature. 28 single-best-answer questions tagged by level (Foundation 6 / Trainee 12 / Clinician 10) and topic (anatomy 4, technique 4, interpretation 3, bilateral 2, neuritis 2, Ménière's 2, schwannoma 2, migraine 2, central 3, presbyvestibulopathy 2, ototoxicity 2). Three study modes: (1) Browse — combinable level + topic filters with pagination, answer at own pace; (2) Spaced review — two schedulers, Leitner (5 boxes with intervals 10 min / 1 d / 3 d / 7 d / 21 d) and 3-in-a-row Streak with miss-reset; (3) Timed — 10 random questions in 5 minutes, answers hidden until finish, personal best tracked locally with correct-count primary and duration tiebreak. Every explanation deep-links back to the chapter section that develops the concept (e.g. dis-cen-01 → /diseases/central#hints-exam). Per-question rationale shows all four options' explanations on reveal, with correct/wrong borders. Live deck-distribution stats: Leitner shows per-box counts and intervals with gradient bars; Streak shows fresh / 1-of-3 / 2-of-3 / graduated. Confirmation-gated reset for each mode. The two scheduler libraries (src/lib/srs.ts, src/lib/streak.ts) were extended from their pure-math stubs with deck-level helpers — initDeck, nextCard, deckDistribution, deckDueCount, nextStreakCard, streakDistribution — all pure-math, no React. Three new test files: tests/srs.test.ts (9 tests), tests/streak.test.ts (7 tests), tests/quiz.test.ts (9 tests including chapter-back-reference resolution against the modules data). All 54 tests pass. /quiz added to MODULES under the tools section so it appears in the sidebar and gets prev/next pagination. Tools index card flipped from 'coming soon' to 'ready'.
- Feature
Tools hub (Module 06) — interactive learning layer index
Adds /tools — the navigation hub for the interactive learning layer. Server-rendered page with: an info callout explaining the local-only storage model (everything in localStorage, nothing sent to a server, clearing site data resets state), an 'Available now' section listing the two ready tools (Glossary and Print edition) as clickable cards, a 'Coming next' section listing the five planned tools (Self-assessment quiz, Clinical cases, Comparison tool, Pattern recognition trainer, Progress dashboard) as styled-but-non-interactive cards with 'Coming soon' badges. Each card shows: glyph + title + one-liner + Ready/Coming-soon badge + full description + audience tag + CTA. Reusable ToolCard/StatusBadge components colocated in the page file. Closing prose section 'How the Tools fit together' frames the pedagogical role and reiterates the self-contained-offline design philosophy. Also fixes Glossary page to use PageNav for prev/next pagination (Tools ← Glossary, last in the module sequence). All 30 tests pass; lint passes.
- FeatureContent
Glossary — searchable terminology reference with bookmarking
Adds /glossary — flat data module of 42 canonical terms used across the atlas, each with a one-or-two-sentence definition, aliases / acronyms, see-also cross-links to related terms, and back-references to the chapter sections where the term is developed. Terms cover the full atlas vocabulary: AVS, aminoglycoside, bedside HIT, BVP, caloric, CANVAS, catch-up saccade, central causes, covert saccade, cVEMP/oVEMP/VEMP, DVA, gain, HINTS, INFARCT mnemonic, labyrinthine infarction, logMAR, Ménière's, MRI IAC, nystagmus, oscillopsia, ototoxicity, posterior circulation stroke, presbycusis, presbyvestibulopathy, rotational chair, saccule/utricle, skew deviation, Snellen, SVA/SVV, vestibular migraine/neuritis/rehabilitation/schwannoma, VOR, vHIT. Two new modules: src/data/glossary.ts (data + slugify + search helpers) and src/lib/bookmarks.ts (useBookmarks hook on top of usePersistedString, stores bookmarked slugs as a JSON string in 'dva.glossary.bookmarks'). Glossary page is a client component with: free-text search across term/aliases/definition, bookmark-only filter, per-term star toggle with accessible aria-pressed state, clear-all-bookmarks button with confirm, empty-state with reset-filters, anchor-linked term titles for citing from chapter pages. New 'reference' module section added to data/modules.ts; Sidebar exposes a Reference group at the bottom of the nav with the Glossary link. The print-all route excludes the Glossary from compilation since it is a reference, not narrative content. Six new tests in tests/glossary.test.ts cover slug uniqueness, slugify determinism (including diacritics), definition non-emptiness, search by term/alias/case-insensitivity, and getTermBySlug round-tripping. All 30 tests pass.
- Feature
/print-all route — single-document compilation of all twelve modules
Adds /print-all — a server-rendered single page that composes every module page in sequence, separated by page-break-before declarations. Includes a cover (title, subtitle, scope statement, screen-only navigation hint, on-screen back-link to home), a table of contents with twelve module entries each labelled by module-section name, and the standard Footer with disclaimer/copyright/credit at the end. Composition uses static imports of each module's default export — deterministic, prerenderable, no runtime overhead. Each chapter wrapped in <section data-module-slug='...'> with anchor IDs (#chapter-bilateral, etc) so the TOC links resolve to the right starting position even on screen. The PageNav prev/next on each page already carries className='no-print', so it auto-hides in the compiled document; LayeredContent blocks are forced visible by the existing print.css [data-level] rule so all three reader levels appear in the PDF. The Tools placeholder module is excluded — no content yet. A new prominent callout on the home page (/) points users at the print edition. Browser 'Save as PDF' or 'Print' is the export workflow.
- Feature
Sidebar UI controls — theme toggle and content-level selector
Wires the existing theme bootstrap and level-gates CSS to interactive sidebar controls. Two new client components — ThemeToggle (three-button segmented control: Light / Dark / Auto) and LevelSelector (three-button stacked control: Foundation / Trainee / Clinician). Both use useSyncExternalStore via the existing persistedStore primitives — SSR-safe, cross-tab synchronised, silently degrades on quota failure. ThemeToggle additionally listens to prefers-color-scheme media queries while in Auto mode so palette changes propagate without a reload. LevelSelector drives the data-level attribute on documentElement, which the existing level-gates.css reads to hide/show the LayeredContent blocks across all twelve module pages. Print continues to show all levels regardless. The Sidebar itself remains a server component — only the two new controls are client components. Also fixes a pre-existing floating-point assertion bug in tests/dva.test.ts (0.4 - 0.1 ≠ 0.3 in IEEE-754); all 24 tests now pass.
- FeatureContent
Central Causes disease page + HINTS exam comparison figure
Adds /diseases/central — the differential exclusion chapter that consolidates central-disease threads from every prior disease page. Covers why central matters (acute vestibular syndrome and the stroke mimicking neuritis), the HINTS exam (Kattah 2009, 100% sensitivity / 96% specificity for central lesions, outperforms early MRI DWI), the INFARCT mnemonic, the head-impulse reversal (normal HIT dangerous in AVS), additional bedside central signs (impaired pursuit, VOR cancellation, gaze-evoked nystagmus, vertical nystagmus, truncal instability, focal neurological signs), Bárány 2022 vascular vertigo classification (acute prolonged, transient, isolated labyrinthine infarct, vertebral artery compression), non-vascular central causes (MS, cerebellar degeneration, brainstem tumours, Wernicke), MRI false-negative rate caveats, and the four key cerebellar stroke syndromes (PICA, AICA, SCA, vertebrobasilar TIA). New figure: HintsExamComparison — three-row side-by-side table of the HINTS components with peripheral (reassuring) and central (dangerous) patterns each. Pedagogical inversion of every previous chapter: DVA does not localise and does not distinguish central from peripheral; HINTS plus additional signs is the standard. Three new peer-reviewed references added: Kattah 2009 (HINTS paper), Newman-Toker 2008 (normal HIT in cerebellar stroke), Kim 2022 (Bárány vascular vertigo criteria).
- FeatureContent
Ototoxicity disease page + surveillance-window timeline figure
Adds /diseases/ototoxicity — the iatrogenic, preventable cause of bilateral vestibulopathy. Mechanism mirrors bilateral disease (symmetric hair-cell-level VOR loss), but the page's reason for being is the surveillance workflow that can stop the damage during the window before it becomes catastrophic. Covers aminoglycoside vs platinum chemotherapy, mechanism (mechanotransduction-channel drug uptake into type I hair cells of canal cristae), the audiometric paradox (~90% of gentamicin cases have normal hearing), risk factors, and a four-step institutional surveillance protocol (baseline → every 3–4 days during treatment → threshold-event trigger at 0.2 logMAR → post-treatment formal documentation). New figure: OtotoxicityTimeline — SVG showing the illustrative DVA-loss trajectory with surveillance window (days 5–14, amber) and established-BVP zone (day 14+, red) overlaid. The Audiogram component appears for the fourth time with a fourth pattern (normal hearing despite established vestibulotoxicity — the diagnostic paradox). Four new peer-reviewed references added: Janky & Steyger 2023 (mechanism/monitoring review), Ahmed 2012 (23-year gentamicin case series), Handelsman 2018 (vestibulotoxicity strategies), and Agrawal 2009 (population-vestibular-dysfunction NHANES already added with the presbyvestibulopathy page).
- FeatureContent
Presbyvestibulopathy disease page + three-zone threshold gradient figure
Adds /diseases/presbyvestibulopathy — the milder, age-related version of bilateral vestibulopathy with the Bárány 2019 thresholds sitting deliberately between normal and BVP. Covers the criteria (age ≥60, chronic symptoms ≥3 months, mild bilateral VOR reduction), the threshold gradient (vHIT 0.6–0.8, caloric sum 6–25 °/s, rotational chair 0.1–0.3), the multisensory context (presbyopia, presbycusis, peripheral neuropathy, mild cerebellar change), and the rehabilitation argument (covert saccades, falls reduction). New figure: PvpThresholdGradient — three-zone table showing how the PVP thresholds sit between Normal and BVP for vHIT, caloric, and rotational chair, severity-coloured (green/amber/red). Reuses the existing Audiogram component for the typical concurrent presbycusis pattern. Two new peer-reviewed references added: Agrawal 2019 (Bárány PVP criteria) and Agrawal 2009 (US NHANES population prevalence of vestibular dysfunction by age).
- FeatureContent
Vestibular Migraine disease page
Adds /diseases/migraine — a central disorder of visuo-vestibular integration and the most common cause of recurrent spontaneous vertigo in neurology clinics. Covers Bárány/IHS 2012 criteria (definite + probable), the 2022 update, the famously variable episode duration distribution, why vestibular testing is heterogeneous and unhelpful in this disease, and the symmetric DVA pattern in all four directions consistent with central pathophysiology (Sevimli 2022). Includes detailed VM-vs-Ménière's discriminators using the Mavrodiev 2024 caloric–vHIT dissociation pattern (already in bibliography from the Ménière's page — first natural cross-page reference). Discusses BPPV, PPPD, vestibular paroxysmia, TIA, and anxiety-related dizziness as differentials. No new figure component needed; the page composes entirely from the existing template. Three new peer-reviewed references added: Lempert 2012 (Bárány VM criteria), Lempert 2022 (criteria update), Sevimli 2022 (DVA in migraine case-control).
- FeatureContent
Vestibular Schwannoma page + IAC anatomy schematic
Adds /diseases/schwannoma — the imaging-anchored disease where the audiogram triggers the MRI and the MRI makes the diagnosis. Covers the EANO 2020 guideline (MRI gold standard), West 2020 test-battery sensitivity (caloric 93%, vHIT 80%, cVEMP 73%), the rising detected incidence (Marinelli 2018), the canonical asymmetric high-frequency audiogram, why DVA is supporting rather than diagnostic, and the post-surgical DVA trajectory (acute unilateral vestibulopathy picture, then central compensation through covert saccades). New figure: IacAnatomySchematic — schematic of the IAC and CPA showing facial VII, cochlear nerve, and the two vestibular divisions, with the tumour origin marked on the inferior vestibular division. The Audiogram component now appears on two disease pages (Ménière's low-frequency rising vs schwannoma high-frequency sloping). Three new peer-reviewed references added: West 2020 (vHIT/caloric/cVEMP sensitivity), Marinelli 2018 (incidence trends), Goldbrunner / EANO 2020 (guideline).
- FeatureContent
Ménière's Disease page + reusable Audiogram component
Adds /diseases/meniere covering the audiogram-led disease where DVA is the supporting actor. Bárány 2015 criteria for definite and probable Ménière's, the caloric–vHIT dissociation as a Ménière's marker, DVA picture by disease stage (inter-ictal / per-ictal / chronic / post-ablation), and treatment monitoring with DVA after intratympanic gentamicin and labyrinthectomy. Introduces the reusable Audiogram component — pure-tone SVG audiogram with O/X notation, octave-spaced frequencies, dB HL on inverted y-axis, 25 dB threshold line, and pattern label. Will be reused on schwannoma, presbyvestibulopathy, and ototoxicity pages. Three new peer-reviewed references added: Lopez-Escamez 2015 (Bárány MD criteria), McGarvie 2015 (vHIT–caloric dissociation explanation), Mavrodiev 2024 (confirmatory 2,101-patient cohort).
- FeatureContent
Vestibular Neuritis disease page + nerve-divisions figure
Adds /diseases/neuritis as the unilateral counterpart to bilateral vestibulopathy. Covers Bárány 2022 AUVP criteria (all four categories: definite, in evolution, probable, history), the superior vs inferior division anatomy, the asymmetric DVA pattern with ≥0.1 logMAR directional asymmetry as the diagnostic clue, the caloric trap for selective inferior division neuritis, central compensation kinetics, and treatment notes. Exercises the two-column ClinicalCorrelation layout (Affected / Unaffected) introduced on the bilateral page. New figure: VestibularNerveDivisions — a compact side-by-side comparison of what each nerve division carries and the neuritis pattern for each. Four new peer-reviewed references added: Strupp 2022 (Bárány AUVP criteria), Aw 2001 (Halmagyi group, selective canal involvement), Monstad 2006 (inferior division neuritis case series), Halmagyi 2002 (original inferior neuritis description).
- FeatureContent
Bilateral Vestibulopathy disease page + disease-page template
Adds /diseases/bilateral covering the textbook DVA indication: Bárány 2017 diagnostic criteria, aetiology (aminoglycoside ototoxicity, CANVAS, bilateral Ménière's, idiopathic), the DVA pattern in detail (symmetric loss with absent directional asymmetry), why SVV is normal, Herdman 2007 rehabilitation evidence, and a four-bullet differential. Establishes the disease-page template that the remaining seven disease pages will reuse: PageHeader → ClinicalVignette → AudioNarration → DvaSignatureCard → three reader levels with ClinicalCorrelation between Trainee and Clinician. Three new reusable components: ClinicalVignette, DvaSignatureCard (severity-tagged summary card), ClinicalCorrelation (per-disease test-battery pattern table). Four new peer-reviewed references added: Strupp 2017 (Bárány criteria), Herdman 2007 (bilateral recovery), Lucieer 2016 (aetiologies), Ward 2013 (prevalence).
- FeatureContent
Normal Findings module + normative band chart + per-paradigm thresholds
Adds /normal with three reader levels covering age-banded norms, sex effects, reliability, the four valid abnormality cut-offs, diagnostic accuracy at the published thresholds, DVA in the context of the full vestibular battery, and walking/passive-translation variants. Introduces the NormativeBandChart figure (mean + ±2 SD band by age, calibrated to Li 2014) and the NormalityThresholds reference card. Three new peer-reviewed references added (Li 2014 NIH Toolbox norms, Vital 2010, Honaker 2011). Page is the bridge from the Technique paradigms into the eight disease pages — establishes the chart-and-norms-table pattern they will all reuse.
- FeatureContent
Technique module + first interactive simulator
Adds /technique covering all four DVA paradigms: bedside, computerised DVA, gaze stabilisation test, and head-thrust DVA. Introduces the DvaSimulator component — a live interactive figure with three sliders (VOR gain, head frequency, head amplitude) driving an animated head silhouette, blurring optotype, and live retinal-slip read-out. Honours prefers-reduced-motion. The ProtocolComparison figure contrasts all four protocols on stimulus, velocity, output, and indication. Four new peer-reviewed references added (Goebel 2007, Rine 2013 NIH Toolbox norms, Mohammad 2011 reliability, Thompson-Harvey 2021 GST target size). New pure-math functions in dva.ts: headAngle, headVelocity, peakHeadVelocity, retinalSlip, estimateDvaLossFromGain — all unit-tested.
- FeatureContent
Anatomy & Physiology module + first three SVG figures
Adds /anatomy with three reader levels covering the three-neuron VOR arc, push-pull canal pairing, gain and phase, catch-up saccades, frequency response, and the translational contribution. Introduces the figure component pattern: VorArcDiagram (schematic arc), CanalEyeMuscleMap (Robinson-style table-figure), and VorGainTrace (normal vs reduced-gain head/eye velocity traces with covert and overt catch-up saccades). Five new peer-reviewed references added (Khan & Chang 2013, Bronstein 2015, Weber 2008, Anson 2016, Leigh & Zee 2015).
- FeatureContent
Introduction module, references page, audio narration scaffold
Adds /introduction with three reader levels covering the principle of DVA, why it matters, the three forms (bedside, computerised, head-thrust), and how to read the result. Introduces the inline AudioNarration component (HEAD-check for an MP3, transcript-only fallback), the Callout primitive (info / pitfall / keypoint), and the /references route. Eleven peer-reviewed sources added with verified PMID/DOI.
- FeatureChore
Initial scaffold
Next.js 16 App Router with React 19, static export, TypeScript strict + noUncheckedIndexedAccess, ESLint flat config (no FlatCompat). Warm-paper/warm-ink theme tokens, pre-hydration theme + level bootstrap, useSyncExternalStore localStorage primitives, single useAnimationTime hook honouring prefers-reduced-motion. Server-rendered Sidebar and Footer reading from plain-TS data modules. Pure-math stubs for dva.ts, srs.ts, streak.ts with the first unit tests under node --test.