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.
This commit is contained in:
Emi Griffith 2026-07-19 16:37:42 -07:00 committed by GitHub
parent c2d9b602dd
commit e82323f11a
2 changed files with 27 additions and 3 deletions

View file

@ -111,15 +111,16 @@ function render(data) {
<span class="score-num">${ov.score}</span>
<div class="score-hero-label">
<span class="score-grade">${esc(ov.grade)}</span>
<span class="score-sub">${recentYrs} vs ${baseYrs} · ${s.n_recent.toLocaleString()} recent days scored</span>
<span class="score-sub">Net change · ${recentYrs} vs ${baseYrs} · ${s.n_recent.toLocaleString()} recent days scored</span>
</div>
</div>
<p class="score-note">0 = no change from the long-term normal · 100 = an extreme shift.
Temperature, feels-like and humidity are weighted most.</p>
<p class="score-note">A net-change total across all metrics: 0 = no change from the long-term
normal · 100 = an extreme shift. Temperature, feels-like and humidity are weighted most.</p>
</div>`;
// ---- per-metric cards (annual) ----
const cards = METRIC_ORDER.map((k) => metricCard(annual.metrics[k])).join("");
const wbNote = wetbulbNote(annual.metrics.wetbulb, baseYrs);
// ---- by-season rows ----
const seasonRows = SEASON_ORDER.map((sk) => {
@ -138,6 +139,7 @@ function render(data) {
scoreBody.innerHTML = `
<section class="normals">${cards}</section>
${wbNote}
<section class="score-section">
<p class="section-title">By season</p>
<p class="score-legend">${METRIC_ORDER.map((k) => shortLabel(k)).join(" · ")}</p>
@ -150,6 +152,22 @@ function render(data) {
wireSummaryToggle();
}
// Explains what wet bulb is and reports the share of heat-stress ("wet-bulb")
// days vs normal days, recent vs baseline. Re-rendered on unit change.
function wetbulbNote(m, baseYrs) {
const def = `<strong>Wet bulb</strong> is how cool evaporation — like sweat — can make you in the
current air. When it climbs, sweat stops shedding heat and high temperatures turn dangerous.`;
if (!m || m.score == null || !m.freq) {
return `<p class="score-callout">${def}</p>`;
}
const thr = fmtTemp(m.freq.threshold_f);
const { f6, f45, d } = m.freq;
const delta = d ? ` <span class="pq-d ${d >= 0 ? "pq-up" : "pq-down"}">(${d >= 0 ? "+" : ""}${d} pts)</span>` : "";
return `<p class="score-callout">${def}
Heat-stress days (peak wet bulb ${esc(String(thr))}): <strong>${f6}%</strong> of recent days
vs <strong>${f45}%</strong> across ${baseYrs}${delta} the rest are normal.</p>`;
}
// One metric score card (reuses .normal-card). Null metrics show a muted reason.
function metricCard(m) {
if (!m) return "";

View file

@ -1732,6 +1732,12 @@ td.rec-date { color: var(--muted); font-size: 13px; }
.score-section { margin: 20px 0; }
.score-legend { font-size: 12px; color: var(--muted); margin: -6px 0 10px; }
/* Wet-bulb explainer + heat-stress-day share. */
.score-callout {
background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px;
padding: 12px 14px; margin: 14px 0 4px; font-size: 13px; line-height: 1.5; color: var(--text);
}
/* One row per season: name · overall · a chip per metric. */
.season-scores {
display: grid; grid-template-columns: 64px 40px 1fr; align-items: center;