/* ============================================================
   WEATHER EFFECT — stylesheet for the cycling kit app
   ------------------------------------------------------------
   Design brief: an instrument, not a lifestyle app. Read on a
   phone at 06:40 in December, outdoors, in poor light, often
   with cold hands. Dark-first for that reason; light mode is
   for daylight and print, not the other way round.

   Fonts (add to <head>):
   <link rel="preconnect" href="https://fonts.googleapis.com">
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
   <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet">
   ============================================================ */

/* ---------- 1. Tokens ---------------------------------------- */

:root {
  /* Temperature ramp. This is data, not decoration: every colour
     in the app that isn't chrome comes from this scale. */
  --t-frozen:  #2E6C9E;   /* below -2 */
  --t-cold:    #4A8299;   /*  -2 – 4  */
  --t-cool:    #6E9A93;   /*   4 – 10 */
  --t-mild:    #A3A075;   /*  10 – 15 */
  --t-warm:    #C9A03F;   /*  15 – 20 */
  --t-hot:     #DF8B26;   /*  20 – 25 */
  --t-baking:  #C85A2B;   /*  above 25 */

  --ramp: linear-gradient(90deg,
    var(--t-frozen) 0%, var(--t-cold) 20%, var(--t-cool) 38%,
    var(--t-mild) 54%, var(--t-warm) 70%, var(--t-hot) 86%, var(--t-baking) 100%);

  /* Set per component to tint its edge. Defaults to mild. */
  --temp: var(--t-mild);
  --temp-ink: var(--t-mild-ink);   /* the same band, as readable text */

  /* Semantic */
  /* --signal (gaps, warnings, "your kit runs out here") is set per theme below:
     it has to read as text on both grounds, and the old #C8452C sat 4 units from
     "baking", so a warning on a hot day looked like part of the scale. */
  --plus:    #D9A441;   /* adjustments that add degrees */
  --minus:   #6FA8C7;   /* adjustments that subtract */

  /* Type */
  --display: "Barlow Condensed", "Arial Narrow", sans-serif;
  --body: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --data: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;

  --step--3: 0.62rem;   /* scale ticks, table heads — the smallest text there is */
  --step--2: 0.7rem;    /* metadata lines, small labels */
  --step--1: 0.78rem;
  --step-0:  0.95rem;
  --step-1:  1.15rem;
  --step-2:  1.6rem;
  --step-3:  2.4rem;
  --step-4:  clamp(3rem, 14vw, 4.6rem);   /* the ride-feel number */

  /* Space — 4px base */
  --sp-1: 0.25rem; --sp-2: 0.5rem;  --sp-3: 0.75rem;
  --sp-4: 1rem;    --sp-5: 1.5rem;  --sp-6: 2rem;    --sp-7: 3rem;

  /* The ride screen is a reading column and wants to stay narrow; the gear
     table is a spreadsheet and does not. Set per screen. */
  --shell: 44rem;

  --tap: 44px;              /* minimum touch target */
  --edge: 3px;              /* the temperature edge on surfaces */
  --radius: 2px;            /* nearly square: instrument, not app-store */

  --ease: cubic-bezier(.2, .7, .3, 1);
}

/* Dark is the default. */
:root,
[data-theme="dark"] {
  --bg:        #0E1A24;
  --surface:   #16242F;
  --surface-2: #1E303C;
  --line:      #2A3E4B;
  --text:      #E8EDEB;
  --text-dim:  #8CA3AF;
  --shadow: 0 1px 0 rgba(255,255,255,.04) inset;
  --signal:    #F95C8F;
  /* The ramp as TEXT. Bars and edges use the ramp itself; figures drawn in a
     band's colour use its ink, which clears 4.5:1 on every surface here. */
  --t-frozen-ink: #5C9ACF;
  --t-cold-ink: #649DB5;
  --t-cool-ink: #729E97;
  --t-mild-ink: #A3A075;
  --t-warm-ink: #C9A03F;
  --t-hot-ink: #DF8B26;
  --t-baking-ink: #E67548;
}

[data-theme="light"] {
  --bg:        #E7EAE8;
  --surface:   #FFFFFF;
  --surface-2: #F2F5F4;
  --line:      #D2D8D9;
  --text:      #14202A;
  --text-dim:  #56646F;   /* 5.0:1 on --bg; #5E6D78 was 4.4 */
  --shadow: none;
  --signal:    #BE3265;
  /* The ramp was drawn for a dark ground: on this one cool, mild, warm and
     hot read at 2.0-2.6:1. Same hues, darkened until they clear 4.5:1. */
  --t-frozen-ink: #2E6C9E;
  --t-cold-ink: #3C7085;
  --t-cool-ink: #4B706A;
  --t-mild-ink: #6C6A48;
  --t-warm-ink: #836413;
  --t-hot-ink: #9B5900;
  --t-baking-ink: #AE4A1D;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --bg:        #E7EAE8;
    --surface:   #FFFFFF;
    --surface-2: #F2F5F4;
    --line:      #D2D8D9;
    --text:      #14202A;
    --text-dim:  #56646F;   /* 5.0:1 on --bg; #5E6D78 was 4.4 */
    --shadow: none;
    --signal:    #BE3265;
    /* The ramp was drawn for a dark ground: on this one cool, mild, warm and
       hot read at 2.0-2.6:1. Same hues, darkened until they clear 4.5:1. */
    --t-frozen-ink: #2E6C9E;
    --t-cold-ink: #3C7085;
    --t-cool-ink: #4B706A;
    --t-mild-ink: #6C6A48;
    --t-warm-ink: #836413;
    --t-hot-ink: #9B5900;
    --t-baking-ink: #AE4A1D;
  }
}

/* Temperature utility classes — put on any element to set its
   --temp, which tints edges, markers and bars beneath it. */
.t-frozen { --temp: var(--t-frozen); --temp-ink: var(--t-frozen-ink); }
.t-cold   { --temp: var(--t-cold); --temp-ink: var(--t-cold-ink); }
.t-cool   { --temp: var(--t-cool); --temp-ink: var(--t-cool-ink); }
.t-mild   { --temp: var(--t-mild); --temp-ink: var(--t-mild-ink); }
.t-warm   { --temp: var(--t-warm); --temp-ink: var(--t-warm-ink); }
.t-hot    { --temp: var(--t-hot); --temp-ink: var(--t-hot-ink); }
.t-baking { --temp: var(--t-baking); --temp-ink: var(--t-baking-ink); }

/* ---------- 2. Base ------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.wx-shell {
  max-width: var(--shell);
  transition: max-width .2s var(--ease);
  margin: 0 auto;
  padding: var(--sp-5) var(--sp-4) var(--sp-7);
}

:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
}

/* One page width on every screen.

   The gear screen used to set this to 82rem to fit nine columns, and because
   --shell drives the whole page, arriving at that tab moved the header, the
   tabs and the footer with it. The four columns that made nine necessary are
   gone -- see GEAR_COLS -- and the table scrolls inside the card on a narrow
   window, which it always did anyway: at anything under about 1300px the
   82rem shell was already scrolling, so the width jump was cost without
   benefit on any laptop. */

/* ---------- 3. Type roles ------------------------------------ */

.wx-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--step-3);
  line-height: .98;
  letter-spacing: .01em;
  text-transform: uppercase;
  margin: 0 0 var(--sp-3);
}

.wx-heading {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--step-1);
  letter-spacing: .03em;
  text-transform: uppercase;
  margin: 0 0 var(--sp-2);
}

/* Eyebrow: always says what kind of thing follows, never decorative. */
.wx-eyebrow {
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 var(--sp-2);
}

.wx-dim { color: var(--text-dim); }

.wx-num {
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------- 4. Signature: the ride-feel readout --------------- */
/* The one number the whole app exists to produce. Oversized,
   monospaced so it doesn't jump as it counts, sitting directly
   on the temperature ramp that produced it. */

.wx-readout {
  position: relative;
  padding: var(--sp-4) 0 var(--sp-5);
}

.wx-readout__row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-4);
}

.wx-readout__value {
  font-family: var(--data);
  font-weight: 600;
  font-size: var(--step-4);
  line-height: .84;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  color: var(--temp-ink);
  transition: color .3s var(--ease);
}

.wx-readout__unit {
  font-family: var(--display);
  font-size: var(--step-1);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text-dim);
}

.wx-readout__air {
  font-family: var(--data);
  font-size: var(--step--1);
  color: var(--text-dim);
  text-align: right;
  line-height: 1.6;
}

/* The ramp rule. Also usable on its own as a divider. */
.wx-strip {
  height: 6px;
  background: var(--ramp);
  margin-top: var(--sp-4);
  position: relative;
}

.wx-strip__mark {
  position: absolute;
  top: -5px;
  bottom: -5px;
  width: 2px;
  background: var(--text);
  transform: translateX(-50%);
  transition: left .3s var(--ease);
}

/* Each label is placed at its own value by scaleMarkup() in model.js, so the
   row is a positioning box rather than an evenly spaced flex row. */
.wx-scale {
  position: relative;
  height: 1.4em;
  font-family: var(--data);
  font-size: var(--step--3);
  color: var(--text-dim);
  margin-top: var(--sp-1);
}
.wx-scale > span { position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap; }
.wx-scale > span:first-child { transform: none; }

/* ---------- 5. Surfaces -------------------------------------- */
/* Every card carries a temperature edge on its left. The chrome
   itself encodes the reading. */

.wx-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: var(--edge) solid var(--temp);
  border-radius: var(--radius);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow);
}

.wx-card--flat { background: none; border-color: var(--line); }

.wx-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

/* ---------- 6. Kit list -------------------------------------- */
/* Head to feet, in that order, because that is how you get dressed. */

.wx-kit { margin: 0; }

.wx-kit__row {
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-top: 1px solid var(--line);
}
.wx-kit__row:first-child { border-top: 0; }

.wx-kit__zone {
  flex: 0 0 4.6rem;
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding-top: .15em;
}

.wx-kit__items { flex: 1; margin: 0; }

.wx-kit__items em {
  font-style: normal;
  color: var(--text-dim);
}

/* A garment's standing note: one line, the rest on tap. The whole text is in
   the button, so a screen reader reads all of it either way. */
.wx-kit__note {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  overflow: hidden;
  width: 100%;
  margin: 2px 0 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-dim);
  text-align: left;
  cursor: pointer;
}
.wx-kit__note[aria-expanded="true"] { display: block; -webkit-line-clamp: unset; }
.wx-kit__note:hover { color: var(--text); }

/* ---------- 7. Adjustment chips ------------------------------ */
/* Air temperature → ride-feel. Sign is carried by colour so the
   arithmetic scans without reading. */

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

.wx-chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-1) var(--sp-3);
  font-size: var(--step--1);
  color: var(--text-dim);
  white-space: nowrap;
}

.wx-chip b {
  font-family: var(--data);
  font-weight: 600;
  color: var(--minus);
}

.wx-chip--plus b { color: var(--plus); }

.wx-chip[aria-pressed="true"] {
  border-color: var(--text);
  color: var(--text);
}

/* ---------- 8. Garment bars ---------------------------------- */
/* A band chart: bars overlap because the wardrobe overlaps.
   Position encodes the range; colour encodes the midpoint. */

.wx-lane {
  position: relative;
  height: 28px;
  background: var(--surface-2);
  margin-bottom: 3px;
  border-radius: var(--radius);
  overflow: hidden;
}

.wx-bar {
  position: absolute;
  inset-block: 0;
  display: flex;
  align-items: center;
  padding: 0 var(--sp-2);
  background: var(--temp);
  color: #0E1A24;
  font-size: var(--step--1);
  font-weight: 500;
  white-space: nowrap;
  transition: opacity .2s var(--ease);
}

.wx-bar--dark-text { color: #0E1A24; }
.wx-bar--light-text { color: #F5F7F6; }

.wx-bar[data-active="false"] { opacity: .25; }

.wx-bar__print { display: none; font-family: var(--data); font-size: var(--step--3); color: #000; padding-left: 2px; }
.wx-bar__range {
  font-family: var(--data);
  font-size: var(--step--3);
  opacity: .7;
  margin-left: var(--sp-2);
}

/* ---------- 9. Controls -------------------------------------- */

/* A <button> centres its text on its own; a <label> wearing this class does
   not, and "Add a ride from GPX" sat 10px high in a 44px box because of it.
   The class carries its own centring rather than relying on which element it
   happens to be put on -- the same way .wx-file already does. */
.wx-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--display);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  min-height: var(--tap);
  padding: 0 var(--sp-5);
  border: 1px solid var(--text);
  border-radius: var(--radius);
  background: none;
  color: var(--text);
  cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease);
}

.wx-btn:hover { background: var(--text); color: var(--bg); }

/* Neutral, not a temperature: the ramp is data, and a control is not a reading. */
.wx-btn--primary {
  background: var(--text);
  border-color: var(--text);
  color: var(--bg);
}
.wx-btn--primary:hover { opacity: .86; background: var(--text); color: var(--bg); }

.wx-btn--quiet {
  border-color: var(--line);
  color: var(--text-dim);
  padding: 0 var(--sp-4);
}

/* Slider — the ramp is the track, so dragging feels like moving
   along the temperature itself rather than an abstract control. */
.wx-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: var(--tap);
  background: none;
  margin: 0;
  display: block;
  cursor: ew-resize;
}
.wx-slider::-webkit-slider-runnable-track {
  height: 6px; background: var(--ramp); border-radius: var(--radius);
}
.wx-slider::-moz-range-track {
  height: 6px; background: var(--ramp); border-radius: var(--radius);
}
.wx-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 6px; height: 30px; margin-top: -12px;
  background: var(--text); border: 0; border-radius: 1px;
}
.wx-slider::-moz-range-thumb {
  width: 6px; height: 30px;
  background: var(--text); border: 0; border-radius: 1px;
}

/* ---------- 10. Data table ----------------------------------- */

.wx-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
}

.wx-table th {
  font-family: var(--data);
  font-weight: 500;
  font-size: var(--step--3);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: left;
  padding-bottom: var(--sp-2);
}

.wx-table td {
  border-top: 1px solid var(--line);
  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
  vertical-align: top;
}

.wx-table td:last-child { padding-right: 0; color: var(--text-dim); }

.wx-table .wx-feel {
  font-family: var(--data);
  font-weight: 600;
  color: var(--temp-ink);
}

/* ---------- 11. Alert ---------------------------------------- */
/* For the honest bits: where the wardrobe runs out. Never used
   for anything the user can simply dismiss. */

.wx-alert {
  border-left: var(--edge) solid var(--signal);
  padding: var(--sp-1) 0 var(--sp-1) var(--sp-4);
  margin: var(--sp-4) 0;
  font-size: var(--step--1);
}

.wx-alert b { color: var(--signal); }

/* ---------- 12. Glove mode ----------------------------------- */
/* Not an accessibility afterthought — the intended winter state.
   Bigger targets, bigger type, more contrast, fewer taps. */

[data-touch="gloves"] {
  --step--3: 0.78rem;
  --step--2: 0.84rem;
  --step--1: 0.9rem;
  --step-0:  1.1rem;
  --step-1:  1.35rem;
  --tap: 60px;
}
[data-touch="gloves"] .wx-lane { height: 38px; }
[data-touch="gloves"] .wx-kit__row { padding: var(--sp-3) 0; }
[data-touch="gloves"] .wx-chip { padding: var(--sp-2) var(--sp-4); }
[data-touch="gloves"] .wx-slider::-webkit-slider-thumb { height: 44px; width: 10px; margin-top: -19px; }
[data-touch="gloves"] .wx-slider::-moz-range-thumb { height: 44px; width: 10px; }

/* ---------- 13. Responsive ----------------------------------- */

@media (max-width: 30rem) {
  .wx-kit__zone { flex-basis: 3.6rem; }
  .wx-bar__range { display: none; }
  .wx-readout__row { align-items: flex-start; }
}

/* ---------- 14. Motion and print ----------------------------- */

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

@media print {
  .wx-bar__print { display: inline; }
  /* Paper is a light ground whatever the screen theme was, so it takes the
     light inks and a light track under the range bars. */
  :root { --bg: #fff; --surface: #fff; --surface-2: #F2F2F2; --text: #000; --text-dim: #444; --line: #bbb;
          --signal: #BE3265;
          --t-frozen-ink: #2E6C9E; --t-cold-ink: #3C7085; --t-cool-ink: #4B706A; --t-mild-ink: #6C6A48;
          --t-warm-ink: #836413; --t-hot-ink: #9B5900; --t-baking-ink: #AE4A1D; }
  body { background: #fff; }
  .wx-slider, .wx-btn { display: none; }
  .wx-bar[data-active="false"] { opacity: 1; }
  .wx-card { break-inside: avoid; }
}

/* ---------- 15. App chrome ----------------------------------- */
/* Everything the design system doesn't cover, built from its own
   tokens: the form that describes a ride, the gear screen, and
   the lookup comparison. Nothing here introduces a new colour. */

.wx-tabs {
  display: flex;
  /* Five of them now, and on a phone the fifth ran off the side and took the
     whole page into horizontal scroll with it. Wrapping to a second line is
     the friendlier answer for navigation: a tab you cannot see is a tab you
     do not know is there, which a hidden sideways scroll would not fix. */
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-5);
  border-bottom: 1px solid var(--line);
}

.wx-tab {
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  min-height: var(--tap);
  padding: 0 var(--sp-4);
  background: none;
  border: 0;
  border-bottom: var(--edge) solid transparent;
  margin-bottom: -1px;
  color: var(--text-dim);
  cursor: pointer;
}
.wx-tab[aria-pressed="true"] { color: var(--text); border-bottom-color: var(--text); }
/* Tighter tabs on a phone, so the six of them wrap onto as few lines as they
   can. At full size and padding they ran to three. */
@media (max-width: 30rem) {
  .wx-tabs { gap: var(--sp-1); }
  .wx-tab { font-size: var(--step-0); letter-spacing: .03em; padding: 0 var(--sp-2); }
}

/* --- Fields --- */

.wx-field { display: block; margin-bottom: var(--sp-4); }

.wx-label {
  display: block;
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: var(--sp-2);
}

.wx-input {
  width: 100%;
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  font-family: var(--data);
  font-size: var(--step-0);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.wx-input:focus { outline: 2px solid var(--text); outline-offset: -1px; border-color: var(--text); }
select.wx-input { font-family: var(--body); cursor: pointer; }
input[type="checkbox"] { accent-color: var(--text); width: auto; min-height: 0; }

.wx-grid { display: grid; gap: var(--sp-3); }
.wx-grid--2 { grid-template-columns: repeat(2, 1fr); }
.wx-grid--3 { grid-template-columns: repeat(3, 1fr); }
.wx-grid--4 { grid-template-columns: repeat(4, 1fr); }

/* The ride row: date, start, distance, hours, speed.

   1fr means minmax(auto, 1fr), and that auto floor is the track's min-CONTENT
   width -- so one stubborn child widens its own column and takes the space
   from its neighbours. A native date input is exactly that child: it carries
   "08/09/2026" plus a picker button and will not shrink below them, which left
   Date half again as wide as the four fields beside it.

   Equal fifths are not the answer either: at this card's width a fifth is
   118px and the date loses its year. So the row asks for as many equal
   columns of at least 140px as will fit and wraps what is left over. Every
   field ends up the same width at every screen size -- which is the point of
   the row -- and the count follows the space instead of being asserted, so
   this needs no breakpoints of its own.

   Scoped to this row rather than applied to every grid: this is the only one
   holding a child that refuses to shrink, and dropping the floor under grids
   that do not need it changes their sizing on no evidence. */
.wx-grid--5 { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.wx-grid--5 > * { min-width: 0; }

/* A cell that takes the rest of the row it lands on.

   The gear form's flags sit in the same four-column row as min, max, clo, wind
   and water, filling the three cells left beside Water -- and asked for that
   with `span 3`, which is only the rest of the row while the row has four
   columns. Below 34rem the grid drops to two, and a span of three then invents
   a third, implicit, content-sized column: it took 215px of a 346px row and
   squeezed Max and Wind until their own values clipped, 15 rendering as 1!.

   So the span holds only where there are four columns to span, and narrower
   than that the cell runs to the last explicit line instead -- which is however
   many columns there are, and never conjures another. */
.wx-grid--4 > .wx-fill { grid-column: span 3; }
@media (max-width: 34rem) { .wx-grid--4 > .wx-fill { grid-column: 1 / -1; } }

.wx-row { display: flex; gap: var(--sp-2); align-items: flex-end; }
.wx-row > .wx-grow { flex: 1; }

@media (max-width: 34rem) {
  .wx-grid--3, .wx-grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 22rem) {
  .wx-grid--2, .wx-grid--3, .wx-grid--4 { grid-template-columns: 1fr; }
}

/* Choice chips reuse the adjustment chip, pressed state and all. */
.wx-choice { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.wx-choice .wx-chip {
  min-height: var(--tap);
  align-items: center;
  cursor: pointer;
  background: none;
  font-family: var(--body);
}
.wx-choice .wx-chip[aria-pressed="true"] {
  background: var(--text);
  border-color: var(--text);
  color: var(--bg);
}

/* --- Location suggestions --- */

.wx-suggest {
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius);
  overflow: hidden;
}
.wx-suggest button {
  display: block;
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
  font-family: var(--body);
  font-size: var(--step-0);
  color: var(--text);
  background: var(--surface);
  border: 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
}
.wx-suggest button:first-child { border-top: 0; }
.wx-suggest button:hover:not(:disabled) { background: var(--surface-2); }
.wx-suggest button:disabled { color: var(--text-dim); cursor: default; }

/* --- Status --- */

.wx-status {
  border-left: var(--edge) solid var(--temp);
  padding: var(--sp-2) 0 var(--sp-2) var(--sp-4);
  margin-top: var(--sp-4);
  font-size: var(--step--1);
  color: var(--text-dim);
}
.wx-status--err { border-left-color: var(--signal); color: var(--signal); }

/* --- Disclosure --- */

.wx-more { margin-top: var(--sp-4); border-top: 1px solid var(--line); padding-top: var(--sp-3); }
.wx-more > summary {
  cursor: pointer;
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-dim);
  min-height: var(--tap);
  display: flex;
  align-items: center;
}
.wx-more > summary:hover { color: var(--text); }
.wx-more[open] > summary { color: var(--text); margin-bottom: var(--sp-3); }
.wx-settings { margin-top: var(--sp-2); }
.wx-settings > summary span { color: var(--text-dim); letter-spacing: .06em; }

/* --- Tables that must scroll rather than squash the page --- */

.wx-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.wx-table td.wx-right, .wx-table th.wx-right { text-align: right; }
.wx-table tr.is-here td { background: var(--surface-2); }
.wx-table tr.is-here td:first-child { box-shadow: inset var(--edge) 0 0 var(--temp); }
.wx-table .wx-total td { border-top: 2px solid var(--line); font-weight: 600; color: var(--text); }

/* --- Gear list --- */

/* A floor so the columns stay legible rather than collapsing to nothing on a
   phone, where the scroller takes over instead. 46rem was the floor for nine
   columns, and it was the real reason the table would not fit a 44rem page --
   every other measurement came back at exactly 736px because this said so.
   Five columns want about 38. */
.wx-gear { min-width: 38rem; }
.wx-gear tbody tr { cursor: pointer; }
.wx-gear tbody tr:hover td { background: var(--surface-2); }
.wx-gear tr.is-off td { opacity: .38; }
.wx-gear tr.is-off td:first-child { opacity: 1; }
.wx-gear tr.is-off .wx-item { text-decoration: line-through; }
.wx-gear tr.is-sel td { background: var(--surface-2); }
.wx-gear tr.is-sel td:first-child { box-shadow: inset var(--edge) 0 0 var(--temp); }
/* Scoped to the garment rows. Unscoped it also caught the detail row's single
   colspan cell -- which IS a first child -- and centred a paragraph of notes
   inside a 4.4rem column hint meant for an In/Out button. */
.wx-gear tbody tr[data-i] td:first-child { text-align: center; width: 4.4rem; }

/* In or out of the advice. A labelled two-state button rather than a tick box:
   the state is readable without decoding it, and it is a real tap target. */
.wx-toggle {
  min-width: 3.2rem;
  min-height: 32px;
  padding: 0 var(--sp-2);
  font-family: var(--display);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}
/* One consistent "on", not the row's temperature tint. Colour is data
   everywhere else in this app; a control that borrowed the ramp would turn the
   column into a chart and make the state harder to read, not easier. */
.wx-toggle[aria-pressed="true"] {
  background: var(--text);
  border-color: var(--text);
  color: var(--bg);
}
.wx-toggle:hover { border-color: var(--text); }
[data-touch="gloves"] .wx-toggle { min-height: var(--tap); min-width: 4rem; }
.wx-gear .wx-item { font-weight: 500; color: var(--text); }
.wx-gear .wx-flagcell { font-family: var(--data); font-size: var(--step--3); color: var(--text-dim); white-space: nowrap; }

/* --- Flags, beside the model they belong to ---------------------
   They had a column of their own, which charged 114px of table width to show
   a word. As badges they cost the slack the model column had anyway, and they
   read better next to the garment than three columns away from it. */
.wx-badge {
  display: inline-block;
  margin-left: 1px;
  padding: 1px 5px;
  font-family: var(--data); font-size: var(--step--3); font-weight: 400;
  letter-spacing: .04em;
  color: var(--text-dim);
  border: 1px solid var(--line);
  border-radius: 2px;
  white-space: nowrap;
  vertical-align: 1px;
  cursor: help;
}
.wx-badge:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }
tr.is-off .wx-badge { opacity: .55; }

/* --- The detail line under the selected row ---------------------
   Where clo, wind, water and the flags went, plus the note, which no column
   ever had room for. It is a row of the same table so it moves with its
   garment and inherits the row's temperature edge. */
.wx-detail > td {
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
/* The detail cell spans the whole table, and on a phone the table is wider
   than the screen -- so without a cap the note wrapped at the table's width
   and half of every line sat off to the right, behind a sideways scroll. The
   cap keeps it inside the visible column instead. */
.wx-detail__row, .wx-detail__note, .wx-detail__hint {
  max-width: calc(100vw - 3rem);
}
.wx-detail__row { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.wx-detail__cell { display: flex; align-items: baseline; gap: var(--sp-2); font-size: var(--step--1); }
.wx-detail__k {
  font-family: var(--display); font-size: var(--step--2); font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-dim);
  cursor: help;
}
.wx-detail__k:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }
.wx-detail__note {
  margin: var(--sp-2) 0 0;
  font-size: var(--step--1); line-height: 1.5; color: var(--text-dim);
}
.wx-detail__hint {
  margin: var(--sp-2) 0 0;
  font-family: var(--data); font-size: var(--step--3); color: var(--text-dim);
}

/* The range column drawn rather than written: same band chart as the
   torso lanes, so a gap in the wardrobe is visible as a gap. */
.wx-gear .wx-lane { height: 18px; min-width: 8rem; margin: 0; }
.wx-gear .wx-bar { font-size: var(--step--3); padding: 0 var(--sp-1); }

.wx-bar--empty { background: none; border: 1px dashed var(--line); color: var(--text-dim); }

/* --- Toolbar --- */

.wx-bar-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-top: var(--sp-4); }
/* Global rather than bar-row only: the card footers on History and Favourites
   use it too, to push Edit and Delete away from the main action, and scoped to
   .wx-bar-row it did nothing there. */
.wx-spacer { flex: 1; }
.wx-file {
  display: inline-flex; align-items: center;
  font-family: var(--display); font-size: var(--step-0); font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase;
  min-height: var(--tap); padding: 0 var(--sp-4);
  border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text-dim); cursor: pointer;
}
.wx-file:hover { border-color: var(--text); color: var(--text); }
/* A toggle needs to show its state, not just respond to hover. */
.wx-btn[aria-pressed="true"] {
  background: var(--text); color: var(--bg); border-color: var(--text);
}
.wx-btn:disabled { opacity: .35; cursor: default; }
.wx-btn:disabled:hover { background: none; color: var(--text); }

/* --- Add / edit form --- */

.wx-form {
  border: 1px solid var(--line);
  border-left: var(--edge) solid var(--temp);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--sp-4);
  margin-top: var(--sp-4);
}

.wx-flags { display: flex; flex-direction: column; gap: var(--sp-1); }
.wx-check {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--step--1); color: var(--text); cursor: pointer;
}

/* --- Lookup result --- */

.wx-found {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: var(--sp-3);
  margin-top: var(--sp-3);
}
.wx-found__head { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-bottom: var(--sp-3); }

.wx-badge {
  font-family: var(--data);
  font-size: var(--step--3); font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  padding: 2px var(--sp-2);
  border: 1px solid currentColor;
  border-radius: var(--radius);
}
.wx-badge--high { color: var(--t-cool); }
.wx-badge--medium { color: var(--plus); }
.wx-badge--low { color: var(--signal); }

.wx-cmp td.wx-new { color: var(--temp-ink); font-weight: 600; }
.wx-cmp td:last-child, .wx-cmp th:last-child { text-align: center; width: 3rem; }

.wx-note { font-size: var(--step--1); color: var(--text-dim); margin: var(--sp-3) 0 0; }

/* Why a lookup returned what it did — a reading trail, so it wants to be
   compact and quiet rather than spaced like the kit list. */
.wx-why { margin: 0; padding: 0; list-style: none; font-size: var(--step--1); }
.wx-why li {
  padding: var(--sp-1) 0 var(--sp-1) var(--sp-3);
  border-left: 1px solid var(--line);
  color: var(--text-dim);
}
.wx-why li:first-child { color: var(--text); }

/* --- Footer --- */

.wx-foot {
  margin-top: var(--sp-7);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
  font-size: var(--step--1);
  color: var(--text-dim);
}
.wx-foot a { color: inherit; }

.wx-hidden { display: none !important; }

@media print { .wx-tabs, .wx-bar-row, .wx-form, .wx-suggest { display: none; } }

/* ---------- 16. Sky, wind and the hours ahead ---------------- */

/* The hourly strip. Ride hours are lit; the hours after are dimmed but
   present, because a shower that lands twenty minutes after you'd be home
   is worth seeing before you decide how long to go out for. */

.wx-hours { display: flex; gap: 1px; min-width: min-content; }

.wx-hour {
  flex: 0 0 3.9rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-2) var(--sp-1);
  background: var(--surface-2);
  border-top: var(--edge) solid transparent;
  opacity: .45;
}
.wx-hour.is-riding { opacity: 1; border-top-color: var(--temp); }

.wx-hour__time {
  font-family: var(--data);
  font-size: var(--step--2);
  letter-spacing: .04em;
  color: var(--text-dim);
}
.wx-hour__temp {
  font-family: var(--data);
  font-weight: 600;
  font-size: var(--step-0);
  color: var(--temp-ink);
}
.wx-hour__wind {
  display: flex; align-items: center; gap: 2px;
  font-size: var(--step--2); color: var(--text-dim);
}
.wx-hour__rain { font-family: var(--data); font-size: var(--step--3); color: var(--text-dim); }
.wx-hour__rain.is-wet { color: var(--minus); font-weight: 600; }

.wx-ico { display: block; }
.wx-windarrow { color: var(--text-dim); flex: 0 0 auto; }

/* --- Wind and route --- */


/* Beaufort, because that is the unit the forecast gets discussed in here,
   and the number carries more than a speed does. */
.wx-bft { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-3); }
.wx-bft__n {
  font-family: var(--data);
  font-weight: 600;
  font-size: var(--step-3);
  line-height: .9;
  color: var(--minus);
}
.wx-bft__u {
  font-family: var(--display);
  font-size: var(--step-0);
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text-dim);
}
.wx-bft__name {
  font-family: var(--display);
  font-size: var(--step-1);
  text-transform: uppercase;
  letter-spacing: .03em;
}

.wx-startdir {
  margin: var(--sp-3) 0 0;
  padding-left: var(--sp-3);
  border-left: var(--edge) solid var(--temp);
  font-size: var(--step-0);
}
.wx-startdir b { color: var(--temp-ink); }

[data-touch="gloves"] .wx-hour { flex-basis: 4.8rem; }

@media print { .wx-hour { opacity: 1; } }

/* ---------- 17. Gear table header controls ------------------- */

/* The heading may wrap; its buttons may not.

   nowrap here was what actually set the column widths -- "Ride-feel range" on
   one line was costing 188px for a chart, and "In advice" 140px for a cell
   holding a 40px button. The browser only wraps a heading when the column is
   squeezed, so on a wide screen nothing looks different and on a narrow one
   the table fits instead of scrolling. */
.wx-th { display: flex; align-items: center; gap: var(--sp-2); white-space: normal; }
.wx-th__btns { white-space: nowrap; }
/* On a right-aligned column the buttons are rendered before the label, so they
   sit inside the cell rather than hard against the next column's heading. */
.wx-right .wx-th { justify-content: flex-end; }
/* Header cells are ruled apart, which is what lets a column's buttons sit at
   its edge without reading as the next column's. */
#geartable thead th { padding-left: var(--sp-3); padding-right: var(--sp-3); vertical-align: bottom; }
#geartable thead th + th { border-left: 1px solid var(--line); }
#geartable thead th:first-child { padding-left: var(--sp-2); }
#geartable tbody td:first-child { padding-left: var(--sp-2); }
#geartable tbody td { padding-left: var(--sp-3); }

.wx-th__label { cursor: help; border-bottom: 1px dotted var(--line); }
.wx-th__label:hover, .wx-th__label:focus-visible { color: var(--text); border-bottom-color: var(--text-dim); }

.wx-th__btns { display: flex; gap: 1px; flex: 0 0 auto; align-self: flex-end; }

/* A table may use the card's padding as table width. Prose wants the inset;
   a grid of numbers would rather have the 32px, and running to the card's own
   border reads as deliberate rather than cramped. */
.wx-scroll--bleed { margin-left: calc(-1 * var(--sp-4)); margin-right: calc(-1 * var(--sp-4)); }
.wx-scroll--bleed > table { width: 100%; }
#geartable tbody td:last-child, #geartable thead th:last-child { padding-right: var(--sp-3); }

/* --- Scale under the range heading --- */
.wx-axis {
  position: relative;
  height: 1.1rem;
  margin-top: var(--sp-1);
  border-top: 1px solid var(--line);
}
.wx-axis__t {
  position: absolute;
  top: 2px;
  transform: translateX(-50%);
  font-family: var(--data);
  font-size: var(--step--3);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-dim);
}
/* The end labels would otherwise hang outside the column. */
.wx-axis__t:first-child { transform: none; }
.wx-axis__t:last-child { transform: translateX(-100%); }

/* Gridlines on every lane at the same 5°C spacing, so each bar can be read
   against the scale rather than only against its neighbours. */
.wx-gear .wx-lane {
  background-image: repeating-linear-gradient(90deg,
    var(--line) 0 1px, transparent 1px calc(100% / 7));
}

.wx-hbtn {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  font-size: 11px;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}
.wx-hbtn:hover { color: var(--text); border-color: var(--line); }
.wx-hbtn svg { display: block; }
.wx-hbtn.is-on { color: var(--bg); background: var(--text); border-color: var(--text); }
[data-touch="gloves"] .wx-hbtn { width: 30px; height: 30px; font-size: 13px; }

.wx-empty { text-align: center; color: var(--text-dim); padding: var(--sp-5) var(--sp-3); }
.wx-link {
  background: none; border: 0; padding: 0;
  color: var(--text); text-decoration: underline; cursor: pointer; font: inherit;
}

/* --- Tooltip and filter popover --- */

.wx-tip, .wx-pop {
  position: fixed;
  z-index: 50;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 6px 24px rgba(0,0,0,.35);
}
.wx-tip {
  max-width: 22rem;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--text);
  pointer-events: none;
}
.wx-pop {
  width: min(20rem, calc(100vw - 2rem));
  max-height: 70vh;
  overflow-y: auto;
  padding: var(--sp-3);
  border-left: var(--edge) solid var(--temp);
}
.wx-poplist { max-height: 14rem; overflow-y: auto; display: flex; flex-direction: column; gap: var(--sp-1); }
.wx-poprow { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
.wx-pop .wx-check { padding: var(--sp-1) 0; }
.wx-pop .wx-input { min-height: 36px; }

@media print { .wx-th__btns, .wx-tip, .wx-pop { display: none; } }

/* ---------- 18. Along the route ------------------------------ */
/* One column per leg, ordered by distance. Same shape as the hourly strip,
   because it answers the same question along a different axis. */

.wx-legs { display: flex; gap: 1px; min-width: min-content; }

.wx-leg {
  flex: 0 0 4.6rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--sp-2) var(--sp-1);
  background: var(--surface-2);
  border-top: var(--edge) solid var(--temp);
}

.wx-leg__km {
  font-family: var(--data);
  font-size: var(--step--3);
  letter-spacing: .04em;
  color: var(--text);
}
.wx-leg__feel {
  font-family: var(--data);
  font-weight: 600;
  font-size: var(--step-1);
  line-height: 1.1;
  color: var(--temp-ink);
}
.wx-leg__air, .wx-leg__ele, .wx-leg__time {
  font-family: var(--data);
  font-size: var(--step--3);
  color: var(--text-dim);
}
.wx-leg__wind {
  display: flex; align-items: center; gap: 2px;
  font-size: var(--step--3); color: var(--text-dim);
}
/* Riding into it is the case worth seeing without reading the number. */
.wx-leg__wind .wx-num { color: var(--minus); font-weight: 600; }

[data-touch="gloves"] .wx-leg { flex-basis: 5.4rem; }
@media print { .wx-leg { break-inside: avoid; } }

/* ---------- 19. The mark ------------------------------------- */
/* The temperature ramp wrapped into a wheel and cut into its seven bands,
   with a needle reading off it. A gauge rather than a bicycle: the app is an
   instrument, and the one number it exists to produce is a needle position. */

.wx-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}
.wx-brand > div { min-width: 0; }
.wx-brand .wx-eyebrow { margin-bottom: 2px; }
.wx-brand .wx-title { margin: 0; }

.wx-logo {
  width: 3.1rem;
  height: 3.1rem;
  flex: 0 0 auto;
  display: block;
}

@media (max-width: 26rem) {
  .wx-logo { width: 2.5rem; height: 2.5rem; }
}
@media print { .wx-logo { width: 2.2rem; height: 2.2rem; } }

/* ---------- 20. Map ------------------------------------------ */
/* Tiles underneath, everything meaningful drawn on top — so a blocked or
   offline tile server costs the basemap and nothing else. */

.wx-map {
  position: relative;
  margin: 0 0 var(--sp-3);
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.wx-map__tiles { position: absolute; inset: 0; }
.wx-map__tiles img {
  position: absolute;
  width: 256px; height: 256px;
  /* OSM's raster tiles are drawn for light backgrounds; damp them down so the
     route and arrows stay the brightest things on a dark screen. */
  filter: grayscale(.55) brightness(.72) contrast(1.05);
}
:root[data-theme="light"] .wx-map__tiles img,
:root:not([data-theme="dark"]) .wx-map__tiles img { filter: grayscale(.35) contrast(1.02); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wx-map__tiles img { filter: grayscale(.55) brightness(.72) contrast(1.05); }
}

.wx-map__ink { position: relative; display: block; }
.wx-map__credit {
  position: absolute; right: 0; bottom: 0;
  margin: 0; padding: 1px 5px;
  font-size: var(--step--3);
  color: var(--text-dim);
  background: color-mix(in srgb, var(--surface) 78%, transparent);
}
.wx-map__credit a { color: inherit; }

.wx-maplegend {
  display: flex; flex-wrap: wrap; gap: var(--sp-3);
  font-size: var(--step--1); color: var(--text-dim);
  margin: 0 0 var(--sp-3);
}
.wx-maplegend span { display: inline-flex; align-items: center; gap: var(--sp-2); }
.wx-maplegend i { width: 20px; height: 0; border-top-width: 3px; border-top-style: solid; }
.wx-maplegend .k-wind { border-top-color: var(--minus); }
.wx-maplegend .k-go { border-top-color: var(--temp); border-top-style: dashed; }
.wx-maplegend .k-route { border-top-color: var(--temp); }

/* ---------- 21. Model agreement ------------------------------ */
/* How much the forecast agrees with itself. A tight spread earns the quiet
   treatment; a wide one gets the warning colour, because it changes whether
   the bearing above is worth acting on. */

.wx-agree {
  margin: var(--sp-3) 0 0;
  padding-left: var(--sp-3);
  border-left: var(--edge) solid var(--line);
  font-size: var(--step--1);
  color: var(--text-dim);
}
.wx-agree b { font-family: var(--data); font-size: var(--step--2); letter-spacing: .06em; }
.wx-agree.is-tight { border-left-color: var(--t-cool); }
.wx-agree.is-tight b { color: var(--t-cool); }
.wx-agree.is-loose { border-left-color: var(--signal); }
.wx-agree.is-loose b { color: var(--signal); }

/* ---------- 22. Ride history --------------------------------- */
/* A list rather than a table: the useful unit is one ride, and it has to read
   on a phone in the hallway when you get back. */

.wx-rides { display: flex; flex-direction: column; gap: var(--sp-3); margin-top: var(--sp-4); }

.wx-ride {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: var(--edge) solid var(--temp);
  border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-4);
}
/* A rated ride is settled — it recedes so the ones still asking stand out. */
.wx-ride.is-rated { background: none; opacity: .78; }

.wx-ride__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-3);
}
.wx-ride__when {
  font-family: var(--data); font-size: var(--step--2); letter-spacing: .06em;
  text-transform: uppercase; color: var(--text); margin: 0;
}
.wx-ride__where { font-size: var(--step--1); color: var(--text-dim); margin: 2px 0 0; }
/* Provenance, not decoration: says the weather behind this entry was looked up
   after the ride rather than forecast before it. */
.wx-tag {
  display: inline-block; vertical-align: 1px; margin-left: .35em;
  padding: .1em .45em; border: 1px solid var(--line); border-radius: 2px;
  font-family: var(--display); font-size: var(--step--3); font-weight: 500;
  letter-spacing: .04em; text-transform: uppercase; color: var(--text-dim);
  cursor: help;
}
.wx-ride__feel {
  text-align: right; white-space: nowrap; color: var(--temp-ink);
  font-family: var(--data); font-weight: 600; font-size: var(--step-2); line-height: 1;
}
.wx-ride__feel small {
  display: block; font-family: var(--display); font-size: var(--step--3); font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-dim); margin-top: 3px;
}

.wx-ride__meta {
  font-family: var(--data); font-size: var(--step--2); color: var(--text-dim);
  margin: var(--sp-2) 0 0;
}
.wx-ride__kit { font-size: var(--step--1); color: var(--text-dim); margin: var(--sp-1) 0 0; }

.wx-ride__foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}
/* ---------- Favourites ------------------------------------------
   The same card as a ride, minus the temperature edge: that stripe is the
   ride-feel colour, and a favourite has no temperature until it is ridden on
   a particular day. The route's own outline identifies it instead. */
.wx-favs { display: flex; flex-direction: column; gap: var(--sp-3); margin-top: var(--sp-4); }
.wx-fav {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-4);
}
.wx-fav.is-editing { border-color: var(--text-dim); }
.wx-fav__body { display: flex; align-items: flex-start; gap: var(--sp-4); }
.wx-fav__map {
  flex: 0 0 auto; width: 96px; height: 72px;
  background: var(--surface-2); border-radius: var(--radius);
}
.wx-fav__line {
  fill: none; stroke: var(--text-dim); stroke-width: 1.6;
  stroke-linejoin: round; stroke-linecap: round;
}
.wx-fav__start { fill: var(--temp); }                        /* where you set off */
.wx-fav__end { fill: var(--surface-2); stroke: var(--text); stroke-width: 1.3; }
.wx-fav__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.wx-fav__name {
  font-family: var(--display); font-size: var(--step-1); font-weight: 600;
  letter-spacing: .03em; color: var(--text); margin: 0; overflow-wrap: anywhere;
}
.wx-fav__meta { font-family: var(--data); font-size: var(--step--2); color: var(--text-dim); margin: 0; }
.wx-fav__field { display: flex; flex-direction: column; gap: var(--sp-1); }
.wx-fav__speed { max-width: 9rem; }
.wx-fav__foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}
@media (max-width: 30rem){
  .wx-fav__map { width: 72px; height: 54px; }
  .wx-fav__body { gap: var(--sp-3); }
}

.wx-choice--tight .wx-chip { min-height: 30px; padding: 0 var(--sp-2); font-size: var(--step--2); }
[data-touch="gloves"] .wx-choice--tight .wx-chip { min-height: var(--tap); font-size: var(--step--1); }

.wx-verdict-set { font-size: var(--step--1); color: var(--text-dim); }
.wx-verdict-set b { color: var(--temp-ink); font-weight: 600; }
.wx-waiting {
  font-family: var(--data); font-size: var(--step--2); letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-dim);
}

.wx-linkbtn {
  background: none; border: 0; padding: var(--sp-1) 0;
  font: inherit; font-size: var(--step--1);
  color: var(--text-dim); text-decoration: underline;
  text-underline-offset: 2px; cursor: pointer;
}
.wx-linkbtn:hover { color: var(--text); }
.wx-linkbtn--warn:hover { color: var(--signal); }

#histlearned b { color: var(--text); }

/* --- What actually happened, against what was assumed --- */
.wx-actual {
  margin-top: var(--sp-3);
  padding: var(--sp-2) 0 var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--line);
}
.wx-actual__line { font-size: var(--step--1); color: var(--text); margin: 0; }
.wx-actual__line b { font-weight: 600; }
.wx-actual__line .wx-num { color: var(--temp-ink); }
.wx-actual__note {
  font-size: var(--step--1); color: var(--text-dim); margin: var(--sp-1) 0 0;
}
.wx-actual__note b { color: var(--text); font-weight: 600; }
label.wx-linkbtn { display: inline-block; }

/* --- Sync state, quiet unless it needs you --- */
.wx-sync { display: block; margin-top: var(--sp-2); font-family: var(--data); font-size: var(--step--2); }
.wx-sync.is-error { color: var(--signal); }
/* Backup errors — a failed "Back up now", and scheduled backups that have
   stopped — were toggling this class onto a note with nothing behind it, so
   they rendered in the same quiet grey as the help text around them. */
.wx-note.is-error { color: var(--signal); }

/* ---------- 23. The scorecard -------------------------------- */

/* No temperature ramp anywhere in here. Colour is data in this app and it
   means temperature; a mean error of +0.8° is the difference between two
   temperatures rather than a temperature, and painting it warm would say
   something untrue before a word is read. */

.wx-score {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-3);
  margin: var(--sp-4) 0;
}
.wx-score__cell {
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.wx-score__value { font-size: var(--step-3); line-height: 1.1; }
.wx-score__label {
  font-family: var(--display); font-size: var(--step--1); font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase;
  margin-top: var(--sp-2);
}
.wx-score__note { font-size: var(--step--1); color: var(--text-dim); margin-top: 2px; }

/* The count behind a figure, never far from it: a mean error with no n beside
   it is the one number on this page that could mislead. */
.wx-score__n {
  display: block;
  /* Below the bottom of the scale on purpose: it sits under a figure and must
     read as its footnote, not compete with it. Same trick as .wx-sync. */
  font-family: var(--data); font-size: var(--step--2); color: var(--text-dim);
}

/* How the verdicts fell. Bars rather than a pie because the question is which
   verdict came up most, and length answers that where angle does not. */
.wx-vbars { display: flex; flex-direction: column; gap: var(--sp-1); margin: var(--sp-3) 0; }
.wx-vbar { display: grid; grid-template-columns: 8.5rem 1fr 2rem; gap: var(--sp-3); align-items: center; }
.wx-vbar__label { font-size: var(--step--1); color: var(--text-dim); }
.wx-vbar__track { height: 12px; background: var(--surface-2); border-radius: 2px; }
.wx-vbar__fill { display: block; height: 100%; border-radius: 2px; background: var(--text-dim); }
/* "About right" is the one bar you want long, so it is the one that is lit. */
.wx-vbar__fill.is-hit { background: var(--temp); }
.wx-vbar__n { text-align: right; font-size: var(--step--1); }

@media (max-width: 34rem) {
  .wx-vbar { grid-template-columns: 6.5rem 1fr 1.75rem; gap: var(--sp-2); }
}
