/* components.css — panels, the instrument readout, form controls,
   verdict states, content blocks. Depends on tokens in base.css. */

/* ----------------------------------------------------------------- panel */

.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  padding: var(--space-5);
}

@media (min-width: 40rem) {
  .panel {
    padding: var(--space-6);
  }
}

.panel__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: baseline;
  justify-content: space-between;
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--rule);
}

.panel__head > * {
  margin: 0;
}

/* ------------------------------------------------- conditions data strip */

.strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  gap: var(--space-4) var(--space-5);
  margin: 0;
}

.strip div {
  display: flex;
  flex-direction: column-reverse;
  gap: var(--space-1);
}

.strip dd {
  margin: 0;
  font-family: var(--face-data);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.1;
}

.strip .u {
  font-size: 0.6em;
  font-weight: 600;
  color: var(--ink-faint);
  margin-left: 0.15em;
}

.strip [data-air="outdoor"] dd {
  color: var(--warm);
}

.strip [data-air="supply"] dd {
  color: var(--cool);
}

/* ------------------------------------------------ THE READOUT (signature)

   A vertical temperature scale read like a psychrometric chart: outdoor
   dry-bulb at the top in ochre, achievable supply temperatures beneath it
   in cyan, and a hard rule at the wet-bulb floor. Everything below that
   rule is hatched, because it is not merely hard to reach — it is
   thermodynamically unavailable to a direct evaporative cooler.

   Marker positions come from --pos-* custom properties (0% = top of the
   scale, 100% = bottom), so the same markup serves both the static example
   here and the live calculator later.                                     */

.readout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5);
}

@media (min-width: 46rem) {
  .readout {
    grid-template-columns: 1fr minmax(15rem, 20rem);
    gap: var(--space-7);
    align-items: stretch;
  }
}

.readout__figures {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.figure__value {
  font-family: var(--face-data);
  font-size: var(--step-5);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.02em;
}

.figure__value .u {
  font-size: 0.4em;
  font-weight: 600;
  letter-spacing: 0;
  margin-left: 0.1em;
  vertical-align: 0.55em;
  color: var(--ink-faint);
}

.figure--supply .figure__value {
  color: var(--cool);
}

.figure--secondary .figure__value {
  font-size: var(--step-4);
}

.figure__note {
  font-size: var(--step--1);
  color: var(--ink-soft);
  max-width: 34ch;
}

.figure__drop {
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--cool);
}

/* --- the scale itself --- */

/* The scale is laid out inside its own box: a 3rem numeral gutter on the
   left, the axis line, then the labels. Nothing uses negative offsets, so
   it cannot bleed out of the panel at narrow widths. */
.scale {
  --gutter: 3.25rem;
  position: relative;
  min-height: 22rem;
}

.scale::before {
  content: "";
  position: absolute;
  left: var(--gutter);
  top: 0;
  bottom: 0;
  border-left: 2px solid var(--rule-strong);
}

.scale__band {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--pos, 0%);
}

/* Worked-example positions for the state the page is in before anyone
   calculates anything — the same figures the answer card shows beside it.
   These belong here rather than in a style attribute on each band: the
   deployed Content-Security-Policy sets `style-src 'self'` with no
   'unsafe-inline', so the browser discards style attributes outright and the
   scale rendered blank until the first calculation. Script assignment via
   element.style.setProperty() goes through the CSSOM, which CSP permits, so
   the calculator still overrides every one of these at run time.

   Keep them in step with the static answer card and the no-JS chart fallback
   in index.html; all three describe 95 °F, 15 % RH, 5,000 ft. */
.scale__band--outdoor { --pos: 0%; }
.scale__band--typical { --pos: 62.6%; }
.scale__band--supply { --pos: 70.5%; }
.scale__band--measured { --pos: 70%; }
.scale__band--floor { --pos: 78.1%; }

/* A marker is a hairline across the scale plus a label sitting above it. */
.scale__band::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  border-top: 1px solid currentColor;
}

.scale__band span {
  position: absolute;
  left: calc(var(--gutter) + 0.25rem);
  bottom: 0.15rem;
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.scale__band b {
  position: absolute;
  left: 0;
  bottom: 0.15rem;
  width: 3rem;
  text-align: right;
  font-family: var(--face-data);
  font-size: var(--step-0);
  font-weight: 700;
}

.scale__band--outdoor {
  color: var(--warm);
}

.scale__band--supply {
  color: var(--cool);
}

.scale__band--typical {
  color: var(--cool);
  opacity: 0.75;
}

.scale__band--floor {
  color: var(--ink);
}

.scale__band--floor::before {
  border-top-width: 2px;
}

/* The unreachable region. 45° hatching is the psychrometric chart's own
   idiom for a constant-wet-bulb line, which is exactly what bounds it. */
.scale__forbidden {
  position: absolute;
  left: 0;
  right: 0;
  /* Worked-example default, per the note on .scale__band--outdoor above. */
  --pos: 78.1%;
  top: var(--pos, 80%);
  bottom: 0;
  background: repeating-linear-gradient(
    45deg,
    transparent 0 8px,
    var(--rule) 8px 9px
  );
  border-top: 0;
}

.scale__forbidden span {
  position: absolute;
  left: calc(var(--gutter) + 0.25rem);
  right: 0.25rem;
  top: var(--space-3);
  font-size: var(--step--1);
  line-height: 1.4;
  color: var(--ink-soft);
}

/* The one equation on the site. Set as a specimen, not as code. */
.formula {
  font-family: var(--face-data);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
  border-left: 3px solid var(--cool);
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  max-width: none;
}

/* --------------------------------------------------------------- callout */

.callout {
  border-left: 3px solid var(--rule-strong);
  padding: var(--space-3) 0 var(--space-3) var(--space-4);
}

.callout--cool {
  border-left-color: var(--cool);
}

.callout--warm {
  border-left-color: var(--warm);
}

.callout h3 {
  margin-bottom: var(--space-2);
}

.callout > * + * {
  margin-top: var(--space-3);
}

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

/* The band reference on the home page: verdict cards presented as a list, so
   the markup stays a list for a screen reader while the markers go away. */
.verdict-list {
  list-style: none;
  padding: 0;
  max-width: 44rem;
}

.verdict {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
  border: 1px solid var(--rule);
  border-left-width: 4px;
  padding: var(--space-4);
  background: var(--surface);
}

.verdict__icon {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.15rem;
}

.verdict__title {
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 700;
  margin-bottom: var(--space-1);
}

/* Verdict state is carried by border, icon AND wording — never colour
   alone. */
.verdict[data-state="ok"] { border-left-color: var(--ok); }
.verdict[data-state="ok"] .verdict__title { color: var(--ok); }
.verdict[data-state="warn"] { border-left-color: var(--warn); }
.verdict[data-state="warn"] .verdict__title { color: var(--warn); }
.verdict[data-state="bad"] { border-left-color: var(--bad); }
.verdict[data-state="bad"] .verdict__title { color: var(--bad); }
.verdict[data-state="info"] { border-left-color: var(--info); }
.verdict[data-state="info"] .verdict__title { color: var(--info); }

/* ---------------------------------------------------------------- forms */

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-4) var(--space-5);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Two fields side by side whose labels wrap to different numbers of lines put
   their boxes at different heights, and the boxes are what the eye lines up
   on. Subgrid hands label, control and hint to shared rows so the boxes agree
   however the labels break. Where subgrid is missing the fields keep their own
   column flow — ragged, but every control is still present and usable, so it
   stays a progressive enhancement rather than a dependency. */
.field-grid--aligned {
  max-width: 38rem;
}

@supports (grid-template-rows: subgrid) {
  .field-grid--aligned {
    grid-template-rows: auto auto auto;
  }

  .field-grid--aligned .field {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
  }
}

.field label {
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.field input,
.field select {
  width: 100%;
  /* Stated, not inherited: a select's default line-height is `normal`, which
     leaves it a good 10 px shorter than a text input of the same font and
     padding. Side by side that reads as a mistake. */
  line-height: 1.6;
  padding: var(--space-2) var(--space-3);
  background: var(--ground);
  border: 1px solid var(--rule-strong);
  border-radius: 0;
  font-family: var(--face-data);
  font-size: var(--step-2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.field__hint {
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.button {
  display: inline-block;
  padding: var(--space-3) var(--space-5);
  border: 1px solid transparent;
  border-radius: 0;
  background: var(--cool);
  color: var(--surface);
  font-family: var(--face-data);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}

.button:hover {
  background: var(--ink);
}

.button--quiet {
  background: transparent;
  border-color: var(--rule-strong);
  color: var(--ink);
}

.button--quiet:hover {
  background: var(--ground);
  color: var(--ink);
}

.field__error {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--bad);
}

.field__error:empty {
  display: none;
}

.field input[aria-invalid="true"] {
  border-color: var(--bad);
  border-width: 2px;
}

/* Interactive controls stay hidden until the module loads, so the no-script
   page never shows a form that cannot compute anything. */
.js-only {
  display: none;
}

.js .js-only {
  display: block;
}

.js fieldset.js-only {
  display: flex;
}

/* Unit toggle, styled as an instrument switch rather than a pair of radios. */
.segmented {
  margin: 0;
  padding: 0;
  border: 1px solid var(--rule-strong);
}

.segmented label {
  position: relative;
  display: block;
}

.segmented input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.segmented span {
  display: block;
  padding: var(--space-1) var(--space-4);
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

.segmented input:checked + span {
  background: var(--cool);
  color: var(--surface);
}

.segmented input:focus-visible + span {
  outline: 3px solid var(--cool-bright);
  outline-offset: 2px;
}

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

.locate > * + * {
  margin-top: var(--space-4);
}

.locate__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.locate__search {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: stretch;
}

.locate__search input {
  flex: 1 1 14rem;
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--ground);
  border: 1px solid var(--rule-strong);
  border-radius: 0;
  font-family: var(--face-data);
  font-size: var(--step-1);
}

.place-results {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--rule);
}

.place-results li + li {
  border-top: 1px solid var(--rule);
}

.place-results button {
  display: block;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  text-align: left;
  background: var(--surface);
  border: 0;
  cursor: pointer;
}

.place-results button:hover {
  background: var(--cool-wash);
}

.place-results strong {
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
}

.place-results span {
  display: block;
  font-size: var(--step--1);
  color: var(--ink-soft);
}

/* Status copy for the location flow. Every failure path lands here with a
   specific message and a route onward, never a bare "error". */
.wx-status {
  border-left: 3px solid var(--rule-strong);
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.wx-status:empty {
  display: none;
}

.wx-status[data-tone="working"] { border-left-color: var(--cool); }
.wx-status[data-tone="problem"] { border-left-color: var(--warn); }
.wx-status[data-tone="done"] { border-left-color: var(--cool); }

.wx-status strong {
  display: block;
  font-family: var(--face-data);
  font-size: var(--step-0);
  color: var(--ink);
}

.source-note {
  margin-top: var(--space-3);
  font-size: var(--step--1);
  color: var(--ink-faint);
}

.advanced summary {
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
  cursor: pointer;
}

.advanced summary:hover {
  color: var(--cool);
}

.advanced .field {
  margin-top: var(--space-4);
  max-width: 20rem;
}

/* The user's own measurement on the scale: a heavier marker than the
   reference bands, because it is the one they came here to see. */
.scale__band--measured {
  color: var(--ink);
}

.scale__band--measured::before {
  border-top-width: 3px;
  border-top-style: solid;
}

.scale__band--measured span,
.scale__band--measured b {
  color: var(--ink);
}

/* Annotated at the right-hand end of its rule, mirroring the other four.
   Their numerals sit in the left gutter and their labels just right of the
   axis; Yours lands wherever the reader's measurement lands, which is often
   within a degree or two of Typical, Best case, or the floor — close enough
   that same-side labels sit on top of each other. Opposite side, no collision,
   and the reader's own figure reads as the distinct thing it is.

   The 3.5rem offset is the numeral's 3rem box plus the 0.25rem edge inset and
   a 0.25rem gap, so the label lands immediately left of the number. */
.scale__band--measured b {
  left: auto;
  right: 0.25rem;
}

.scale__band--measured span {
  left: auto;
  right: 3.5rem;
}

/* Below about 375 px the scale is only ~14rem wide, and the longest reference
   label — "Wet-bulb floor", at 118 px — plus Yours in a row needs 269 px
   against the 223 px actually available. So the annotation stacks against the
   right edge instead of sitting in a row: the caption above the numeral, both
   above the rule. That needs only the numeral's own width on the right, which
   fits with room to spare.

   Both stay above the rule rather than straddling it, so the annotation only
   ever grows away from the hatched region below — a caption hanging under the
   rule lands in that region's own note whenever the reading sits just above
   the floor, which is a good reading and a common one. */
@media (max-width: 23.5rem) {
  .scale__band--measured b {
    width: 2.75rem;
  }

  .scale__band--measured span {
    right: 0.25rem;
    bottom: 1.45rem;
    letter-spacing: 0.03em;
  }
}

/* When the reader's own measurement lands inside the impossible region, the
   verdict card below is already explaining exactly why, in full prose. Two
   explanations competing for the same 5rem of hatching is worse than one, and
   the specific one wins. */
.scale__forbidden--quiet span {
  display: none;
}

/* --------------------------------------------------------------- content */

.note {
  border: 1px dashed var(--rule-strong);
  padding: var(--space-4);
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.note p {
  max-width: none;
}

.steps {
  counter-reset: step;
  padding: 0;
  list-style: none;
}

.steps li {
  counter-increment: step;
  position: relative;
  padding-left: 2.5rem;
  max-width: var(--measure);
}

.steps li + li {
  margin-top: var(--space-4);
}

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0.1em;
  width: 1.75rem;
  text-align: right;
  font-family: var(--face-data);
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--cool);
}

/* Ranked failure modes. The rank is real information — the list is
   ordered by how often each cause is actually the answer. */
.ranked {
  padding: 0;
  list-style: none;
  counter-reset: rank;
}

.ranked > li {
  counter-increment: rank;
  max-width: none;
  padding-top: var(--space-5);
  border-top: 1px solid var(--rule);
}

.ranked > li + li {
  margin-top: var(--space-5);
}

.ranked__rank {
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* These are h2 because each cause is a top-level section of the page, but
   they read at h3 scale so the rank eyebrow above stays the entry point. */
.ranked__title {
  font-size: var(--step-2);
  margin: var(--space-1) 0 var(--space-3);
}

.spec-list {
  display: grid;
  gap: var(--space-2) var(--space-4);
  margin: var(--space-3) 0 0;
}

@media (min-width: 34rem) {
  .spec-list {
    grid-template-columns: 8rem minmax(0, 1fr);
  }
}

.spec-list dt {
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
  padding-top: 0.15em;
}

.spec-list dd {
  margin: 0;
  max-width: var(--measure);
}

/* Card grid for the page links at the foot of the home page. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-4);
  padding: 0;
  list-style: none;
}

.card {
  display: block;
  height: 100%;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--rule);
  text-decoration: none;
  color: var(--ink);
}

.card:hover {
  border-color: var(--cool);
}

.card h3 {
  color: var(--cool);
  margin-bottom: var(--space-2);
}

.card p {
  font-size: var(--step--1);
  color: var(--ink-soft);
  max-width: none;
}

/* ------------------------------------------------- psychrometric chart */

.figure-block {
  margin: var(--space-6) 0;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--rule);
}

.figure-block figcaption {
  margin-top: var(--space-3);
  font-size: var(--step--1);
  color: var(--ink-soft);
  max-width: var(--measure);
}

.psychchart {
  width: 100%;
  height: auto;
  overflow: visible;
}

.psychchart text {
  font-family: var(--face-data);
  font-weight: 600;
  fill: var(--ink-faint);
}

.pc-axis {
  stroke: var(--rule-strong);
  stroke-width: 1.5;
}

.pc-tick line {
  stroke: var(--rule-strong);
  stroke-width: 1.5;
}

.pc-tick text {
  font-size: 13px;
  text-anchor: middle;
}

.pc-axis-label {
  font-size: 13px;
  text-anchor: middle;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.pc-rh {
  fill: none;
  stroke: var(--rule);
  stroke-width: 1.25;
}

.pc-rh-label {
  font-size: 12px;
  text-anchor: end;
}

/* Labels that sit alongside their own curve rather than at its end. */
.pc-rh-label--inline {
  text-anchor: start;
}

.pc-sat {
  fill: none;
  stroke: var(--rule-strong);
  stroke-width: 2.5;
}

.pc-sat-label {
  font-size: 13px;
  fill: var(--ink-soft);
}

.pc-process {
  stroke: var(--cool-bright);
  stroke-width: 3;
}

/* The last stretch to saturation is drawn dashed: it is available in
   principle and unreachable in practice. */
.pc-process--unreached {
  stroke: var(--cool-bright);
  stroke-width: 2;
  stroke-dasharray: 4 4;
  opacity: 0.6;
}

.pc-state--outdoor { fill: var(--warm-bright); }
.pc-state--supply { fill: var(--cool-bright); }
.pc-state--floor { fill: var(--ink); }

.pc-state-label {
  font-size: 15px;
  font-weight: 700;
}

.pc-state-sub {
  font-size: 13px;
}

.pc-state-label--outdoor,
.pc-state-sub--outdoor { fill: var(--warm); }

.pc-state-label--supply,
.pc-state-sub--supply { fill: var(--cool); }

.pc-state-sub--floor { fill: var(--ink-soft); }

/* --------------------------------------------------------------------- faq */

.faq h3 {
  margin-top: var(--space-6);
  margin-bottom: var(--space-2);
  font-size: var(--step-2);
  color: var(--cool);
}

.faq h3:first-child {
  margin-top: var(--space-4);
}

/* ------------------------------------------------------ reference tables */

/* Wide content scrolls inside its own box; the page body never scrolls
   sideways. */
.table-wrap {
  overflow-x: auto;
  margin: var(--space-5) 0;
  border: 1px solid var(--rule);
  background: var(--surface);
}

.ref-table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.ref-table caption {
  caption-side: bottom;
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font-size: var(--step--1);
  color: var(--ink-soft);
  border-top: 1px solid var(--rule);
}

.ref-table th,
.ref-table td {
  padding: var(--space-2) var(--space-4);
  text-align: right;
  white-space: nowrap;
  border-bottom: 1px solid var(--rule);
}

.ref-table thead th {
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
  border-bottom: 2px solid var(--rule-strong);
}

.ref-table tbody th {
  text-align: left;
  font-family: var(--face-data);
  font-weight: 700;
  color: var(--warm);
}

.ref-table tbody td {
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--cool);
}

.ref-table tbody tr:last-child th,
.ref-table tbody tr:last-child td {
  border-bottom: 0;
}

/* --------------------------------------------- wet-bulb depression map */

.heatmap {
  width: 100%;
  height: auto;
}

.heatmap text {
  font-family: var(--face-data);
  font-weight: 600;
}

.hm-ax,
.hm-ay {
  font-size: 14px;
  fill: var(--ink-faint);
}

.hm-ax { text-anchor: middle; }
.hm-ay { text-anchor: end; }

.hm-v {
  font-size: 15px;
  font-weight: 700;
  text-anchor: middle;
  fill: var(--ink);
}

/* Banded, not a continuous gradient: the bands are the decision boundaries,
   so the reader should be able to see where they fall. */
.hm--0 { fill: var(--warm-wash); }
.hm--1 { fill: #e8dcc4; }
.hm--2 { fill: #cfe0dd; }
.hm--3 { fill: #a9d3dd; }
.hm--4 { fill: #7fc4d8; }

@media (prefers-color-scheme: dark) {
  .hm--0 { fill: #3a2c12; }
  .hm--1 { fill: #43391f; }
  .hm--2 { fill: #1d4048; }
  .hm--3 { fill: #1d5563; }
  .hm--4 { fill: #1d6c80; }
  .hm-v { fill: var(--ink); }
}

.hm-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-3);
  padding: 0;
  list-style: none;
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.hm-legend li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  max-width: none;
}

.hm-legend i {
  width: 1.1rem;
  height: 1.1rem;
  flex: none;
  border: 1px solid var(--rule-strong);
}

/* ------------------------------------------------------- state chart

   Three points on a temperature/humidity plane: outdoor air, the cooler's
   output, and the wet-bulb floor those two are always somewhere between.
   A dashed line traces the constant-wet-bulb process connecting them, so
   the geometry itself argues the site's thesis — output sits partway along
   a path that ends at saturation, not at some arbitrary cold number.       */

.statechart {
  width: 100%;
  height: auto;
  overflow: visible;
}

.statechart text {
  font-family: var(--face-data);
  font-weight: 600;
  fill: var(--ink-faint);
}

.pt-axis {
  stroke: var(--rule-strong);
  stroke-width: 1.5;
}

.pt-grid {
  stroke: var(--rule);
  stroke-width: 1;
}

.pt-tickmark {
  stroke: var(--rule-strong);
  stroke-width: 1.5;
}

.pt-xtick,
.pt-ytick {
  font-size: 13px;
}

.pt-xtick { text-anchor: middle; }
.pt-ytick { text-anchor: end; }

.pt-axis-label {
  font-size: 13px;
  text-anchor: middle;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Comfortable is about output temperature regardless of humidity, so the
   band is vertical — every point left of the line is a comfortable output
   temperature, whatever its humidity happens to be. */
.pt-comfort {
  fill: var(--cool-wash);
}

.pt-comfort-label {
  font-size: 12px;
  fill: var(--cool);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.pt-line {
  fill: none;
  stroke: var(--ink-faint);
  stroke-width: 2;
  stroke-linejoin: round;
}

/* The stretch from the cooler's actual output onward to full saturation —
   available in principle, not delivered by this machine. */
.pt-line--beyond {
  stroke-dasharray: 4 4;
  opacity: 0.6;
}

.pt-point {
  cursor: default;
}

.pt-point:focus-visible {
  outline: 3px solid var(--cool-bright);
  outline-offset: 3px;
  border-radius: 50%;
}

.pt-dot {
  stroke: var(--surface);
  stroke-width: 2.5;
}

/* Name and reading share one line now, so both sizes come down a little —
   two labels that would have stacked instead sit side by side, and the line
   has to fit the gutter at the right of the plot. */
.pt-label {
  font-size: 13px;
  font-weight: 700;
}

.pt-sub {
  font-size: 12.5px;
  font-weight: 600;
}

/* Drawn only when a label had to be nudged clear of its neighbour, which is
   the one case where the pairing stops being obvious from position alone. */
.pt-leader {
  stroke: var(--rule-strong);
  stroke-width: 1;
}

/* Two classes each, deliberately: a single-class rule here loses a
   specificity tie to the earlier ".statechart text" rule (class+element
   beats class alone), which would silently grey out every label regardless
   of which point it belongs to. */
.pt-label.pt-outdoor, .pt-sub.pt-outdoor { fill: var(--warm); }
.pt-label.pt-output, .pt-sub.pt-output { fill: var(--cool); }
.pt-label.pt-floor, .pt-sub.pt-floor { fill: var(--ink); }
.pt-label.pt-typical, .pt-sub.pt-typical { fill: var(--cool); }

/* Typical is still a conditioned-air figure (cool), but a secondary,
   reference one next to the primary output dot — hollow rather than
   filled, the way a map distinguishes a landmark from a waypoint. */
.pt-dot.pt-typical {
  fill: var(--surface);
  stroke: var(--cool);
  stroke-width: 2.5;
}

.chart-readout {
  min-height: 1.5em;
  margin-top: var(--space-3);
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--warm);
}

.chart-hint {
  margin-top: var(--space-2);
  font-size: var(--step--1);
  color: var(--ink-faint);
}

/* ------------------------------------------- troubleshooting <details> */

.tree {
  border-top: 1px solid var(--rule);
}

.tree details {
  border-bottom: 1px solid var(--rule);
}

.tree summary {
  padding: var(--space-3) 0;
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
  cursor: pointer;
}

.tree summary:hover {
  color: var(--cool);
}

.tree details details {
  margin-left: var(--space-5);
  border-bottom: 0;
  border-top: 1px solid var(--rule);
}

.tree details details summary {
  font-size: var(--step-0);
}

.tree__body {
  padding: 0 0 var(--space-4);
  max-width: var(--measure);
}

.tree__body > * + * {
  margin-top: var(--space-3);
}

.tree__prompt {
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
  margin-bottom: var(--space-2);
}

.tree__help {
  font-size: var(--step--1);
  color: var(--ink-soft);
  margin-bottom: var(--space-3);
}

.tree .outcome {
  border-left: 3px solid var(--cool);
  padding-left: var(--space-4);
}

/* h2 for correct document outline, sized as a subheading because that is what
   it is visually. */
.tree .outcome h2 {
  font-size: var(--step-2);
}

.tree .outcome > * + * {
  margin-top: var(--space-3);
}

/* --------------------------------------------------- the interactive walker */

.walk {
  border-top: 0;
}

.walk__crumbs {
  margin-bottom: var(--space-4);
}

.walk__crumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.walk__crumbs li + li::before {
  content: "›";
  margin-right: var(--space-2);
  color: var(--ink-faint);
}

.walk__crumbs li {
  display: flex;
  align-items: baseline;
}

.walk__crumbs button {
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--face-data);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.walk__crumbs button:hover {
  color: var(--cool);
}

.walk__card {
  background: var(--surface);
  border: 1px solid var(--rule);
  padding: var(--space-5);
}

@media (min-width: 40rem) {
  .walk__card {
    padding: var(--space-6);
  }
}

.walk__card--outcome {
  border-left: 4px solid var(--cool);
}

.walk__prompt {
  font-size: var(--step-3);
  margin: 0;
}

.walk__prompt:focus-visible {
  outline: 3px solid var(--cool-bright);
  outline-offset: 4px;
}

.walk__card > * + * {
  margin-top: var(--space-4);
}

.walk__help {
  font-size: var(--step--1);
  color: var(--ink-soft);
}

.walk__options {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.walk__option {
  display: block;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  text-align: left;
  background: var(--ground);
  border: 1px solid var(--rule-strong);
  border-radius: 0;
  cursor: pointer;
  font-family: var(--face-data);
  font-size: var(--step-1);
  font-weight: 600;
}

.walk__option:hover {
  border-color: var(--cool);
  background: var(--cool-wash);
  color: var(--cool);
}

.walk__see {
  font-size: var(--step--1);
}

.walk__controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.walk__controls:empty {
  display: none;
}
