SEO: monthly & seasonal records + colour-coded climate pages (#107)

* SEO: monthly & seasonal records on the city records page

The /climate/<city>/records page showed only all-time records per metric.
Expand it into a full records page: record high/low for each of the 12
months (each linking its month page) and for each meteorological season,
alongside the existing all-time table.

Seasons are hemisphere-aware — Dec–Feb reads as winter for northern cities
and summer for southern ones (picked from the city's latitude). Records
reuse grading.all_time_records over a month-filtered archive, so no new
data fetching or warming is needed. Adds a Dataset + breadcrumb JSON-LD
block and richer title/description for the expanded coverage.

* SEO: colour-code climate & records pages (heat-map + range strip)

The city, records and month pages were plain muted tables — generic climate-site
styling. Bring the interactive grader's diverging cold→hot palette onto them so
they read as heat maps in the site's own visual language:

- Map absolute °F to the 9-tier palette (temp_class, a Jinja global) and tint the
  temperature cells across the monthly-normals, monthly/seasonal/all-time records
  tables. The value stays in the ink token; the tint is a wash behind it. Non-temp
  rows (humidity/wind/precip) and date columns stay untinted.
- Add a monthly temperature-range strip on the city page: one gradient bar per
  month spanning the average low→high on a shared −10..115°F axis, so a city's
  whole-year rhythm (and hot-vs-cold character) reads at a glance.
- Tint the month page's hero high/low and its record values inline.

Palette, light/dark, and 390–1920px layouts verified by rendering hot (Phoenix)
and cold (Anchorage) cities.
This commit is contained in:
Emi Griffith 2026-07-15 20:50:59 -07:00 committed by GitHub
parent 30608ff6de
commit 536d9541bd

View file

@ -1492,6 +1492,41 @@ table.normals-table tbody tr:hover { background: var(--surface-2); }
.climate-records ul { margin: 8px 0; padding-left: 20px; } .climate-records ul { margin: 8px 0; padding-left: 20px; }
.climate-records li { margin: 4px 0; } .climate-records li { margin: 4px 0; }
.season-span { color: var(--muted); font-weight: 400; font-size: 13px; }
/* Temperature heat-map tints the interactive tool's diverging coldhot palette
brought onto the static tables. The value itself stays in the ink token; the tint
is a wash behind it, so a record high glows red and a record low reads cold-blue.
Mix percentages are tuned per tier (saturated darks need less) to keep the number
legible in both themes; the date column stays muted and untinted. */
td.t-cell { font-weight: 600; }
td.rec-date { color: var(--muted); font-size: 13px; }
.t-rec-cold { background: color-mix(in srgb, var(--rec-cold) 26%, var(--surface)); }
.t-very-cold { background: color-mix(in srgb, var(--very-cold) 26%, var(--surface)); }
.t-cold { background: color-mix(in srgb, var(--cold) 28%, var(--surface)); }
.t-cool { background: color-mix(in srgb, var(--cool) 34%, var(--surface)); }
.t-normal { background: color-mix(in srgb, var(--normal) 26%, var(--surface)); }
.t-warm { background: color-mix(in srgb, var(--warm) 40%, var(--surface)); }
.t-hot { background: color-mix(in srgb, var(--hot) 40%, var(--surface)); }
.t-very-hot { background: color-mix(in srgb, var(--very-hot) 40%, var(--surface)); }
.t-rec-hot { background: color-mix(in srgb, var(--rec-hot) 30%, var(--surface)); }
.t-none { background: transparent; }
/* Inline tinted value (month-page hero numbers, record list) same washes, padded
so the colour reads as a chip around the number. */
.t-inline { padding: 1px 7px; border-radius: 7px; }
/* Monthly temperature-range strip: one gradient bar per month, lowhigh on a shared
axis. The signature climate visual colourful and unmistakably Thermograph. */
.range-strip { display: grid; gap: 6px; margin: 16px 0 8px; max-width: 620px; }
.range-row { display: grid; grid-template-columns: 34px 1fr 92px; align-items: center; gap: 10px; }
.range-month { font-size: 13px; font-weight: 600; color: var(--muted); }
.range-track { position: relative; height: 14px; background: var(--surface-2); border-radius: 999px; }
.range-fill {
position: absolute; top: 0; bottom: 0; border-radius: 999px;
background: linear-gradient(90deg, var(--c1), var(--c2));
}
.range-vals { font-size: 13px; color: var(--text); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.range-note { font-size: 13px; margin-top: 6px; }
.climate-foot { font-size: 14px; margin-top: 28px; border-top: 1px solid var(--border); padding-top: 16px; } .climate-foot { font-size: 14px; margin-top: 28px; border-top: 1px solid var(--border); padding-top: 16px; }
.city-blurb { font-size: 15px; margin: 4px 0 14px; } .city-blurb { font-size: 15px; margin: 4px 0 14px; }
.blurb-src { color: var(--muted); text-decoration: none; font-size: 13px; white-space: nowrap; } .blurb-src { color: var(--muted); text-decoration: none; font-size: 13px; white-space: nowrap; }