/* ===================================================================
   Direction 07 — "Editorial"
   Full-bleed powder photograph, charcoal serif type sitting directly
   on the snow. No scrim: white on bright snow measures 1.12:1, while
   charcoal measures 13.6:1 on the same pixels (see IMAGES.md), so the
   snow is treated as the page ground rather than an obstacle.
   =================================================================== */

:root {
  --ink: #101A22;          /* 13.6:1 on this photo's snow */
  --ink-soft: #2E4152;     /* 9.4:1 — captions, nav, labels */
  --ink-faint: #5B7086;
  --paper: #F4F7FA;
  --paper-raised: #FFFFFF;
  --line: rgba(16, 26, 34, 0.16);
  --accent: #C1372B;       /* drawn from the skier's jacket, used sparingly */

  --serif: "Cormorant Garamond", Georgia, serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  --hero: url("assets/hero.jpg");

  /* Coverage map status colors: reserved-meaning tokens (see
     DESIGN.md "The Reference Palette Rule") - kept at their exact hex
     because they trace to an external dataviz reference palette, not
     to this page's own ink/paper palette, so they don't get
     re-harmonized to match it. */
  --status-good: #0ca30c;
  --status-good-bg: rgba(12, 163, 12, 0.12);
  --status-warning: #b8860b;
  --status-warning-bg: rgba(184, 134, 11, 0.14);
  --status-critical: #C1372B;
  --status-critical-bg: rgba(193, 55, 43, 0.1);

  --chart-accent: #2E4152;
  --chart-accent-fill: rgba(46, 65, 82, 0.12);
  --chart-accent-stroke: rgba(46, 65, 82, 0.4);
  --chart-gridline: rgba(16, 26, 34, 0.1);
  --chart-muted: var(--ink-faint);

  --chart-suggestion: #C1372B;
  --chart-suggestion-fill: rgba(193, 55, 43, 0.12);
  --chart-suggestion-stroke: rgba(193, 55, 43, 0.4);

  --tooltip-divider: rgba(255, 255, 255, 0.15);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
}

::selection { background: var(--ink); color: var(--paper); }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 2px; }
* { scrollbar-width: thin; scrollbar-color: rgba(16,26,34,0.3) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb {
  background: rgba(16,26,34,0.28); border-radius: 99px;
  border: 2px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(16,26,34,0.48); background-clip: content-box; }

/* ---------- hero ---------- */

html {
  /* anchor jumps glide instead of teleporting */
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* The hero used to stop dead against flat paper, which is what made
   the first scroll feel like a jolt. This dissolves the photograph
   into the page colour over the last slice of the hero, so the snow
   runs out rather than being cut off. */
.hero::after {
  content: "";
  position: absolute; z-index: 1;
  left: 0; right: 0; bottom: 0;
  height: clamp(90px, 16vh, 190px);
  background: linear-gradient(to bottom, rgba(244,247,250,0) 0%, var(--paper) 100%);
  pointer-events: none;
}

/* The skier sits centre-right in the source frame and a `cover` crop
   cannot reliably push a subject out of the way — narrowing the
   viewport drags it back toward centre. So the type moves instead:
   the copy column sits LEFT, and the crop is anchored left too, which
   keeps the figure out at the right edge with untouched snow between.
   The composition then holds at every width rather than being tuned
   for one. */
.hero-photo {
  position: absolute; inset: 0;
  background-image: var(--hero);
  background-size: cover;
  background-position: 18% 52%;
}

.nav {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  padding: clamp(1.4rem, 3vw, 2.2rem) clamp(1.3rem, 4vw, 3.2rem);
}
.brand {
  font-family: var(--mono);
  font-size: 0.6875rem; font-weight: 500;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink); text-decoration: none;
}
.nav nav { display: flex; gap: clamp(1.1rem, 2.4vw, 1.8rem); }
.nav nav a {
  font-size: 0.625rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--ink-soft); text-decoration: none;
  transition: color 0.25s var(--ease);
}
.nav nav a:hover { color: var(--ink); }

/* Centred within the hero, but the block is capped so that on a very
   tall viewport the copy does not float to the middle of an otherwise
   empty field — it settles just above centre and the photograph keeps
   the rest. */
.copy {
  position: relative; z-index: 2;
  flex: 1;
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 clamp(1.3rem, 4vw, 3.2rem) clamp(3rem, 8vh, 5rem);
  max-width: min(560px, 54%);
}
@media (min-height: 900px) {
  /* Nudge the block below centre on a tall viewport instead of pinning
     it to the bottom — flex-end plus a large pad overflowed the hero. */
  .copy { justify-content: center; padding-top: clamp(2rem, 12vh, 9rem); }
}

/* Slender serif mixing upright and italic within one line, which is
   the single move that makes this read editorial rather than corporate. */
.copy h1 {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2.9rem, 6.4vw, 5.6rem);
  line-height: 1.02;
  letter-spacing: 0.004em;
  margin: 0;
  color: var(--ink);
}
.copy h1 em { font-style: italic; font-weight: 400; }

.sub {
  font-family: var(--mono);
  font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--ink-soft);
  margin: clamp(1.1rem, 2.4vh, 1.6rem) 0 0;
}

/* ---------- search ---------- */

.search-module { margin-top: clamp(1.6rem, 3.4vh, 2.4rem); width: min(420px, 100%); }

.field {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0 0.4rem 0 1rem;
  background: rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  border: 1px solid var(--line);
  border-radius: 100px;
  transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.field:focus-within {
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(16, 26, 34, 0.1);
}
.field-icon { color: var(--ink-faint); flex: none; }
.field:focus-within .field-icon { color: var(--ink); }

#ski-search {
  flex: 1; min-width: 0;
  border: none; background: none; outline: none;
  font-family: var(--mono); font-size: 0.875rem;
  color: var(--ink);
  caret-color: var(--accent);
  padding: 0.9rem 0.4rem 0.9rem 0;
}
#ski-search::placeholder { color: rgba(16, 26, 34, 0.62); }

.results {
  list-style: none; margin: 0.5rem 0 0; padding: 0.3rem;
  max-height: 210px; overflow-y: auto;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 18px 40px -18px rgba(16, 26, 34, 0.38);
}
.results li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.9rem;
  padding: 0.55rem 0.7rem;
  border-radius: 9px;
  cursor: pointer;
  font-size: 0.78rem;
  transition: background 0.16s var(--ease);
}
.results li:hover, .results li[data-active="true"] { background: rgba(16, 26, 34, 0.07); }
.results li .spec { font-size: 0.6875rem; letter-spacing: 0.08em; color: var(--ink-faint); white-space: nowrap; }
.results li.empty { cursor: default; color: var(--ink-faint); }
.results li.empty:hover { background: none; }

.quiver { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0; padding: 0; }
.quiver:not(:empty) { margin-top: 0.75rem; }
.quiver li {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.3rem 0.3rem 0.3rem 0.75rem;
  background: var(--ink); color: var(--paper);
  border-radius: 100px;
  font-size: 0.6875rem; letter-spacing: 0.04em;
  animation: rise 0.4s var(--ease) both;
}
@keyframes rise { from { opacity: 0; transform: translateY(5px); } }
.quiver button {
  width: 21px; height: 21px; flex: none;
  display: inline-grid; place-items: center;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(244, 247, 250, 0.24); color: var(--paper);
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}
.quiver button:hover { background: var(--paper); color: var(--ink); }

.quiver .length-picker {
  font-family: var(--mono);
  font-size: 0.625rem;
  color: var(--paper);
  background: rgba(244, 247, 250, 0.16);
  border: none;
  border-radius: 100px;
  padding: 0.15rem 0.35rem;
  flex-shrink: 0;
}
.quiver .length-picker option { color: var(--ink); }

/* The one primary action in the hero, so it's solid-filled (var(--ink))
   rather than the outlined/frosted treatment everything else in the
   search module uses - it needs to read as "the next step," not
   another passive control next to the search field. Disabled until
   there's at least one ski to search on (see renderQuiver in app.js). */
.search-btn {
  display: block;
  margin-top: 0.85rem;
  padding: 0.75rem 1.4rem;
  background: var(--ink); color: var(--paper);
  border: none; border-radius: 100px;
  font-family: var(--mono);
  font-size: 0.6875rem; font-weight: 500;
  letter-spacing: 0.1em; text-transform: uppercase;
  cursor: pointer;
  transition: opacity 0.25s var(--ease), transform 0.15s var(--ease);
}
.search-btn:hover:not(:disabled) { opacity: 0.85; }
.search-btn:active:not(:disabled) { transform: scale(0.98); }
.search-btn:disabled { opacity: 0.3; cursor: default; }

.readout {
  font-family: var(--mono);
  font-size: 0.625rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0.95rem 0 0;
  line-height: 1.7;
}
.readout .gap { color: var(--accent); font-weight: 500; }

/* ---------- scroll cue ---------- */

/* Fixed to the viewport, not the hero, so it sits where the reader
   actually is — and fades out once they have taken the hint. */
.scroll-cue {
  position: fixed; z-index: 4;
  left: clamp(1.3rem, 4vw, 3.2rem);
  bottom: 0;
  display: flex; flex-direction: column; align-items: center; gap: 0.85rem;
  transition: opacity 0.5s var(--ease);
}
.scroll-cue[data-hidden="true"] { opacity: 0; pointer-events: none; }
.scroll-cue span {
  font-size: 0.5625rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--ink-soft);
  writing-mode: vertical-rl;
}
.scroll-cue::after {
  content: "";
  width: 1px; height: clamp(38px, 7vh, 62px);
  background: linear-gradient(to bottom, var(--ink-soft), transparent);
}

/* ---------- coverage map ----------
   Styling for the class contract coverage-map.js's render functions
   emit (.coverage-map, .ski-mark, .chart-tooltip, ...), values in this
   page's ink/paper/serif language. Sits in its own section directly
   under the hero (see index.html); reveals behind the "See my
   coverage" button, then updates live for any change after that. */

.map-section {
  background: var(--paper);
  padding: clamp(3rem, 8vh, 5rem) clamp(1.3rem, 4vw, 3.2rem) clamp(2.5rem, 6vh, 4rem);
  border-top: 1px solid var(--line);
}
.map-section-inner { max-width: 1080px; margin: 0 auto; }

.map-section h2 {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem);
  line-height: 1.08;
  margin: 0 0 clamp(1.4rem, 3vh, 2rem);
  color: var(--ink);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
.card-header h3 {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
}

.table-toggle-btn {
  background: none;
  border: 1px solid var(--line);
  color: var(--ink-soft);
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.4rem 0.8rem;
  border-radius: 100px;
  cursor: pointer;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.table-toggle-btn:hover { border-color: var(--ink); color: var(--ink); }

.info-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  margin-left: 0.4rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: none;
  color: var(--ink-faint);
  font-size: 0.65rem;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  vertical-align: middle;
}
.info-btn:hover,
.info-btn[aria-pressed="true"] { border-color: var(--ink); color: var(--ink); }

.map-caption {
  margin: 0 0 1rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 62ch;
}
.map-caption .gap { color: var(--accent); }

.map-info-panel {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.7rem;
  line-height: 1.6;
  color: var(--ink-soft);
}
.map-info-panel p { margin: 0.3rem 0; }
.map-info-panel p:first-child { margin-top: 0; }
.map-info-panel p:last-child { margin-bottom: 0; }
.map-info-panel strong { color: var(--ink); }
.map-info-panel--table { margin-bottom: 1rem; }

.chart-wrap { width: 100%; }

.coverage-map { width: 100%; height: auto; display: block; overflow: visible; }

.map-plot-border { fill: none; stroke: var(--line); stroke-width: 1; }
.map-gridline { stroke: var(--chart-gridline); stroke-width: 1; }
.map-axis-label { fill: var(--chart-muted); font-family: var(--mono); font-size: 10px; letter-spacing: 0.02em; }
.map-axis-label-y { font-size: 9.5px; }

.ski-region { fill: var(--chart-accent-fill); stroke: var(--chart-accent-stroke); stroke-width: 1; }
.ski-dot { fill: var(--chart-accent); stroke: var(--paper); stroke-width: 2; }
.ski-hit { fill: transparent; }
.ski-mark-label { fill: var(--ink); font-family: var(--mono); font-size: 10px; }
.ski-mark { cursor: pointer; outline: none; }
.ski-mark:hover .ski-dot,
.ski-mark:focus-visible .ski-dot { r: 7; }
.ski-mark:hover .ski-region,
.ski-mark:focus-visible .ski-region { stroke-width: 2; }
.ski-mark:focus-visible .ski-hit { stroke: var(--ink); stroke-width: 2; fill: none; }

.ski-mark--suggestion .ski-region { fill: var(--chart-suggestion-fill); stroke: var(--chart-suggestion-stroke); }
.ski-mark--suggestion .ski-dot { fill: var(--chart-suggestion); }

.chart-table-wrap { overflow-x: auto; }
.chart-table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 0.75rem; }
.chart-table th,
.chart-table td { border: 1px solid var(--line); padding: 0.55rem 0.7rem; text-align: left; }
.chart-table th {
  font-size: 0.625rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 500;
}
.count-badge {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.1rem 0.45rem;
  font-size: 0.5625rem; letter-spacing: 0.06em; text-transform: uppercase;
  border-radius: 100px;
  background: var(--chart-suggestion-fill);
  color: var(--chart-suggestion);
}

/* Temperament gauge - word + position widget, identical markup in the
   table view and the tooltip. currentColor/inherit for text so it
   reads correctly on the tooltip's inverted background too. */
.temperament-gauge { display: flex; flex-direction: column; gap: 0.3rem; min-width: 108px; }
.temperament-gauge-track { position: relative; height: 4px; border-radius: 100px; background: var(--line); }
.temperament-gauge-divider { position: absolute; top: -2px; bottom: -2px; width: 1px; background: var(--chart-muted); opacity: 0.6; }
.temperament-gauge-dot {
  position: absolute; top: 50%; width: 8px; height: 8px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--chart-accent);
  border: 2px solid var(--paper);
}
.temperament-gauge-caption {
  display: flex; align-items: baseline; gap: 0.35rem;
  font-family: var(--mono); font-size: 0.6875rem;
}
.temperament-gauge-value { color: inherit; opacity: 0.6; font-size: 0.625rem; }

/* "What if I added this" - reuses .search-wrap/.search-results/
   .quiver-chip/.quiver-list styling from the hero search where the
   markup matches; the rest is defined here since the map section has
   no equivalent of those classes yet. */
.candidate-picker {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}
.candidate-picker h4 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.05rem;
  margin: 0 0 0.4rem;
  color: var(--ink);
}
.candidate-picker .search-wrap { position: relative; max-width: 420px; }
.candidate-picker .search-input {
  width: 100%;
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 100px;
  padding: 0.7rem 1rem;
}
.candidate-picker .search-input:focus {
  outline: none;
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(16, 26, 34, 0.1);
}
.candidate-picker .search-input:disabled { opacity: 0.6; cursor: not-allowed; }
.candidate-picker .search-results {
  list-style: none;
  position: absolute;
  z-index: 3;
  left: 0; right: 0; top: calc(100% + 0.4rem);
  margin: 0; padding: 0.3rem;
  max-height: 220px; overflow-y: auto;
  background: var(--paper-raised);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 14px 32px -16px rgba(16, 26, 34, 0.3);
}
.candidate-picker .search-results li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem;
  padding: 0.5rem 0.65rem;
  border-radius: 8px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.75rem;
}
.candidate-picker .search-results li:hover { background: rgba(16, 26, 34, 0.06); }
.candidate-picker .search-results li .ski-spec { color: var(--ink-faint); font-size: 0.7rem; }
.candidate-picker .search-results li.no-match { cursor: default; color: var(--ink-faint); }
.candidate-picker .search-results li.no-match:hover { background: none; }
.candidate-picker .quiver-list {
  list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem;
  margin: 0.85rem 0 0; padding: 0;
}
.candidate-picker .quiver-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.3rem 0.3rem 0.3rem 0.75rem;
  background: var(--chart-suggestion-fill);
  color: var(--chart-suggestion);
  border-radius: 100px;
  font-family: var(--mono); font-size: 0.6875rem; letter-spacing: 0.02em;
}
.candidate-picker .quiver-chip button {
  width: 19px; height: 19px; flex: none;
  display: inline-grid; place-items: center;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(193, 55, 43, 0.16); color: var(--chart-suggestion);
  transition: background 0.2s var(--ease);
}
.candidate-picker .quiver-chip button:hover { background: var(--chart-suggestion); color: var(--paper); }
.candidate-picker .length-picker {
  font-family: var(--mono);
  font-size: 0.625rem;
  color: var(--chart-suggestion);
  background: rgba(193, 55, 43, 0.1);
  border: none;
  border-radius: 100px;
  padding: 0.15rem 0.35rem;
  flex-shrink: 0;
}
.candidate-picker .length-picker option { color: var(--ink); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Tooltip - inverted relative to the page (dark bg / light text),
   positioned in JS (see coverage-map.js positionTooltip). */
.chart-tooltip {
  position: fixed; z-index: 20;
  max-width: 240px;
  padding: 0.85rem 1rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: 10px;
  font-family: var(--mono);
  box-shadow: 0 18px 40px -16px rgba(16, 26, 34, 0.5);
  pointer-events: none;
}
.tooltip-title { font-family: var(--serif); font-size: 1.05rem; font-weight: 400; color: var(--paper); }
.tooltip-year { font-size: 0.65rem; letter-spacing: 0.04em; color: rgba(244, 247, 250, 0.6); margin: 0.15rem 0 0.7rem; }
.tooltip-spec-grid { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.7rem; padding-bottom: 0.7rem; border-bottom: 1px solid var(--tooltip-divider); }
.tooltip-spec-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; font-size: 0.6875rem; }
.tooltip-spec-label { color: rgba(244, 247, 250, 0.55); }
.tooltip-spec-value { color: var(--paper); }
.tooltip-gauge-wrap .temperament-gauge-track { background: rgba(244, 247, 250, 0.2); }
.tooltip-gauge-wrap .temperament-gauge-dot { background: var(--paper); border-color: var(--ink); }
.tooltip-gauge-wrap .temperament-gauge-caption { color: var(--paper); }

/* ---------- quiver summary + condition cards ----------
   Styling for the class contract condition-cards.js's render
   functions emit. The summary sits above the map as the section's
   TL;DR; condition cards sit below it as their own block. */

/* First-reveal stagger: the four content blocks below the hero arrive
   in one short sequence (summary, then map+cards, then details) rather
   than all at once - see playMapReveal() in app.js. Re-renders after
   the first (a chip toggle, a length change) skip this entirely; only
   .is-in state applies, so nothing re-animates on every update. */
#map-section .stage {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
#map-section .stage.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  #map-section .stage { transition: none; }
}

.summary-block {
  margin-bottom: clamp(2rem, 5vh, 3rem);
}
.quiver-summary-text {
  margin: 0;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.3rem, 2.2vw, 1.7rem);
  line-height: 1.45;
  max-width: 62ch;
  color: var(--ink);
}
.quiver-summary-text strong { color: var(--accent); font-weight: 500; }

.condition-block { margin-top: clamp(2.4rem, 6vh, 3.6rem); }
.condition-block h3 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.3rem;
  margin: 0 0 0.3rem;
  color: var(--ink);
}

.condition-cards {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.7rem;
  margin-top: 1rem;
}
.condition-card {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  border-radius: 4px;
  border-left: 3px solid transparent;
  padding: 0.9rem 1rem;
  background: var(--paper-raised);
}
.condition-card[data-status="good"] { background: var(--status-good-bg); border-left-color: var(--status-good); }
.condition-card[data-status="warning"] { background: var(--status-warning-bg); border-left-color: var(--status-warning); }
.condition-card[data-status="critical"] { background: var(--status-critical-bg); border-left-color: var(--status-critical); }

.condition-icon { font-size: 0.9rem; font-weight: 700; line-height: 1.4; font-family: var(--mono); }
.condition-card[data-status="good"] .condition-icon { color: var(--status-good); }
.condition-card[data-status="warning"] .condition-icon { color: var(--status-warning); }
.condition-card[data-status="critical"] .condition-icon { color: var(--status-critical); }

.condition-body { min-width: 0; }
.condition-desc {
  margin: 0 0 0.25rem;
  font-family: var(--serif);
  font-size: 1.05rem;
  font-weight: 400;
  color: var(--ink);
  line-height: 1.3;
}
.condition-detail {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* ---------- collapsible plain-language detail ----------
   Styling for condition-cards.js's renderDetailsSection - the
   bullet-point gap/redundancy list, kept as a fallback under the
   map/condition cards for anyone who wants the raw per-bucket
   detail. */
.result-details {
  margin-top: clamp(2rem, 5vh, 3rem);
  border-top: 1px solid var(--line);
  padding-top: 1.2rem;
}
.result-details summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.6875rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-soft);
}
.result-details summary:hover { color: var(--ink); }
.result-group { margin: 1.25rem 0; }
.result-group:last-child { margin-bottom: 0; }
.result-group h4 {
  margin: 0 0 0.6rem;
  font-family: var(--serif);
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink);
}
.result-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column;
  gap: 0.5rem;
}
.result-item {
  border-radius: 4px;
  padding: 0.75rem 0.9rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.6;
  display: flex; align-items: flex-start;
  gap: 0.5rem;
}
.result-suggestion {
  display: inline-block;
  font-style: italic;
  color: var(--ink-faint);
}
.result-item.gap { background: var(--status-critical-bg); }
.result-item.redundancy { background: var(--status-warning-bg); }
.result-item.ok { background: var(--status-good-bg); }
.status-icon { font-weight: 700; flex-shrink: 0; line-height: 1.5; }
.status-icon.status-good { color: var(--status-good); }
.status-icon.status-warning { color: var(--status-warning); }
.status-icon.status-critical { color: var(--status-critical); }

@media (max-width: 680px) {
  .condition-cards { grid-template-columns: 1fr; }
}

@media (max-width: 680px) {
  .map-section { padding: clamp(2.4rem, 7vh, 3.6rem) 1.1rem 2.2rem; }
  .candidate-picker .search-wrap { max-width: none; }
}

/* ---------- zones ---------- */

.zones {
  background: var(--paper);
  padding: clamp(3rem, 8vh, 5.5rem) clamp(1.3rem, 4vw, 3.2rem) clamp(3.4rem, 9vh, 6rem);
}
.zones-inner { max-width: 1080px; margin: 0 auto; }

.zones h2 {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2rem, 4.2vw, 3.4rem);
  line-height: 1.06;
  margin: 0 0 clamp(2.2rem, 5vh, 3.4rem);
  color: var(--ink);
}
.zones h2 em { font-style: italic; }

.zone-grid {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
.zone-grid li {
  background: var(--paper);
  padding: clamp(1.1rem, 2.4vw, 1.7rem);
  display: flex; flex-direction: column;
  /* transition is a shorthand, so listing only "background" here was
     silently replacing .reveal's opacity/transform transition (higher
     specificity: element+class beats class alone) - the scroll-reveal
     motion was getting clobbered to nothing, and cards snapped straight
     to opacity:1 instead of fading in. List all three properties so
     hover and reveal share one declaration instead of one erasing the
     other. */
  transition: background 0.4s var(--ease), opacity 0.85s var(--ease), transform 0.85s var(--ease);
}
.zone-grid li:hover { background: #FFFFFF; }
.zone-n {
  /* Smallest + lowest-contrast text on the page (--ink-faint, 4.11:1),
     on the one section meant to teach a first-timer what the zones
     mean - moved up to the Label scale's more common step rather than
     its floor (DESIGN.md: 0.5625rem-0.7rem, "most commonly 0.625rem"). */
  font-size: 0.625rem; letter-spacing: 0.18em;
  color: var(--ink-faint);
  margin-bottom: 0.9rem;
}
.zone-w {
  font-family: var(--serif);
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  font-weight: 400; line-height: 1.1;
  color: var(--ink);
}
.zone-f {
  font-family: var(--serif);
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  font-style: italic; font-weight: 300; line-height: 1.1;
  color: var(--ink-soft);
  margin-bottom: 0.8rem;
}
.zone-grid p {
  font-size: 0.6875rem; line-height: 1.75;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  margin: auto 0 0;
}

/* ---------- pitch ---------- */
/* The outcome-focused sales pitch, right after the hero - not a
   rewrite of .method below, a separate section. .method stays put
   further down the page as a short method teaser (links to
   method.html) for anyone who scrolls that far and remains the page's
   one dark ("ink") moment; this one sells the result to everyone
   else. Kept on paper deliberately - an
   ink spread landing this early, right off the hero photo, read as a
   stark flip rather than a deliberate beat (no established
   paper/ink rhythm yet to place it in). Matches .map-section and
   .zones on either side instead. */
.pitch {
  background: var(--paper);
  padding: clamp(3rem, 8vh, 5rem) clamp(1.3rem, 4vw, 3.2rem);
}
.pitch-inner { max-width: 1080px; margin: 0 auto; }
.pitch-lead {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.5rem, 3.2vw, 2.6rem);
  line-height: 1.34;
  margin: 0;
  max-width: 32ch;
  color: var(--ink);
}
.pitch-lead em { font-style: italic; }

/* ---------- method ---------- */

.method {
  background: var(--ink);
  color: var(--paper);
  padding: clamp(4rem, 11vh, 7rem) clamp(1.3rem, 4vw, 3.2rem);
}
.method-inner { max-width: 1080px; margin: 0 auto; }
.method h2 {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem);
  line-height: 1.08;
  margin: 0 0 clamp(1.4rem, 3vh, 2rem);
  color: var(--paper);
}
.method-lead {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.5rem, 3.2vw, 2.6rem);
  line-height: 1.34;
  margin: 0 0 clamp(1.8rem, 4vh, 2.6rem);
  max-width: 26ch;
  color: var(--paper);
}
.method-lead em { font-style: italic; }
.method-link {
  display: inline-block;
  font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--paper);
  text-decoration: none;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid rgba(244, 247, 250, 0.35);
  transition: border-color 0.3s var(--ease);
}
.method-link:hover { border-bottom-color: var(--paper); }

/* ---------- foot ---------- */

.foot {
  background: var(--ink);
  color: rgba(244, 247, 250, 0.6);
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.7rem;
  padding: 0 clamp(1.3rem, 4vw, 3.2rem) clamp(2.2rem, 5vh, 3rem);
  font-size: 0.5625rem; letter-spacing: 0.2em; text-transform: uppercase;
}
.foot a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid rgba(244, 247, 250, 0.35);
  padding-bottom: 0.15rem;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.foot a:hover { color: var(--paper); border-bottom-color: var(--paper); }

/* ---------- motion ---------- */

@media (prefers-reduced-motion: no-preference) {
  .copy h1 { animation: lift 1.25s var(--ease) 0.12s both; }
  .sub { animation: lift 1s var(--ease) 0.4s both; }
  .search-module { animation: lift 1s var(--ease) 0.54s both; }
  .nav { animation: lift 0.9s var(--ease) both; }
  .scroll-cue { animation: lift 1s var(--ease) 0.9s both; }
  @keyframes lift {
    from { opacity: 0; transform: translateY(15px); }
    to   { opacity: 1; transform: none; }
  }

  /* Content arrives as you reach it rather than sitting there already
     finished. Only opacity and transform animate, so this stays on the
     compositor and never triggers layout while scrolling. */
  .reveal {
    opacity: 0;
    transform: translateY(22px);
    transition: opacity 0.85s var(--ease), transform 0.85s var(--ease);
  }
  .reveal.is-in { opacity: 1; transform: none; }

  /* The nine zones stagger in sequence so the grid reads as a list
     being laid out rather than a block appearing. 55ms was too tight
     against the three-column layout - three cards in one row differ by
     only 110ms total, which reads as one row snapping in rather than a
     cascade. 110ms/card gives each row a visible left-to-right beat
     while the whole grid still finishes well under a second. */
  .zone-grid li { transition-delay: calc(var(--i, 0) * 110ms); }
}

/* Without JS, or with reduced motion, everything is simply visible. */
.no-js .reveal, .reveal.is-in { opacity: 1; transform: none; }

/* ---------- responsive ---------- */

@media (max-width: 900px) {
  .copy { max-width: 68%; }
}
@media (max-width: 680px) {
  .nav nav { display: none; }
  .scroll-cue { display: none; }
  /* Keep desktop's text-over-image composition rather than splitting
     photo and type into separate bands. The jacket occupies 44-55% of
     the source width and 33-48% of its height (measured), so a plain
     `cover` crop on a tall viewport puts the figure exactly where the
     headline goes. Instead the image is scaled up and anchored so the
     skier reads large in the upper half, with the type on the churned
     snow below — the pole line runs down toward the headline the way
     it does on desktop.

     `background-size: 260% auto` leaves the lower frame unpainted, so
     a second layer continues the slope. Its start colour is sampled
     from the actual pixels along the crop's bottom edge —
     rgb(213,235,244) — rather than guessed; an eyeballed value left a
     visible seam. It then drifts a couple of steps lighter, which
     reads as the slope running out of frame rather than as a panel. */
  .hero-photo {
    background-image: linear-gradient(to bottom,
      rgb(213, 235, 244) 0%,
      rgb(221, 239, 247) 46%,
      rgb(230, 243, 249) 100%);
  }
  /* The photo sits in its own layer so its lower edge can be masked to
     transparent. Handing off with two stacked backgrounds left a hard
     line where the image stopped; a mask dissolves the last 90px of
     the crop into the gradient instead. */
  .hero-photo::before {
    content: "";
    position: absolute; inset: 0;
    background-image: var(--hero);
    background-repeat: no-repeat;
    background-size: 260% auto;
    background-position: 52% 26%;
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 80%);
    mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 80%);
  }
  /* The mobile crop already ends in its own sampled gradient, so the
     hero's fade-out would stack a second ramp on top and produce a
     visible band. One gradient only. */
  .hero::after { display: none; }
  .copy {
    max-width: none;
    justify-content: flex-end;
    padding-bottom: clamp(2.2rem, 5vh, 3rem);
  }
  .copy h1 { font-size: clamp(2.5rem, 11.5vw, 3.4rem); }
  #ski-search { font-size: 1rem; }
  .search-module { width: 100%; }
}
