/* Windsock.
 *
 * The design brief is an instrument, not a brochure. A student checks this on a
 * phone in a car park with ten minutes before a lesson, so the priorities are:
 * the numbers are large and unambiguous, colour means exactly one thing, and
 * nothing decorative competes with the data.
 *
 * Colour is used for verdict and for danger only. Everything else is greyscale.
 * That is what keeps a red crosswind figure meaning something.
 *
 * System fonts throughout. No webfonts, so nothing blocks the first paint and
 * no third party sees a request.
 */

:root {
  --ws-bg: #fbfaf8;
  --ws-surface: #ffffff;
  --ws-line: #ddd8d0;
  --ws-line-soft: #ece8e2;
  --ws-ink: #1c1a17;
  --ws-ink-soft: #5d574e;
  --ws-ink-faint: #857e73;

  /* Verdict. Desaturated on purpose: these sit next to each other in a long
     list and saturated greens and reds turn it into a fruit machine. */
  --ws-good: #2f6b46;
  --ws-marginal: #97631a;
  --ws-no: #9a3324;
  --ws-unknown: #6b6459;

  --ws-danger: #9a3324;
  --ws-accent: #1c4f7c;

  --ws-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --ws-sans: system-ui, -apple-system, "Segoe UI", sans-serif;

  --ws-measure: 38rem;
  --ws-radius: 3px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ws-bg: #14130f;
    --ws-surface: #1c1a16;
    --ws-line: #35312a;
    --ws-line-soft: #27241e;
    --ws-ink: #ece7dd;
    --ws-ink-soft: #b0a89a;
    --ws-ink-faint: #837b6d;

    --ws-good: #6fbf8d;
    --ws-marginal: #d9a441;
    --ws-no: #e08472;
    --ws-unknown: #8d8578;

    --ws-danger: #e08472;
    --ws-accent: #7fb4e0;
  }
}

* { box-sizing: border-box; }

html {
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ws-bg);
  color: var(--ws-ink);
  font-family: var(--ws-sans);
  font-size: 16px;
  line-height: 1.45;
}

a { color: var(--ws-accent); }
a:hover { text-decoration-thickness: 2px; }

/* Focus must stay obvious. This gets used one-handed on a phone in daylight. */
:focus-visible {
  outline: 2px solid var(--ws-accent);
  outline-offset: 2px;
}

.ws-sr,
.ws-skip:not(:focus) {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.ws-skip:focus {
  position: static;
  display: block;
  padding: 0.6rem 1rem;
  background: var(--ws-accent);
  color: #fff;
}

/* --- frame ------------------------------------------------------------- */

.ws-head { border-bottom: 1px solid var(--ws-line); background: var(--ws-surface); }

.ws-head-in,
main,
.ws-foot {
  max-width: 62rem;
  margin: 0 auto;
  padding: 0 1rem;
}

.ws-head-in { padding-top: 0.9rem; padding-bottom: 0.9rem; }

.ws-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.15rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--ws-ink);
  text-decoration: none;
}

/* A windsock, drawn in CSS rather than shipped as an asset. */
.ws-brand-mark {
  width: 0; height: 0;
  border-left: 14px solid var(--ws-danger);
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
}

.ws-tag { margin: 0.15rem 0 0; color: var(--ws-ink-faint); font-size: 0.85rem; }

/* The disclaimer is permanent and above the content, not tucked in a footer.
   People act on this page. */
.ws-warn {
  margin: 0;
  padding: 0.55rem 1rem;
  background: var(--ws-danger);
  color: #fff;
  font-size: 0.85rem;
  text-align: center;
}
.ws-warn strong { font-weight: 700; }

main { padding-top: 1.5rem; padding-bottom: 3rem; }

.ws-foot {
  border-top: 1px solid var(--ws-line);
  padding-top: 1.2rem;
  padding-bottom: 2.5rem;
  color: var(--ws-ink-faint);
  font-size: 0.82rem;
}
.ws-foot p { max-width: var(--ws-measure); }

.ws-stale {
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--ws-danger);
  border-left-width: 4px;
  border-radius: var(--ws-radius);
  background: var(--ws-surface);
  color: var(--ws-ink);
}

/* --- limits form -------------------------------------------------------- */

.ws-controls { margin-bottom: 1.5rem; }
.ws-controls fieldset {
  margin: 0;
  border: 1px solid var(--ws-line);
  border-radius: var(--ws-radius);
  background: var(--ws-surface);
  padding: 0.75rem 1rem 1rem;
}
.ws-controls legend { padding: 0 0.4rem; font-weight: 650; font-size: 0.9rem; }
.ws-controls-why {
  margin: 0 0 0.75rem;
  max-width: var(--ws-measure);
  color: var(--ws-ink-faint);
  font-size: 0.82rem;
}

.ws-fields { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: flex-end; }
.ws-fields label { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.78rem; }
.ws-fields label > span:first-child { color: var(--ws-ink-soft); }
.ws-fields input {
  width: 5.5rem;
  padding: 0.35rem 0.45rem;
  border: 1px solid var(--ws-line);
  border-radius: var(--ws-radius);
  background: var(--ws-bg);
  color: var(--ws-ink);
  font: 600 1rem/1.2 var(--ws-mono);
}
.ws-unit { color: var(--ws-ink-faint); font-size: 0.72rem; }
.ws-fields button {
  padding: 0.45rem 1.1rem;
  border: 1px solid var(--ws-accent);
  border-radius: var(--ws-radius);
  background: var(--ws-accent);
  color: #fff;
  font: 600 0.9rem var(--ws-sans);
  cursor: pointer;
}

/* --- the list ----------------------------------------------------------- */

/* One heading scale for the front page's sections. The PIREP heading once
   fell outside every scoped rule and rendered at browser-default 1.5em,
   louder than the ranking heading that is the page's point. */
.ws-list h2,
.ws-pireps h2,
.ws-advisories h2 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  margin: 0 0 0.9rem;
  font-size: 1rem;
  font-weight: 650;
}
.ws-pireps, .ws-advisories { margin-top: 2rem; }
/* --ws-ink-soft, not faint: faint measures 4.02:1 on the card surface and
   4.15:1 in dark mode, both under AA, and this token now qualifies the largest
   figure on the card. */
.ws-asof { color: var(--ws-ink-soft); font-size: 0.8rem; font-weight: 400; }

.ws-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }

.ws-card {
  background: var(--ws-surface);
  border: 1px solid var(--ws-line);
  /* The verdict is a left edge, not a filled panel. A wall of coloured blocks
     stops meaning anything by the fourth one. */
  border-left: 4px solid var(--ws-unknown);
  border-radius: var(--ws-radius);
  padding: 0.7rem 0.9rem 0.8rem;
}
/* Every verdict has a rule below, so this only shows if one is ever added
   without one. It has to come FIRST: these all have the same specificity, so
   whichever is written last wins, which is how the detail page ended up grey. */
.ws-card,
.ws-detail-page { border-left-color: var(--ws-unknown); }
.ws-v-good { border-left-color: var(--ws-good); }
.ws-v-marginal { border-left-color: var(--ws-marginal); }
.ws-v-no { border-left-color: var(--ws-no); }
/* Grey covers both "could not judge it" and "never an option": neither is a
   verdict about the weather, and the badge word is what tells them apart.
   Spending a fifth colour here would break the rule that a colour on this page
   means exactly one thing. */
.ws-v-unknown,
.ws-v-unavailable { border-left-color: var(--ws-unknown); }

.ws-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.ws-card h3 { margin: 0; font-size: 1rem; font-weight: 650; }
.ws-card h3 a { font: 700 1.15rem/1 var(--ws-mono); text-decoration: none; color: var(--ws-ink); }
.ws-card h3 a:hover { color: var(--ws-accent); text-decoration: underline; }
.ws-name { color: var(--ws-ink-soft); font-weight: 400; font-size: 0.85rem; }

.ws-verdict { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
.ws-v-good .ws-verdict { color: var(--ws-good); }
.ws-v-marginal .ws-verdict { color: var(--ws-marginal); }
.ws-v-no .ws-verdict { color: var(--ws-no); }
.ws-v-unknown .ws-verdict,
.ws-v-unavailable .ws-verdict { color: var(--ws-unknown); }

/* --- the wind, and what it does to a runway ------------------------------
 *
 * One grammar for the whole readout and only one: a figure is mono at full
 * ink, and every word about a figure is small soft sans. That is what answers
 * "this is a lot of plain text" without abbreviating anything a reader, or a
 * screen reader, then has to decode. HW/XW were considered and rejected: every
 * pilot-facing crosswind calculator spells both words out, so the pair is not
 * notation a student already reads, and this project does not coin its own.
 *
 * The observed wind leads because it is the only measured number on the card
 * and the one a student checks against ATIS.
 */

.ws-wind {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.75rem;
  margin: 0.5rem 0 0.15rem;
  min-width: 0;
}

.ws-best {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.85rem;
  margin: 0.15rem 0 0.45rem;
  min-width: 0;
}

/* A figure and the words bound to it. inline-block rather than a nested flex,
   so ordinary word spacing works inside it, it wraps at its own spaces on a
   narrow screen, and it still reads as one run if the flex above never
   applies. No fixed track widths anywhere: the line takes another row instead
   of pushing the page sideways under text-only zoom. */
.ws-tok { display: inline-block; }

.ws-fig {
  font: 600 0.95rem/1.35 var(--ws-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  color: var(--ws-ink);
  white-space: nowrap;
}
.ws-wind .ws-fig { font-size: 1.05rem; font-weight: 700; }

.ws-rwy { font: 700 0.95rem/1.35 var(--ws-mono); letter-spacing: 0.02em; white-space: nowrap; }

/* Every word on these two lines. --ws-ink-soft, not --ws-ink-faint: these
   labels are the only thing saying which figure is which, and faint measures
   4.02:1 on the card surface and 4.15:1 in dark mode, both under AA. Soft
   measures 7.15:1 and 7.37:1. */
.ws-lbl { font: 400 0.72rem/1.45 var(--ws-sans); color: var(--ws-ink-soft); }

/* A word standing where a figure would be: calm, variable, not reported, any
   runway. It carries the weight of the figure it replaces, so the cards with
   the least to say do not look blank. */
.ws-word { font: 600 0.9rem/1.35 var(--ws-sans); color: var(--ws-ink); }

/* Where the two figures came from. Printed only when that is not simply the
   wind on the line above, so a steady card carries nothing at all. */
.ws-basis { font: 400 0.72rem/1.45 var(--ws-sans); color: var(--ws-ink-soft); }

/* Too old to plan on, so not the headline of anything. Keyed to the verdict
   class the server already sets, so a renamed line degrades to stock emphasis
   rather than a dead rule. Never the only signal: the age sits beside it and
   the gate reason underneath says how old. A ruled-out field keeps its
   full-weight wind on purpose, since Moffett is on this page for weather alone. */
.ws-v-unknown .ws-wind .ws-fig,
.ws-v-unknown .ws-wind .ws-word { color: var(--ws-ink-soft); font-weight: 600; }

/* Danger, and never on its own: every red thing here also carries a word
   ("tailwind", "over limit") and is restated in the reasons below. Compound
   selectors, so a stylesheet reorder cannot silently drop the only red on the
   page: .ws-fig and .ws-bad would otherwise set colour at equal specificity. */
.ws-bad { color: var(--ws-danger); font-weight: 700; }
.ws-fig.ws-bad { color: var(--ws-danger); font-weight: 700; }
.ws-flag.ws-flag-bad { border-color: var(--ws-danger); color: var(--ws-danger); }

@media (forced-colors: active) {
  /* Colour is gone in this mode, including the verdict edge. Outline and
     border-width survive it. Both are a third signal on top of a word, not a
     rescue. */
  .ws-fig.ws-bad { outline: 1px solid currentColor; outline-offset: 2px; }
  .ws-flag.ws-flag-bad { border-width: 2px; }
}

.ws-flag {
  display: inline-block;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--ws-line);
  border-radius: 2px;
  color: var(--ws-ink-soft);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* The wind and its age have left this row, so what remains really is context.
   It is still text a pilot reads, so it keeps the legible ink. */
.ws-cond {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.85rem;
  margin: 0;
  color: var(--ws-ink-soft);
  font-size: 0.78rem;
}

.ws-reasons { list-style: none; margin: 0.55rem 0 0; padding: 0; display: grid; gap: 0.25rem; }
.ws-reasons li { font-size: 0.82rem; padding-left: 0.85rem; position: relative; }
.ws-reasons li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.55em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--ws-ink-faint);
}
.ws-r-gate { color: var(--ws-ink); font-weight: 600; }
.ws-r-gate::before { background: var(--ws-no) !important; }
.ws-r-penalty::before { background: var(--ws-marginal) !important; }
.ws-r-note { color: var(--ws-ink-soft); }
.ws-r-more::before { display: none; }
.ws-r-more { padding-left: 0; font-size: 0.78rem; }
.ws-detail { color: var(--ws-ink-faint); font-weight: 400; }
.ws-muted { color: var(--ws-ink-faint); }

/* --- airport page ------------------------------------------------------- */

.ws-back { margin-bottom: 1rem; font-size: 0.85rem; }

.ws-detail-page {
  /* Width and style only. The `border-left` shorthand used to sit here and
     restated the grey, which at equal specificity and a later source position
     beat every .ws-v-* colour above: this page painted an unknown-grey stripe
     on a GOOD field. The verdict rules own the colour; this rule must not
     mention it. */
  border-left-width: 4px;
  border-left-style: solid;
  padding-left: 1rem;
}
.ws-detail-head h1 { margin: 0; font-size: 1.5rem; }
.ws-detail-head h1 { font-family: var(--ws-mono); }
.ws-detail-head .ws-name { font-family: var(--ws-sans); font-size: 1rem; }
.ws-sub { margin: 0.2rem 0 1.25rem; color: var(--ws-ink-faint); font-size: 0.85rem; }

.ws-detail-page h2 {
  margin: 1.75rem 0 0.6rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ws-ink-faint);
  font-weight: 700;
}

.ws-gates {
  border: 1px solid var(--ws-no);
  border-radius: var(--ws-radius);
  padding: 0.5rem 1rem 0.9rem;
  background: var(--ws-surface);
}
.ws-gates h2 { margin-top: 0.6rem; color: var(--ws-no); }
/* The gate box was red for every verdict that produces one, so "No call" and
   "Not an option" both arrived in the colour that means today's weather ruled
   this out. Red is reserved for the answer that changes. */
.ws-v-unknown .ws-gates,
.ws-v-unavailable .ws-gates { border-color: var(--ws-unknown); }
.ws-v-unknown .ws-gates h2,
.ws-v-unavailable .ws-gates h2 { color: var(--ws-unknown); }
.ws-gates ul { margin: 0; padding-left: 1.1rem; }

.ws-metar {
  margin: 0 0 1rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--ws-line);
  border-radius: var(--ws-radius);
  background: var(--ws-surface);
  font: 0.85rem/1.5 var(--ws-mono);
  white-space: pre-wrap;
  word-break: break-word;
}

.ws-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 0.6rem; margin: 0; }
.ws-facts dt { color: var(--ws-ink-faint); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; }
.ws-facts dd { margin: 0.1rem 0 0; font: 600 0.95rem var(--ws-mono); }

/* Wide content scrolls inside its own box; the page body never does. */
.ws-scroll { overflow-x: auto; }
.ws-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.ws-table th, .ws-table td { padding: 0.45rem 0.6rem; text-align: left; border-bottom: 1px solid var(--ws-line-soft); }
.ws-table thead th {
  border-bottom: 1px solid var(--ws-line);
  color: var(--ws-ink-faint);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.ws-table tbody th { font-family: var(--ws-mono); font-weight: 700; white-space: nowrap; }
.ws-table td { font-family: var(--ws-mono); }
.ws-table .ws-detail { display: block; font-size: 0.72rem; font-family: var(--ws-sans); }
.ws-is-best { background: color-mix(in srgb, var(--ws-good) 8%, transparent); }

.ws-raw { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.ws-raw li {
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--ws-line);
  border-radius: var(--ws-radius);
  background: var(--ws-surface);
  font: 0.8rem/1.5 var(--ws-mono);
  word-break: break-word;
}
.ws-n-critical { border-left: 3px solid var(--ws-no); }
.ws-n-relevant { border-left: 3px solid var(--ws-marginal); }
/* Unclassified means "read it": the triage module's own doctrine is that an
   unrecognised NOTAM ranks as worth reading, so it cannot carry the least
   visual weight in the band. */
.ws-n-unknown { border-left: 3px solid var(--ws-accent); }

details summary { cursor: pointer; margin: 0.7rem 0; font-size: 0.85rem; color: var(--ws-ink-soft); }

.ws-note {
  margin: 0.9rem 0 0;
  max-width: var(--ws-measure);
  color: var(--ws-ink-soft);
  font-size: 0.85rem;
}

/* --- prose -------------------------------------------------------------- */

.ws-prose { max-width: var(--ws-measure); }
.ws-prose h1 { font-size: 1.5rem; margin: 0 0 1rem; }
.ws-prose h2 { font-size: 1rem; margin: 2rem 0 0.5rem; }
.ws-prose p { margin: 0 0 0.9rem; }

/* --- map ---------------------------------------------------------------- */

.ws-map {
  /* Tall enough that fitting Santa Rosa to Monterey does not zoom the Bay down
     to a smudge: the airports span about 130 nm north to south, so a short wide
     box fits on latitude and wastes the width. */
  height: 30rem;
  margin: 0 0 1.5rem;
  border: 1px solid var(--ws-line);
  border-radius: var(--ws-radius);
  background: var(--ws-surface);
  overflow: hidden;
}
.ws-map:empty::after {
  content: "Map needs JavaScript. Everything it shows is in the list below.";
  display: grid;
  place-items: center;
  height: 100%;
  padding: 1rem;
  color: var(--ws-ink-faint);
  font-size: 0.85rem;
  text-align: center;
}
/* JavaScript ran and the map still failed: a different message from the
   :empty one, because "needs JavaScript" would be the wrong diagnosis. */
.ws-map-failed {
  display: grid;
  place-items: center;
  height: 100%;
  margin: 0;
  padding: 1rem;
  color: var(--ws-ink-faint);
  font-size: 0.85rem;
  text-align: center;
}
.ws-map { position: relative; }
.ws-map-legend {
  position: absolute;
  left: 0.5rem;
  bottom: 0.4rem;
  z-index: 2;
  margin: 0;
  padding: 0.1rem 0.4rem;
  background: color-mix(in srgb, var(--ws-bg) 82%, transparent);
  border-radius: var(--ws-radius);
  color: var(--ws-ink-faint);
  font-size: 0.72rem;
}

@media (max-width: 34rem) {
  .ws-wind { gap: 0.15rem 0.55rem; }
  .ws-wind .ws-fig { font-size: 1rem; }
  .ws-best { gap: 0.15rem 0.6rem; }
  .ws-card-head { flex-direction: column; gap: 0.1rem; }
  .ws-fields input { width: 4.5rem; }
  .ws-detail-page { padding-left: 0.7rem; }
}

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