Screen-reader and keyboard support, shareable calculation links, home search, cross-page navigation, and high-DPI plots. No formulas changed in this release.
site/home — All 26 calculators — programmatic labels (WCAG 1.3.1/4.1.2)High
Error: ~578 inputs and selects had visually adjacent but programmatically unassociated labels — screen readers announced unlabeled spinbuttons.
Fix: Every static
Worked example: thermal/ R-ladder: 'spinbutton' → 'resistance value, kelvin per watt, spinbutton'.
Reference: WCAG 2.2 SC 1.3.1, 4.1.2
site/home — All calculators — live result regions (WCAG 4.1.3)High
Error: Results updated via innerHTML swaps that screen readers never announced.
Fix: 137 result containers now carry aria-live="polite".
Reference: WCAG 2.2 SC 4.1.3
site/home — All plots — canvas text alternatives (WCAG 1.1.1)High
Error: All 28 canvases were bare
Fix: Each canvas now has role="img" and a descriptive aria-label (e.g. 'Nozzle contour with Mach, pressure and temperature distributions').
Error: No way to link, bookmark or cite a calculation — every visit started from defaults.
Fix: URL-state permalinks: a '🔗 Copy link' header button serializes every changed input/select to #in=id:value;… and loading such a link restores the values and recomputes via native input events (page code untouched). Known limit: dynamically added table rows (stack-up contributors, PSD breakpoints, R-ladder rows) are not yet serialized.
Worked example: thermal/#in=fa_cfm:40 reopens forced-air cooling with 40 CFM → ṁ = 22.66 g/s. Locked by self-tests on vacuum, thermal, gdt.
Error: 26 cards with no findability — a user looking for 'Paschen' had no way to know it lives in Vacuum.
Fix: Filter box above the grid matching card text plus curated per-card keywords (data-kw), e.g. 'paschen', 'buckling', 'grms', 'geneva', 'die per wafer'.
Worked example: Typing 'paschen' leaves exactly one card visible (Vacuum System Designer).
Error: Canvases rendered at fixed 640-px backing stores — blurry on retina/high-DPI displays (the audit's top visual-quality finding).
Fix: A transparent getContext wrapper scales every 2D backing store by devicePixelRatio (≤3×) while keeping the page's logical coordinate space, so all 28 plots stay pixel-identical in layout but render sharp. Draw code untouched. Note: backing resolution is set at load; window resizes still display via CSS scaling, now from a 2–3× sharper buffer.
Worked example: On a 2× display the PSD editor renders from a 1280×640 buffer instead of 640×320.
A full independent audit of every calculator (formulas recomputed from first principles) found the defects below. All are fixed in this release. Each entry shows the error, the fix, and a before → after number you can reproduce on the page.
Error: Townsend constants were 10× too high (A=112.5, B=2737.5 per Pa·m) — a cm→m conversion applied without the Torr→Pa division. Breakdown voltage was overestimated ~3–7× away from the curve minimum (non-conservative for HV design).
Fix: A = 11.25 (Pa·m)⁻¹, B = 273.8 V/(Pa·m) — the standard air values A=15 (cm·Torr)⁻¹, B=365 V/(cm·Torr) converted correctly.
Worked example: p·d = 10 Pa·m: V_b 4,981 V → 857 V. p·d = 1 Pa·m: 857 V → 307 V. Curve minimum ≈305 V now sits at p·d ≈ 1.1 Pa·m (literature: ~0.7–1 Pa·m), not 0.11.
Reference: Townsend/Paschen air coefficients (A=15 /cm·Torr, B=365 V/cm·Torr, γ≈0.01)
Error: Orifice conductance used C = 11.6·D², i.e. 11.6 L/(s·cm²) applied to D² instead of the area — the π/4 was missing, making every network with an orifice ~27% optimistic.
Fix: C = 11.6·A = 11.6·(π/4)·D² ≈ 9.11·D² (D in cm, air 20 °C, molecular flow).
Error: The boundary-condition coefficients were beam/strip values, not plate values: 'SS square C≈76.8' is the simply-supported strip constant (384/5); 'Clamped square C≈220' likewise. Deflection was over-predicted 3.2–3.6×.
Fix: SS square C = 246 (α = 0.00406), clamped square C = 794 (α = 0.00126) in δ = q·a⁴/(D·C).
Worked example: Defaults (E=70 GPa, t=3 mm, ν=0.33, a=200 mm, q=20 kPa, SS): δ 2.357 mm → 0.735 mm.
Reference: Timoshenko & Woinowsky-Krieger, Theory of Plates and Shells, Table 8
Error: Time-to-exceedance used the Gaussian fraction-of-time P(|x|>nσ) as if it were a crossing rate — 2–5× optimistic (non-conservative): at fₙ=300 Hz, 3σ it gave 1.23 s.
Fix: Rice narrow-band upcrossing rate ν = fₙ·e^(−n²/2); T = 1/ν. Card relabeled 'mean rate of upcrossings of ±nσ'.
Worked example: fₙ=300 Hz, 3σ: T 1.23 s → 0.30 s (ν=3.33 s⁻¹, e^(−4.5)=0.0111). 4σ: 52.6 s → 9.9 s.
Error: The qual uplift multiplied Grms by √2 but labeled it '+6 dB'. ×√2 Grms = +3 dB (PSD ×2); a true +6 dB qual is ×2 Grms / ×4 PSD. A user could under-test by 3 dB.
Fix: Existing line relabeled '+3 dB, ×√2 (PSD ×2)'; added an explicit '+6 dB, ×2 Grms (PSD ×4)' output.
Worked example: Acceptance 6.1 Grms: +3 dB → 8.6 Grms (PSD ×2); +6 dB → 12.2 Grms (PSD ×4).
Reference: SMC-S-016 / NASA GEVS qualification-margin practice
vibration/ — Natural Frequency Library — unit toggleMed-High
Error: Toggling mm↔inch re-rendered the card from its default template, silently discarding user inputs and reinterpreting the defaults in the new unit (L=50 mm became L=50 in).
Fix: Fields now convert in place with the correct power of 25.4 (lengths ×f, area ×f², I ×f⁴); user values survive the toggle.
Worked example: Cantilever L=50 mm, I=100 mm⁴ toggled to inch now reads L=1.9685 in, I=0.000240 in⁴ — same physical fₙ.
Error: Shear strain used γ = Δα·ΔT·D/(2h) while labeling D as the distance to neutral point — strain 2× low, predicted life ~3.7× optimistic at k=1.9.
Fix: Engelmaier DNP convention: γ = Δα·ΔT·D/h. C default changed from 1.0 to 0.2 and tagged 'illustrative' (calibrate to test data).
Worked example: Defaults (Δα=14.4 ppm/K, ΔT=165 K, D=15 mm, h=0.3 mm): γ 5.94% → 11.88%; N_f (C=1) 214 → 57. With the new illustrative C=0.2: N_f ≈ 11.
Reference: Engelmaier solder-joint fatigue model
gdt/ — ① True Position + ② Bonus / Virtual ConditionHigh
Error: Bonus tolerance used |actual − MMC|, which granted bonus to size-nonconforming features (a ⌀10.00-MMC hole measured 9.95 got 0.05 bonus), and Module ①'s Feature-type select was read but never used.
Fix: Directional bonus (internal: max(0, actual−MMC); external: max(0, MMC−actual); mirrored for LMC), hard 'OUT OF SIZE LIMITS — no bonus' flag when actual is outside [MMC, LMC], and the feature-type select is now wired into both modules.
Worked example: Internal ⌀10.00 MMC / 10.10 LMC, actual 9.95: bonus 0.0500 → 0.0000 + size-fail flag.
Error: The page defines 'transmission line if t_r < 2·t_prop' (⇒ l_crit = v·t_r/2) but the code computed v·t_r/4 — self-contradictory (conservative by 2×).
Fix: Code now matches its own definition: l_crit = v·t_r/2.
Worked example: t_r=1 ns, εr=4 (v=1.5×10⁸ m/s): 37.5 mm → 75.0 mm.
thermal/ — TIM & Thermal Via Array — unit toggleMed-High
Error: Toggling mm↔inch flipped the TIM area label to in² without converting the value or re-running the card — stale output, and the next edit reinterpreted the number as in² (645× area error).
Fix: tim_A is now converted (×/÷ 25.4²) with the other fields and tim() re-runs on toggle.
Worked example: A=100 mm² toggled to inch now reads 0.1550 in² and R_TIM is unchanged (0.217 K/W at defaults).
Error: g varied across the site: 9.81 in thermal (Grashof), vibration (shock), sealing (hydrostatic), nozzle (Isp) vs 9.80665 in rotor.
Fix: All five pages now use FORGE.G0 = 9.80665 from the new shared /assets/constants.js (with a documented rounding policy).
Worked example: Nozzle Isp = F/(ṁ·9.80665); ≤0.1% shifts elsewhere.
Reference: ISO 80000-3 standard gravity (exact)
site/home — Home page — vault/live reconciliationMedium
Error: The vault (deploy source) home page had diverged from the live site — it was missing the Aerodynamics, Rotor & Propeller and GD&T cards, still carried tier groupings and 'SOON' placeholders, and the file itself was truncated mid-footer.
Fix: Vault index.html rebuilt to match the live 26-card discipline-grouped layout (existing CSS preserved byte-for-byte). The repo is the single source of truth again — no more hand-edits to the deployed site.
Worked example: Card count in the deploy source: 21 (truncated) → 26; live layout order preserved.
Error: Physical constants were per-page literals and drifted (g₀ 9.81 vs 9.80665).
Fix: New static /assets/constants.js (no build step) with FORGE.G0 = 9.80665 and a documented rounding policy; included by thermal, vibration, sealing, nozzle, rotor.
Error: No regression protection existed — the class of every defect in this release could silently recur.
Fix: Each corrected page now carries a TESTS array of golden cases (seeded from the audit's worked numbers) and paints a pass/fail banner when opened with ?selftest. 21 pages, 40 golden cases.
Worked example: e.g. /vacuum/?selftest asserts V_b(10 Pa·m)=857 V and C_orifice(63 mm)=361.6 L/s.
/changelog/ — New page + version tagMedium
Error: No changelog or visible version existed; corrections would ship silently.
Fix: New /changelog/ page (this one) mirrored to the vault as CHANGELOG.md; v2026.07 tag and Changelog/Roadmap links added to every page footer.
/roadmap/ — New pageMedium
Error: No public status board for shipped vs planned work.
Fix: New /roadmap/ page from the audit's §12 master table (shipped / next up / under consideration), mirrored as ROADMAP.md.
Error: 0 of 29 pages had a meta description, Open Graph/Twitter tags, or a canonical URL; no sitemap.xml or robots.txt existed. Shared links rendered as bare URLs and crawl coverage was luck-based.
Fix: Per-page meta description (from each page's own subtitle), og:*/twitter tags, canonical link on all 28 content pages + home; sitemap.xml (30 URLs) and robots.txt at the root. No visible layout change.