What changed in this calculator, when, and why. Corrections that moved a threshold in the less protective direction are called out explicitly in the entries below rather than folded into a general "updated thresholds" line.
The date each registered standard’s numeric parameters last changed, from data/changelog.json. This is not the date the standard itself was published or amended — that is on each standard page with its sources.
usariem_hsda, HGC-070) as unsupported by its own citations (GitHub issue #106). Its load-bearing citation, "USARIEM TR-13-4", does not exist: USARIEM designates technical reports T##-## (T01-11, T08-05, T13-91), no such report is retrievable, and it appears nowhere in the reference list of the other cited source. The in-repo copy of that other source (Potter et al. 2021, Journal of Sport and Human Performance, PDFs/USARIEM_HSDA_Potter_et_al_2021_JHP.md) is a field-validation study containing none of the engine's constants — 70 kg body mass, 3500 J/(kg·°C), 245000 J/°C, and T_start 37.0/37.2 appear nowhere in it, and its cohort mass was 79–84 kg. The physiology actually implemented was the NIOSH REL/RAL curve (56.7 − 11.5·log10 M / 59.9 − 14.1·log10 M), which niosh_curve already covers, so relabeling would have left a duplicate engine with no independent basis.schedule_engine, making it composite-eligible, so it could set the work/rest verdict and trigger stop-work. Two further defects went with it: t_safe >= 60 reported a bounded safeWorkMinutes while recommending 60/0 indefinitely with no cross-hour accumulation, and the metabolicRateW override path returned stale safeWorkMinutes / predictedNetHeatStorageW alongside a recomputed schedule.eps_atm * Ta^4 with Ta in Celsius; every other term in that numerator is divided by the Stefan-Boltzmann constant and therefore carries units of K⁴, so at 35 °C the term contributed 1.5e6 instead of 9.0e9 and the entire downwelling-longwave input to the globe was effectively deleted. Impact was decision-changing: at 30 °C air / 62 % RH / 6 m/s the corrected chain moves a moderate-workload acclimatized crew from "60 min work per hour, 30/30 composite" to "45/15 and a full stop-work composite".Tnwb > Ta + 5 °C -> null sanity envelope that is not in the reference implementation. It fired in calm, humid, strongly sunlit air — where a solar-loaded wick genuinely runs 5-6 °C above air — and each rejection fell back to the psychrometric wet bulb, understating WBGT by roughly 4 °C in the highest-hazard hours the tool exists to catch. Replaced with the one physical bound that does hold: the wick cannot cool below the dew point.Ta - 5 where the reference starts from the dew point. With the reference's 0.9/0.1 relaxation and 0.02 K stopping rule the converged value is start-dependent at the ~0.05 K level, so this was a real divergence from the cited model.Tg = Ta for any hour below 10 W/m². A black globe is not equal to air temperature at night: in dry air it loses longwave to the sky and sits up to 5 °C below air, and in hot humid air (where the Brutsaert-form sky emissivity exceeds 1) it sits up to 2 °C above.FORMULAS.estimateGlobeTempLiljegrenC, an iterative port of the Argonne reference Tglobe(), as the primary globe model. Chosen over the Dimiceli linearization because it shares one set of constants, one beam fraction and one wind height with the wick model; because Nu = 2 + 0.6Re^0.5Pr^(1/3) keeps the conduction limit and so stays physical as wind approaches zero without an artificial wind floor; and because it is correct at night. Dimiceli remains as a labelled fallback — measured |Liljegren - Dimiceli| on the globe is 4.6 °C at 7 m/s, 5.6 °C at 3 m/s and 12.4 °C at 1 m/s, which is the case for the demotion.vendor/liljegren-wbgt/ — the upstream Argonne C source (MIT, via mdljts/wbgt), vendored with pinned SHA-256 hashes — plus scripts/liljegren-parity.mjs, which compiles it and regenerates a 320-point reference fixture. test/wbgt-liljegren-parity.test.js holds the JavaScript port to within 0.08 °C of that fixture at every point. This harness found three of the defects listed above on its first run.uncertaintyC, uncertaintyTerms) on every WBGT estimate, computed rather than asserted: the spread between the two independent globe models, the gap the psychrometric fallback would cost if used, and the estimate re-run at ±0.25 m/s wind, summed in quadrature and floored at the 0.05 °C output rounding. Surfaced through /api/v1/weather/wbgt.test/wbgt-golden-vectors.test.js (8 named real-world hours, each checked against committed values, physical brackets and the 0.7/0.2/0.1 weights) and test/wbgt-bias-direction.test.js (monotonicity in temperature, humidity, sun and wind; disclosure invariants; and a targeted regression test for the Celsius/kelvin class of bug, asserting the globe responds to sky emissivity).scripts/validate-wbgt-observations.mjs and test/wbgt-observation-validation.test.js: a bias gate over 144 real observed hours from six sites across the climates the registry covers (Phoenix, Houston, Chinandega, Delhi, Doha, Niamey). Current bias is +0.003 °C with an RMSE of 0.034 °C and no hour reading materially below the reference. Both this and the parity fixture check now run in CI.test/acgih-action-limit.test.js, pinning both published ACGIH tables. Nothing in the suite failed when those tables were corrected, because no test had ever pinned an ACGIH threshold.wind <= 0.75 m/s comparison to the criterion it was a proxy for: an hour is flagged when a ±0.25 m/s wind error — the resolution an hourly forecast cannot beat — moves WBGT by 1 °C or more. It crosses at 0.70 m/s under the current models. The flag still never enters a calculation.forecast_estimate_physical_weather) to require solar geometry computed from real coordinates. A physical model run on an assumed sun angle now lands one tier down.solarZenithCos and beamFraction directly, rather than having solar geometry silently re-derived or defaulted to a cza = 0.5 stand-in.about.html and the physics header of api/weather-wbgt.js, both of which documented the replaced model, the old wind floors, and a "matches the full quartic solution to within ~0.3 °C" claim that was not true of the shipped code.scripts/validate-wbgt-observations.mjs.guidance rather than regulation because the authority's claim that the figures were confirmed by the Supreme Administrative Court (KHO) could not be independently verified against a case citation./api/v1/weather/air-quality presenting Open-Meteo's composite multi-pollutant us_aqi (a max across PM2.5/PM10/O3/NO2/SO2/CO) as if it were the PM2.5-specific AQI; the displayed AQI and the value feeding California's AQI-canonical §5141.1 wildfire-smoke engine are now always derived from measured PM2.5 via the EPA 2024 breakpoint formula (FORMULAS.pm25ToAqi), the endpoint now selects the correct forecast hour by converting to the location's local hour instead of comparing against server UTC, and app.js no longer has a fallback path that could substitute the composite AQI for the PM2.5-specific value when live PM2.5 data was unavailable.api/heat-api.js) accepting physically impossible sensor inputs — e.g. a -999°C WBGT reading previously returned a confident "safe, continuous work" recommendation. Added range validation (rejected outright, never silently clamped) for WBGT, relative humidity, air/indoor/max/wet-bulb/globe/mean-radiant temperature, wind speed, metabolic rate, atmospheric pressure, thermal work limit, heat index, and PM2.5/AQI; calendar-invalid dates like 2026-13-45 (which JS's date auto-rollover let slip past the old digit-only regex) are now rejected, and hourly WBGT rows with an empty-string value are now rejected at the API boundary instead of being silently dropped later.api/weather-wbgt.js): omitting time/shiftStart no longer picks the wrong forecast hour for non-UTC locations (the current hour is now derived from the location's own UTC offset rather than the server's UTC wall clock); the hourlyWbgt array is now correctly narrowed to the shiftStart/shiftEnd window when both are supplied; and the documented provider query parameter (auto/open-meteo/nws) is now actually honored instead of being silently ignored./api/v1/* request pipeline (functions/_middleware.js) so an uncaught exception in a route handler now returns the app's standard structured {success:false, error, detail} JSON response instead of a raw runtime error.POST /api/v1/guidance/country so it actually filters results by country: standards with no meaningful relationship to the target country/region (internal relevance priority 7) are now excluded, while exact-country, regional-bloc, text-hint, and global standards (NIOSH, ISO, ACGIH, etc.) remain visible; requests with no resolvable country code are unaffected and still return the full unfiltered list. Also added the missing POST /api/v1/guidance/country route to the local Express server (server.js), which previously only existed in the Cloudflare Pages Functions deployment.country_guidance.md row; research dossiers for all 19 EU countries scanned are kept under docs/research/.control_overlay (excluded from the work/rest composite) and deliberately scoped to city contracts to avoid Texas HB 2127 state preemption of local heat rules; a new US-TX region pill surfaces it in the hero chart for Texas users.GuidanceMap, MapToolbar, MapModal) above the Framework Comparison table, pinning every jurisdiction that has a registered heat or smoke standard. Clicking a pin lists every standard at that location with links to its comparison-table row and source. Global standards (NIOSH, ISO, ACGIH, La Isla, etc.) are listed separately as "applies everywhere" instead of being mis-pinned to one country. Includes filter chips, search-to-fly, a "you are here" auto-focus highlight, coverage-gap shading for un-covered countries, and a fullscreen expand view. Backed by map-geo.js (pure, unit-tested grouping/filter helpers) and data/jurisdiction-coordinates.json, with a validate-standards.mjs + data-integrity guard that every pinnable jurisdiction has a coordinate.time-format.js) so displayed times follow the detected country's locale conventions instead of always rendering US-style.USA city guidance status.md), used to scope the San Antonio ordinance and confirm NYC/Phoenix/San Antonio are currently the only cities with enforceable local rules.missing entry instead of silently collapsing onto the country-level pin. Pseudo-regions (EU/GCC) still intentionally fall through to the country pin.stopWork, so it was reclassified from stop_work_rule to control_overlay; Poland no longer advertises a drink/meal duty on the no-trigger path; Latvia now requires an explicit workload input instead of defaulting to the least-restrictive tier.control_overlay (no work/rest schedule, excluded from the composite): it reinforces baseline outdoor controls (water, shade/cooled rest, breaks, bathroom access, acclimatization, training, symptom reporting) and ties heat-illness-prevention plans to City Heat Emergency activation (heat index ≥100°F single-day or ≥95°F for two consecutive days). A follow-up pass reclassified it from guidance to regulation after rereading the order: it has a specific enforceable effect for food-delivery workers (mandatory restaurant bathroom access under NYC Administrative Code §20-563), split out via a new regulatoryScope.mandatoryFor/advisoryFor distinction so the engine doesn't overstate or understate who it binds.REGION_STD_SETS mechanism that extends the existing country-scoped pill logic without changing what non-NY US users see.chart-lanes.js, ThresholdLanesChart): a horizontal-lane overview of every applicable standard's trigger point, grouped by metric (WBGT, heat index, air temperature, PM2.5 smoke), with a "you are here" marker per lane showing which standards current conditions have already crossed.filterCountryScopedResults / COUNTRY_SCOPED_ENGINE_IDS) so region-specific rules (e.g. Qatar) only display for users detected in the relevant region (e.g. GCC), instead of appearing for every user worldwide.LinGuidanceCard now folds the La Isla RSH-s primary recommendation, AKI kidney-risk panel, productivity-impact panel, and hydration panel into one "shift stats" card, replacing the separate PrimaryRecommendation / KidneyRiskPanel / ProductivityPanel / HydrationPanel components (now unused).chart-lanes.js script include that had left the new chart non-functional after the initial build/dev-server changes.sourceIds and parametersLastUpdated to every standard result and surfaced them in the UI: the comparison-table row now shows an "updated YYYY-MM-DD" meta line, and the standard detail card shows a "Parameters last updated" field. Backed by a new data/changelog.json (generated from git history via scripts/gen-changelog.mjs) and exported through data/standards-loader.js.legalStatus/ruleType enum validation to scripts/validate-standards.mjs, catching typos or unrecognized values before they reach the UI.validate-standards and the changelog-freshness check into npm test (test/data-integrity.test.js), so an inconsistent registry or a stale changelog now fails the suite instead of only being caught manually.main when tests fail.homeassistant-wbgt, which calls the /api/v1/weather/wbgt endpoint. The integration had briefly been added directly into this repo (calling /api/v1/compare) but was removed in favor of the standalone repo so it can be a proper HACS-installable package rather than living inside this monorepo.formulas.js, the standard JSON, country_guidance.md, and the README were all updated in lockstep to note OSHA's National Emphasis Program as the current federal enforcement vehicle.regionCode: ["US-CA"] on the Cal/OSHA Indoor Heat standard — the only California-scoped engine that lacked it, inconsistent with the other CA engines.legalStatus/ruleType values that no longer validate.computeWashingtonWildfireSmoke reporting the generic ≥20.5 µg/m³ trigger message for readings in the [35.5, 500.4) µg/m³ exposure-controls tier instead of the tier-specific "≥35.5 µg/m³ mandatory exposure controls + voluntary N95" message, matching the pattern already used by the Oregon engine. Added boundary tests at 35.5 µg/m³ and tightened the APF≥25 test to the exact 555.0 µg/m³ boundary (was previously tested at 600).A standards-grounded audit of the calculation layer. Every numeric finding was verified against a primary source (NWS/WPC, ACGIH, NIOSH 2016-106, the cited TB MED 507 PDF, EPA 40 CFR Part 58 App. G, Liljegren et al. 2008, Dimiceli et al., WA L&I) before any change. Several flagged "issues" were verified to be CORRECT as written and left unchanged (see Verified-correct below).
wet bulb (provider value or Stull 2011) directly as the natural wet bulb (Tnwb). Under solar load and low wind Tnwb runs ~0.8-2°C higher, so this under-estimated WBGT (unsafe direction) — enough to drop a heat category in calm, sunny conditions. Added FORMULAS.estimateNaturalWetBulbLiljegrenC, a faithful port of the Liljegren et al. 2008 energy-balance model (NWS operational standard; Argonne reference code), used for the 0.7 term whenever solar geometry allows, with a psychrometric fallback at night / when inputs are missing. Validated: night ≈ psychrometric, +2-3°C midday sun, +~5°C calm desert sun.
estimateHeatIndexF now follows the full NWS algorithm: switchto the Rothfusz regression on the computed simple heat index (not raw temperature), and apply the NWS low-RH (RH<13%, 80-112°F) and high-RH (RH>85%, 80-87°F) adjustments, both previously omitted.
(WAC 296-62-085, mandatory at 555.5 µg/m³) to the permanent WAC 296-820: mandatory full respiratory protection program at 500.4 µg/m³, APF ≥ 25 at 555.0 µg/m³, with the 20.5 / 35.5 µg/m³ lower tiers. Workers in 500.4-555.4 µg/m³ were previously under-protected.
to the most-severe default band.
offset, not a clothing (CAF) factor; fixed the mislabeled note.
t^4 terms are intentional: the constants256000 = 4·40³ and 7,680,000 = 3·40⁴ are the exact Taylor linearization of t⁴ about 40°C, confirming the model is formulated in Celsius. A reported "Stefan-Boltzmann Kelvin bug" was a false positive; converting to Kelvin would have broken validated math.
4 work columns at 250/425/600/800 W, fluid caps 1.5 qt/hr & 12 qt/day, ±¼ qt sun/shade modifier). A subagent's "broken table" claim used an outdated 3-column reproduction.
metabolic categories, unit conversions, WBGT coefficients, and acclimatization handling (effectiveC = wbgtC + cafC, acclimatization applied once) all verified correct.
range is 2.5-3.5°C); the +1.5°C "partial" tier has no direct published basis. Left as disclosed approximations.
date was persisted to localStorage and restored for up to 30 days, so a value saved on an earlier day (e.g. May 22) drove the live-forecast fetch to the wrong day and displayed an out-of-date WBGT as if current. sanitizeStoredCalculatorInputs now drops any persisted date, so the calculator always starts on today. This was the root cause of the large WBGT discrepancy versus live WBGT tools (e.g. 53 °F shown for a cool past day vs. ~84 °F for the actual day) — the WBGT engine itself was correct.new Date().toISOString()), which rolls to the next calendar day on US evenings and would request the wrong forecast day. Added todayLocalDate(), which builds YYYY-MM-DD from local date components.shiftStats counted every hourly sample whose integer hour fell within the shift as a full 60 minutes and used inclusive bounds on both ends. It now weights each sample by the fraction of its hour block that overlaps the shift, so work + rest can no longer exceed the shift duration.WBGTForecastChart component (defined but never rendered) contains the same whole-hour shift-accounting pattern. Left untouched because it is dead code and out of scope for this fix.StateRulesPanel — replaces WildfireSmokePanel; generalizes it to include both control_overlay (heat) and smoke_overlay (smoke) engines for the active US state. Shows jurisdiction header ("California — enforceable workplace rules"), per-engine employer-duty list, legalStatus badge, trigger threshold, and respirator tier.resolveStateEngines fields (oshaCoverage, heatStatus, note). Uses --info (#9CBECF), never --heat-danger. No fabricated numeric guidance for unserviced states.FORMULAS.pm25ToAqi(pm25) and FORMULAS.aqiToPm25(aqi) — EPA piecewise-linear AQI formula with 2024-revised PM2.5 24-hour breakpoints (89 FR 16202 / aqs.epa.gov). PM2.5-specific AQI only.results useMemo: CA gets aqiPm25 (manual AQI → converted from PM2.5 → live AQI); OR/WA get pm25ugm3 (manual/live PM2.5 → converted from AQI). CA smoke guidance now computes from PM2.5-only input.AirQualityCard: when an applicable smoke engine is active, the hero card shows the respirator requirement badge beside the AQI value.GuidanceLegend — collapsible glossary above the comparison table. Explains "regulation" vs "guidance" and the three respirator tiers (voluntary / FFR required / full RPP) in worker/supervisor language.test/us-state-guidance.test.js with 27 new tests: EPA 2024 breakpoint boundary values, pm25ToAqi / aqiToPm25 inverses, round-trip stability, CA smoke engine cross-input correctness, and resolveStateEngines coverage-line fields for TX and NY.activeStateCode is now a prop on ResultsPanel and threaded to StateRulesPanel.activeSmokeResult is derived at the main component and passed into WBGTHeroSection → AirQualityCard.data/standards/*.json file, registry + rule-type entries, a dispatch handler, a pure compute function with named threshold constants, and source footnotes.smoke_overlay rule type for the wildfire-smoke engines. Smoke overlays are excluded from the work/rest composite (getMostProtective), so air-quality guidance never alters the heat schedule math.data/us-state-inventory.json: the regulatory status of all 50 states plus D.C. (OSHA coverage, heat status, AQI status, applicable engine ids). Loaded in Node via standards-loader.js and injected into the browser bundle by scripts/dev.mjs / scripts/build.mjs.FORMULAS.resolveStateEngines(), FORMULAS.logUnservicedState(), and FORMULAS.STATE_SCOPED_ENGINE_IDS. Recognized states with no engine yet are logged to the console and render nothing — a deliberate hook for future use.WildfireSmokePanel to the results view showing respirator tier, trigger threshold, controls, and source for the active state's wildfire-smoke rule./api/v1/geo/country to return Cloudflare regionCode/region, enabling U.S. state auto-detection (with a manual override).test/us-state-guidance.test.js covering engine thresholds, state scoping, composite isolation, the 50-state inventory, helper behavior, and data-source integrity.chart-thresholds.js, a standalone module containing WBGT_CHART_STANDARDS (24 entries), COUNTRY_STD_SETS, getLocationStandards, computeChartThreshold, deriveChartStandards, and getDisplayStandards. The browser bundle no longer embeds these constants inline (2891a50).87805e1).COUNTRY_STD_SETS covers 40+ country codes) and seeds enabledStdIds on first load (3ba1de7).WBGT_CHART_STANDARDS from 10 to 24 entries, adding Japan WBGT, South Korea KOSHA, South Africa PAR 2024, Australia Safe Work, Mexico NOM-015, Brazil NR-15, El Salvador D89, Nicaragua RM, Colombia Res 2400, Costa Rica DE, Italy INAIL, Germany ASR A3.5, France Décret 2025-482, Spain RD 486, Greece heatwave decree, and UK HSE guidance. Standards with no fixed WBGT threshold (framework-based or heat-index-based) are now shown as non-toggleable pills with an em-dash value (3ba1de7).3caa49c, 40f53bb).a619f6d).SegmentedControl component replacing the workload and sun exposure <select> dropdowns, and a teal-bordered "Set Conditions" card that visually separates primary inputs from secondary conditions (3ba1de7).3ba1de7).!hasStoredUnit()); detectedCountry state is set regardless, enabling the country-aware pill strip while the unit auto-select logic remains unchanged (3ba1de7).computeChartThreshold for very-heavy workload: VeryHeavy.continuous and VeryHeavy.w75 are both null in ACGIH_TLV, causing the function to return null and silently bypass acclOffset and clothing CAF adjustments. Now falls back to w50 (28.0°C), the highest work-share tier with a defined ACGIH limit, so adjustments are applied correctly (3ba1de7).washington_osha_outdoor: Washington DOSH outdoor heat thresholds by clothing type, including 80 F standard-clothing action level, lower double-layer/vapor-barrier thresholds, 90 F and 100 F high-heat break schedules, one-quart-per-hour hydration guidance, and acclimatization observation notes.oregon_osha_heat: Oregon OSHA heat-index tiers, paid shade-rest schedules from 90 F through 105 F, one-quart-per-hour hydration, and PPE escalation when double-layer or impermeable clothing increases heat burden.maryland_osha_heat: Maryland MOSH indoor/outdoor heat-index trigger logic at 80 F, 90 F, and 100 F, with mandatory cool-down breaks, shaded or cooled rest areas, and one-quart-per-hour water guidance.minnesota_osha_indoor: Minnesota indoor WBGT limits by workload, including light/moderate/heavy thresholds and 30/30 work-rest recommendations when limits are exceeded.bc_worksafebc_heat: WorkSafeBC heat exposure guidance that delegates to ACGIH TLV/AL screening while adding BC-specific Exposure Control Plan, monitoring, hydration, and seven-day unacclimatized-worker notes.chile_ds594_heat: Chile DS 594 TGBH/WBGT work-rest tables for light, moderate, heavy, and very-heavy work, including 60/0, 45/15, 30/30, 15/45, and stop-work outcomes.belgium_bien_etre: Belgium WBGT action ceilings by workload, with mandatory ventilation, refreshing drinks, and administrative controls when ceilings are exceeded.us_navy_ashore_flags: US Navy and Marine Corps ashore flag conditions, mapping WBGT to White/Green/Yellow/Red/Black flags with acclimatization restrictions and body-armor/vapor-barrier considerations.costa_rica_de_39147: Costa Rica four-level heat-index model, heavy-work/PPE risk escalation, Level IV stop-work handling, hydration notes, and ERCnt prevention context.cyprus_heat_decree: Cyprus dry-bulb temperature and relative-humidity stop-work matrix by workload, plus orange/red heat warning midday suspension notes.austria_hitze_scheg: Austria construction-sector Hitzefrei rule at 32.5 C shade temperature, modeled as a financial/operational stop-work option rather than a universal outdoor-work ban.ontario_ohcow_humidex: Ontario OHCOW adjusted Humidex schedule, including 15/45, 30/30, 45/15, and stop-work bands, PPE/acclimatization adjustments, and 240 mL every 20 minutes hydration guidance.nicaragua_rm_higiene: Nicaragua TGBH/WBGT work-rest tables and clothing penalties for cotton robes, winter uniforms, and impermeable wet-weather gear.colombia_res_2400: Colombia Resolucion 2400 WBGT-aligned compliance checks, periodic rest-pause guidance, continuous fluid supply, and industrial-hygiene survey notes.data/standards-index.json, data/sources.json, data/standard-details.json, formulas.js, test/engine-shape.test.js, and test/formulas.test.js for the pending 14-engine expansion.country_guidance.md is currently staged separately from the implementation changes. Convert this section into a numbered release after the staged docs and unstaged implementation receive a real commit hash.CalendarTimeline experience with a chart-first workflow for inspecting forecast heat risk over time (3867d3d).3867d3d).cloud_cover support and expanded forecast coverage to seven days, making the hero chart useful for shift planning beyond the current day (3867d3d).3867d3d).a701c68).e64cdfc).e64cdfc).e64cdfc).Water mode, tying hydration estimates to effective WBGT, workload, shift length, and sun/shade exposure (9ebee8f).1fe79d9).raw WBGT + CAF) when PPE changes the thermal burden (e64cdfc).README.md, including accepted inputs, unit persistence, Scenario Quick-Select, weather-derived WBGT, all 55 standards, comparison behavior, output panels, API routes, and architecture diagrams (e64cdfc).9ebee8f).163dcf0).f2e363c).5bcfa91).f5b94d3).3d9eccc).npm run start (55a4cc5).f8bc2d4).STANDARDS_REGISTRY into per-standard JSON files and added validation scripts so new standards can be added through data files instead of editing one monolithic registry (4fbb862)._runEngine switch with a generated dispatch map, reducing coupling between the registry and individual standard engines (ef15e8f).82b762e, 8e8aa72).91ffc9d).useReducer, then followed with accessibility fixes for the main landmark, form labels, and WBGT slider descriptors (af2ff14, 7108366).cal_osha_indoor metadata so implemented optional inputs such as high radiant heat and heat index are documented in the registry (5bcfa91).countryCode, regionCodes, sorting logic, and a Cloudflare Pages Function, allowing country-specific recommendations to be requested without running the full comparison UI (6becd4b).de8f9a1).7e8798b).459725b).49cb51d).aae1e5c).2021c82).4d1f6ba).2116f4d).ca48a79).ca48a79).ca48a79).ca48a79).69f62c9).ca48a79).heat-guidance-service.js, payload validation, health check, standards listing, standards comparison, single-standard evaluation, Supertest coverage, and package scripts (82ca3c0).0b95098).api/heat-api.js, added Cloudflare Pages Functions for standards listing, standard evaluation, comparison, and health checks, and introduced shared JSON/method utility helpers (e567eff).e567eff).f5ebb2b).3c7705d, 6d13a39, 8fd666a)./api/v1/weather/wbgt Pages Function (69a0ff5).02481c4)./api/v1/wbgt weather-derived WBGT endpoint and Dimiceli/Piltz-related calculations for estimating heat exposure from forecast conditions (722235f).bdf7a4d).db65bef).country_guidance.md with new standards and more operational notes during the API/registry expansion (24edcc4).18c5b2e).2db89ff).ba5dae7).42c9fe1).6cfcc9e).16f0e5b).eef4126).oldstuff/ as the project shifted from static prototype toward tested calculator and API surfaces (2db89ff).c02f846).773d8f2).app.js, formulas.js, styles.css, index.html, test fixtures, and package setup, moving the app from research artifacts toward a runnable tested calculator (773d8f2).dcac4df).42e9941).ce45e1a).