diff --git a/client/src/App.tsx b/client/src/App.tsx
index 07b7344..4bea38c 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -16,6 +16,7 @@ import Settings from '@/pages/Settings';
import Learning from '@/pages/Learning';
import PeGuide from '@/pages/PeGuide';
import Bedside from '@/pages/Bedside';
+import Calculators from '@/pages/Calculators';
const queryClient = new QueryClient({
defaultOptions: { queries: { staleTime: 30_000, retry: 1 } },
@@ -63,6 +64,7 @@ export default function App() {
} />
} />
} />
+ } />
} />
{/* catch-all falls back to home while more tabs port over */}
} />
diff --git a/client/src/components/Layout.tsx b/client/src/components/Layout.tsx
index 3436d8a..7f53b59 100644
--- a/client/src/components/Layout.tsx
+++ b/client/src/components/Layout.tsx
@@ -43,7 +43,7 @@ const NAV: NavGroup[] = [
{ to: '/catchup', label: 'Catch-Up Schedule', available: true },
{ to: '/peguide', label: 'Physical Exam Guide', available: true },
{ to: '/bedside', label: 'Bedside', available: true },
- { to: '/calculators', label: 'Calculators', available: false },
+ { to: '/calculators', label: 'Calculators', available: true },
{ to: '/extensions', label: 'Pagers & Extensions', available: true },
{ to: '/learning', label: 'Learning Hub', available: true },
],
diff --git a/client/src/pages/Calculators.tsx b/client/src/pages/Calculators.tsx
new file mode 100644
index 0000000..e83eab4
--- /dev/null
+++ b/client/src/pages/Calculators.tsx
@@ -0,0 +1,108 @@
+// ============================================================
+// CALCULATORS — shell + sub-nav. This port intentionally delivers
+// ONLY the page shell and sub-navigation. The actual math runs in
+// the vanilla viewer until test vectors land.
+//
+// WHY this is gated on test vectors (from the migration checkpoint):
+// • AAP 2017 BP percentile uses Rosner quantile splines with long
+// hard-coded coefficient arrays.
+// • Fenton 2013 LMS preterm growth carries 210 validated cases.
+// • AAP 2022 bilirubin phototherapy + exchange: per-week risk
+// curves, 1190 validated cases.
+// • Bhutani nomogram risk zones.
+// • APLS + Best Guess weight-for-age.
+//
+// Per the checkpoint: "An LLM will sometimes 'simplify' a long array
+// of numbers and silently break it — don't let that happen." Every
+// calculator needs a JSON vector file (~20 known inputs + expected
+// outputs captured from public/js/calculators.js) before its React
+// port lands, and the port must match every vector byte-for-byte.
+//
+// Pill order + labels match public/components/calculators.html.
+// ============================================================
+
+import { useState } from 'react';
+
+const card = 'rounded-lg border border-border bg-card p-5 space-y-3';
+const btnPrimary = 'rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium';
+
+interface Pill {
+ id: string;
+ label: string;
+ summary: string;
+ source: string; // where the formulas live
+}
+
+const PILLS: Pill[] = [
+ { id: 'bp', label: 'BP Percentile', summary: 'AAP 2017 age/height/sex-adjusted BP percentiles (Rosner quantile splines).', source: 'AAP 2017 (Flynn) — Rosner splines' },
+ { id: 'bmi', label: 'BMI Percentile', summary: 'BMI-for-age (CDC 2000 z-score tables).', source: 'CDC 2000 LMS' },
+ { id: 'growth', label: 'Growth Charts', summary: 'WHO 0–2 y / CDC 2–20 y; Fenton 2013 preterm (weight, length, head).', source: 'WHO 2006 + CDC 2000 + Fenton 2013 LMS' },
+ { id: 'bili', label: 'Bilirubin', summary: 'AAP 2022 phototherapy + exchange thresholds and Bhutani nomogram risk zones.', source: 'AAP 2022 (Kemper) + Bhutani 1999' },
+ { id: 'vitals', label: 'Vital Signs', summary: 'Normal HR / RR / BP ranges by age.', source: 'PALS + AHA reference' },
+ { id: 'bsa', label: 'Body Surface Area', summary: 'Mosteller + DuBois formulas.', source: 'Mosteller 1987 / DuBois 1916' },
+ { id: 'dose', label: 'Weight-Based Dosing', summary: 'Common pediatric med doses per kg + max dose.', source: 'Lexicomp / Harriet Lane' },
+ { id: 'resus', label: 'Resus Meds', summary: 'Code-cart dosing (epinephrine, amiodarone, atropine, etc.).', source: 'PALS' },
+ { id: 'gcs', label: 'GCS', summary: 'Child/adult and infant Glasgow Coma Scale variants.', source: 'Teasdale + pediatric modification' },
+ { id: 'equipment', label: 'Equipment', summary: 'ETT size, blade, NG, Foley, suction by age/weight.', source: 'PALS + Broselow cross-reference' },
+];
+
+function LegacyPanel({ pill }: { pill: Pill }) {
+ return (
+
+
{pill.label}
+
{pill.summary}
+
+
+ Source of truth: {pill.source}.
+
+
+ This calculator runs in the legacy viewer. A React port is gated on capturing test vectors
+ from the vanilla implementation so the numerical output can be verified byte-for-byte —
+ the migration checkpoint specifically flags this class of data as the one an LLM is most
+ likely to silently simplify.
+
+ Pediatric calculators — BP percentiles, bilirubin thresholds, growth, dosing, equipment sizing.
+ Formula ports arrive one at a time, each verified against captured test vectors.
+