/* AllThatStrat — visual running season planner. Desktop dark theme. */

:root {
  color-scheme: dark;

  --surface-0: #0e0e10;   /* app background */
  --surface-1: #141416;   /* chart surface, panels */
  --surface-2: #1c1c1f;   /* inputs, chips */
  --surface-3: #26262a;   /* hover */
  --border:    #2b2b2e;
  --border-strong: #3a3a40;

  --text-primary:   #f4f4f2;
  --text-secondary: #c3c2b7;
  --text-muted:     #8b8a83;

  /* The accent dresses the app's chrome — buttons, focus rings, the
     selected-week band. The chart's series wear the fixed sport palette below,
     so a series never changes colour when you re-theme. 6.27:1 on --surface-1 */
  --accent:      #f2703a;
  --accent-hi:   #f58a5c;
  --accent-dim:  #d95926;
  --accent-soft: rgba(242, 112, 58, 0.16);
  --accent-band: rgba(242, 112, 58, 0.09);   /* selected-week wash */
  --accent-edge: rgba(242, 112, 58, 0.28);   /* selected-week edge */
  --accent-glow: rgba(242, 112, 58, 0.32);   /* all-weeks-locked glow */
  --accent-ink:  #15100c;   /* readable on top of an accent fill */

  --warn:   #eda100;
  --danger: #e66767;
  --good:   #199e70;

  /* One permanent colour per sport, so a series keeps its identity across
     themes and plans. All four are lifted from the accent presets, every one of
     which already clears 3:1 on --surface-1 with room to spare. Run keeps
     Ember, so a run-only plan looks exactly as it always did. */
  --sport-run:      #f2703a;
  --sport-bike:     #4a97f0;
  --sport-swim:     #1bb583;
  --sport-strength: #e87ba4;
  /* Total is derived rather than a sport, so it is the one series drawn in a
     metal rather than a hue: silver line, gold week markers. Nothing else on
     the chart glows, which is what keeps a summed line from being mistaken for
     a fifth sport. */
  --sport-total:      #cdd2da;   /* silver */
  --sport-total-mark: #e2b544;   /* gold   */

  --phase-base:     #3987e5;
  --phase-build:    #199e70;
  --phase-peak:     #e66767;
  --phase-taper:    #9085e9;
  --phase-recovery: #8b8a83;
  --phase-off:      #3a3a40;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  --radius: 8px;
  --radius-sm: 6px;
  --topbar-h: 52px;
  --inspector-w: 320px;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--surface-0);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

button, input, select, textarea { font: inherit; color: inherit; }

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

/* The app owns the viewport exactly: nothing here ever scrolls the page.
 *
 * Each region is placed on its row explicitly. Relying on auto-placement means
 * that hiding the storage banner shifts .main up into the auto row, where it
 * collapses to content height and leaves dead space below the chart. */
.app {
  display: grid;
  grid-template-rows: var(--topbar-h) auto minmax(0, 1fr);
  height: 100vh;
  overflow: hidden;
}
.topbar { grid-row: 1; }
#storage-banner { grid-row: 2; }
.main { grid-row: 3; }

.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  background: var(--surface-1);
  border-bottom: 1px solid var(--border);
}

.brand {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-weight: 650;
  letter-spacing: -0.01em;
  margin-right: 4px;
  white-space: nowrap;
}
.brand .mark { color: var(--accent); font-size: 16px; }
.brand .sub {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* The prerelease badge. This build goes out to the public, so the version
   cannot live in an About box: it sits inside the brand, wears the accent, and
   is the one piece of the bar that is never dropped on a narrow window. Two
   halves so the number reads as the label and the word reads as the warning. */
.version {
  align-self: center;
  display: inline-flex;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  overflow: hidden;
  font-size: 11px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  white-space: nowrap;
}
.version-num {
  background: var(--accent);
  color: var(--accent-ink);
  padding: 5px 6px;
}
.version-tag {
  background: var(--accent-soft);
  color: var(--accent-hi);
  padding: 5px 6px;
}

.topbar .spacer { flex: 1; }

.lineage {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 150px;
  min-width: 0;
  flex: 0 1 auto;
}
.lineage[hidden] { display: none; }

.plan-name {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 5px 8px;
  flex: 0 1 200px;
  min-width: 84px;
  font-weight: 550;
}

/* The switcher would otherwise size itself to its longest option name and
   crowd the action buttons off the bar. */
#plan-switcher {
  flex: 0 1 156px;
  min-width: 84px;
  max-width: 156px;
}
.plan-name:hover { background: var(--surface-2); }
.plan-name:focus { background: var(--surface-2); border-color: var(--border-strong); outline: none; }

/* ---------- Controls ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 11px;
  cursor: pointer;
  color: var(--text-secondary);
  white-space: nowrap;
  transition: background 120ms, color 120ms, border-color 120ms;
}
.btn:hover:not(:disabled) { background: var(--surface-3); color: var(--text-primary); }
.btn:disabled { opacity: 0.38; cursor: default; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.btn.primary:hover:not(:disabled) { background: var(--accent-hi); color: var(--accent-ink); }
.btn.icon { padding: 6px 8px; font-size: 13px; }
.btn.danger:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); }

select, input[type="number"], input[type="date"], input[type="text"], textarea {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 8px;
  color: var(--text-primary);
}
select:focus, input:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
}
input[type="number"] { font-variant-numeric: tabular-nums; }
textarea { resize: vertical; width: 100%; font-size: 13px; }

.seg {
  display: inline-flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 2px;
  gap: 2px;
}
.seg button {
  background: transparent;
  border: 0;
  border-radius: 4px;
  padding: 4px 10px;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 550;
}
.seg button:hover { color: var(--text-primary); }
.seg button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
}

/* ---------- Main layout ---------- */

.main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--inspector-w);
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  overflow: hidden;
}

.stage {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  min-width: 0;
  min-height: 0;
  padding: 14px 16px 0;
  gap: 12px;
}

.toolbar-wrap {
  display: flex;
  flex-direction: column;
  gap: 9px;
  min-width: 0;
}

.toolbar-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.toolbar-head .disclosure {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  flex: 0 0 auto;
}
.toolbar-head .disclosure:hover { color: var(--text-secondary); }

/* Collapsed, the summary carries the same facts the controls would -- so
   folding the row away hides the inputs, not the information. */
.timeline-summary {
  font-size: 11.5px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.timeline-summary:empty { display: none; }
.timeline-summary .sep { color: var(--text-muted); margin: 0 5px; }
.timeline-summary .race { color: var(--accent); }

.toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
/* An explicit display beats the [hidden] attribute, so hiding has to be
   restated for any element that sets one. */
.toolbar[hidden] { display: none; }
.field {
  display: flex;
  align-items: center;
  gap: 7px;
}
.field > label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  font-weight: 600;
  white-space: nowrap;
}
.toolbar .divider {
  width: 1px;
  align-self: stretch;
  background: var(--border);
  margin: 2px 0;
}

#length-input { width: 62px; }
#length-slider { width: 132px; accent-color: var(--accent); }

/* Stays hard right whether or not the toolbar wraps onto a second row. */
#viewport { margin-left: auto; }

/* ---------- Stats ---------- */

.stats {
  display: flex;
  gap: 0;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.stat {
  flex: 1;
  padding: 9px 14px;
  border-right: 1px solid var(--border);
  min-width: 0;
}
.stat:last-child { border-right: 0; }

/* Names whose numbers these are. Without it the strip silently describes one
   sport while the chart shows four. */
.stat-sport {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 9px 12px;
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  background: var(--surface-2);
  border-right: 1px solid var(--border);
}

/* The whole-athlete figure, set apart from the per-sport cells beside it. */
.stat.all { background: var(--surface-2); }
.stat.all .label { color: var(--text-secondary); }
.stat .label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stat .value {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin-top: 1px;
}
.stat .value .unit {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-muted);
  margin-left: 2px;
}

/* ---------- Chart ---------- */

.chart-wrap {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  min-height: 0;
  overflow: hidden;
  transition: box-shadow 220ms ease, border-color 220ms ease;
}

/* Every week frozen: the whole plan is protected, so the chart says so from
   its outside edge rather than needing the sidebar to be read. */
.chart-wrap.all-locked {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-edge), 0 0 16px var(--accent-glow);
}
#chart {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: crosshair;
}
#chart.dragging { cursor: ns-resize; }

.tooltip {
  position: absolute;
  pointer-events: none;
  background: rgba(38, 38, 42, 0.97);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 8px 11px;
  min-width: 150px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 90ms;
  z-index: 5;
}
.tooltip.on { opacity: 1; }
.tooltip .tt-week {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  font-weight: 600;
}
.tooltip .tt-value {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 1px 0;
}
.tooltip .tt-value .unit {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-muted);
  margin-left: 3px;
}
.tooltip .tt-meta { font-size: 12px; color: var(--text-secondary); }
.tooltip .tt-sport {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  margin-top: 2px;
}
.tooltip .tt-sport i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  display: inline-block;
}
/* The other series switched on, and what they add up to. The total is the
   number that decides whether the week is actually hard. */
.tooltip .tt-breakdown {
  margin-top: 6px;
  padding-top: 5px;
  border-top: 1px solid var(--border);
  font-size: 11px;
  color: var(--text-muted);
}
.tooltip .tt-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.tooltip .tt-row i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: 0 0 6px;
  display: inline-block;
}
.tooltip .tt-row span { flex: 1 1 auto; }
.tooltip .tt-row b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}
.tooltip .tt-row.total {
  margin-top: 3px;
  padding-top: 3px;
  border-top: 1px dashed var(--border);
  color: var(--text-secondary);
}
.tooltip .tt-row.total i {
  border-radius: 1px;
  background: var(--sport-total-mark);
  box-shadow: 0 0 0 1px var(--sport-total);
  transform: rotate(45deg);
}
.tooltip .tt-row.total b { color: var(--text-primary); }
.tooltip .tt-delta { font-size: 12px; margin-top: 3px; font-variant-numeric: tabular-nums; }
/* An increase is not by itself bad news — only warn-colour it when a guardrail
   actually fired, so the tooltip agrees with the checks panel. */
.tooltip .tt-delta.up { color: var(--text-secondary); }
.tooltip .tt-delta.down { color: var(--text-muted); }
.tooltip .tt-delta.hot { color: var(--warn); font-weight: 600; }
.tooltip .tt-warn {
  font-size: 12px;
  color: var(--warn);
  margin-top: 5px;
  padding-top: 5px;
  border-top: 1px solid var(--border);
  max-width: 230px;
}

.chart-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 2px 10px;
  min-width: 0;
}
.suffix {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  min-width: 52px;
}
kbd {
  font-family: var(--mono);
  font-size: 11px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 1px 5px;
  color: var(--text-secondary);
}

/* A card standing down because the sum is selected: still readable, plainly
   not accepting input. */
.card.view-only .sport-min:disabled,
.card.view-only select:disabled,
.card.view-only textarea:disabled,
.card.view-only input:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.card.view-only .sport-lock:disabled { opacity: 0.15; cursor: not-allowed; }
.card.view-only .btn.primary:disabled { opacity: 0.4; cursor: not-allowed; }

/* The Total row is the subject in total mode, not a disabled sport. */
.sport-row[data-sport="total"] .sport-min:disabled {
  opacity: 1;
  color: var(--text-primary);
  font-weight: 600;
}
.sport-row[data-sport="total"] { margin-top: 3px; border-top: 1px solid var(--border); padding-top: 4px; }

/* ---------- Sport chips ---------- */

/* One chip carries two states at once: drawn (the checkbox) and being edited
   (the chip itself). They sit in the footer next to Lock all and share its pill
   shape, so the strip reads as one family of controls rather than two. */
.sport-chips {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}

.chip {
  --chip: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  padding: 3px 9px 3px 6px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  white-space: nowrap;
  transition: border-color 160ms, box-shadow 160ms, background 160ms;
}

/* Drawn on the chart: the chip takes the series' own colour, so the strip and
   the plot agree at a glance about which line is which. */
.chip.on { border-color: color-mix(in srgb, var(--chip) 45%, var(--border)); }

/* Being edited: the outline is the loud one, because this is the chip that
   decides where a drag lands. */
.chip.active {
  background: color-mix(in srgb, var(--chip) 12%, var(--surface-1));
  border-color: var(--chip);
  box-shadow: 0 0 10px color-mix(in srgb, var(--chip) 28%, transparent);
}

.chip-box {
  accent-color: var(--chip);
  margin: 0;
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  cursor: pointer;
}
/* Disabled on the chip being edited — you cannot hide what you are editing.
   It still reads as ticked, because it is. */
.chip-box:disabled { cursor: not-allowed; opacity: 0.75; }

.chip-face {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: none;
  border: 0;
  padding: 1px 0;
  font-size: 12px;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.chip-face.is-total { cursor: default; }
.chip.on .chip-face { color: var(--text-secondary); }
.chip.active .chip-face { color: var(--text-primary); font-weight: 600; }
.chip-face:focus-visible { outline: 2px solid var(--chip); outline-offset: 2px; border-radius: 3px; }

.chip-h {
  font-weight: 500;
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.sdot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  display: inline-block;
}
/* Total is derived, not a sport — a gold diamond rimmed in silver says so, and
   matches the marks on the line itself. */
.sdot.total {
  width: 7px;
  height: 7px;
  border-radius: 1px;
  background: var(--sport-total-mark);
  box-shadow: 0 0 0 1px var(--sport-total);
  transform: rotate(45deg);
}

/* ---------- Lock all ---------- */

.lock-all {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  padding: 4px 10px;
  font-size: 12px;
  color: var(--text-muted);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: color 200ms, border-color 200ms, box-shadow 200ms, background 200ms;
}
.lock-all:hover { color: var(--text-secondary); }
.lock-all.disabled { opacity: 0.4; cursor: not-allowed; }
.lock-all.disabled input { cursor: not-allowed; }
.lock-all input { accent-color: var(--accent); margin: 0; cursor: pointer; }

/* Lit while the whole season is frozen, matching the chart's own glow so the
   two read as one state rather than two coincidences. */
.lock-all.on {
  color: var(--accent);
  font-weight: 600;
  background: var(--accent-soft);
  border-color: var(--accent);
  box-shadow: 0 0 12px var(--accent-glow);
}

/* ---------- Controls popover ---------- */

.controls-wrap { position: relative; flex: 0 0 auto; }

.controls-pop {
  position: absolute;
  bottom: calc(100% + 9px);
  right: 0;
  z-index: 40;
  width: 340px;
  padding: 13px 15px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.6);
}
.controls-pop[hidden] { display: none; }

.controls-pop h3 {
  margin: 0 0 7px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}
.controls-pop h3 + dl { margin: 0 0 13px; }
.controls-pop dl:last-child { margin-bottom: 0; }

.controls-pop dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 5px 12px;
  margin: 0;
  font-size: 12px;
  align-items: baseline;
}
.controls-pop dt {
  color: var(--text-secondary);
  white-space: nowrap;
  font-weight: 550;
}
.controls-pop dd {
  margin: 0;
  color: var(--text-muted);
}

/* ---------- Inspector ---------- */

.inspector {
  background: var(--surface-1);
  border-left: 1px solid var(--border);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
  overflow: hidden;
}
.inspector > .card { flex: 0 0 auto; }

/* The only element permitted its own scrollbar: warning counts are unbounded,
   everything else is fixed-size and must stay visible. */
/* Shrinks to absorb a long warning list, but never grows: an empty checks card
   stretched down the sidebar just pushes the generator to the bottom. */
/* The floor is the header plus a sliver of list. It has to be low enough that
   a four-sport plan on a short window still fits the sidebar without the whole
   column scrolling — the chart's warning rings carry the signal when the list
   itself is down to one line. */
#checks-card {
  display: flex;
  flex-direction: column;
  flex: 0 1 auto;
  min-height: 46px;
}

.card {
  background: var(--surface-0);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 11px;
}
.card > h2 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  font-weight: 600;
  margin: 0 0 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.card > h2 .note { text-transform: none; letter-spacing: 0; color: var(--text-muted); font-weight: 500; }

.row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 7px;
}
.row:last-child { margin-bottom: 0; }
.row > label {
  flex: 0 0 84px;
  font-size: 12px;
  color: var(--text-secondary);
}
.row > input, .row > select { flex: 1; min-width: 0; }
.row.stack { flex-direction: column; align-items: stretch; gap: 5px; }
.row.stack > label { flex: none; }

.check {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
}
.check input { accent-color: var(--accent); margin: 0; }
/* Two checkboxes share the lock row, so neither stretches to fill it. */
.row > .check { flex: 0 0 auto; }
.row > .check + .check { margin-left: 4px; }

/* One row per sport the plan actually uses, so every number for this week is
   visible and editable at once. Editing any row switches to that sport — the
   chips are for the chart, not a prerequisite for typing.

   The metrics here are deliberately tighter than .row: four of these stack up,
   and the sidebar has no room to spare on a short window. */
.sport-rows {
  display: grid;
  gap: 2px;
  margin-bottom: 8px;
}
.sport-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 3px 0 2px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
}
.sport-row[hidden] { display: none; }
.sport-row.active {
  background: var(--surface-2);
  border-color: var(--border);
}
.sport-pick {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 78px;
  background: none;
  border: 0;
  padding: 2px 0;
  font-size: 12px;
  color: var(--text-muted);
  cursor: pointer;
  text-align: left;
}
.sport-row.active .sport-pick { color: var(--text-primary); font-weight: 600; }
.sport-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.sport-min[type="number"] {
  flex: 1 1 auto;
  min-width: 0;
  /* The attribute selector is load-bearing: the global input rule sets 6px of
     padding at (0,1,1), which a bare .sport-min would lose to. */
  padding: 2px 6px;
  font-size: 12px;
  line-height: 18px;
  text-align: right;
}
/* A locked week refuses the edit, so the field should not invite one. */
.sport-row.locked .sport-min { opacity: 0.55; }
.sport-lock {
  flex: 0 0 auto;
  background: none;
  border: 0;
  padding: 0 1px;
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.35;
  transition: opacity 120ms;
}
.sport-lock:hover { opacity: 0.85; }
.sport-lock[aria-pressed="true"] { opacity: 1; }
.sport-lock:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

.sport-hm {
  flex: 0 0 48px;
  font-size: 11px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* Sports this plan does not use yet. A runner should not have to look past
   three permanent rows of zeros to find their own numbers, but picking one up
   mid-season has to be one click. */
.sport-add {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 4px 0 8px;
}
.sport-add:empty { display: none; }
.sport-add button {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: none;
  border: 1px dashed var(--border-strong);
  border-radius: 999px;
  padding: 1px 8px;
  font-size: 11px;
  color: var(--text-muted);
  cursor: pointer;
}
.sport-add button:hover { color: var(--text-secondary); border-color: var(--text-muted); }
.sport-add button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Phase belongs to the season rather than to any one sport, and the row says so. */
.suffix.shared {
  min-width: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.gen-phases {
  margin-top: 8px;
  font-size: 12px;
}

.week-title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.week-dates { font-size: 12px; color: var(--text-muted); margin-bottom: 10px; }

.empty {
  color: var(--text-muted);
  font-size: 13px;
  text-align: center;
  padding: 18px 8px;
  line-height: 1.5;
}

.btn-row { display: flex; gap: 6px; }

/* Two fields per line: the generator is the tallest fixed block in the sidebar,
   and halving its row count is what keeps the whole panel on screen. */
.gen-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 8px;
}
.fld {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.fld.wide { grid-column: 1 / -1; }
.fld > label {
  font-size: 11px;
  color: var(--text-secondary);
  flex: 0 0 auto;
  white-space: nowrap;
}
.fld > label.mid { color: var(--text-muted); }
.fld > input, .fld > select {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 6px;
  font-size: 13px;
}
.fld > input[type="number"] { text-align: right; }
.fld.wide > #gen-recovery-pct { flex: 0 0 46px; }
.fld > .u {
  font-size: 11px;
  color: var(--text-muted);
  flex: 0 0 auto;
}
/* The generator rewrites the whole season, so say so where the button is,
   not in muted small print that reads as a caption. */
.gen-warn {
  margin: 8px 0 0;
  padding: 6px 9px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--warn);
  background: rgba(237, 161, 0, 0.10);
  border: 1px solid rgba(237, 161, 0, 0.32);
  border-radius: 6px;
}
.gen-warn strong { font-weight: 650; }
.gen-warn .kept { color: var(--text-secondary); }

/* Disclosure header, matching the checks card's header affordance. */
.disclosure {
  display: flex;
  align-items: center;
  gap: 5px;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
}
.disclosure:hover { color: var(--text-secondary); }
.disclosure:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.disclosure .chev {
  display: inline-block;
  font-size: 9px;
  transition: transform 140ms;
}
.disclosure[aria-expanded="false"] .chev { transform: rotate(-90deg); }
.btn-row .btn { flex: 1; justify-content: center; }

/* ---------- Warnings ---------- */

/* ---------- Checks toggle ---------- */

.h2-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}

.switch {
  position: relative;
  width: 30px;
  height: 17px;
  padding: 0;
  border-radius: 9px;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  cursor: pointer;
  flex: 0 0 auto;
  transition: background 130ms, border-color 130ms;
}
.switch > span {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--text-muted);
  transition: transform 130ms, background 130ms;
}
.switch[aria-checked="true"] {
  background: var(--accent);
  border-color: var(--accent);
}
.switch[aria-checked="true"] > span {
  transform: translateX(13px);
  background: var(--accent-ink);
}
.switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.checks-off {
  font-size: 12px;
  color: var(--text-muted);
  padding: 4px 2px;
}

/* ---------- Accent picker ---------- */

.accent-wrap { position: relative; flex: 0 0 auto; display: flex; }

.accent-btn {
  width: 26px;
  height: 26px;
  padding: 2px;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  /* The rim is a colour wheel; the middle shows the accent in use. */
  background: conic-gradient(#f2703a, #eda100, #5bc46a, #1bb583,
                             #4a97f0, #9085e9, #e87ba4, #e66767, #f2703a);
}
.accent-btn:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 2px; }
.accent-dot {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface-1);
}

.accent-pop {
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  z-index: 40;
  width: 214px;
  padding: 11px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.6);
}
.accent-pop[hidden] { display: none; }

.accent-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
.accent-sw {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 6px;
  border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.35);
  cursor: pointer;
  padding: 0;
}
.accent-sw[aria-pressed="true"] { border-color: var(--text-primary); }
.accent-sw:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 1px; }

.accent-custom {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 12px;
  color: var(--text-secondary);
}
.accent-custom input[type="color"] {
  width: 30px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  background: none;
  cursor: pointer;
}
.accent-custom code {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-muted);
  margin-left: auto;
}
.accent-note {
  margin: 8px 0 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--warn);
}
.accent-note[hidden] { display: none; }

.warn-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.warn-item {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 2px solid var(--warn);
  border-radius: var(--radius-sm);
  padding: 7px 9px;
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.warn-item:hover { background: var(--surface-3); }
.warn-item.high { border-left-color: var(--danger); }
.warn-item .sdot { margin-top: 4px; }
.warn-item .wk {
  font-weight: 650;
  color: var(--text-primary);
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.warn-ok {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--good);
}

/* ---------- Phase legend ---------- */

.phase-key {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  font-size: 11px;
  color: var(--text-secondary);
  min-width: 0;
  margin: 0 0 13px;
}
.phase-key span { display: inline-flex; align-items: center; gap: 5px; }
.phase-key i {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  display: inline-block;
}

/* ---------- Banner ---------- */

.banner {
  background: rgba(237, 161, 0, 0.12);
  border: 1px solid rgba(237, 161, 0, 0.35);
  color: var(--warn);
  border-radius: var(--radius-sm);
  padding: 8px 11px;
  font-size: 12px;
  margin: 0 16px;
}
.banner[hidden] { display: none; }

/* ---------- Modal ---------- */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: grid;
  place-items: center;
  z-index: 50;
}
.modal-backdrop[hidden] { display: none; }
.modal {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  padding: 18px;
  width: 380px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}
.modal h2 { margin: 0 0 4px; font-size: 16px; }
.modal p.sub { margin: 0 0 14px; font-size: 13px; color: var(--text-muted); }
.modal .btn-row { margin-top: 16px; }

/* Screen-reader-only live region */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.too-small {
  display: none;
  position: fixed;
  inset: 0;
  background: var(--surface-0);
  place-items: center;
  text-align: center;
  padding: 40px;
  color: var(--text-secondary);
  z-index: 100;
}
.too-small-version { margin: 0 0 14px; }
@media (max-width: 899px) {
  .too-small { display: grid; }
}

/* ---------- SVG chart ---------- */

.ats-gridline {
  stroke: var(--border);
  stroke-width: 1;
  shape-rendering: crispEdges;
}
.ats-axis-line {
  stroke: var(--border-strong);
  stroke-width: 1;
  shape-rendering: crispEdges;
}
.ats-ylabel {
  fill: var(--text-muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.ats-yunit {
  fill: var(--text-muted);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.09em;
}
.ats-month {
  fill: var(--text-muted);
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.09em;
}

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

/* An overlay is a reference, not the thing you are editing: thinner, dimmer,
   and without dots or a fill, so the active series stays unambiguous even with
   four sports on the plot at once. */
.ats-line.overlay {
  stroke-width: 1.5;
  opacity: 0.55;
}
.ats-line.total {
  stroke-width: 2;
  filter: drop-shadow(0 0 2px rgba(205, 210, 218, 0.55))
          drop-shadow(0 0 6px rgba(205, 210, 218, 0.28));
}

/* One diamond per week. Barely a glow — at this size the shape carries itself,
   and a halo on every week smears the line it sits on. The glow sits on the
   group rather than on each mark, so a long season composites one filter
   instead of a hundred. */
.ats-total-marks {
  filter: drop-shadow(0 0 1.5px rgba(226, 181, 68, 0.2));
  pointer-events: none;
}
.ats-totalmark {
  fill: var(--sport-total-mark);
  /* A surface-coloured rim keeps each diamond distinct where the silver line
     runs behind it. */
  stroke: var(--surface-1);
  stroke-width: 1;
}
/* In total mode there are no dots to ring, so the diamond takes the mark. */
.ats-totalmark.warn {
  stroke: var(--warn);
  stroke-width: 2;
}
.ats-totalmark.warn.high { stroke: var(--danger); }

.ats-point {
  fill: var(--accent);
  /* 2px surface ring keeps dots legible where they sit on the line */
  stroke: var(--surface-1);
  stroke-width: 2;
}
.ats-point.warn {
  stroke: var(--warn);
  stroke-width: 2.5;
}
.ats-point.warn.high { stroke: var(--danger); }

/* Invisible until you go near one, then a clear grab target. */
/* A locked week reads as a bigger dot with a dark diamond punched out of it. */
.ats-lockmark {
  fill: #000;
  pointer-events: none;
}

.ats-phase-handle {
  fill: var(--text-primary);
  fill-opacity: 0;
  pointer-events: all;
  cursor: ew-resize;
  transition: fill-opacity 110ms;
}
.ats-phase-handle:hover { fill-opacity: 0.28; }
.ats-phase-handle.active { fill-opacity: 0.45; }
#chart.resizing { cursor: ew-resize; }

.ats-selband {
  fill: var(--accent-band);
  stroke: var(--accent-edge);
  stroke-width: 1;
}

.ats-crossline {
  stroke: var(--text-muted);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  pointer-events: none;
}
.ats-crossdot {
  fill: var(--accent);
  stroke: var(--surface-1);
  stroke-width: 2;
  pointer-events: none;
}

.ats-raceline {
  stroke: var(--text-secondary);
  stroke-width: 1;
  stroke-dasharray: 2 4;
}
.ats-racelabel {
  fill: var(--text-secondary);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
}

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

/* ---------- Narrow desktop windows ---------- */

/* Below this the bar has to choose: keep every action reachable, and drop the
   two decorations. Both stay available as tooltips on the plan name. */
/* Below this the footer cannot hold the chips, the full lock-all sentence and
   Controls at once. The sport name is the part that goes: it is already on the
   lit chip two inches to the left, and it stays in the tooltip. */
@media (max-width: 1150px) {
  #lock-all-label { font-size: 0; }
  #lock-all-label::after { content: 'Lock all'; font-size: 12px; }
  /* Chips give up padding before they give up their labels — the colour alone
     is not enough to tell four series apart. */
  .sport-chips { gap: 3px; }
  .chip { padding: 2px 6px 2px 4px; gap: 4px; }
  .chip-face { gap: 4px; }
}

@media (max-width: 1320px) {
  .brand .sub { display: none; }
  /* Everything decorative in the bar gives way before the version does. It
     gives up padding and letter-spacing, never its size or its place — a badge
     nobody can read is not a warning. */
  .version { letter-spacing: 0.04em; }
  .version-num, .version-tag { padding: 5px 5px; }
  .lineage { display: none; }
  /* Undo and redo keep their glyphs; the words are the first thing to go,
     since both also have tooltips and keyboard shortcuts. */
  #undo .lbl, #redo .lbl { display: none; }
  .topbar { gap: 8px; padding: 0 12px; }
  .topbar .btn { padding: 6px 8px; }
  .plan-name { flex: 0 1 150px; min-width: 88px; }
  /* The hours are also on the stats strip and in the tooltip; the label and
     the colour are what make a chip identifiable, so they are what stay. */
  .chip-h { display: none; }
  .stat-sport { padding: 9px 9px; }
  #plan-switcher { max-width: 130px; }
}

/* Below this the bar can no longer seat the wordmark and the version badge at
   once — it already sat within a few pixels of full before the badge existed.
   The wordmark is what goes: the tab title and the ◢ still say which app this
   is, while on a prerelease build the version is the one thing a user must not
   miss. Every button keeps its label and stays reachable. */
@media (max-width: 1060px) {
  .brand .name { display: none; }
  .topbar { gap: 6px; }
  #plan-switcher { max-width: 96px; }
}
