/**
 * conditions.css · the "Growing conditions, right now" module (design.md §7.7).
 *
 * Loaded after app.css so this file owns everything inside
 * [data-mount="conditions"] and nothing outside it. Every colour resolves to a
 * token; the only derived values are color-mix()es of tokens (see --risk-ink).
 *
 * Two rules from the dataviz skill shape the geometry:
 *   - the bar is a thin mark on a faint full-height track, 4px rounded at the
 *     data end and square at the baseline, so magnitude reads from a single
 *     zero line;
 *   - colour is never the only channel, so each column reserves fixed rows for
 *     the risk WORD, the hour VALUE and the condensation marker. The rows keep
 *     their height when empty, which is what keeps the seven baselines aligned.
 *
 * Order: shell · panel anatomy (head row, stamp, hidden panels, the all-empty
 * line) · picker and badge · strip · columns · tooltip · legend · humidity
 * trend · humidity scale · growth conditions field · ventilation planner ·
 * table.
 *
 * ======================================================================
 *  TOKEN CONTRACT
 * ======================================================================
 *
 * This file consumes the custom properties below and NOTHING else from the
 * host. It defines no colours of its own; every value here either resolves to
 * one of these tokens or is a color-mix() of them.
 *
 *  SURFACES
 *   --surface-0   page ground; the scroller fade masks and the tooltip veil
 *                 blend back to it
 *   --surface-1   the card itself, and every chip, tooltip and label halo
 *   --surface-2   raised or hovered fills: pressed picker tab, table zebra
 *
 *  INK
 *   --ink-0       primary text: hero numerals, benefit headings, values
 *   --ink-1       secondary text: eyebrows, freshness stamps, the all-empty
 *                 line, how-to-read lines, captions, axes
 *   --ink-2       tertiary text: tick labels, footnotes, gridline glyphs
 *
 *  LINE AND ACCENT
 *   --line        every hairline: panel rules, table borders, chart gridlines
 *   --accent      interactive state only: active picker tab, focus rings
 *
 *  RISK PALETTE (data only, per design.md §2.2)
 *   --risk-low    low risk fills, OPEN window chips, the dry scale band
 *   --risk-watch  watch fills, CAUTION chips, the humid band
 *   --risk-high   high fills, CLOSED chips, the damp band
 *   --risk-severe severe fills, the top region of the growth field
 *
 *  RADII AND SHADOW
 *   --r-1         chips, bars, tooltips, scale marker
 *   --shadow-1    the tooltip's only elevation
 *
 *  SPACING (a 4-step rhythm; the module uses 1 to 5)
 *   --sp-1 --sp-2 --sp-3 --sp-4 --sp-5
 *
 *  TYPE
 *   --font-display  Libre Baskerville: hero numerals and the benefit headings
 *   --font-mono     IBM Plex Mono: eyebrows, labels, values, axes, legends
 *   --text-h3       the benefit heading's size, capped inside the card
 *   --text-stat     the humidity hero numeral
 *   --text-body     how-to-read lines and table cells
 *   --text-small    captions, footnotes, legend copy
 *   --text-mono     eyebrows, specimen labels, data readouts
 *   --track-mono    mono letter-spacing
 *   --track-display display letter-spacing
 *   --leading-tight headings and numerals
 *   --leading-body  prose
 *
 *  MOTION
 *   --dur-fast --dur-base --ease-out   every transition in this file
 *
 * Locals defined and consumed here, NOT part of the contract: --track-h,
 * --bar-w, --col-w, --label-room (chart geometry), --risk / --risk-ink / --band
 * (per-element hooks resolved from the risk palette above), and --fill (each
 * outlook column's own bar height, written as an inline style by the view).
 *
 * To port: map these tokens to the host design system; no other coupling exists.
 */

.cond {
  /* Locals, all composed from tokens or from the chart's own fixed geometry. */
  --track-h: 120px;
  --bar-w: 14px;
  --col-w: 4.5rem;
  --label-room: 1.5rem;              /* headroom so a 24h value never clips */

  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-4);
}

/* Every risk-bearing element resolves one local hook, so a mark and its chip
   and its table row can never drift apart. The planner speaks in window states
   rather than risk levels, so it gets its own attribute onto the same hook. */
[data-level="low"]    { --risk: var(--risk-low); }
[data-level="watch"]  { --risk: var(--risk-watch); }
[data-level="high"]   { --risk: var(--risk-high); }
[data-level="severe"] { --risk: var(--risk-severe); }

[data-status="open"]    { --risk: var(--risk-low); }
[data-status="caution"] { --risk: var(--risk-watch); }
[data-status="closed"]  { --risk: var(--risk-high); }

/* Risk words are small text, and two of the four status hues sit under 3:1 as
   ink. Nudging each hue toward --ink-0 keeps its identity and clears AA (4.5:1)
   on --surface-1 in both themes; the raw token still fills the bars. */
[data-level],
[data-status] { --risk-ink: color-mix(in oklab, var(--risk) 65%, var(--ink-0)); }

/* ====================================================================== */
/*  PANELS                                                                */
/* ====================================================================== */

/*
 * Three modules, one card. The hairline between them is the field-notebook
 * device from design.md §4: a rule, not a gap, not a box.
 */
.cond__panel {
  display: grid;
  gap: var(--sp-3);
  min-width: 0;                        /* the wide bits scroll, the page does not */
}

.cond__panel + .cond__panel {
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--line);
}

/*
 * Panel anatomy (design.md §6): eyebrow label, benefit heading, how-to-read
 * line, then the visualisation. The mono line is a paragraph rather than a
 * heading because it names the instrument; the heading below it states what the
 * reader gains, and is what the panel's aria-labelledby points at.
 *
 * The eyebrow shares its row with the panel's freshness stamp (design.md §7.7's
 * resilience policy): instrument name left, age of the reading right, one mono
 * line. It wraps rather than shrinks, so the stamp drops under the label on a
 * narrow screen instead of colliding with it.
 */
.cond__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-1) var(--sp-3);
}

.cond__eyebrow {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 400;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

/* Capped below the token's ceiling: at --text-h3's full 1.75rem this heading
   starts arguing with the section h2 two levels up the page. 1.35rem is the
   token's own floor, so the cap is a clamp of the scale, not a departure. */
.cond__benefit {
  margin: 0;
  font-family: var(--font-display);
  font-size: min(var(--text-h3), 1.35rem);
  font-weight: 700;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-display);
  color: var(--ink-0);
}

.cond__how {
  margin: 0;
  max-width: 65ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink-1);
}

/*
 * The freshness stamp. Same mono register as the eyebrow it sits beside, and
 * the same --ink-1 rather than a fainter one: at this size --ink-2 is a tick
 * label, and this is a sentence about the data's honesty.
 */
.cond__stamp {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 400;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}

/* No reading, no stamp: an empty element must not open a gap in the row. */
.cond__stamp:empty { display: none; }

/*
 * A panel with nothing honest to draw is hidden outright (design.md §7.7).
 * The [hidden] attribute alone cannot do it here: the UA rule that implements
 * it loses to this file's own display declarations, so each one is answered.
 */
.cond__panel[hidden],
.cond__live[hidden] { display: none; }

/* The all-empty state: one mono line where four instruments were, and nothing
   else. The picker above it stays, so another city is still one tap away. */
.cond__unavailable {
  margin: 0;
  padding-block: var(--sp-5);
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

.cond__unavailable:empty { display: none; }

/* The header stack is one unit, so it closes up inside the panel's own gap:
   an eyebrow belongs ON its heading, not a full step above it. */
.cond__head + .cond__benefit { margin-block-start: calc(var(--sp-1) - var(--sp-3)); }
.cond__benefit + .cond__how { margin-block-start: calc(var(--sp-2) - var(--sp-3)); }

/* ====================================================================== */
/*  CITY PICKER                                                           */
/* ====================================================================== */

/*
 * The card's top row: cities left, one liveness badge right, on the hairline
 * they share. The badge sits HERE rather than under the outlook chart because
 * it speaks for all four panels and any one of them can be hidden; a badge
 * inside a panel would only be visible when the card was already fine.
 */
.cond__top {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2) var(--sp-4);
  border-block-end: 1px solid var(--line);
}

.picker {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

.picker__btn {
  appearance: none;
  background: none;
  border: 0;
  border-block-end: 2px solid transparent;
  margin-block-end: -1px;
  padding: var(--sp-2) 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}

.picker__btn:hover { color: var(--ink-0); }

.picker__btn.is-active {
  color: var(--ink-0);
  border-block-end-color: var(--accent);
}

.picker__btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-1);
}

/* ====================================================================== */
/*  STRIP                                                                 */
/* ====================================================================== */

.strip {
  margin: 0;
  display: grid;
  gap: var(--sp-3);
}

.strip__axis {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

/* The strip scrolls inside itself; the page never scrolls sideways. */
.strip__scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  transition: opacity var(--dur-base) var(--ease-out);
}

.strip__scroll.is-pending { opacity: 0.45; }

.strip__days {
  display: flex;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding-block: var(--sp-1);          /* room for a focus ring inside the scroller */
  padding-inline: 0;
}

/* ---- one day column ---- */

.day {
  position: relative;
  flex: 1 0 var(--col-w);
  min-width: var(--col-w);
}

.day__btn {
  appearance: none;
  width: 100%;
  background: none;
  border: 0;
  padding: var(--sp-1) 0;
  border-radius: var(--r-1);
  display: grid;
  gap: var(--sp-2);
  justify-items: center;
  cursor: default;
  font: inherit;
  color: inherit;
  text-align: center;
}

.day__btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.day__level {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-0);                 /* fallback where color-mix is absent */
  color: var(--risk-ink);
}

/* The track IS the 0 to 24 scale: its floor is 0h, its ceiling is 24h. */
.day__track {
  position: relative;
  width: 100%;
  height: var(--track-h);
  margin-block-start: var(--label-room);
  background-color: var(--surface-2);
  border-radius: var(--r-1);
}

.day__fill {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  width: var(--bar-w);
  margin-inline: auto;
  height: var(--fill, 0%);
  background-color: var(--risk);
  border-start-start-radius: 4px;      /* rounded data end */
  border-start-end-radius: 4px;        /* square at the baseline */
}

/* Direct value label, riding the tip of its own fill. */
.day__value {
  position: absolute;
  inset-inline: 0;
  bottom: var(--fill, 0%);
  margin-block-end: var(--sp-1);
  font-size: var(--text-small);
  color: var(--ink-1);
  line-height: 1.1;
}

/* Reserved whether or not there is a droplet, so the weekdays stay on one line. */
.day__wet {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 10px;
}

.day__drop {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--risk);
}

.day__when {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

.day:first-child .day__when { color: var(--ink-0); }

/* ====================================================================== */
/*  TOOLTIP                                                               */
/* ====================================================================== */

/*
 * The card sits INSIDE the scroll container, overlaying the plot area, because
 * a scroll container clips anything hung above or below it. Overlaying keeps
 * the tooltip inside the strip's own box at every scroll position; the two
 * columns at each end anchor to their edge so the card never leaves that box,
 * and therefore never leaves the viewport.
 */
.tip {
  display: none;                       /* shown/hidden with display, no motion */
  position: absolute;
  z-index: 5;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 13rem;
  padding: var(--sp-3);
  text-align: start;
  background-color: var(--surface-0);
  border: 1px solid var(--line);
  border-radius: var(--r-1);
  box-shadow: var(--shadow-1);
  pointer-events: none;
}

.tip.is-start { left: 0; transform: none; }
.tip.is-end   { left: auto; right: 0; transform: none; }

.day:hover .tip,
.day:focus-within .tip { display: block; }

.tip__date {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
  margin-block-end: var(--sp-2);
  padding-block-end: var(--sp-2);
  border-block-end: 1px solid var(--line);
}

.tip__row {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  margin: 0;
}

.tip__row + .tip__row { margin-block-start: var(--sp-1); }

.tip__k {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

.tip__v {
  font-size: var(--text-small);
  color: var(--ink-0);
  font-variant-numeric: tabular-nums;
}

/* ====================================================================== */
/*  LEGEND + LIVENESS                                                     */
/* ====================================================================== */

.strip__foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-4);
  align-items: center;
  justify-content: space-between;
  padding-block-start: var(--sp-2);
  border-block-start: 1px solid var(--line);
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.legend__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

/* inline-block, not bare inline: the same dot is reused in a table cell, where
   there is no flex parent to blockify it. */
.legend__dot {
  display: inline-block;
  vertical-align: middle;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: var(--risk);
  flex: none;
}

.cond__live {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-2);
}

.cond__live.is-live,
.cond__live.is-partial,
.cond__live.is-offline { color: var(--ink-0); }

.live__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--risk-low);
  flex: none;
}

/* One badge, four feeds: a mixed answer is not a live answer. */
.cond__live.is-partial .live__dot { background-color: var(--risk-watch); }

/* ====================================================================== */
/*  HUMIDITY TREND (design.md §7.7b)                                      */
/* ====================================================================== */

.hum {
  margin: 0;
  display: grid;
  gap: var(--sp-4);
}

/* Hero and explainer sit side by side where there is room; the number leads. */
.hum__head {
  display: grid;
  gap: var(--sp-3) var(--sp-5);
  align-items: start;
}

@media (min-width: 720px) {
  .hum__head { grid-template-columns: auto minmax(0, 1fr); }
}

.hum__stat {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-stat);
  font-weight: 500;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-display);
  color: var(--ink-0);
  font-variant-numeric: tabular-nums;
}

.hum__state {
  margin: var(--sp-1) 0 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
}

.hum__explain {
  margin: 0;
  max-width: 40ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink-1);
}

/*
 * The chart scrolls inside itself below ~660px so every hit zone keeps its
 * 24px minimum target (24 readings across 640px is 26.7px each). The page
 * still never scrolls sideways.
 */
.hum__scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  transition: opacity var(--dur-base) var(--ease-out);
}

.hum__scroll.is-pending { opacity: 0.45; }

/* The overlay layer is positioned in percentages of this box, which is exactly
   the box the SVG viewBox describes: no measuring, no resize listener. */
.hum__plot {
  position: relative;
  min-width: 640px;
}

.hum__svg svg,
.chart {
  display: block;
  width: 100%;
  height: auto;
}

.chart__grid {
  stroke: var(--line);
  stroke-width: 1;
}

.chart__tick {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: var(--track-mono);
  fill: var(--ink-1);
}

.chart__stop--top {
  stop-color: color-mix(in srgb, var(--accent) 12%, transparent);
}

.chart__stop--bottom {
  stop-color: transparent;
}

.chart__area {
  fill: url(#hum-area-fill);
  stroke: none;
}

.chart__line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The one threshold that matters, direct-labelled rather than legended. */
.chart__damp {
  stroke: var(--risk-watch);
  stroke-width: 1;
  stroke-dasharray: 4 4;
}

/*
 * The dashed rule is a MARK and keeps the raw token; its label is small text,
 * so design.md 2.2 sends it through the ink formula instead (4.55:1 on
 * --surface-1 in light, 9.04:1 in dark; the raw gold would sit at 2.59:1).
 */
.chart__damp-label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: var(--track-mono);
  fill: var(--ink-0);                  /* fallback where color-mix is absent */
  fill: color-mix(in oklab, var(--risk-watch) 65%, var(--ink-0));

  /* The series crosses this label whenever the right-hand hours sit near 70%,
     so it wears the surface as a halo: the dataviz spec's 2px gap between
     overlapping marks, spelled in SVG. */
  paint-order: stroke fill;
  stroke: var(--surface-1);
  stroke-width: 3;
  stroke-linejoin: round;
}

/* 8px mark with a 2px surface ring, per the dataviz mark spec. */
.chart__marker {
  fill: var(--accent);
  stroke: var(--surface-1);            /* the card's own ground, per the 2px ring spec */
  stroke-width: 2;
}

/* ---- the probe layer: crosshair, dot, hit zones, tooltip ---- */

.hum__cross,
.hum__dot,
.hum__tip {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.hum__plot.is-probing .hum__cross,
.hum__plot.is-probing .hum__dot,
.hum__plot.is-probing .hum__tip { opacity: 1; }

.hum__cross {
  top: 8%;                              /* plot top:  20 / 250 */
  height: 82.4%;                        /* plot height: 206 / 250 */
  width: 1px;
  margin-inline-start: -0.5px;
  background-color: var(--ink-2);
}

.hum__dot {
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background-color: var(--accent);
  box-shadow: 0 0 0 2px var(--surface-1);
}

.hum__hits { position: absolute; inset: 0; }

.hum__hit {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: var(--r-1);
}

.hum__hit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Same card as the outlook tooltip, same family, same rules. */
.hum__tip {
  z-index: 5;
  top: 0;
  width: 11rem;
  padding: var(--sp-3);
  transform: translateX(-50%);
  text-align: start;
  background-color: var(--surface-0);
  border: 1px solid var(--line);
  border-radius: var(--r-1);
  box-shadow: var(--shadow-1);
}

.hum__tip.is-start { transform: none; margin-inline-start: -0.5rem; }
.hum__tip.is-end   { transform: translateX(-100%); margin-inline-start: 0.5rem; }

.hum__caption {
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--ink-1);
}

/* ---- the banded threshold scale ---- */

/*
 * A ruler for the hero number, on a 40 to 90% domain: four proportional
 * segments, the three boundary values above them, the four state words below,
 * and a marker where the latest reading lands. Decorative reinforcement, not a
 * second reading: the wrapper is aria-hidden and a spoken sentence sits beside
 * the hero instead.
 *
 * Both label rows are positioned absolutely at their target percentages rather
 * than laid out in segment-width boxes. HUMID is a five-letter word over a
 * five-point band, which at phone width is thirty pixels: only the centre of a
 * segment is a reliable anchor, never its box.
 */
.scale {
  display: grid;
  gap: var(--sp-1);
}

.scale__ticks,
.scale__words {
  position: relative;
  height: 0.85rem;
}

.scale__tick,
.scale__word {
  position: absolute;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  line-height: 0.85rem;
  letter-spacing: var(--track-mono);
  white-space: nowrap;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}

.scale__word { text-transform: uppercase; }

/* The track holds the bar plus the headroom the caret and chip stand in. */
.scale__track {
  position: relative;
  padding-block-start: 1.15rem;
}

.scale__bar {
  display: flex;
  height: 14px;
  border-radius: var(--r-1);
  overflow: hidden;                    /* the --r-1 ends clip the end segments */
  background-color: var(--surface-2);
}

.scale__band {
  display: block;
  height: 100%;
}

/* Tinted ground for the shape of the scale; a 2px raw-token edge for the read.
   MILD is the neutral band: it names the absence of a risk, so it takes the
   surface and the hairline rather than a hue. */
.scale__band[data-band="dry"]   { --band: var(--risk-low); }
.scale__band[data-band="humid"] { --band: var(--risk-watch); }
.scale__band[data-band="damp"]  { --band: var(--risk-high); }

.scale__band[data-band="dry"],
.scale__band[data-band="humid"],
.scale__band[data-band="damp"] {
  background-color: color-mix(in srgb, var(--band) 25%, var(--surface-1));
  box-shadow: inset 0 2px 0 0 var(--band);
}

.scale__band[data-band="mild"] {
  background-color: var(--surface-2);
  box-shadow: inset 0 2px 0 0 var(--line);
}

/* The marker is a zero-width column: everything hangs off its own left edge,
   so a position is a single percentage and nothing has to be measured. */
.scale__marker {
  position: absolute;
  inset-block: 0;
  width: 0;
}

.scale__line {
  position: absolute;
  bottom: 0;
  width: 2px;
  height: 14px;
  margin-inline-start: -1px;
  background-color: var(--ink-0);
}

.scale__caret {
  position: absolute;
  bottom: 14px;
  width: 0;
  height: 0;
  margin-inline-start: -4px;
  border-inline: 4px solid transparent;
  border-block-start: 5px solid var(--ink-0);
}

.scale__chip {
  position: absolute;
  bottom: 20px;
  inset-inline-start: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  line-height: 1;
  letter-spacing: var(--track-mono);
  white-space: nowrap;
  color: var(--ink-0);
  font-variant-numeric: tabular-nums;
}

/* Near the damp end the chip flips inboard, so it never leaves the bar. */
.scale__marker.is-end .scale__chip {
  inset-inline-start: auto;
  inset-inline-end: 0.5rem;
}

/* ====================================================================== */
/*  GROWTH CONDITIONS FIELD                                               */
/* ====================================================================== */

.field {
  margin: 0;
  display: grid;
  gap: var(--sp-4);
}

.field__intro {
  margin: 0;
  max-width: 65ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink-1);
}

/* Same scroller contract as the humidity chart: the diagram keeps a floor
   width so its region labels stay apart, and the page never scrolls sideways. */
.field__scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  transition: opacity var(--dur-base) var(--ease-out);
}

.field__scroll.is-pending { opacity: 0.45; }

/*
 * Floor and ceiling, both earned. The floor keeps the region labels apart on a
 * phone (below it the scroller takes over). The ceiling is the viewBox's own
 * width: past 720px the diagram would only get taller, and a field chart has
 * no more to say at 500px of height than at 340 (this is not a time series
 * gaining resolution, it is a fixed plane). At the ceiling the drawing renders
 * one unit to one pixel, which is where its 12px labels are truly 12px.
 */
.field__plot {
  min-width: 460px;
  max-width: 720px;
}

.field__svg {
  display: block;
  width: 100%;
  height: auto;
}

/*
 * The four regions ARE the ground here, so the grid is painted over them
 * rather than under: hairlines at the ticks, nothing else.
 */
.field__grid {
  stroke: var(--line);
  stroke-width: 1;
}

.field__tick,
.field__axis {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: var(--track-mono);
  fill: var(--ink-1);
}

.field__axis { font-size: 11px; }

/* Region fills: a whisper each, rising with severity so the four read as one
   ramp rather than four flat plates. Mixed into the card's own ground. */
.field__area[data-level="low"]    { fill: color-mix(in srgb, var(--risk-low) 10%, var(--surface-1)); }
.field__area[data-level="watch"]  { fill: color-mix(in srgb, var(--risk-watch) 12%, var(--surface-1)); }
.field__area[data-level="high"]   { fill: color-mix(in srgb, var(--risk-high) 14%, var(--surface-1)); }
.field__area[data-level="severe"] { fill: color-mix(in srgb, var(--risk-severe) 16%, var(--surface-1)); }

/* A boundary is the floor of what lies above it, so it wears that region's
   raw token. Held back to 55% so the line divides without ruling. */
.field__edge {
  fill: none;
  stroke-width: 1.5;
  stroke-opacity: 0.55;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.field__edge[data-level="watch"]  { stroke: var(--risk-watch); }
.field__edge[data-level="high"]   { stroke: var(--risk-high); }
.field__edge[data-level="severe"] { stroke: var(--risk-severe); }

/*
 * Direct labels, not a legend (four series, per the dataviz spec). Small risk
 * text, so design.md 2.2's ink formula applies; the surface halo is the same
 * 2px overlap gap the humidity chart's threshold label uses, spelled in SVG,
 * because two of the four labels sit near their own boundary line.
 */
.field__label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: var(--track-mono);
  fill: var(--ink-0);                  /* fallback where color-mix is absent */
  paint-order: stroke fill;
  stroke: var(--surface-1);
  stroke-width: 3;
  stroke-linejoin: round;
}

.field__label[data-level="low"]    { fill: color-mix(in oklab, var(--risk-low) 65%, var(--ink-0)); }
.field__label[data-level="watch"]  { fill: color-mix(in oklab, var(--risk-watch) 65%, var(--ink-0)); }
.field__label[data-level="high"]   { fill: color-mix(in oklab, var(--risk-high) 65%, var(--ink-0)); }
.field__label[data-level="severe"] { fill: color-mix(in oklab, var(--risk-severe) 65%, var(--ink-0)); }

/* 9px mark with a 2px surface ring, per the dataviz mark spec. */
.field__dot {
  fill: var(--ink-0);
  stroke: var(--surface-1);
  stroke-width: 2;
}

/*
 * The halo breathes because this dot is a live reading rather than a printed
 * one: the only thing on the diagram that changes is the one thing that moves.
 * Opacity only, and only on the halo, so the mark itself never wavers.
 */
.field__halo {
  fill: var(--accent);
  fill-opacity: 0.3;
  animation: field-breathe 3s ease-in-out infinite;
}

@keyframes field-breathe {
  0%, 100% { opacity: 0.6; }
  50%      { opacity: 1; }
}

.field__foot {
  display: grid;
  gap: var(--sp-2);
  padding-block-start: var(--sp-3);
  border-block-start: 1px solid var(--line);
}

/* The diagram's spoken twin: one instrument line carrying every value the dot
   encodes by position. */
.field__read {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--ink-0);
  font-variant-numeric: tabular-nums;
}

.field__note {
  margin: 0;
  max-width: 65ch;
  font-size: var(--text-small);
  line-height: var(--leading-body);
  color: var(--ink-1);
}

/* ====================================================================== */
/*  VENTILATION PLANNER (design.md §7.7c)                                 */
/* ====================================================================== */

.vent {
  margin: 0;
  display: grid;
  gap: var(--sp-4);
}

.vent__intro {
  margin: 0;
  max-width: 65ch;
  font-size: var(--text-body);
  line-height: var(--leading-body);
  color: var(--ink-1);
}

/* Same scroller contract as the outlook strip. */
.vent__scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  transition: opacity var(--dur-base) var(--ease-out);
}

.vent__scroll.is-pending { opacity: 0.45; }

.vent__grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(7rem, 1fr));
  gap: var(--sp-2);
  min-width: 38rem;
  padding-block: var(--sp-1);
}

.vent__col {
  display: grid;
  gap: var(--sp-2);
  align-content: start;
}

.vent__day {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
  padding-block-end: var(--sp-2);
  border-block-end: 1px solid var(--line);
}

.vent__col:first-child .vent__day { color: var(--ink-0); }

.vent__chips {
  display: grid;
  gap: var(--sp-2);
}

/*
 * The chip is the status made physical: a tinted ground for recognition at a
 * glance, an icon and a word for everyone whose eyes do not sort these three
 * hues apart (design.md §2.2, binding label rule). Never a button: it reports,
 * it does not act.
 */
.vent__chip {
  /*
   * design.md 2.2's ink formula is validated against a FLAT --surface-1. This
   * chip tints its ground 12% toward the same hue, which eats the margin
   * (light CAUTION lands at 4.13:1). Re-solving the same constraint by the
   * same method on the tinted ground gives 55%: light 5.00 / 6.78 / 7.20,
   * dark 7.87 / 7.23 / 6.79. Marks still use the raw token.
   */
  --risk-ink: color-mix(in oklab, var(--risk) 55%, var(--ink-0));

  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "time time"
    "icon word";
  gap: var(--sp-1) var(--sp-2);
  align-items: center;
  padding: var(--sp-2);
  border-radius: var(--r-1);
  background-color: color-mix(in srgb, var(--risk) 12%, var(--surface-1));
}

.vent__time {
  grid-area: time;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}

.vent__icon {
  grid-area: icon;
  display: block;
  width: 18px;
  height: 18px;
  color: var(--ink-0);                 /* fallback where color-mix is absent */
  color: var(--risk-ink);
}

.vent__icon svg {
  display: block;
  width: 18px;
  height: 18px;
}

/* In the legend the same icon sits in a plain two-column grid: the chip's
   named grid-area would spawn implicit columns and strand it at row end. */
.vent__key-row .vent__icon {
  grid-area: auto;
}

.vent__word {
  grid-area: word;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-0);                 /* fallback where color-mix is absent */
  color: var(--risk-ink);
}

/* A gap in the feed is drawn as a gap: dashed edge, no invented value. */
.vent__chip[data-gap] {
  background-color: var(--surface-1);
  box-shadow: inset 0 0 0 1px var(--line);
}

/* ---- legend + credit ---- */

.vent__foot {
  display: grid;
  gap: var(--sp-3);
  padding-block-start: var(--sp-3);
  border-block-start: 1px solid var(--line);
}

.vent__key {
  display: grid;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Grid, not flex: the icon holds its own column and the copy wraps as one
   sentence, so a narrow screen never strands the interpunct on its own line. */
.vent__key-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--ink-1);
}

.vent__key-word {
  color: var(--ink-0);                 /* fallback where color-mix is absent */
  color: var(--risk-ink);
}

/*
 * Copernicus licensing requires this credit wherever CAMS data is shown, so it
 * is set as running small print (sentence case, no tracking) rather than as
 * another instrument label: it is a legal line, not a readout.
 *
 * --ink-1 rather than --ink-2: design.md 2.1 restricts --ink-2 to AA-large,
 * and at --text-small this line is not large (3.96:1 on --surface-1). A credit
 * that licensing obliges us to show has to be readable.
 */
.vent__credit {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-small);
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.5;
  color: var(--ink-1);
}

/* ====================================================================== */
/*  TABLE VIEW                                                            */
/* ====================================================================== */

/*
 * min-width: 0 is the whole reason the page does not pan sideways on a phone.
 *
 * <details> is a grid item of .cond__panel, so its automatic minimum size is
 * min-content, and a table's min-content is the sum of its columns, which at
 * six nowrap columns is far wider than a phone. That minimum inflated the
 * panel's grid TRACK, and every sibling item (the eyebrow row, the benefit
 * heading, the chart) stretched to it and overflowed the card. The scroller on
 * the wrapper below could never help, because the blowout happened one level
 * above it. Zeroing the minimum lets the track collapse to the card; the
 * wrapper's overflow-x then absorbs whatever the table still needs.
 */
.cond__details {
  border-block-start: 1px solid var(--line);
  min-width: 0;
}

.cond__summary {
  padding-block: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
  cursor: pointer;
}

.cond__summary:hover { color: var(--ink-0); }

.cond__summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-1);
}

/*
 * Belt and braces: the four table slots are contained scrollers, on the same
 * contract as the chart scrollers above. Selected by class rather than by each
 * data-* slot name, because the growth slot was missed off the old attribute
 * list and its table escaped the card entirely; one class cannot be forgotten
 * when a fifth panel arrives.
 */
.cond__details > .cond__tablewrap {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  padding-block-end: var(--sp-3);
}

.cond-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-small);
  color: var(--ink-1);
}

.cond-table caption {
  text-align: start;
  color: var(--ink-1);
  padding-block-end: var(--sp-2);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
}

.cond-table th,
.cond-table td {
  text-align: start;
  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
  border-block-end: 1px solid var(--line);
  white-space: nowrap;
}

.cond-table thead th {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
  color: var(--ink-1);
  font-weight: 400;
}

.cond-table tbody th {
  font-weight: 500;
  color: var(--ink-0);
}

.cond-table td[data-level],
.cond-table td[data-status] { color: var(--ink-0); }

.cond-table td[data-level] .legend__dot,
.cond-table td[data-status] .legend__dot,
.cond-table th[data-level] .legend__dot { margin-inline-end: var(--sp-2); }

.cond-table .num { font-variant-numeric: tabular-nums; }

/*
 * Two spellings of one column name. The full words are what the table is
 * called and what a screen reader always reads; the short form is a phone-only
 * stand-in, aria-hidden so it is never spoken. Below, the full span becomes
 * visually hidden rather than display: none, so nothing leaves the
 * accessibility tree at any width. No data column is ever dropped.
 */
.cond-table__abbr { display: none; }

/* ====================================================================== */
/*  NARROW                                                                */
/* ====================================================================== */

/*
 * The tables fit the phone rather than merely scrolling on it.
 *
 * Four levers, in the order they earn their keep: the inter-column gutter
 * halves (sp-3 to sp-2, still a clear gutter at this type size); the cell type
 * drops one step of the scale, from --text-small to --text-mono; the column
 * headers wrap instead of forcing their own single-line width; and the two
 * longest header names swap in their short spelling. Together the ventilation
 * planner (5 columns), the humidity table (3) and the growth regions (3) all
 * land inside a 390px phone with no scroll at all. Only the outlook, at six
 * columns, still overruns (by about 35px), and it uses the scroller above.
 */
@media (max-width: 700px) {
  .cond-table { font-size: var(--text-mono); }

  .cond-table th,
  .cond-table td { padding: var(--sp-2) var(--sp-2) var(--sp-2) 0; }

  /* Header cells wrap; data cells stay on one line so a reading is never cut
     in half. Bottom alignment lands a wrapped header's last line on the same
     row as its one-line neighbours, so the header rule stays a rule. */
  .cond-table thead th {
    white-space: normal;
    vertical-align: bottom;
  }

  .cond-table__abbr { display: inline; }

  .cond-table__full {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

/*
 * The growth field's labels, made readable on a phone.
 *
 * The diagram draws in a 720-unit viewBox and renders at the .field__plot
 * floor, so on a phone one unit is NOT one pixel: at the 460px floor a 12px
 * label landed at 7.7 effective pixels and the 11px axis unit at 7.0, which is
 * below anything a person should be asked to read. Two moves, because either
 * alone would cost too much: the floor rises to 520px (a 13% wider scroll for
 * a 13% larger everything) and the type rises with it. The result is 10.1px
 * for ticks and band labels, 9.4px for the axis units, all comfortably over
 * the 8px floor. The viewBox, the data encoding, the colours and the risk
 * words are untouched: this is type size and rendered scale only.
 */
@media (max-width: 700px) {
  .field__plot { min-width: 520px; }

  .field__tick,
  .field__label { font-size: 14px; }

  .field__axis { font-size: 13px; }
}

@media (max-width: 559px) {
  .cond { padding: var(--sp-3); }
  .tip,
  .hum__tip { width: 11rem; }
}

@media (prefers-reduced-motion: reduce) {
  .strip__scroll,
  .hum__scroll,
  .field__scroll,
  .vent__scroll,
  .picker__btn { transition: none; }

  .field__halo { animation: none; }
}
