What’s new
Build history of the Subjective Visual Vertical, newest first.
Reference audit — dropped two orphans, swapped one unverifiable citation
Honest audit of the bibliography. Removed two references that were defined but never cited in body text (Friedmann 1970, Bergenius 2006) — a smaller verified bibliography is more credible than a padded one. Replaced four uses of a book-chapter citation whose bibliographic details I couldn't fully verify (Halmagyi-Curthoys 'Otolith function tests') with Brandt-Dieterich 1994, which supports the same claims with high confidence. Refined the schwannoma signature table — the previous size-magnitude table contradicted the prose ('correlates poorly with size'); replaced with a feature-finding table that matches the literature better. The references page footer note now transparently acknowledges that older entries should be cross-checked against PubMed before academic use. Total references: 11 (all cited).
Zero-warning lint — refactored 13 react-hooks v7 issues
All 13 deferred react-hooks v7 violations refactored to satisfy strict mode. FeedbackLink composes the mailto in the click handler instead of useEffect/useState. SearchPalette uses the key-reset pattern (no setState-in-effect on query change). Sidebar replaces the route-change effect with the React 19 "store info from previous renders" pattern, and folds the changelog-seen logic into the same render-time block. Trainer drops the hydration-randomise effect for an explicit Start button. OtrDiagram moves the prefers-reduced-motion check to the toggle click handler. QuizPlayer.SpacedCardWrap uses key={question.id} for auto-reset; TimedMode uses a deadline-based timer with a single tick effect. The eslint config is back to strict defaults — no warning-level downgrades. npm run lint: 0 errors, 0 warnings. npm run build: green. npm test: 8/8.
Build verified — npm install, npm run build, npm test all green
Verified the full pipeline on Node 22, npm 10: dependencies install cleanly, the static export completes (46 routes including 15 case dynamic prerenders), all 8 unit tests pass, and the lint step runs with 0 errors. Fixed three real bugs caught by ESLint: the eslint config referenced a plugin not loaded in flat mode; the TimedMode quiz referenced 'finish' before its const declaration (temporal dead zone); and a role=tab button carried an aria-pressed attribute that ARIA forbids on that role. Fixed the test script glob so node --test resolves files on Node 22. Static export is 5.4 MB, 47 HTML files, ready to deploy anywhere static (GitHub Pages, Netlify, S3, hospital LAN, USB stick).
Quick reference cheat sheet, hydration audit, content-level pre-flight
A new /quick-reference route presents every disease signature as a filterable card grid — direction, magnitude, variability, OTR status, and a one-line bedside pearl, colour-coded by category (peripheral / central / canal / third-window / functional). Filter by category, fuzzy-search by name or pearl, click through to the full disease page. Fixed a hydration mismatch in the pattern trainer (Math.random in useState init produced different server vs. client renders); now deterministic on first render and randomises after mount. Pre-flight script now also sets data-active-level on the html element so the level filter applies before paint instead of after hydration — eliminates the flash of hidden content. The print-all route's heading hierarchy is now correct (single h1 in each view).
Print cover page, How-to guide, OTR autoplay, broken print button fixed
The print-all route now starts with a proper PDF cover page (title, author, affiliations, build date, disclaimer) followed by a numbered table of contents — each on its own page. A new /how-to route gives first-time visitors a five-minute orientation to the reader-level system, search palette, tools, quiz modes, bookmarks, audio, print, and feedback. The OTR diagram gains an autoplay mode that cycles through the four scenarios on a 4-second loop (respects prefers-reduced-motion). Fixed a regression: the print-all page's 'Open print dialog' button was a server-rendered onClick that did nothing — now extracted to a proper client component.
Mobile drawer, feedback button, and bucket-test step-by-step
The sidebar is now a slide-in drawer on phones (≤900px) controlled by a hamburger button, with a scrim backdrop and Escape-to-close. An eight-step bucket-test instructional card sits inline on the Technique module — searchable via /technique/#bucket-howto. A 'Feedback' button in the bottom-right of every page opens a pre-filled mailto with the page URL. The sidebar 'What's new' link now shows an amber unseen-dot badge until the user visits /changelog. Rigid two-column grids in Compare and CaseRunner now stack on small screens.
Six new narration scripts, overlay mode for Compare, anchor IDs
Narration scripts now cover Wallenberg, Ménière's, schwannoma, SCD, OTR, and cerebellar — bringing the atlas to eleven timestamped walkthroughs. The Compare tool gains an overlay mode that draws both traces on a single bucket with a difference computation and pattern interpretation. Every disease page has section anchor IDs (#signature, #mechanism, #clinical-use, #companions, etc.) so search excerpts deep-link into specific paragraphs.
Audio narration, site-wide search, and three new foundation cases
Five timestamped narration scripts (Anatomy, Technique, Normal findings, Dynamic SVV, Vestibular neuritis) with an inline player that wires up an MP3 if one exists at /audio/{id}.mp3 and falls back to transcript-only otherwise. Clickable timestamps scrub the audio. A ⌘K search palette indexes chapters, glossary, cases, quiz questions, and 18 curated excerpts; navigable by keyboard. Three new foundation-level cases on classical BPPV, peripheral vertigo with a clean HIT pattern, and gentamicin bilateral vestibulopathy bring the case library to 15.
Dynamic SVV module and six new clinical cases
Dynamic SVV gets a dedicated route covering centrifugation, off-vertical-axis rotation, and galvanic vestibular stimulation, with a decision table on when each paradigm earns its place in a work-up. Six new cases bring the total to twelve: inferior-division neuritis, compensated vestibular schwannoma, vestibular migraine with variable SVV direction, PPPD after a resolved neuritis, pusher syndrome, and bilateral gentamicin vestibulopathy. The print-all route now includes the dynamic SVV section.
Initial public scaffold
First public build of the Subjective Visual Vertical. Includes the introduction, anatomy & physiology, technique, and normal-findings modules; full disease pages for vestibular neuritis, ocular tilt reaction, Ménière's, Wallenberg, schwannoma, SCD, BPPV, cerebellar, vestibular migraine, PPPD, and pusher; interactive SVV simulator, OTR diagram, comparison tool, and pattern recognition trainer; single-best-answer self-assessment with browse, spaced (Leitner), and timed modes; glossary with bookmarking; and a peer-reviewed references page.