/* Palette roles come from the validated reference palette. Dark steps are chosen
   for the dark surface, not flipped from light. The data-theme scope must beat the
   OS preference in both directions. */
:root {
  color-scheme: light;

  --surface:        #fcfcfb;
  --plane:          #f9f9f7;
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --text-muted:     #898781;
  --gridline:       #e1e0d9;
  --axis:           #c3c2b7;
  --hairline:       rgba(11, 11, 11, 0.10);

  --series-1:       #2a78d6;   /* measured */
  --series-2:       #eb6834;   /* forecast */
  --diverging-up:   #2a78d6;
  --diverging-down: #d03b3b;
  --neutral-mid:    #f0efec;

  --status-good:     #0ca30c;
  --status-warning:  #fab219;
  --status-serious:  #ec835a;
  --status-critical: #d03b3b;

  /* The diverging heat ramp for shaded cells (`td[data-heat]`): red below the
     column's average, gray ordinary (step 4, near the surface), blue above. A sign,
     not a magnitude (collector/heat.py). Each step has its own ink, measured: every
     pair clears 4.5:1, worst step 2 at 4.98. The dark blocks re-step both arms,
     since departure reads *brighter* there. The number is always in the cell. */
  --heat-1: #8f1f1f; --heat-ink-1: #ffffff;
  --heat-2: #e34948; --heat-ink-2: #0b0b0b;
  --heat-3: #f0b3b2; --heat-ink-3: #0b0b0b;
  --heat-4: #f0efec; --heat-ink-4: #0b0b0b;
  --heat-5: #9ec5f4; --heat-ink-5: #0b0b0b;
  --heat-6: #3987e5; --heat-ink-6: #0b0b0b;
  --heat-7: #0d366b; --heat-ink-7: #ffffff;

  /* The mean marker: a dark step of the bar's own hue, a mark on the bar, not a
     series. */
  --span-mid: #0d366b;

  /* The trail chart's horizons: steps of --series-2 (forecast), a narrow vivid
     order, 2.98 / 4.17 / 5.96. Only one shows at a time, so a pale near step was
     just faint (1.94:1). */
  --trail-1: #ec6e3c;
  --trail-2: #d64c15;
  --trail-3: #ac3d11;

  /* Tile fills (`collector/meter.py`): pastel, because a card-sized wash of a
     saturated step would be the loudest thing on the page. Status fills keep the
     verdict's hues at one pale step. Ink is `--text-secondary` (worst 6.40);
     `--text-muted` clears none, nor the plain surface (3.50), so a filled tile lifts
     its quiet text (`.tile-filled`). */
  --fill-water:     #dce9f8;
  --fill-warm:      #fadfd8;   /* the warm half of the departure pair */
  --fill-calm:      #e2efe1;
  --fill-watch:     #f8eed3;
  --fill-strained:  #f8e2df;
  --fill-critical:  #f8e2df;

  --radius: 10px;
  --gap: 1.25rem;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --surface:        #1a1a19;
    --plane:          #0d0d0d;
    --text-primary:   #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted:     #898781;
    --gridline:       #2c2c2a;
    --axis:           #383835;
    --hairline:       rgba(255, 255, 255, 0.10);
    --series-1:       #3987e5;
    --series-2:       #d95926;
    --diverging-up:   #3987e5;
    --diverging-down: #d03b3b;
    --neutral-mid:    #383835;

    --heat-1: #e66767; --heat-ink-1: #0b0b0b;
    --heat-2: #c33636; --heat-ink-2: #ffffff;
    --heat-3: #743a36; --heat-ink-3: #ffffff;
    --heat-4: #383835; --heat-ink-4: #ffffff;
    --heat-5: #2f5080; --heat-ink-5: #ffffff;
    --heat-6: #3987e5; --heat-ink-6: #0b0b0b;
    --heat-7: #86b6ef; --heat-ink-7: #0b0b0b;
    --span-mid: #cde2fb;
    /* Lighter with distance on #1a1a19: 4.60 / 6.21 / 8.31. */
    --trail-1: #da5c2a;
    --trail-2: #db8461;
    --trail-3: #eda182;
    /* Chosen for the dark surface, not flipped: a tint must be darker and more
       saturated to read as a fill. Ink --text-secondary, worst pair 5.02. */
    --fill-water:     #23374f;
    --fill-warm:      #4a2a24;
    --fill-calm:      #22382a;
    --fill-watch:     #3c3320;
    --fill-strained:  #402825;
    --fill-critical:  #402825;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --surface:        #1a1a19;
  --plane:          #0d0d0d;
  --text-primary:   #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted:     #898781;
  --gridline:       #2c2c2a;
  --axis:           #383835;
  --hairline:       rgba(255, 255, 255, 0.10);
  --series-1:       #3987e5;
  --series-2:       #d95926;
  --diverging-up:   #3987e5;
  --diverging-down: #d03b3b;
  --neutral-mid:    #383835;

  --heat-1: #e66767; --heat-ink-1: #0b0b0b;
  --heat-2: #c33636; --heat-ink-2: #ffffff;
  --heat-3: #743a36; --heat-ink-3: #ffffff;
  --heat-4: #383835; --heat-ink-4: #ffffff;
  --heat-5: #2f5080; --heat-ink-5: #ffffff;
  --heat-6: #3987e5; --heat-ink-6: #0b0b0b;
  --heat-7: #86b6ef; --heat-ink-7: #0b0b0b;
  --span-mid: #cde2fb;
  --trail-1: #da5c2a;
  --trail-2: #db8461;
  --trail-3: #eda182;
  /* As above for the dark surface; worst pair 5.02. */
  --fill-water:     #23374f;
  --fill-warm:      #4a2a24;
  --fill-calm:      #22382a;
  --fill-watch:     #3c3320;
  --fill-strained:  #402825;
  --fill-critical:  #402825;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--plane);
  color: var(--text-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.5;
}

.wrap { max-width: 68rem; margin: 0 auto; padding: 0 1rem; }

/* Keyboard users land here first; everyone else never sees it. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 10;
  background: var(--surface); color: var(--text-primary);
  padding: 0.5rem 0.9rem; border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

/* -- header ------------------------------------------------------------- */

.page-header { border-bottom: 1px solid var(--hairline); background: var(--surface); }
.header-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding-block: 1rem;
}
.brand { text-decoration: none; color: inherit; display: block; }
.brand-name {
  display: block; font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em;
}
.header-nav { display: flex; align-items: center; gap: 0.75rem; }
.header-nav a { color: var(--text-secondary); font-size: 0.85rem; }
/* A nav label must never break inside itself ("Az erőmű" wrapped on a 412px Pixel
   9). */
.header-nav a, .header-nav button { white-space: nowrap; }

h1 { font-size: 1.5rem; margin: 0; letter-spacing: -0.015em; }
.tagline { margin: 0; color: var(--text-secondary); font-size: 0.95rem; }

/* -- page intro & breadcrumb -------------------------------------------- */

.page-intro { margin-top: var(--gap); }
/* No measure cap: everything else runs the full `.wrap`, and one column stopping
   short read as a layout fault. */
.page-intro p { color: var(--text-secondary); }
.page-intro .lede { font-size: 1.02rem; }

.breadcrumb {
  display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: baseline;
  margin-top: var(--gap); font-size: 0.82rem; color: var(--text-muted);
}
.breadcrumb a { color: var(--text-secondary); }

.ghost-button {
  font: inherit; font-size: 0.85rem;
  color: var(--text-secondary); background: transparent;
  border: 1px solid var(--hairline); border-radius: 999px;
  padding: 0.3rem 0.85rem; cursor: pointer;
}
/* A control, so not underlined; `.prose` is where the underline comes from. */
.ghost-button { text-decoration: none; }
.ghost-button:hover { background: var(--plane); color: var(--text-primary); }
/* A glyph, so a square button (messages.THEME_TOGGLE). */
#theme-toggle { padding-inline: 0.5rem; font-size: 1rem; line-height: 1.2; }
.ghost-button[aria-pressed="true"] {
  background: var(--text-primary); color: var(--surface); border-color: transparent;
}

/* -- notifications ------------------------------------------------------- */

/* The permission switch and its sentence: a line under a hairline inside the block
   tiles' card, not a box of its own. web/js/notify.js unhides it; `hidden` loses to
   any `display` rule, so there is none on the hidden state. */
.notify {
  margin: 0.9rem 0 0; padding-top: 0.7rem;
  border-top: 1px solid var(--hairline);
}
/* The promise, at the size of the note above it: this is a control, not content. */
.notify > p {
  margin: 0 0 0.6rem; font-size: 0.8rem; color: var(--text-secondary);
}
/* A link-styled `<button>`: a `.ghost-button` in the tile card read as a fifth tile. */
.notify-switch {
  font: inherit; color: var(--text-primary); background: none;
  border: 0; padding: 0; margin: 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 0.15em;
}
.notify-switch:hover { color: var(--series-1); }
/* Keyboard focus has to survive the button's chrome being stripped off. */
.notify-switch:focus-visible { outline: 2px solid var(--series-1); outline-offset: 2px; }

/* Gone while empty: a reserved line only moved the card's bottom edge on every load. */
.notify-status {
  margin: 0.5rem 0 0;
  font-size: 0.85rem; color: var(--text-secondary);
}
.notify-status:empty { display: none; }

/* -- /labor -------------------------------------------------------------- */

/* Deliberately plain: one person, one phone, reading an answer. */
.labor-state {
  display: grid; grid-template-columns: auto 1fr;
  gap: 0.35rem 1rem; margin: 0;
}
.labor-state dt { color: var(--text-secondary); }
.labor-state dd { margin: 0; font-variant-numeric: tabular-nums; }
/* Unwrapped and unedited, however long the endpoint. */
.labor-state dd, .labor-raw { overflow-wrap: anywhere; }
.labor-raw {
  margin: 0; padding: 0.6rem; overflow-x: auto;
  background: var(--plane); border-radius: 6px;
  font-size: 0.75rem; white-space: pre-wrap;
}
.labor-topic + .labor-topic { margin-top: calc(var(--gap) * 1.5); }
/* Here the switch is its own block: no rule above, no smaller type. */
.labor-topic .notify { margin-top: 0.6rem; padding-top: 0; border-top: none; }
.labor-topic .notify > p { font-size: inherit; color: inherit; }
.labor-topic h3 { margin-bottom: 0.2rem; }
/* A word, not a colour: "nobody else can see this yet" has no scale. */
.labor-stage {
  font-size: 0.75rem; font-weight: normal; color: var(--text-muted);
  border: 1px solid var(--hairline); border-radius: 999px;
  padding: 0.05rem 0.5rem; vertical-align: middle;
}

/* -- verdict ------------------------------------------------------------ */

/* -- the reader's own gauge ---------------------------------------------- */

/* Quieter than the verdict: a way out of the page, not a claim. One line in both
   states, because the label is swapped after paint; that is a copy constraint,
   measured in `messages.py`. */
.nearby { margin: var(--gap) 0 0; }

/* A pill (`.ghost-button`'s shape), not a sentence: as bare prose between the lede
   and the verdict it read as a stray footnote. */
.nearby-link {
  display: inline-block;
  font-size: 0.85rem;
  color: var(--text-secondary); text-decoration: none;
  border: 1px solid var(--hairline); border-radius: 999px;
  padding: 0.3rem 0.85rem;
}
.nearby-link:hover { background: var(--plane); color: var(--text-primary); }
.nearby-link::after { content: " →"; color: var(--text-muted); }

.verdict {
  margin-top: var(--gap);
  padding: 1.1rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  border-left: 3px solid var(--status-serious);
}
.verdict[data-level="calm"]     { border-left-color: var(--status-good); }
.verdict[data-level="watch"]    { border-left-color: var(--status-warning); }
.verdict[data-level="strained"] { border-left-color: var(--status-serious); }
.verdict[data-level="critical"] { border-left-color: var(--status-critical); }
/* No measurement is not a severity: the default `serious` border would read as an
   alarm. */
.verdict[data-level="unknown"]  { border-left-color: var(--hairline); }

/* The front page's whole-river card (#56): no plant status to colour it. */
.verdict[data-level="river"]    { border-left-color: var(--series-1); }
.plant-line { margin: 0.6rem 0 0; font-size: 0.9rem; color: var(--text-secondary); }

/* The reader's nearest gauge, marked by web/js/nearby.js wherever a link names it:
   bold instead of a pill of its own (the pill went unclicked, #31). */
a.is-near { font-weight: 700; }
.tile:has(a.is-near) { border-color: var(--series-1); }
/* A linked tile is one tap target: the label's link stretches over the whole tile
   (`.tile` is its containing block; nothing inside is positioned). Its text stays the
   gauge name, which is what a screen reader announces. `markup.Tile` refuses a hint
   on a linked tile, which the overlay would cover. */
.tile-label a { color: inherit; text-decoration: none; }
.tile-label a::after { content: ""; position: absolute; inset: 0;
  border-radius: var(--radius); }
.tile:has(.tile-label a):hover { border-color: var(--text-muted); }
/* The only sign on a phone that the tile opens (they were not links before #56). In
   the label row, so it costs no height above the fold; empty alt text keeps it out of
   the link's announced name. */
.tile-label:has(a)::after { content: "›" / ""; margin-left: auto;
  font-size: 1.25rem; line-height: 0.6; color: var(--text-muted); }
.tile:has(.tile-label a):hover .tile-label::after { color: var(--text-primary); }
.tile:has(.tile-label a):active { border-color: var(--text-muted);
  background: var(--hairline); }
.tile:has(.tile-label a:focus-visible) { outline: 2px solid var(--series-1);
  outline-offset: 2px; }
.tile-label a:focus-visible { outline: none; }

.verdict-text { margin: 0; font-size: 1.05rem; }
/* The morning sentence's dateline (collector/headline.py), on the front page's verdict
   and a gauge page's `.notable`: the sentence itself names no day. */
.headline-when {
  display: block;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.verdict-meta { margin: 0.4rem 0 0; font-size: 0.85rem; color: var(--text-muted); }
.verdict-badge { font-weight: 600; }
/* The badge links to /paksi-atomeromu at no height cost (a link line pushed the
   first tile off a Pixel 8a's first screen). The text keeps the sentence colour, so
   the underline carries the affordance, in the link colour: at `--axis` it was
   1.75:1 against 4.3:1 for every other link. */
a.verdict-badge {
  color: inherit;
  text-decoration-color: var(--series-1);
  text-underline-offset: 0.18em;
}
/* Atomic, so the underline stops at the status icon. */
a.verdict-badge::before { display: inline-block; margin-right: 0.3em; }
a.verdict-badge:hover, a.verdict-badge:focus-visible { text-decoration-color: currentColor; }
.verdict-badge::before { content: attr(data-icon) " "; }

/* -- the day's one notable thing ---------------------------------------- */

/* Quieter than the verdict: no status colour, icon or fill. Rarity (about one day in
   thirteen) is the emphasis; an alert style would make a turn of the river a
   warning. */
.notable {
  position: relative;               /* containing block for its hint note */
  margin: 0.75rem 0 0;
  padding-left: 0.85rem;
  border-left: 2px solid var(--series-1);
  font-size: 0.95rem;
}

/* -- stat tiles --------------------------------------------------------- */

.tiles {
  display: grid; gap: 0.75rem; margin-top: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
}
/* `position: relative`: the containing block for a hint note opened inside
   (`.hint-note`). */
.tile {
  position: relative;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius); padding: 0.9rem 1rem;
}
.tile-label {
  display: flex; align-items: center; gap: 0.3rem;
  font-size: 0.78rem; color: var(--text-muted); margin: 0 0 0.15rem;
}

/* -- the tile's fill ----------------------------------------------------- */

/* One reading in its range, as a height (`collector/meter.py`), out of flow;
   geometry as SVG attributes because `style-src 'self'`, and
   `preserveAspectRatio="none"` stretches the 100x100 viewBox. Both rules below broke
   `.hint-note` when done the obvious way:

   * Clipped by `clip-path` on the fill, **not** `overflow: hidden` on the tile: the
     note
     hangs below the card and would be hidden.
   * `z-index: -1` under an `isolation: isolate` tile, not text lifted in a
     positioned
     wrapper, which would become the note's containing block. */
.tile-filled { isolation: isolate; }
.tile-meter {
  position: absolute; inset: 0; width: 100%; height: 100%; z-index: -1;
  display: block;
  clip-path: inset(0 round var(--radius));
}
.tile-meter-fill { fill: var(--fill-water); }
/* Zero output is marked, or a blank tile looks like a missing meter. */
.tile-meter-floor { fill: var(--status-critical); }

/* The departure pair: height says how far, colour only which way; cool below, warm
   above, as quiet as the water tint. */
.tile-meter[data-tone="cool"] .tile-meter-fill { fill: var(--fill-water); }
.tile-meter[data-tone="warm"] .tile-meter-fill { fill: var(--fill-warm); }

.tile-meter[data-state="calm"]     .tile-meter-fill { fill: var(--fill-calm); }
.tile-meter[data-state="watch"]    .tile-meter-fill { fill: var(--fill-watch); }
.tile-meter[data-state="strained"] .tile-meter-fill { fill: var(--fill-strained); }
.tile-meter[data-state="critical"] .tile-meter-fill { fill: var(--fill-critical); }

/* `--text-muted` is 3.50:1 on the surface and ~3.0 on these fills, so a filled tile
   lifts its quiet text one step. */
.tile-filled .tile-label,
.tile-filled .tile-note { color: var(--text-secondary); }

/* Without hue the fill goes; the number and its note are text. */
@media (forced-colors: active) {
  .tile-meter { display: none; }
}

/* -- the `i` that opens one sentence ------------------------------------- */

/* One affordance for a tile's colour legend and the notable sentence's basis: a
   short claim does not drag an explanation of its own length. One renderer,
   `markup.hint`. */
/* Not a positioning context: the note anchors to the `i`'s box (`.hint-note`). */
.hint { display: inline-block; vertical-align: middle; }
.hint > summary {
  list-style: none; cursor: help;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  border: 1px solid var(--axis); color: var(--text-muted);
  font-size: 0.68rem; font-style: italic; line-height: 1;
}
/* Safari still needs the vendor pseudo-element to drop the disclosure triangle. */
.hint > summary::-webkit-details-marker { display: none; }
.hint > summary:hover { color: var(--text-primary); border-color: var(--text-muted); }
.hint > summary:focus-visible { outline: 2px solid var(--series-1); outline-offset: 2px; }

/* The glyph is 16.8px; a finger must land within ±8px of its centre (measured
   2026-08-13, after a reader's tap did nothing; a miss is silent). The hit area
   grows invisibly to 44px, the circle does not.

   **Not behind `@media (pointer: coarse)`**: headless Chrome never matches it, so
   the gated version could only ship on reasoning, which is what shipped the dead
   `i`. Cost: the `help` cursor within 22px of the glyph.

   `position: relative` is safe only because `.hint-note` is the summary's sibling,
   not its child. */
.hint > summary { position: relative; }
.hint > summary::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}

/* Out of flow, so opening it moves nothing in the grid. Anchored to its box
   (`.tile`, `.notable`), edge to edge and hanging below: pinned to the left edge it
   overflowed the right on a phone, pinned to the `i` it overflowed the left. */
/* `.tile-filled`'s `isolation` makes each tile a stacking context that traps the
   note's `z-index`, so later tiles painted over it (measured 2026-08-13: the topmost
   element was the next tile's `svg.tile-meter`). Only lifting the trapping tile
   frees it, and only `:has()` can name "the tile with an open disclosure". Without
   `:has()` it degrades to that bug, nothing worse; the beacon stores no user agent,
   so support cannot be claimed from our own traffic. */
.tile:has(details.hint[open]) { z-index: 3; }

.hint-note {
  position: absolute; z-index: 2; top: 100%; left: 0; right: 0;
  margin: 0.3rem 0 0; padding: 0.6rem 0.7rem;
  background: var(--plane); color: var(--text-secondary);
  border: 1px solid var(--hairline); border-radius: var(--radius);
  font-size: 0.78rem; line-height: 1.4; font-style: normal;
}

.tile-value { font-size: 1.85rem; font-weight: 600; margin: 0; letter-spacing: -0.02em; }
.tile-unit { font-size: 0.95rem; font-weight: 400; color: var(--text-secondary); }
.tile-sub { font-size: 0.8rem; color: var(--text-secondary); margin: 0.15rem 0 0; }
/* Enough gap that "▼−4" does not run together. */
.tile-arrow { font-size: 0.7em; vertical-align: 0.1em; margin-right: 0.3em; }

/* The arrow always shows; colour only where `WaterRegime` knows the good direction.
   Green for rising water would be true in a drought and false in a flood. */
.tile-sub[data-sentiment="good"] { color: var(--status-good); }
.tile-sub[data-sentiment="bad"]  { color: var(--status-critical); }

.tile-note { font-size: 0.78rem; color: var(--text-muted); margin: 0.15rem 0 0; }

/* A sibling of the tile grid: as a grid item it kept the trailing `auto-fit` track
   from collapsing. */
.tiles-note {
  margin: 0.5rem 0 0; font-size: 0.8rem; color: var(--text-muted);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* -- controls ----------------------------------------------------------- */

.range-buttons { display: flex; gap: 0.4rem; flex-wrap: wrap; }

/* -- cards & charts ----------------------------------------------------- */

.card {
  margin-top: var(--gap); padding: 1rem 1.1rem 1.2rem;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius);
}
.card-grid {
  display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
}
.card-grid .card { margin-top: var(--gap); }
.card-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; margin-bottom: 0.5rem;
}
.card-head h2 { font-size: 1rem; margin: 0; }
.card-note { margin: 0.2rem 0 0; font-size: 0.82rem; color: var(--text-muted); }
/* Air between a table and its note (hairline, scrollbar, then text). **The `.card`
   is load-bearing**: `.table-view + p` ties in specificity with the later `.prose
   p`, whose margin silently won. */
.card .table-view + p { margin-top: 1.5rem; }

/* The chart arrives empty: its height is reserved against layout shift, then
   released, since a bar chart is as tall as its rows. `position: relative` holds the
   tooltip; without it the tooltip lands at the top of the document. */
/* Touch: a tap's readout vanished as the drag became a selection of the axis labels.
   `user-select` stops that, `-webkit-touch-callout` the copy menu, and
   `touch-action: pan-y pinch-zoom` keeps vertical scroll and zoom for the reader
   while sideways drags scrub the axis (`charts.js:bindReadout`). */
.chart {
  margin: 0; position: relative; min-height: 220px;
  touch-action: pan-y pinch-zoom;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent;
}
.card > .chart { min-height: 320px; }
.card-grid .chart { min-height: 220px; }
.chart[data-drawn="true"] { min-height: 0; }
/* Panels of one stacked figure sharing an x-domain: no gap, only the first reserved. */
/* The range row inside a card, above the panels it governs. */
.controls-inline {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem;
  margin: 0 0 0.5rem;
}

/* On a phone the row scrolls sideways rather than wrapping eight chips into three
   rows; `range-buttons.js:revealSelected` brings the selected chip into view. Chips,
   not a `<select>`: the beacon counts `.range-buttons button` clicks. */
@media (max-width: 34rem) {
  /* Below this width the nav gets its own row: at 412px brand, three labels and the
     theme button need ~394px of 380. */
  .header-row { flex-wrap: wrap; row-gap: 0.6rem; }
  .header-row > .brand { flex: 1 1 100%; }
  .header-row > .header-nav { flex: 1 1 100%; justify-content: flex-start; }

  .controls-inline { flex-wrap: nowrap; }
  /* `min-width: 0` lets the flex item shrink and scroll instead of overflowing. */
  .controls-inline .range-buttons {
    flex: 1 1 auto; min-width: 0; flex-wrap: nowrap; overflow-x: auto;
    scrollbar-width: thin; padding-bottom: 0.15rem;
  }
  .controls-inline .range-buttons > button { flex: 0 0 auto; }
}

.card > .chart-panel { min-height: 0; margin-top: -0.25rem; }
.card > .chart-panel:first-of-type { min-height: 130px; margin-top: 0; }
.chart-panel .legend { margin-top: 0.15rem; }
.chart svg { display: block; width: 100%; height: auto; }
.chart text { font-family: inherit; }
/* The pointer-catching plot rect; here because a style attribute would need
   unsafe-inline. */
.chart svg .hover-layer { cursor: crosshair; }

/* -- the span chart (server-drawn, one bar per year) --------------------- */

/* Server-drawn (collector/spanchart.py), deliberately without a `viewBox`:
   horizontal geometry in percentages, vertical in pixels, so the text stays one size
   everywhere. */
/* What the scripted readout needs from its host, as `.chart` gives the drawn charts:
   a positioned box, no stolen drag, no text selection. On every `.span-chart`:
   without `position` the bubble was placed against an ancestor and landed
   off-screen. */
.span-chart {
  margin: 0.9rem 0 0.3rem;
  position: relative; touch-action: pan-y pinch-zoom;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.span-chart svg { display: block; width: 100%; }
.span-chart text { font-family: inherit; font-size: 0.72rem; fill: var(--text-muted); }
.span-chart .span-name { fill: var(--text-secondary); font-variant-numeric: tabular-nums; }
/* The optional second line of a row label, within `ROW_HEIGHT`. */
.span-chart .span-name-sub { font-size: 0.62rem; fill: var(--text-muted);
                             font-variant-numeric: tabular-nums; }
/* The reading, and under it the noise it has to beat. */
.span-chart .span-value { fill: var(--text-secondary); font-variant-numeric: tabular-nums; }
.span-chart .span-value-sub { font-size: 0.62rem; fill: var(--text-muted);
                              font-variant-numeric: tabular-nums; }
.span-chart .span-tick { font-variant-numeric: tabular-nums; }
/* The axis unit, recessive against the last tick's number. */
.span-chart .span-unit { font-size: 0.62rem; fill: var(--text-muted); }
.span-chart .span-bar { fill: var(--series-1); }
/* A darker step of the bar's hue, ringed in the card colour. */
.span-chart .span-mid { fill: var(--span-mid); stroke: var(--surface); stroke-width: 1.5; }
/* Transparent, not `fill: none`, which takes no pointer events. */
.span-chart .span-mid-hit { fill: transparent; cursor: help; }

/* The front page's river schematic (collector/rivermap.py): the line in the series
   colour, dots hollow unless under the catalogue's record, one row per gauge. */
.river-schema { margin: 0.5rem 0 0; }
.river-schema svg { display: block; overflow: visible; }
.river-schema .schema-line { fill: none; stroke: var(--series-1); stroke-width: 4;
  stroke-linejoin: round; stroke-linecap: round; }
.river-schema .schema-dot { fill: var(--surface); stroke: var(--series-1);
  stroke-width: 2.5; }
.river-schema .schema-below { fill: var(--status-critical);
  stroke: var(--status-critical); }
.river-schema .schema-hit { fill: transparent; }
.river-schema a:hover .schema-hit,
.river-schema a:focus-visible .schema-hit { fill: var(--plane); }
.river-schema text { font-family: inherit; font-size: 0.85rem;
  fill: var(--text-primary); font-variant-numeric: tabular-nums; }
.river-schema .schema-change { font-size: 0.78rem; fill: var(--text-secondary); }
.river-schema .schema-change[data-sentiment="good"] { fill: var(--status-good); }
.river-schema .schema-change[data-sentiment="bad"] { fill: var(--status-critical); }
.river-schema .schema-reach { fill: none; stroke: var(--text-muted); stroke-width: 1; }
.river-schema .schema-reach-label { font-size: 0.62rem; fill: var(--text-muted);
  letter-spacing: 0.02em; }
.river-schema a.is-near .schema-name { font-weight: 700; }
.river-schema a.is-near .schema-dot { stroke-width: 4; }
/* The front page's profile (#56): the band is only the reference (the middle half of
   each gauge's days), so it recedes; the reading is the mark. */
.river-profile .span-bar { fill: var(--gridline); }
.river-profile .span-mid { fill: var(--series-1); r: 5px; }
/* The same mark as a ring, for a second reading of the row above; filled with the
   surface, since a transparent ring on the bar was too faint. */
.span-chart .span-open { fill: var(--surface); stroke: var(--span-mid);
                         stroke-width: 2; }
/* The departure figure: the band is a wash of the bar hue, the line is ink (the
   reading judged against the series). */
.span-chart .series-band { fill: var(--series-1); fill-opacity: 0.22; }
.span-chart .series-line { fill: none; stroke: var(--text-primary); stroke-width: 2;
                           stroke-linejoin: round; }
/* The band's centre, in its hue: not a second measurement. */
.span-chart .series-usual { fill: none; stroke: var(--series-1); stroke-width: 1.5;
                            stroke-linejoin: round; }
/* Transparent rather than `fill: none` (pointer events). */
.span-chart .series-hit { fill: transparent; cursor: crosshair; }
.span-chart .series-crosshair { stroke: var(--axis); stroke-width: 1; }
/* The legend, as markup under the drawing so it wraps. */
.chart-key { margin: 0.35rem 0 0; font-size: 0.82rem; color: var(--text-muted); }
.chart-key ul { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem;
                margin: 0; padding: 0; list-style: none; }
.chart-key li { display: flex; align-items: center; gap: 0.35rem; }
.key-swatch { width: 1.1rem; height: 0.65rem; border-radius: 2px; flex: none; }
/* Each swatch is the mark itself: weight differs as well as hue. */
.key-line { height: 0; border-top: 2px solid var(--text-primary); }
.key-usual { height: 0; border-top: 1.5px solid var(--series-1); }
.key-band { background: var(--series-1); opacity: 0.22; }
/* The sluice chart's marks, drawn as in the figure. */
.key-noise { background: var(--series-1); }
.key-step { width: 0.65rem; border-radius: 50%; background: var(--span-mid); }
.key-shifted { width: 0.65rem; border-radius: 50%; background: var(--surface);
               box-shadow: inset 0 0 0 2px var(--span-mid); }
/* The reach schematic: the sill drawn across the river line. The note is its own
   line a size down: SVG text does not wrap, and ran off a 340px card. */
.reach-chart .reach-river { stroke: var(--series-1); stroke-width: 3; opacity: 0.3; }
.reach-chart .reach-gauge { fill: var(--surface); stroke: var(--span-mid); stroke-width: 2; }
.reach-chart .reach-works { stroke: var(--text-primary); stroke-width: 4; }
.reach-chart .reach-name { fill: var(--text-primary); font-weight: 600; }
.reach-chart .reach-note { font-size: 0.66rem; fill: var(--text-muted); }

.span-chart .span-grid { stroke: var(--gridline); stroke-width: 1; }
.span-chart .span-axis { stroke: var(--axis); stroke-width: 1; }
/* The current reading, in ink: the second series colour means "forecast". */
.span-chart .span-marker { stroke: var(--text-primary); stroke-width: 1.5; }
.span-chart .span-marker-label { fill: var(--text-primary); font-weight: 600; }

/* The trail chart (`spanchart.render_trails`): dots, and a faint connector grouping
   them. */
.trail-chart .trail-line { stroke: var(--axis); stroke-width: 2; stroke-linecap: round; }
.trail-chart .trail-step { stroke: var(--surface); stroke-width: 1.5; }
/* Size, the ramp's second channel, rides on `r` and survives `forced-colors`
   (STEP_RADII). */

/* One horizon at a time with no script: radios before the figure, sibling selectors.
   Visually hidden but focusable (`display: none` would drop the control for a
   keyboard). Four ranks for `outlook.HORIZON_DAYS`' three;
   tests/test_forecast_page.py reads this file. Without CSS every horizon shows. */
.horizon-picker { margin: 0.9rem 0 0.3rem; }
.horizon-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.horizon-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.horizon-chip {
  padding: 0.25rem 0.7rem; border: 1px solid var(--hairline); border-radius: 999px;
  font-size: 0.8rem; color: var(--text-secondary); cursor: pointer;
  background: var(--surface); white-space: nowrap;
}
.horizon-chip:hover { border-color: var(--axis); }
.trail-chart .trail-horizon { display: none; }
#horizon-0:checked ~ .horizon-chips .horizon-chip[for="horizon-0"],
#horizon-1:checked ~ .horizon-chips .horizon-chip[for="horizon-1"],
#horizon-2:checked ~ .horizon-chips .horizon-chip[for="horizon-2"],
#horizon-3:checked ~ .horizon-chips .horizon-chip[for="horizon-3"] {
  background: var(--text-primary); border-color: var(--text-primary);
  color: var(--surface); font-weight: 600;
}
#horizon-0:focus-visible ~ .horizon-chips .horizon-chip[for="horizon-0"],
#horizon-1:focus-visible ~ .horizon-chips .horizon-chip[for="horizon-1"],
#horizon-2:focus-visible ~ .horizon-chips .horizon-chip[for="horizon-2"],
#horizon-3:focus-visible ~ .horizon-chips .horizon-chip[for="horizon-3"] {
  outline: 2px solid var(--series-1); outline-offset: 2px;
}
#horizon-0:checked ~ .trail-chart .trail-horizon[data-rank="0"],
#horizon-1:checked ~ .trail-chart .trail-horizon[data-rank="1"],
#horizon-2:checked ~ .trail-chart .trail-horizon[data-rank="2"],
#horizon-3:checked ~ .trail-chart .trail-horizon[data-rank="3"] { display: block; }
/* The published tolerance, the dot's hue at low opacity. */
.trail-chart .trail-band { fill: var(--trail-2); fill-opacity: 0.35; }
.trail-chart .trail-horizon[data-rank="0"] .trail-band { fill: var(--trail-1); }
.trail-chart .trail-horizon[data-rank="2"] .trail-band { fill: var(--trail-3); }
.trail-chart .trail-horizon[data-rank="0"] .trail-step { fill: var(--trail-1); }
.trail-chart .trail-horizon[data-rank="1"] .trail-step { fill: var(--trail-2); }
.trail-chart .trail-horizon[data-rank="2"] .trail-step { fill: var(--trail-3); }

/* Level, then movement, with the front-page tile's arrow. */
.cell-value { display: block; }
.cell-change { display: block; font-size: 0.82em; color: var(--text-muted);
               font-variant-numeric: tabular-nums; }
.cell-arrow { margin-right: 0.15em; }

.legend { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0.5rem 0 0; font-size: 0.8rem;
          color: var(--text-secondary); list-style: none; padding: 0; }
.legend li { display: flex; align-items: center; gap: 0.4rem; }
.legend .swatch { width: 0.85rem; height: 0.2rem; border-radius: 2px; flex: none; }
.legend .swatch[data-dashed="true"] {
  background: repeating-linear-gradient(90deg, currentColor 0 4px, transparent 4px 7px);
}

/* `top`/`left` are the origin; `charts.js:showTooltip` moves the bubble with
   `transform`, since `left`/`top` is a layout shift per pointer move. Only `opacity`
   animates. */
.tooltip {
  position: absolute; top: 0; left: 0; pointer-events: none; z-index: 2;
  background: var(--surface); color: var(--text-primary);
  border: 1px solid var(--hairline); border-radius: 8px;
  padding: 0.45rem 0.6rem; font-size: 0.8rem;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
  white-space: nowrap; opacity: 0; transition: opacity 120ms;
}
.tooltip[data-visible="true"] { opacity: 1; }
.tooltip dl { margin: 0.25rem 0 0; display: grid; grid-template-columns: auto auto;
              gap: 0.1rem 0.6rem; }
.tooltip dt { display: flex; align-items: center; gap: 0.35rem; color: var(--text-secondary); }
.tooltip dd { margin: 0; font-variant-numeric: tabular-nums; text-align: right; }
.tooltip .swatch { width: 0.6rem; height: 0.6rem; border-radius: 2px; flex: none; }
/* A sentence in the `nowrap` bubble needs a width to fold inside, or it runs off a
   phone. */
.tooltip .tip-note {
  margin: 0.45rem 0 0; padding-top: 0.4rem;
  border-top: 1px solid var(--hairline);
  white-space: normal; max-width: 18rem;
  color: var(--text-secondary); line-height: 1.35;
}
.tooltip .tip-note + .tip-note { border-top: 0; padding-top: 0.2rem; }

/* -- tables (the WCAG-clean twin of every chart) ------------------------ */

/* Padding, not margin on the note: the horizontal scrollbar is painted inside this
   box. */
/* `position: relative` makes this the containing block, so `overflow-x` clips
   absolute descendants. On `/elorejelzes` a `.visually-hidden` span escaped and made
   the page 767px wide on a 500px viewport. */
.table-view { position: relative; margin-top: 0.75rem; overflow-x: auto; padding-bottom: 0.35rem; }
.table-view table { border-collapse: collapse; width: 100%; font-size: 0.82rem; }
.table-view caption { text-align: left; color: var(--text-muted); padding-bottom: 0.4rem; }
.table-view th, .table-view td {
  text-align: right; padding: 0.3rem 0.55rem; border-bottom: 1px solid var(--gridline);
  font-variant-numeric: tabular-nums;
}
/* The first column is a name, never prose: `nowrap`. Measured at 412px: 62 → 105px,
   absorbed by the scroll; the /paks event table lost 236px of height. */
.table-view th:first-child, .table-view td:first-child {
  text-align: left; font-variant-numeric: normal; white-space: nowrap;
}
.table-view thead th { color: var(--text-muted); font-weight: 500; white-space: nowrap; }
.table-view tbody tr:hover { background: var(--plane); }
/* A table too wide to scroll well becomes one card per row on a phone, each cell
   under its column's name (`markup.table(labelled=True)`). The head stays for screen
   readers. Measured at 412px on /fenekkuszob-hatasa: 884px of table in a 343px box,
   two prose columns out of sight, rows up to 345px of mostly nothing (#50). It fits
   from ~953px (884 + the page's margins), hence 60rem rather than the phone's 46. */
@media (width < 60rem) {
  .table-view.stacked table, .table-view.stacked tbody, .table-view.stacked tr,
  .table-view.stacked td { display: block; }
  .table-view.stacked thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .table-view.stacked tr { padding: 0.6rem 0; border-bottom: 1px solid var(--gridline); }
  .table-view.stacked tbody tr:hover { background: none; }
  .table-view.stacked td {
    border: 0; padding: 0.15rem 0; text-align: left; font-variant-numeric: normal;
    white-space: normal;
  }
  .table-view.stacked td[data-label]::before {
    content: attr(data-label); display: block; font-size: 0.75rem;
    color: var(--text-muted);
  }
  /* The date heads the card; its label would only repeat what it plainly is. */
  .table-view.stacked td:first-child { font-weight: 600; }
  .table-view.stacked td:first-child::before { content: none; }
}
/* Two tables in one box: space them, or they read as one. */
.table-view table + table { margin-top: 1.5rem; }
.flag { font-size: 0.75rem; color: var(--status-critical); white-space: nowrap; }
/* The saved copy beside a citation: the quietest thing in the cell. */
a.archived {
  font-size: 0.75rem; color: var(--text-muted);
  white-space: nowrap; margin-left: 0.15rem;
}

/* -- shaded cells (the heatmap) ----------------------------------------- */

/* Each cell shaded by its departure (`collector/heat.py`, one scale per column, as
   the caption says). The surface-coloured border is a separator, not a gridline. */
.table-view td[data-heat] {
  border: 2px solid var(--surface);
  background: var(--heat-bg); color: var(--heat-ink);
}
.table-view td[data-heat="1"] { --heat-bg: var(--heat-1); --heat-ink: var(--heat-ink-1); }
.table-view td[data-heat="2"] { --heat-bg: var(--heat-2); --heat-ink: var(--heat-ink-2); }
.table-view td[data-heat="3"] { --heat-bg: var(--heat-3); --heat-ink: var(--heat-ink-3); }
.table-view td[data-heat="4"] { --heat-bg: var(--heat-4); --heat-ink: var(--heat-ink-4); }
.table-view td[data-heat="5"] { --heat-bg: var(--heat-5); --heat-ink: var(--heat-ink-5); }
.table-view td[data-heat="6"] { --heat-bg: var(--heat-6); --heat-ink: var(--heat-ink-6); }
.table-view td[data-heat="7"] { --heat-bg: var(--heat-7); --heat-ink: var(--heat-ink-7); }
/* Forced colours drop the fills, so borders separate the cells. */
@media (forced-colors: active) {
  .table-view td[data-heat] { border-color: canvastext; }
}

/* -- prose, FAQ, neighbours --------------------------------------------- */

.prose h2, .faq h2 { font-size: 1.05rem; margin: 0 0 0.6rem; }
.prose p, .prose li { color: var(--text-secondary); }
.prose p { margin: 0 0 0.7rem; }
.prose ul { margin: 0 0 0.7rem; padding-left: 1.1rem; }
.prose li { margin-bottom: 0.35rem; }
.prose strong, .faq strong { color: var(--text-primary); font-weight: 600; }
/* `min-width` here: two columns are the enhancement, one column the default. */
.reach-layout { display: grid; gap: 0 1.5rem; }
@media (min-width: 46rem) {
  /* The drawing needs about 17rem and no more. */
  .reach-layout {
    grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
    /* Centred: the drawing is taller than its text. */
    align-items: center;
  }
  .reach-layout .card-note { font-size: 0.88rem; }
  .reach-layout .card-note + .card-note { margin-top: 0.6rem; }
}

/* The summary box: `.notable`'s left rule, no tinted panel, since every sentence in
   it repeats one below. */
.summary { border-left: 3px solid var(--series-1); }
/* Underlined only on hover: four underlined questions read as navigation. */
.summary-link { text-decoration: none; }
.summary-link:hover { text-decoration: underline; }
.summary-link strong { color: inherit; }
.prose.summary p:last-child { margin-bottom: 0; }

/* Written as a descendant to out-specify `.prose p`'s muted colour. */
.prose .finding-answer {
  color: var(--text-primary); font-size: 0.98rem; font-weight: 600;
  line-height: 1.45; margin: 0 0 0.75rem;
}
.prose code, .table-view code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85em; background: var(--plane); padding: 0.1rem 0.35rem;
  border-radius: 4px;
}

.faq-item { border-top: 1px solid var(--hairline); padding-top: 0.75rem;
            margin-top: 0.75rem; }
.faq-item:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.faq-item h3 { font-size: 0.95rem; margin: 0 0 0.25rem; }
.faq-item p { margin: 0; color: var(--text-secondary); }

.neighbours { list-style: none; margin: 0; padding: 0;
              display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem;
              font-size: 0.9rem; }
.neighbours li { display: flex; gap: 0.4rem; align-items: baseline; }
.neighbours span:first-child { color: var(--text-muted); }
.muted { color: var(--text-muted); font-variant-numeric: tabular-nums; }

a { color: var(--series-1); }
a:hover { color: var(--text-primary); }
.table-view a { color: inherit; text-decoration-color: var(--axis); }
.table-view a:hover { color: var(--series-1); }

/* -- footer ------------------------------------------------------------- */

.page-footer {
  margin-top: 2.5rem; padding: 1.5rem 0 2.5rem;
  border-top: 1px solid var(--hairline); background: var(--surface);
  font-size: 0.82rem; color: var(--text-secondary);
}
/* The breadcrumb closes every subpage; the full gap left it floating (#57). */
main:has(> .breadcrumb:last-child) + .page-footer { margin-top: var(--gap); }
.disclaimer { margin: 0 0 0.75rem; }
.attribution { margin: 0; padding: 0; list-style: none; color: var(--text-muted); }
/* Six source links. In the link colour the footer was the loudest block on the page;
   at this size the underline alone says link. */
.attribution a { color: inherit; text-decoration: underline; }
.attribution a:hover { color: var(--text-primary); }
.generated { margin: 0.75rem 0 0; color: var(--text-muted); }
/* Underlined: at 0.82rem against muted text, colour alone did not mark a link. */
.footer-links { margin: 0.75rem 0 0; }
.footer-links a { color: var(--text-secondary); text-decoration: underline; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}
