Split Very Heavy into Very Heavy + Severe; make Dry mean zero rain (#216)

Rain intensity gains a Severe tier and Dry becomes strictly no-rain:

- The top half of Very Heavy (95th–99th rain-day percentile) becomes a new
  "Severe" tier; Very Heavy keeps the 90–95 band. Eight rain tiers now — Trace /
  Light / Brisk / Typical / Heavy / Very Heavy / Severe / Extreme — which refill
  the wet-2..wet-9 colour ramp contiguously (no gap), so Heavy/Very Heavy shift
  one shade lighter and Severe takes the second-darkest.
- A day is Dry only when it didn't rain at all; any measurable rain, however
  slight, is at least Trace. _grade_precip splits on > 0 rather than the 0.01"
  threshold (which still governs the separate dry-streak metric).
- The distribution strip drops the range under the Dry column — every dry day is
  zero, so a "0–0" span was noise.

_precip_ladder derives its percentile marks from RAIN_BANDS now, so adding or
splitting a tier can't leave a hard-coded list behind (that was the bug the 95th
mark would have hit). The detail-view ladder derives from the band table as before.

Claude-Session: https://claude.ai/code/session_013dRZmX9D3JEntfMKWMTWZ8
This commit is contained in:
Emi Griffith 2026-07-19 22:09:22 -07:00 committed by GitHub
parent 1d32ec6b40
commit e105869363

View file

@ -49,8 +49,9 @@ export const SCALE_RAIN = [
{ c: "wet-3", label: "Light", range: "1025" }, { c: "wet-3", label: "Light", range: "1025" },
{ c: "wet-4", label: "Brisk", range: "2540" }, { c: "wet-4", label: "Brisk", range: "2540" },
{ c: "wet-5", label: "Typical", range: "4060" }, { c: "wet-5", label: "Typical", range: "4060" },
{ c: "wet-7", label: "Heavy", range: "6090" }, { c: "wet-6", label: "Heavy", range: "6090" },
{ c: "wet-8", label: "Very Heavy", range: "9099" }, { c: "wet-7", label: "Very Heavy", range: "9095" },
{ c: "wet-8", label: "Severe", range: "9599" },
{ c: "wet-9", label: "Extreme", range: ">99" }, { c: "wet-9", label: "Extreme", range: ">99" },
]; ];
@ -212,7 +213,10 @@ export function distStrip(buckets, showCount) {
const bar = h const bar = h
? `<div class="ct-bar" style="height:${h}px"></div>` ? `<div class="ct-bar" style="height:${h}px"></div>`
: `<div class="ct-bar ct-bar-0"></div>`; : `<div class="ct-bar ct-bar-0"></div>`;
const range = lo != null ? fmtMetricRange(b.unit, lo, hi) : ""; // Dry precip days are all zero, so a "00" range is noise — the bar and its
// share say all there is to say.
const isDryPrecip = b.unit === "precip" && b.group === "dry";
const range = (lo != null && !isDryPrecip) ? fmtMetricRange(b.unit, lo, hi) : "";
return `<div class="ct-col${b.cls ? ` ct-${b.cls}` : ""}" style="--c:${b.color}" ` + return `<div class="ct-col${b.cls ? ` ct-${b.cls}` : ""}" style="--c:${b.color}" ` +
`title="${b.label} · ${pctText(b.n, b.group)} (${b.n})${avg != null ? ` · avg ${fmtMetricVal(b.unit, avg)}` : ""}` + `title="${b.label} · ${pctText(b.n, b.group)} (${b.n})${avg != null ? ` · avg ${fmtMetricVal(b.unit, avg)}` : ""}` +
`${lo != null ? ` · ${fmtMetricVal(b.unit, lo)} to ${fmtMetricVal(b.unit, hi)}` : ""}">` + `${lo != null ? ` · ${fmtMetricVal(b.unit, lo)} to ${fmtMetricVal(b.unit, hi)}` : ""}">` +