Commit graph

9 commits

Author SHA1 Message Date
Emi Griffith
c95d5ec5f8 Pin API version + make frontend boot resilient to backend (async FE/BE deploy)
Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
2026-07-22 11:50:00 -07:00
Emi Griffith
71e2e6ec97 Score every metric equally in the overall total (#218)
The overall climate-shift score was a weighted roll-up (feels-like and humidity
2x, wind and gusts 0.5x, and so on). Make it an equal average instead: every
metric — and, through each metric's own mean over the 10/25/50/75/90 percentiles,
every percentile — counts the same.

- scoring._overall: plain mean of the present metrics' divergences, mapped to
  0-100, replacing the weighted sum. Missing metrics still drop out cleanly.
- Drop the now-unused per-metric weights from the METRICS table and the scored
  entries (nothing rendered them; only the roll-up read them).
- views.SCORE_VER s4 -> s5 so cached weighted scores are recomputed.
- score.js: the hero note now reads "Every metric counts equally" instead of
  "Temperature, feels-like and humidity are weighted most".

Verified against Seattle: overall mad equals the equal mean of the eight metric
mads (7.9), and the entries no longer carry a weight.

Claude-Session: https://claude.ai/code/session_013dRZmX9D3JEntfMKWMTWZ8
2026-07-20 05:45:57 +00:00
Emi Griffith
169678e1bc Simplify the score module and page: dedup builders, consolidate metric metadata (#209)
Backend (scoring.py):
- Fold the parallel WEIGHTS/METRIC_LABELS/DIRECTION_WORDS/TEMP_DIR_METRICS maps
  into one METRICS descriptor so a scored metric is defined in one place.
- Share one _build_entry between the seasonal and annual paths.
- Add _freq_for so the annual entry reads the wet-day / heat-stress-day share
  directly instead of re-running the full precip_divergence just for it.
- Drop unconsumed payload: per-q v6/pct and the overall bias.
- Drop _slice's dead 'annual' branch; derive SLICES from SEASONS.

Frontend (score.js):
- One scoreCell() and signedPts() helper for the four score-cell sites and the
  two signed-points chips; reuse .section-title / .table-wrap instead of cloning.

Behavior-preserving (identical scores); bumps the score cache version.
2026-07-20 04:02:40 +00:00
Emi Griffith
8588051141 Remove em-dashes from site copy and tighten the prose (#206)
Replace em-dashes in user-facing copy across the server-rendered pages,
static frontend views, and the strings the app injects at runtime, using
colons, commas, parentheses or full stops as the context wants. Data
placeholder glyphs (a lone "—" for a missing reading) are left alone,
since a hyphen there reads as a minus sign in temperature columns.

Also tighten the high-visibility surfaces (home hero and meta, about,
privacy, city and records ledes, glossary blurbs) toward a plainer,
more direct voice while keeping every factual claim intact.

Claude-Session: https://claude.ai/code/session_01XXxmNFy9cZ6Gh8Y9thZn62
2026-07-20 01:48:33 +00:00
Emi Griffith
9823fd47ec Stack score-card tier labels; clarify the wet-bulb explainer (#201)
On a narrow (2-up) phone card, a long grade like 'Extreme shift — windier'
crammed into the card's top-right corner and wrapped into a cluster. Restructure
each card to a clean vertical stack — metric name, score, tier, detail line — so
the label always has room, on mobile and desktop alike.

Also reword the wet-bulb explainer to state plainly what it is (the lowest
temperature evaporating sweat can cool you to).
2026-07-20 00:22:38 +00:00
Emi Griffith
c008ece671 Score each metric on the average of its seasonal differentials (#200)
Annual scores were computed from an all-year pooled distribution, which widens
the reference spread and hides a shift confined to one season — Seattle's daily
high read 16 despite a summer high of 65. Build each metric's annual score
(and the overall) as the mean of its four seasonal divergences instead, so a
real seasonal shift shows through (that high now reads 28). Frequency read-outs
(precip wet days, wet-bulb heat-stress days) stay pooled over the year.

Also lock the by-season table to fixed, uniform columns (min-width to scroll on
a phone) so each metric lines up vertically across the seasons, and show the
per-percentile detail as the season-averaged shift.

Bumps the score cache version.
2026-07-19 23:57:55 +00:00
Emi Griffith
91a780dd55 Align the by-season scores into a columns-aligned table (#198)
The by-season chips wrapped freely, so a metric's scores didn't line up
across seasons. Render it as a table instead — seasons as rows, one aligned
column per metric (plus an Overall column), tinted cells, horizontal scroll
on narrow screens.
2026-07-19 23:41:03 +00:00
Emi Griffith
e82323f11a Clarify wet bulb, add heat-stress-day share, frame total as net change (#197)
- Explain on the page what wet-bulb temperature measures (the evaporative-cooling
  ceiling on shedding heat), so the metric isn't opaque.
- Report the share of heat-stress "wet-bulb" days (peak wet bulb >= 26 C) vs
  normal days, recent window vs the full record — mirroring the precip wet-day
  frequency.
- Present the overall total as a direction-agnostic net change (magnitude only),
  not "warmer/cooler"; per-metric cards still carry direction.

Bumps the score cache version.
2026-07-19 23:37:42 +00:00
Emi Griffith
c2d9b602dd Add a climate-score page from recent-vs-baseline percentile divergence (#196)
Score how far a location's last 6 years have drifted from its full 45-year
record. For each metric and percentile category (p10/p25/p50/p75/p90), the
recent-years value is placed on the baseline distribution and the gap from the
expected percentile is the divergence — unit-free, so metrics compare directly.
Scored per meteorological season plus annual, weighted into per-metric and
overall scores (temps, humidity and feels-like weighted heaviest).

- backend/scoring.py: divergence math, seasonal slicing, precip zero-inflation
  split (wet-day frequency + amount), tier mapping onto the existing temp scale.
- climate.py: derive a wet-bulb column (Stull 2011) at the read boundary, before
  the humidity column is converted to absolute — via a shared _derive_metrics
  wrapper at all four read sites.
- api/v2/score endpoint + build_score payload, cached on the history token with
  a scoring-version key.
- frontend score page: overall hero, per-metric cards, by-season chips, and a
  button-revealed summary (sentences + metrics×season table + per-percentile
  detail). Score nav link across all headers.
- Tests for the scoring math, wet-bulb formula, payload shape and route.
2026-07-19 23:02:33 +00:00