/* ==========================================================================
   Stream Deck — web/assets/css/site.css

   The theme over the vendored Bootstrap 5.3.8 CSS (docs/research/
   Dashboard-Template-Survey.md §4). Section order is fixed by
   docs/architecture/Repo-Structure.md §5: tokens, layout, components,
   utilities, print, prefers-reduced-motion. No !important is used, and no rule
   here sets a value a template also carries in markup.

   The --sd-* colour tokens below are the same names
   docs/architecture/View-Specs.md §2.3 defines. chart-common.js reads them from
   this file at run time so a chart and the page can never disagree about what a
   measurement or a band looks like, which makes these names as load-bearing as
   their values.
   ========================================================================== */

:root {
  /* --- View-Specs §2.3, verbatim: Okabe–Ito colour-blind-safe palette ----- */
  --sd-color-level: #0072B2;
  --sd-color-temperature: #D55E00;
  --sd-color-discharge: #009E73;
  --sd-color-turbidity: #CC79A7;
  --sd-color-ink: #1B1B1B;
  --sd-band-optimal: rgba(0, 158, 115, 0.15);
  --sd-band-stressful: rgba(230, 159, 0, 0.22);
  --sd-band-lethal: rgba(178, 24, 43, 0.22);
  --sd-color-nodata: #D9D9D9;
  /* The stale-data banner is Bootstrap's warning alert; this is that alert's own
     text colour (§2.3), checked: 7.21:1 on #FFF3CD. */
  --sd-color-stale: var(--bs-warning-text-emphasis, #664D03);

  /* --- Supporting tokens, added by the Phase 04 contrast check -------------
     The §2.3 band hues are area *fills*, and a fill is not required to reach 3:1.
     Where a band hue becomes an edge — a chip border, a band boundary, a decal
     line — WCAG 1.4.11 does require 3:1, and #E69F00 gets only 2.25:1 on white.
     These darkened versions are used for edges and nothing else; §2.3's own
     tokens are unchanged, so a chart reading --sd-band-stressful still gets the
     specified fill. Edges: 4.46:1, 4.75:1, 4.64:1 on their own band fill. */
  --sd-band-optimal-edge: #0B7A5A;
  --sd-band-stressful-edge: #8A5F00;
  --sd-band-lethal-edge: #B2182B;

  /* Text and structure. All checked against white and against the card surface:
     --sd-color-ink 17.22:1 / 16.34:1, --sd-color-text-muted 8.86:1 / 8.41:1,
     --sd-color-link 7.39:1 / 7.01:1. Bootstrap's own link colour (#0D6EFD) is
     4.50:1 / 4.27:1, so it is overridden below for headroom. */
  --sd-color-text-muted: #4A4A4A;
  --sd-color-link: #005A8C;
  --sd-color-link-hover: #004063;
  --sd-color-surface: #F8F9FA;
  --sd-color-rule: #DEE2E6;
  --sd-color-focus: #1B1B1B;

  --sd-radius: 0.375rem;
  --sd-space-section: 2.5rem;
  --sd-measure: 70ch;
}

/* --- Overrides of vendored defaults, not of our own rules -----------------
   Bootstrap's default link colour (#0D6EFD) measures 4.50:1 on white: exactly the
   AA text threshold §2.2 requires, so it has no margin for a hover state or any
   surface that is not pure white. On this site's card surface (#F8F9FA) it is
   4.27:1, which does fail, and the header nav link renders there with no colour
   rule of its own. --sd-color-link is therefore a headroom retune (7.39:1 /
   7.01:1), not a rescue. The focus ring is a genuine failure of §2.2's 3:1. */
:root {
  --bs-body-color: var(--sd-color-ink);
  --bs-link-color: var(--sd-color-link);
  --bs-link-hover-color: var(--sd-color-link-hover);
  --bs-focus-ring-color: rgba(27, 27, 27, 0.55);
}

/* ==========================================================================
   Layout
   ========================================================================== */

html {
  scroll-behavior: smooth;
  scroll-padding-top: 1rem;
}

body {
  background-color: #FFFFFF;
  color: var(--sd-color-ink);
}

main.container {
  padding-bottom: var(--sd-space-section);
}

.page-title {
  margin-bottom: 0.5rem;
}

.page-lede {
  color: var(--sd-color-text-muted);
  margin-bottom: 2rem;
}

.sd-section {
  margin-bottom: var(--sd-space-section);
}

.sd-section > h2 {
  border-bottom: 1px solid var(--sd-color-rule);
  margin-bottom: 1rem;
  padding-bottom: 0.25rem;
}

/* Every page has exactly one <h1> and a heading per section (§2.2); a section
   that no phase has built yet is still present so the page order and the table of
   contents are the finished site's (View-Specs §3). */
.sd-section--pending > h2 {
  color: var(--sd-color-text-muted);
}

.sd-section__pending {
  color: var(--sd-color-text-muted);
}

.sd-section__empty {
  color: var(--sd-color-text-muted);
}

p {
  max-width: var(--sd-measure);
}

/* ==========================================================================
   Components — page chrome
   ========================================================================== */

.site-header {
  background-color: var(--sd-color-surface);
  border-bottom: 1px solid var(--sd-color-rule);
  padding-bottom: 0.5rem;
  padding-top: 0.5rem;
}

.site-header__inner {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
}

.site-header__brand,
.site-header__organization,
.site-header__nav {
  margin: 0;
}

/* View 12's toggle (contract §1): pushed to the header's right edge, past "All
   streams". Server output is hidden and US-pressed; unit-toggle.js reveals it and
   moves aria-pressed. The buttons wrap inside the group — and the group itself wraps
   to its own line under the brand via the header's flex-wrap — so the header fits at
   320 px with no horizontal overflow. The pressed state is drawn from aria-pressed
   alone, in Bootstrap's own active tokens plus a weight change, so what looks pressed
   is always what is announced pressed, and the cue is not colour alone (§2.2). */
.sd-unit-toggle {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  justify-content: flex-end;
  margin-left: auto;
}

.sd-unit-toggle .btn[aria-pressed="true"] {
  background-color: var(--bs-btn-active-bg);
  border-color: var(--bs-btn-active-border-color);
  color: var(--bs-btn-active-color);
  font-weight: 600;
}

.site-header__title {
  color: var(--sd-color-ink);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.site-header__title:hover {
  color: var(--sd-color-link-hover);
}

.site-header__organization {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-left: 0.75rem;
}

.site-header__nav {
  margin-left: auto;
}

.site-header__nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-header__nav-item {
  display: inline-block;
  margin-left: 1rem;
}

.section-toc {
  border-top: 1px solid var(--sd-color-rule);
  margin-bottom: var(--sd-space-section);
  padding-bottom: 0.5rem;
  padding-top: 0.5rem;
}

.section-toc__heading {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  margin-bottom: 0.25rem;
  text-transform: uppercase;
}

.section-toc__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.section-toc__item {
  display: inline-block;
  margin-bottom: 0.25rem;
  margin-right: 1.25rem;
}

.site-footer {
  border-top: 1px solid var(--sd-color-rule);
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  padding-bottom: 2rem;
  padding-top: 1rem;
}

.site-footer p {
  margin-bottom: 0.25rem;
}

.sd-demo-banner {
  font-weight: 700;
  margin-bottom: 0;
  margin-top: 1rem;
  text-align: center;
}

/* ==========================================================================
   Components — one printed measurement
   ========================================================================== */

.sd-measurement {
  white-space: nowrap;
}

.sd-measurement__number {
  font-variant-numeric: tabular-nums;
}

.sd-measurement__unit {
  color: var(--sd-color-text-muted);
  font-size: 0.6em;
  text-decoration: none;
}

/* ==========================================================================
   Components — the band chip (View-Specs §4 view 1)
   The word is always present, and the pattern is always present where §2.3 gives
   one, so a band never depends on colour alone (§2.2).
   ========================================================================== */

.sd-chip {
  border: 1px solid currentColor;
  border-radius: var(--sd-radius);
  display: inline-block;
  padding: 0.125rem 0.5rem;
}

.sd-chip__word {
  font-weight: 700;
}

.sd-chip__pattern {
  border: 1px solid currentColor;
  display: inline-block;
  height: 0.75em;
  margin-right: 0.375rem;
  vertical-align: baseline;
  width: 0.75em;
}

.sd-chip--band-optimal {
  background-color: var(--sd-band-optimal);
  border-color: var(--sd-band-optimal-edge);
  color: var(--sd-color-ink);
}

.sd-chip--band-optimal .sd-chip__pattern {
  background-color: var(--sd-band-optimal);
  border-color: var(--sd-band-optimal-edge);
}

.sd-chip--band-stressful {
  background-color: var(--sd-band-stressful);
  border-color: var(--sd-band-stressful-edge);
  color: var(--sd-color-ink);
}

.sd-chip--band-stressful .sd-chip__pattern {
  background-image: repeating-linear-gradient(45deg, var(--sd-band-stressful-edge) 0 1px, transparent 1px 4px);
  background-color: var(--sd-band-stressful);
  border-color: var(--sd-band-stressful-edge);
}

.sd-chip--band-lethal {
  background-color: var(--sd-band-lethal);
  border-color: var(--sd-band-lethal-edge);
  color: var(--sd-color-ink);
}

.sd-chip--band-lethal .sd-chip__pattern {
  background-image: repeating-linear-gradient(45deg, var(--sd-band-lethal-edge) 0 1px, transparent 1px 4px),
  repeating-linear-gradient(-45deg, var(--sd-band-lethal-edge) 0 1px, transparent 1px 4px);
  background-color: var(--sd-band-lethal);
  border-color: var(--sd-band-lethal-edge);
}

/* A band that cannot be rated carries no colour claim at all — grey fill, dotted
   edge, and the words in --sd-chip--band-insufficient-data carry the meaning. */
.sd-chip--band-insufficient-data {
  background-color: var(--sd-color-nodata);
  border-color: var(--sd-color-text-muted);
  border-style: dotted;
  color: var(--sd-color-ink);
}

.sd-chip--band-insufficient-data .sd-chip__pattern {
  background-color: var(--sd-color-nodata);
  border-color: var(--sd-color-text-muted);
}

/* ==========================================================================
   Components — view 1, the hero
   ========================================================================== */

.hero__title {
  margin-bottom: 0.25rem;
}

.hero__description,
.hero__location {
  color: var(--sd-color-text-muted);
}

.hero__location {
  font-size: 0.9375rem;
}

.hero__summary {
  font-size: 1.125rem;
  margin-bottom: 1.5rem;
  margin-top: 1rem;
}

.hero__readings {
  margin-bottom: 1rem;
}

.hero__tile {
  background-color: var(--sd-color-surface);
  border-top: 3px solid var(--sd-color-rule);
}

/* Measurement codes become CSS class names with hyphens (Repo-Structure §4), so
   a tile's accent is the same token its chart line uses in a later phase. */
.hero__tile.measurement-water-level {
  border-top-color: var(--sd-color-level);
}

.hero__tile.measurement-water-temperature,
.hero__tile--steelhead {
  border-top-color: var(--sd-color-temperature);
}

.hero__tile.measurement-discharge {
  border-top-color: var(--sd-color-discharge);
}

.hero__tile.measurement-turbidity {
  border-top-color: var(--sd-color-turbidity);
}

.hero__label {
  color: var(--sd-color-text-muted);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-bottom: 0.25rem;
  text-transform: uppercase;
}

.hero__metric-label {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-bottom: 0;
}

.hero__value {
  line-height: 1.1;
  margin-bottom: 0.5rem;
}

.hero__number {
  font-size: 2.5rem;
  font-weight: 700;
}

.hero__window,
.hero__tile-time,
.hero__updated,
.hero__history,
.hero__note {
  color: var(--sd-color-text-muted);
}

.hero__history,
.hero__note {
  font-size: 0.875rem;
}

.hero__band {
  margin-bottom: 0.75rem;
}

.hero__trend {
  margin-bottom: 0.25rem;
}

.hero__trend-arrow {
  font-weight: 700;
}

.hero__flag {
  font-weight: 600;
  margin-bottom: 0.75rem;
}

.hero__flag-mark {
  color: var(--sd-band-lethal-edge);
  margin-right: 0.25rem;
}

.hero__stale {
  color: var(--sd-color-stale);
  margin-top: 0.5rem;
}

.hero__empty {
  font-size: 1.125rem;
  margin-top: 1rem;
}

/* ==========================================================================
   Components — About this data
   ========================================================================== */

.about-data table {
  font-size: 0.9375rem;
}

.about-data caption {
  caption-side: top;
  color: var(--sd-color-text-muted);
  font-size: 0.9375rem;
  text-align: left;
}

.about-data__note {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

.about-data__note--unreviewed {
  color: var(--sd-color-ink);
  font-weight: 600;
}

/* ==========================================================================
   Components — views 2, 3, and 6: a chart as a <figure> (View-Specs §2.1 rule 2)
   The <figcaption> holds the summary sentence, the chart container is where the
   script draws, and the fallback table sits in a collapsed <details>. The chart's
   text alternative is the summary sentence plus the table, so the container
   carries no role that would hide the description ECharts generates (§2.2).
   ========================================================================== */

.sd-figure {
  margin: 0 0 1rem 0;
}

.sd-figure > figcaption {
  margin-bottom: 1rem;
}

.sd-summary-sentence {
  font-weight: 600;
  margin-bottom: 0;
}

.sd-chart-container {
  height: 350px;
  width: 100%;
}

.sd-chart-container noscript p {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin: 0.5rem 0;
  max-width: none;
}

#hydrograph-chart {
  height: 440px;
}

#thresholds-chart {
  height: 380px;
}

.sd-fallback-details {
  margin-top: 1rem;
}

.sd-fallback-details > summary {
  color: var(--sd-color-text-muted);
  cursor: pointer;
}

.sd-figure__note {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

/* The in-HTML band legend beside view 3's chart (§4 view 3, "Legend"). Each row
   is the sd-chip — word and pattern, never colour alone — plus its plain-language
   explanation and, from meta.json, the limit it stands for. */
.sd-band-legend__title {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-bottom: 0.5rem;
  text-transform: uppercase;
}

.sd-band-legend__row {
  margin-bottom: 0.5rem;
}

.sd-band-legend__metric {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

.sd-legend-rule--nmfs {
  border-top: 1px dashed var(--sd-color-text-muted);
  display: inline-block;
  vertical-align: middle;
  width: 16px;
}

/* The threshold honesty labels (§2.4) and the unreviewed flag sit inside the
   figure, where the chart's reader will find them. */
.sd-honesty-note {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-top: 0.75rem;
}

.sd-review-flag {
  color: var(--sd-color-ink);
  font-size: 0.875rem;
  font-weight: 600;
}

/* Day-status marks in the fallback tables ("So far today", "Incomplete day") —
   annotation, not data, so it reads quieter than the numbers beside it. */
.sd-table-status {
  color: var(--sd-color-text-muted);
  font-size: 0.8125rem;
  font-weight: 400;
}

/* ==========================================================================
   Components — view 8, the stat tiles
   The card chrome is Bootstrap's; these rules are only the tile's own type
   scale. A tile is a `<div role="group" aria-labelledby>`, so its label is the
   thing a screen reader reads first and must not be a decorative caption.
   ========================================================================== */

.sd-tile {
  background-color: var(--sd-color-surface);
  border-top: 3px solid var(--sd-color-rule);
}

.sd-tile__label {
  color: var(--sd-color-text-muted);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-bottom: 0.25rem;
  text-transform: uppercase;
}

.sd-tile__value {
  line-height: 1.1;
  margin-bottom: 0.25rem;
}

.sd-tile__number {
  font-size: 2rem;
  font-weight: 700;
}

.sd-tile__unit {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

/* Tile 3's three chips are the value, so they wrap instead of shrinking; the
   percentages beside them are the numbers, and the bar below is decoration. */
.sd-tile__value--bands {
  line-height: 1.6;
}

.sd-tile__value--bands .sd-chip {
  margin-bottom: 0.25rem;
  margin-right: 0.25rem;
}

/* The stacked bar of view 8's tile 3 (§4: "% … as a stacked text bar"). Twenty
   cells of 5% carry the proportions, and each keeps §2.3's band fill and decal
   pattern, so the bar is never colour alone — the words above it are the value. */
.sd-band-bar {
  border: 1px solid var(--sd-color-rule);
  display: flex;
  height: 0.75rem;
  margin-bottom: 0.25rem;
  max-width: 20rem;
}

.sd-band-bar__cell {
  flex: 0 0 5%;
}

.sd-band-bar__cell--optimal {
  background-color: var(--sd-band-optimal);
}

.sd-band-bar__cell--stressful {
  background-color: var(--sd-band-stressful);
  background-image: repeating-linear-gradient(45deg, var(--sd-band-stressful-edge) 0 1px, transparent 1px 4px);
}

.sd-band-bar__cell--lethal {
  background-color: var(--sd-band-lethal);
  background-image: repeating-linear-gradient(45deg, var(--sd-band-lethal-edge) 0 1px, transparent 1px 4px),
  repeating-linear-gradient(-45deg, var(--sd-band-lethal-edge) 0 1px, transparent 1px 4px);
}

.sd-tile__sub,
.sd-tile__window,
.sd-tile__scale {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-bottom: 0.25rem;
}

.sd-tile__window {
  font-weight: 600;
}

.sd-tile__note {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-bottom: 0;
}

/* ==========================================================================
   Components — view 9, the readings table and the CSV download
   ========================================================================== */

.sd-readings__count {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

.sd-readings__heading {
  font-weight: 600;
}

/* PHP renders the sort buttons hidden (§2.1 rule 5), and data-table.js reveals
   them. They are link-styled buttons so a header still reads as a header. */
.sd-readings__sort {
  font-size: 0.75rem;
  padding: 0 0.25rem;
}

#sd-readings-table td .sd-measurement {
  font-variant-numeric: tabular-nums;
}

/* The UTC column is one unbreakable 20-character ISO 8601 stamp per row, and with the
   column labels that min-content sum made the table wider than its .table-responsive
   scroller on phones — and Chrome lets a table's own overflow escape a scroll container
   into the page: documentElement.scrollWidth measured 344 (milliken) and 326 (suscol) at
   a 320 viewport, a WCAG SC 1.4.10 reflow failure that hid in the one always-visible
   table (the fallback tables, scrolled the same way inside closed <details>, never
   leaked). overflow-wrap: anywhere collapses the cells' contribution to the table's
   min-content so the readings table fits its scroller at any width; a token breaks
   only where a line had no room for it, and a genuinely wide table still scrolls
   inside .table-responsive. Scoped to this table on purpose: the fallback tables
   proved clean, and their column heads keep their words whole. */
#sd-readings-table th,
#sd-readings-table td {
  overflow-wrap: anywhere;
}

/* data-table.js builds the paging controls itself — a <nav aria-label> around two
   buttons, inserted after the .table-responsive wrapper — so nothing but this file
   can give them their place under the table. The two buttons are appended with no
   text node between them, so the space has to come from CSS or "Previous" and
   "Next" touch. */
.sd-readings__pager {
  display: flex;
  margin-top: 0.75rem;
}

.sd-readings__pager .btn + .btn {
  margin-left: 0.25rem;
}

.sd-download__title {
  color: var(--sd-color-text-muted);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-top: 1.5rem;
  text-transform: uppercase;
}

.sd-download__size {
  color: var(--sd-color-text-muted);
  font-size: 0.9375rem;
}

.sd-download__note,
.sd-download__off,
.sd-download__format,
.sd-download__terms {
  color: var(--sd-color-text-muted);
  font-size: 0.9375rem;
}

.sd-download__off {
  font-weight: 600;
}

.sd-columns {
  margin-bottom: 1rem;
}

.sd-columns dt {
  font-weight: 600;
  margin-top: 0.5rem;
}

.sd-columns dd {
  color: var(--sd-color-text-muted);
  margin-bottom: 0;
  margin-left: 1rem;
}

/* ==========================================================================
   Components — views 4 and 5, the analysis charts' own additions
   ========================================================================== */

/* A calendar is twelve months wide and one week tall, so it needs far less
   height than a line chart and must not be squeezed into the default 350px. */
.sd-chart-container--calendar {
  height: 220px;
}

/* View 4's legend row for the level scale: a continuous gradient is not a band,
   so it carries no chip and no threshold — the swatch says "darker is deeper". */
.sd-legend-scale--level {
  background-color: var(--sd-color-level);
  background-image: linear-gradient(90deg, rgba(0, 114, 178, 0.15), rgba(0, 114, 178, 1));
  display: inline-block;
  height: 0.75em;
  margin-right: 0.375rem;
  vertical-align: baseline;
  width: 2rem;
}

/* View 5 puts this water year first and marks it; the weight and the surface
   are the distinction §4 asks for, so it does not depend on the line colour. */
.sd-overlay__column--current {
  background-color: var(--sd-color-surface);
  font-weight: 700;
}

.sd-overlay table[id^="water-year-overlay-table"] th[scope="row"] {
  font-variant-numeric: tabular-nums;
  font-weight: 400;
}

/* ==========================================================================
   Components — ADR-002 multi-gauge blocks
   One labeled block per gauge in views 1, 4, 8, and 10 (ADR-002 §4). Color still
   means measurement; the gauge is told apart by the heading and, in the charts, by
   dash style (web/assets/js/chart-common.js). These rules are spacing and the
   heading's place in the type scale only.
   ========================================================================== */

.sd-gauge-block + .sd-gauge-block {
  margin-top: 2rem;
}

.sd-gauge-block__heading {
  color: var(--sd-color-ink);
  font-size: 1.125rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
}

.hero__gauge + .hero__gauge {
  margin-top: 1.5rem;
}

/* View 4's legend and notes print once for the section, below the last gauge block
   (round-2 change): the block spacing keeps them apart from the calendars they explain. */
.sd-heatmap__legend {
  margin-top: 1.5rem;
}

.hero__gauge-heading {
  font-size: 1.125rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
}

.sd-card__gauge + .sd-card__gauge {
  border-top: 1px solid var(--sd-color-rule);
  margin-top: 0.75rem;
  padding-top: 0.75rem;
}

.sd-card__gauge-heading {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-bottom: 0.5rem;
  text-transform: uppercase;
}

/* ==========================================================================
   Components — views 7, 10, and 11: the maps and the index cards
   The map canvas is the container Leaflet's script takes over (views 7 and 11 on
   the stream page, the overview map on the index); it needs an explicit height or
   Leaflet paints nothing. Everything else in these sections is server-rendered and
   complete without JavaScript: the gauge lists, coordinates, OSM links, attribution,
   the placeholder panel, the cards, and Sue's build-time sparklines.
   ========================================================================== */

.sd-map-canvas {
  background-color: var(--sd-color-surface);
  border: 1px solid var(--sd-color-rule);
  border-radius: var(--sd-radius);
  height: 320px;
  margin-bottom: 0.5rem;
}

.sd-map__figure {
  margin-bottom: 0;
}

.sd-map__attribution,
.sd-map__status,
.sd-map__centre {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-bottom: 0;
}

.sd-map__unavailable {
  color: var(--sd-color-text-muted);
  font-style: italic;
}

.sd-map__list-heading {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-bottom: 0.5rem;
  text-transform: uppercase;
}

.sd-map__gauges {
  list-style: none;
  margin: 0;
  padding: 0;
}

.sd-map__gauge {
  margin-bottom: 0.75rem;
}

.sd-map__gauge-name {
  display: block;
  font-weight: 600;
}

.sd-map__coords {
  color: var(--sd-color-text-muted);
  display: block;
  font-variant-numeric: tabular-nums;
}

.sd-map__osm-link {
  display: inline-block;
  font-size: 0.875rem;
}

/* View 11's state with no KML published (View-Specs §4, view 11, "Empty / states"):
   a panel that reads as the place the map will go, not as a failed map. */
.sd-map-placeholder {
  background-color: var(--sd-color-surface);
  border: 1px dashed var(--sd-color-text-muted);
  border-radius: var(--sd-radius);
  color: var(--sd-color-text-muted);
  padding: 1.5rem;
}

.sd-map-placeholder p {
  margin-bottom: 0;
}

/* The sparklines paint from the same §2.3 tokens the charts read: the line is a
   stroke with no fill, an isolated single reading is a dot filled with the same
   colour, so neither ever reads as a blob (templates/lib/sparkline.php emits the
   classes; preserveAspectRatio="none" means the stroke stays crisp in the box). */
.sd-sparkline__line {
  fill: none;
  stroke-width: 2;
}

.sd-sparkline__dot {
  stroke: none;
}

.sd-sparkline__line--water-level {
  stroke: var(--sd-color-level);
}

.sd-sparkline__dot--water-level {
  fill: var(--sd-color-level);
}

.sd-sparkline__line--water-temperature {
  stroke: var(--sd-color-temperature);
}

.sd-sparkline__dot--water-temperature {
  fill: var(--sd-color-temperature);
}

/* View 10's card grid. Bootstrap's row/col classes (col-12 col-md-6 col-xl-4) give
   the §5.3 column counts; these rules are only the card's own type scale and the
   sparkline row that sits under each number. */
.sd-card {
  background-color: var(--sd-color-surface);
  border-top: 3px solid var(--sd-color-rule);
}

.sd-card__title {
  font-size: 1.125rem;
  margin-bottom: 0.25rem;
}

.sd-card__description {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

.sd-card__empty {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
}

.sd-card__reading {
  margin-bottom: 0.25rem;
}

.sd-card__metric {
  color: var(--sd-color-text-muted);
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.sd-card__number {
  font-size: 1.5rem;
  font-weight: 700;
}

.sd-card__trend {
  display: block;
  font-size: 0.875rem;
}

.sd-card__trend-arrow {
  font-weight: 700;
}

.sd-card__sparkline {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
  max-width: none;
}

.sd-card__sparkline-range {
  color: var(--sd-color-text-muted);
  font-size: 0.8125rem;
}

.sd-card__band {
  margin-bottom: 0.5rem;
}

.sd-card__band-label {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-right: 0.25rem;
}

.sd-card__stale {
  color: var(--sd-color-stale);
  font-size: 0.875rem;
}

.sd-card__updated {
  color: var(--sd-color-text-muted);
  font-size: 0.875rem;
  margin-bottom: 0;
}

/* ==========================================================================
   Utilities
   ========================================================================== */

/* A visible focus ring is required at 3:1 (§2.2, Keyboard row). Bootstrap's own
   ring is a 25%-alpha tint that reaches about 1.3:1 on white, so every focusable
   element in our markup gets this instead. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
table:focus-visible,
abbr:focus-visible {
  outline: 3px solid var(--sd-color-focus);
  outline-offset: 2px;
}

/* …but that rule alone left the buttons winning the cascade wrong: Bootstrap's
   `.btn:focus-visible` (0,2,0) sets outline:0 and a 25%-alpha box-shadow ring
   (~1.9:1 on white, under 1.4.11's 3:1), and `button:focus-visible` (0,1,1)
   could not outvote it — the audit measured `none 0px` on every button. Equal
   specificity, later file: the solid 17:1 ring now lands on .btn controls too. */
.btn:focus-visible {
  outline: 3px solid var(--sd-color-focus);
  outline-offset: 2px;
}

/* ==========================================================================
   Print (§2.2, Print row: hide controls and maps, keep summaries, hero numbers,
   and tables)
   ========================================================================== */

@media print {
  .site-header__nav,
  .section-toc,
  #sd-live,
  .btn,
  .btn-group,
  .sd-map-canvas {
  display: none;
  }

  body {
  background-color: #FFFFFF;
  color: #000000;
  }

  .hero__tile,
  .sd-chip {
  background-color: transparent;
  }

  .site-header {
  background-color: transparent;
  }

  .sd-section {
  page-break-inside: avoid;
  }
}

/* ==========================================================================
   Motion (§2.2, Motion row)
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html {
  scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
  animation-duration: 0.001ms;
  animation-iteration-count: 1;
  transition-duration: 0.001ms;
  }
}
