@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;1,400;1,500&display=swap');

/* Serra — paper theme, matching the camp site's visual language:
   cream paper, IBM Plex Mono, thin 2px-radius borders, muted earthy accents. */

:root {
  --bg: #F2EFE7;
  --paper: #FBF9F4;
  --ink: #1F2420;
  --ink-soft: #5B5F54;
  --line: #D8D2C0;

  --accent: #3F5B4E;        /* cycling green — primary action */
  --accent-ink: #FBF9F4;
  --running: #B3542E;

  /* Macrocycle phases */
  --phase-base: #6E7F3A;
  --phase-build: #B3542E;
  --phase-taper: #4A6D8C;
  --phase-event: #8A3324;
  --phase-recovery: #9A9587;

  /* Ride types */
  --rt-rest: #9A9587;
  --rt-recovery: #7C8AA0;
  --rt-endurance: #3F5B4E;
  --rt-long: #2F5346;
  --rt-tempo: #6E7F3A;
  --rt-sweetspot: #8A8A3A;
  --rt-threshold: #B3542E;
  --rt-vo2: #9A5A5A;
  --rt-anaerobic: #7A2820;
  --rt-test: #7A5A6B;
  --rt-event: #8A3324;

  --good: #3F6B45;
  --warn: #A8322A;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 15px;
  line-height: 1.6;
  overflow-wrap: break-word;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---- top nav ---- */
header.top {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  flex-wrap: wrap;
}
header.top h1 {
  font-size: 16px;
  margin: 0;
  font-weight: 500;
  letter-spacing: 0.02em;
}
/* ---- brand logo (v4.6): replaces the old emoji mark ---- */
.brand { display: inline-flex; align-items: center; gap: 9px; }
.brand-logo {
  height: 26px;
  width: auto;
  vertical-align: middle;
  display: inline-block;
}
.brand-logo.lg { height: 44px; }
/* ---- the drawn marks (v1.7), built from graphics/serra-*.mp. Sized in em so
   they ride with whatever text they sit in, and nudged down because their
   optical centre is below the middle of the disc. ---- */
.sym { width: 1.6em; height: 1.6em; vertical-align: -0.42em; display: inline-block; }
.sym.lg { width: 30px; height: 30px; vertical-align: middle; }
tr.act-nonbike .sym { opacity: .85; }
.rw-flag .sym, .tag .sym, button .sym { width: 1.35em; height: 1.35em; vertical-align: -0.3em; }
.rw-cell-flags .sym { width: 1.5em; height: 1.5em; margin-right: 2px; }
/* The symbol beside a picker. Not .row: every select in the app is width:100%,
   which in a flex row wraps the symbol onto its own line. */
.sport-pick { display: flex; align-items: center; gap: 9px; }
.sport-pick select { width: auto; }
.hero .brand-logo.lg { height: 52px; }
header.top nav { display: flex; gap: 4px; margin-left: auto; flex-wrap: wrap; }
header.top nav a {
  color: var(--ink-soft);
  font-size: 13px;
  padding: 5px 10px;
  border: 1px solid transparent;
  border-radius: 2px;
}
header.top nav a:hover { color: var(--ink); text-decoration: none; border-color: var(--line); }
header.top nav a.active {
  color: var(--ink);
  border-color: var(--line);
  background: var(--bg);
}
/* v6.0: the cross-link to the other app — set apart by a hairline so it reads
   as "leave this app", not as another page of it. */
header.top nav a.ext {
  margin-left: 6px;
  padding-left: 14px;
  border-left: 1px solid var(--line);
  border-radius: 0 2px 2px 0;
}
header.top nav a.ext:hover { border-color: var(--line); border-left-color: var(--line); color: var(--accent); }

main, .wrap { max-width: 940px; margin: 0 auto; padding: 24px; }

h2 {
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 500;
  font-size: 22px;
  margin: 28px 0 14px;
  letter-spacing: 0.01em;
}
h2:first-child { margin-top: 8px; }
h3 {
  font-size: 12px;
  margin: 22px 0 10px;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 500;
}

/* ---- cards ---- */
.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 18px 20px;
  margin: 12px 0;
}

.eyebrow {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 8px;
}

/* ---- grids ---- */
.grid { display: grid; gap: 12px; }
.grid.two { grid-template-columns: 1fr 1fr; }
.grid.three { grid-template-columns: repeat(3, 1fr); }
.grid.four { grid-template-columns: repeat(4, 1fr); }
/* v5.9: the dashboard's readiness / .fit-upload / Strava row needs three real
   columns only on a wide screen — below that two reads better than three
   squeezed ones. */
@media (max-width: 1040px) {
  .grid.three.dash-row { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 720px) {
  .grid.two, .grid.three, .grid.four { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 460px) {
  .grid.three, .grid.four { grid-template-columns: 1fr; }
}

/* ---- forms ---- */
label {
  display: block;
  font-size: 11px;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 12px 0 4px;
}
input, select, textarea {
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 9px 11px;
  width: 100%;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--ink-soft); }
input[type=file] { padding: 7px; }

button, .btn {
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--accent-ink);
  cursor: pointer;
  border-radius: 2px;
  padding: 9px 16px;
  width: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
button:hover, .btn:hover { opacity: 0.9; text-decoration: none; }
button.ghost, .btn.ghost {
  background: var(--paper);
  border: 1px solid var(--line);
  color: var(--ink);
}
button.ghost:hover, .btn.ghost:hover { border-color: var(--ink-soft); opacity: 1; }
button:disabled { opacity: 0.5; cursor: default; }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* ---- stat tiles ---- */
.stat {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 14px 16px;
}
.stat .n { font-size: 28px; font-weight: 500; line-height: 1; }
.stat .l {
  font-size: 10px;
  color: var(--ink-soft);
  margin-top: 6px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* ---- tables ---- */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); }
th {
  color: var(--ink-soft);
  font-weight: 500;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--bg); }

/* ---- tags / pills ---- */
.tag {
  display: inline-block;
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 2px;
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.tag.A { background: #f0e7de; border-color: var(--running); color: var(--running); }
.tag.B { color: var(--phase-taper); }
.tag.C { color: var(--ink-soft); }
.tag.phase-base { color: var(--phase-base); border-color: var(--phase-base); }
.tag.phase-build { color: var(--phase-build); border-color: var(--phase-build); }
.tag.phase-taper { color: var(--phase-taper); border-color: var(--phase-taper); }
.tag.phase-event { color: var(--phase-event); border-color: var(--phase-event); }
.tag.phase-recovery { color: var(--phase-recovery); border-color: var(--phase-recovery); }

.pill { display: inline-block; width: 10px; height: 10px; border-radius: 2px; vertical-align: middle; }
.muted { color: var(--ink-soft); }
.small { font-size: 12px; }
.note { font-size: 13px; color: var(--ink-soft); margin: 8px 0; max-width: 68ch; }

/* ---- messages ---- */
.msg {
  padding: 10px 14px;
  border-radius: 2px;
  margin: 8px 0;
  font-size: 13px;
  border: 1px solid var(--line);
  background: var(--paper);
}
.msg.ok { background: #edf3ed; border-color: #c3d6c4; color: #33402f; }
.msg.warn { background: #f9e2de; border-color: #dda49c; color: #6b3a34; }
.msg.info { background: #eef2ef; border-color: #c8d2c9; color: #34402f; }

/* ---- hero + home nav ---- */
.hero { padding: 56px 0 32px; border-bottom: 1px solid var(--line); }
.hero h1 {
  font-family: 'IBM Plex Mono', monospace;
  font-weight: 500;
  font-size: clamp(30px, 5vw, 36px);
  margin: 0 0 12px;
}
.hero p { color: var(--ink-soft); max-width: 58ch; margin: 0; font-style: italic; }

/* ==========================================================================
   Plan page — week menu + single-week view
   ========================================================================== */

.week-menu {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 6px;
}
.week-btn {
  position: relative;
  font: inherit;
  font-size: 12px;
  min-width: 46px;
  padding: 8px 6px 7px;
  border: 1px solid var(--line);
  border-top: 3px solid var(--wk, var(--line));
  border-radius: 2px;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  line-height: 1.1;
}
.week-btn:hover { border-color: var(--ink-soft); border-top-color: var(--wk, var(--line)); opacity: 1; }
.week-btn .wk-no { font-weight: 500; font-size: 13px; }
.week-btn .wk-ph {
  font-size: 8px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.week-btn.is-active {
  background: var(--wk, var(--ink));
  border-color: var(--wk, var(--ink));
  border-top-color: var(--wk, var(--ink));
  color: #fff;
}
.week-btn.is-active .wk-ph { color: rgba(255,255,255,0.85); }
.week-btn.is-projected { opacity: 0.45; border-style: dashed; }
.week-btn.is-projected.is-active { opacity: 0.8; }
/* v1.36 — the block review */
.block-banner { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0 10px; padding: 10px 12px;
  background: #f4f1e8; border-left: 3px solid #B3542E; border-radius: 2px; font-size: 14px; }
.block-review h3 { margin: 10px 0 6px; }
.block-table { border-collapse: collapse; font-size: 13px; margin: 6px 0; }
.block-table th, .block-table td { padding: 3px 10px 3px 0; text-align: left; border-bottom: 1px solid #e4e2d8; }
.block-table tr.muted td { color: var(--ink-soft); }
.block-table td.warn { color: var(--warn); font-weight: 500; }
.block-table td button { margin-right: 4px; }
.projected-note { margin: 10px 0; padding: 12px; border: 1px dashed #c9c6bb; border-radius: 4px; color: var(--ink-soft); }
.explain { margin: 8px 0; }
.explain .explain-text { border-left: 3px solid #5E7C6A; padding-left: 10px; }
/* v1.39 — the thread */
.thread { margin-top: 10px; border-top: 1px solid #e4e2d8; padding-top: 6px; }
.thread-msgs { display: flex; flex-direction: column; gap: 8px; margin: 6px 0; }
.thread-msg { padding: 8px 10px; border-radius: 6px; background: #f4f1e8; }
.thread-msg.assistant { background: #eef2ec; }
.thread-msg.refused { background: #f6ebe6; border-left: 3px solid #B3542E; }
.thread-text { white-space: pre-wrap; margin-top: 2px; }
.proposal { margin-top: 6px; padding: 6px 8px; border-left: 3px solid #B3542E; background: #fff; }
.proposal .warn { color: var(--warn); }
.packet { font-size: 11px; max-height: 320px; overflow: auto; background: #fff; padding: 6px; border: 1px solid #e4e2d8; white-space: pre-wrap; }
.projected-note p { margin: 0 0 8px; }
.week-btn.is-current::after {
  content: "";
  position: absolute;
  bottom: 3px;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: currentColor;
}
.week-btn.is-recovery .wk-no::after {
  content: "·R";
  font-size: 9px;
  color: var(--phase-recovery);
  vertical-align: super;
}
.week-btn.is-active.is-recovery .wk-no::after { color: rgba(255,255,255,0.9); }

.phase-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 4px 0 20px;
  font-size: 11px;
  color: var(--ink-soft);
}
.phase-legend span { display: inline-flex; align-items: center; gap: 6px; }
.phase-legend i { width: 12px; height: 12px; border-radius: 2px; display: inline-block; }

.week-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  border-left: 3px solid var(--wk, var(--line));
  padding: 4px 0 4px 14px;
  margin-bottom: 4px;
}
.week-head .wh-title { font-size: 18px; font-weight: 500; }
.week-head .wh-meta { font-size: 12px; color: var(--ink-soft); }
.week-focus { font-size: 13px; color: var(--ink-soft); font-style: italic; margin: 0 0 14px; }

.week-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 16px;
}
.week-stats .ws {
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--bg);
  padding: 8px 12px;
  min-width: 88px;
}
.week-stats .ws .k { font-size: 9px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); }
.week-stats .ws .v { font-size: 17px; font-weight: 500; }
.week-stats .ws .p { font-size: 11px; color: var(--ink-soft); margin-top: 2px; }

/* ---- ride rows ---- */
.ride {
  border: 1px solid var(--line);
  border-left: 3px solid var(--rt, var(--ink-soft));
  border-radius: 2px;
  background: var(--paper);
  padding: 12px 16px;
  margin: 8px 0;
}
.ride.done { background: #f4f6f1; }
.ride-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.ride .t { font-weight: 500; font-size: 14px; }
.ride .rt-badge {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--rt, var(--ink-soft));
  border: 1px solid currentColor;
  border-radius: 2px;
  padding: 1px 6px;
  opacity: 0.85;
}
.ride .meta { font-size: 12px; color: var(--ink-soft); margin-top: 3px; }
.ride .narrative { font-size: 12px; margin-top: 6px; white-space: pre-line; color: var(--ink); }
.ride .exports { margin-top: 8px; display: flex; gap: 8px; flex-wrap: wrap; }
.ride .exports a {
  font-size: 11px;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 3px 9px;
}
.ride .exports a:hover { border-color: var(--ink-soft); text-decoration: none; }
.ride .done-flag { color: var(--good); font-weight: 500; }

/* ==========================================================================
   Dashboard — chart + activities
   ========================================================================== */

/* period selector row above the fitness chart (v4.8) */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 16px;
}
.period-pick {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.period-pick select {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 4px 8px;
}

/* custom from→to range */
.period-controls {
  display: inline-flex;
  align-items: center;
  gap: 8px 14px;
  flex-wrap: wrap;
}
.period-custom {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.period-custom input[type="date"] {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 3px 6px;
}
.period-custom button { font-size: 11px; padding: 4px 10px; }
.period-custom .rmsg {
  font-size: 10px;
  color: var(--phase-event);
  text-transform: none;
  letter-spacing: 0;
}

.chart-wrap {
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--paper);
  overflow: hidden;
  margin-top: 14px;
  position: relative;
}
.chart-wrap svg { display: block; width: 100%; height: auto; }

.ch-grid { stroke: var(--line); stroke-width: 1; }
.ch-grid.dash { stroke-dasharray: 3 5; }
.ch-axis { stroke: var(--ink-soft); stroke-width: 1; }
.ch-axis-label { font-family: 'IBM Plex Mono', monospace; font-size: 10px; fill: var(--ink-soft); }
.ch-zero { stroke: var(--ink-soft); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.6; }
.ch-line { fill: none; stroke-width: 2; vector-effect: non-scaling-stroke; }
.ch-line.ctl { stroke: var(--phase-build); }
.ch-line.atl { stroke: var(--phase-taper); }
.ch-line.tsb { stroke: var(--phase-base); stroke-width: 1.5; }
.ch-tsb-area { fill: var(--phase-base); opacity: 0.08; }
.ch-crosshair { stroke: var(--ink-soft); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.7; }
.ch-dot { stroke: var(--paper); stroke-width: 1.5; }
.ch-hit { fill: transparent; }

.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  padding: 10px 14px;
  border-top: 1px solid var(--line);
  font-size: 11px;
  color: var(--ink-soft);
}
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }

.ch-tip {
  position: absolute;
  pointer-events: none;
  background: var(--ink);
  color: var(--paper);
  font-size: 11px;
  line-height: 1.5;
  padding: 6px 9px;
  border-radius: 2px;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, 4px);
  transition: opacity 0.08s;
  z-index: 5;
}
.ch-tip b { font-weight: 500; }
.ch-tip .tip-ctl { color: #e79a6a; }
.ch-tip .tip-atl { color: #9db8d4; }
.ch-tip .tip-tsb { color: #b7c78a; }

.act-src {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 1px 6px;
}

/* readiness score band */
.ready-out .msg b { font-size: 14px; }

/* ---- v5.8 ride analysis ------------------------------------------------ */

/* page header: title block left, actions right, wrapping on narrow screens */
.an-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin: 28px 0 4px;
}
.an-head h2 { margin: 2px 0 4px; }
.an-sub { font-size: 12px; color: var(--ink-soft); display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.an-sub .sep { opacity: 0.5; }
.an-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.an-actions .btn, .an-actions button { font-size: 12px; padding: 5px 12px; }

/* "analyse →" link in an activity table */
.an-link {
  font-size: 11px;
  white-space: nowrap;
  border: 1px solid var(--line);
  border-radius: 2px;
  padding: 2px 8px;
  color: var(--accent);
}
.an-link:hover { border-color: var(--accent); text-decoration: none; background: #EDF2EC; }

/* verdict card: the messages stack tight, each already carries its own band */
.an-verdict .msg { margin: 0 0 8px; }
.an-verdict .msg:last-child { margin-bottom: 0; }
.an-verdict .msg b { font-weight: 600; }

/* channel toggles above the ride trace */
.an-toggles { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 4px; }
.an-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--ink-soft);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  margin: 0;
  cursor: pointer;
}
.an-toggle input { width: auto; margin: 0; }

/* horizontal distribution bars (power zones, HR zones, cadence) */
.zbars { display: flex; flex-direction: column; gap: 5px; }
.zrow {
  display: grid;
  /* v1.33: the label may be squeezed and wrap, the bar may not vanish. With a
     178 px floor on the label, any card under ~310 px (a two-column card on a
     1000 px window) left the track zero wide: a 1 px border where the bar
     should be, in every distribution table on the page. */
  grid-template-columns: minmax(84px, 2fr) minmax(48px, 3fr) 52px 46px;
  align-items: center;
  gap: 9px;
  font-size: 11.5px;
  line-height: 1.35;
}
.zlab { color: var(--ink); overflow-wrap: anywhere; }
.ztrack { background: var(--bg); border: 1px solid var(--line); border-radius: 2px; height: 14px; overflow: hidden; }
.zfill { height: 100%; }
.zval { text-align: right; color: var(--ink-soft); }
.zpct { text-align: right; font-weight: 500; }

@media (max-width: 640px) {
  .zrow { grid-template-columns: 1fr 44px; row-gap: 2px; }
  .ztrack { grid-column: 1 / -1; }
  .zval { text-align: left; }
}

/* ---- footer ---- */
.footer {
  max-width: 940px;
  margin: 48px auto 0;
  padding: 20px 24px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink-soft);
}

@media (max-width: 640px) {
  main, .wrap { padding: 16px; }
  .week-stats .ws { min-width: 72px; flex: 1; }
}

/* ---- setup: stage toughness bar (setup.js builds .zbar of coloured spans) ---- */
.zbar {
  display: flex;
  height: 24px;
  border-radius: 2px;
  overflow: hidden;
  border: 1px solid var(--line);
  margin: 12px 0;
}
.zbar span { display: block; }

/* ---- home hero eyebrow ---- */
.hero .eyebrow { margin-bottom: 14px; }

/* ======================================================================== */
/* v4.2 Plan-window rework                                                   */
/* ======================================================================== */
:root { --phase-peak: #7A5A6B; --strength: #6B5B95; }

/* ---- schematic workout: interval-profile bar + readable line ---- */
.wo-schematic { margin: 8px 0 4px; }
/* v1.8: a real chart, not a 46px strip. Fixed viewBox aspect + width:100%
   means it scales without the horizontal stretch the old
   preserveAspectRatio="none" applied to every unit on it. */
.wo-chart { position: relative; }
.wo-bars {
  width: 100%; height: auto; display: block; touch-action: none;
  border: 1px solid var(--line); border-radius: 2px; background: var(--paper);
  cursor: crosshair;
}
.wo-bars .wo-ax { font-size: 9px; fill: var(--ink-soft); font-family: inherit; }
.wo-bars .wo-seg { transition: opacity .12s; }
.wo-tip {
  position: absolute; top: 4px; z-index: 5; pointer-events: none;
  background: var(--paper); border: 1px solid var(--line); border-radius: 2px;
  padding: 5px 8px; font-size: 11.5px; line-height: 1.45; color: var(--ink);
  box-shadow: 0 2px 8px rgba(0,0,0,.10); white-space: nowrap; max-width: 100%;
}
.wo-tip b { font-weight: 600; }
.wo-tip-t { color: var(--ink-soft); font-size: 10.5px; }
.wo-sum { margin-top: 5px; }
.wo-sum b { color: var(--ink); font-weight: 500; }
.wo-line {
  font-size: 12.5px; color: var(--ink-soft); margin-top: 6px;
  letter-spacing: .2px;
}
.wo-line b { color: var(--ink); font-weight: 500; }
.wo-strength { font-size: 13px; color: var(--ink); }

/* ---- indoor / outdoor toggle on a ride ---- */
.env-toggle { display: inline-flex; gap: 0; border: 1px solid var(--line); border-radius: 2px; overflow: hidden; margin-left: 8px; vertical-align: middle; }
.env-toggle button {
  font-family: inherit; font-size: 11px; text-transform: uppercase; letter-spacing: .5px;
  padding: 2px 8px; border: 0; background: var(--paper); color: var(--ink-soft); cursor: pointer;
}
.env-toggle button.on { background: var(--accent); color: var(--accent-ink); }
.env-note { font-size: 11.5px; color: var(--ink-soft); margin-top: 4px; }
.env-note.heat { color: var(--phase-build); }

/* ---- discipline badge ---- */
.disc-badge {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .5px;
  padding: 1px 6px; border-radius: 2px; border: 1px solid var(--line); margin-left: 6px;
}
.disc-run { color: var(--running); border-color: var(--running); }
.disc-strength { color: var(--strength); border-color: var(--strength); }
.ride.disc-d-run { --rt: var(--running); }
.ride.disc-d-strength { --rt: var(--strength); }

/* ---- per-week "edit days" ---- */
.week-tools { display: flex; gap: 8px; align-items: center; margin: 6px 0 2px; flex-wrap: wrap; }
.day-editor { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }
.day-editor .dcell {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  border: 1px solid var(--line); border-radius: 2px; padding: 5px 7px; min-width: 52px; background: var(--paper);
}
.day-editor .dcell.off { opacity: .5; }
.day-editor .dcell label { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--ink-soft); }
.day-editor .dcell input[type=number] { width: 46px; text-align: center; padding: 2px; }
.proj-ftp { font-size: 12px; color: var(--ink-soft); }
.proj-ftp b { color: var(--phase-build); }

/* ---- setup: availability grid ---- */
.avail-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; margin: 10px 0; }
.avail-grid .acell {
  border: 1px solid var(--line); border-radius: 2px; padding: 8px 6px; text-align: center; background: var(--paper);
}
.avail-grid .acell.off { opacity: .5; }
.avail-grid .acell .dn { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: var(--ink-soft); margin-bottom: 4px; }
.avail-grid .acell input[type=number] { width: 100%; text-align: center; padding: 3px; }
.avail-grid .acell input[type=checkbox] { margin: 4px 0; }

/* ---- setup: distribution radio cards ---- */
.dist-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; margin: 10px 0; }
.dist-card {
  border: 1px solid var(--line); border-radius: 2px; padding: 10px; cursor: pointer; background: var(--paper);
  transition: border-color .12s, box-shadow .12s;
}
.dist-card.sel { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.dist-card h4 { margin: 0 0 6px; font-size: 13px; }
.dist-card p { margin: 3px 0; font-size: 11.5px; color: var(--ink-soft); }
.dist-card .care { color: var(--phase-build); }
.dist-card input { margin-right: 6px; }

.feas { font-size: 12.5px; margin-top: 6px; padding: 6px 8px; border-radius: 2px; border: 1px solid var(--line); }
.feas.ok { color: var(--good); }
.feas.warn { color: var(--warn); border-color: var(--warn); }
details.adv summary { cursor: pointer; font-size: 12px; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .5px; }
.phase-peak { color: var(--phase-peak); }
.tag.phase-peak { color: var(--phase-peak); }

/* --------------------------------------------------------------------------- */
/* v4.3 — hand-placed runs & strength (place-by-hand panel)                    */
.xtrain { margin: 10px 0 4px; padding: 10px 12px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 8px; }
.xt-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.xt-head b { font-weight: 600; }
.xt-quota { font-size: 12px; color: var(--ink-soft); }
.xt-strip { display: flex; gap: 5px; margin: 10px 0 4px; overflow-x: auto; }
.xt-day { flex: 1 0 66px; display: flex; flex-direction: column; gap: 4px;
  padding: 5px; border-radius: 6px; background: rgba(0,0,0,0.015); }
.xt-day.off { opacity: 0.45; }
.xt-dow { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-soft); text-align: center; }
.xt-cat { font-size: 10px; text-align: center; color: var(--ink-soft); margin-bottom: 2px; }
.xt-cat.cat-hard { color: var(--rt-threshold); }
.xt-cat.cat-long { color: var(--rt-long); }
.xt-cat.cat-easy { color: var(--rt-endurance); }
.xt-chip { font-size: 11px; padding: 3px 4px; border: 1px solid var(--line); border-radius: 5px;
  background: var(--paper); cursor: pointer; text-align: center; line-height: 1.2; color: var(--ink); }
.xt-chip:hover { border-color: var(--ink-soft); }
.xt-chip.sc-good { border-color: var(--good); color: var(--good); }
.xt-chip.sc-avoid { color: #A08C7A; }
.xt-chip.rec { border-color: var(--good); background: rgba(63,107,69,0.10); font-weight: 600; }
.xt-chip.strength.rec, .xt-chip.strength.sc-good { border-color: var(--strength); color: var(--strength); background: rgba(107,91,149,0.10); }
.disc-badge.by-hand { color: var(--ink-soft); border-color: var(--line); }
.ride-ctrls { display: flex; align-items: center; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.ride-ctrls select.mv { font-size: 12px; padding: 2px 4px; }
.xt-remove, .wf-replace, .add-second { font-size: 12px; padding: 2px 8px; }
.xt-remove { color: var(--warn); border-color: var(--line); }
.xt-remove:hover { border-color: var(--warn); }

/* v4.4 — feeling-today quick action, reshape controls, edited badge, score      */
.feel-today { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 6px 0 2px; padding: 7px 10px; background: var(--paper);
  border: 1px dashed var(--line); border-radius: 8px; }
.feel-today button { font-size: 12px; padding: 3px 10px; }
.feel-today .feel-tired:hover { border-color: var(--rt-threshold); }
.feel-today .feel-great:hover { border-color: var(--good); }
.ride-ctrls select.rs-swap { font-size: 12px; padding: 2px 4px; }
.ride-ctrls .rs-easier, .ride-ctrls .rs-harder { font-size: 12px; padding: 2px 8px; }
.ride-ctrls .rs-easier:hover { border-color: var(--rt-endurance); color: var(--rt-endurance); }
.ride-ctrls .rs-harder:hover { border-color: var(--rt-threshold); color: var(--rt-threshold); }
.disc-badge.edited { color: var(--rt-tempo); border-color: var(--rt-tempo); }
.disc-badge.second { color: var(--rt-vo2, #B3542E); border-color: var(--rt-vo2, #B3542E); }
/* v4.6 non-bike activities read a touch quieter in the recent-activity list */
tr.act-nonbike td { color: var(--ink-soft); }

/* v4.4 — route + weather along the route (feature 3)                            */
.route-wx { margin: 8px 0 2px; padding: 8px 10px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 8px; }
.rw-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rw-head .rw-name { font-size: 13px; }
.rw-head button { font-size: 12px; padding: 2px 8px; }
.rw-file { font-size: 12px; max-width: 190px; }
.rw-overview { margin: 8px 0 6px; }
.rw-flags { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 4px; }
.rw-flag { font-size: 11px; padding: 1px 6px; border-radius: 4px; background: rgba(0,0,0,0.04); border: 1px solid var(--line); }
.rw-flag.f-heat, .rw-flag.f-headwind { border-color: var(--rt-threshold); color: var(--rt-threshold); }
.rw-flag.f-cold { border-color: var(--rt-taper, #4A6D8C); color: #4A6D8C; }
.rw-scroll { overflow-x: auto; }
.rw-table { border-collapse: collapse; width: 100%; font-size: 12px; margin-top: 4px; }
.rw-table th { text-align: left; font-weight: 600; color: var(--ink-soft); border-bottom: 1px solid var(--line); padding: 3px 8px 3px 0; }
.rw-table td { padding: 3px 8px 3px 0; border-bottom: 1px solid rgba(0,0,0,0.05); white-space: nowrap; }
.rw-cell-flags { font-size: 11px; }

/* v4.4 — per-workout upload & performance score (feature 4)                     */
.score-panel { margin: 8px 0 2px; padding: 8px 10px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 8px; }

/* v1.16 — the rider's own record of a strength session (no score to fall back on) */
.note-panel { margin: 8px 0 2px; padding: 8px 10px; background: var(--paper);
  border: 1px solid var(--line); border-radius: 8px; }
.note-row { margin: 4px 0; }
.note-text { width: 100%; box-sizing: border-box; font: inherit; resize: vertical; }
.note-ctrl { display: flex; align-items: center; gap: 8px; }
.note-ctrl button { font-size: 12px; padding: 2px 8px; }
/* v1.34 — what happened: one tag, an RPE, the note (today-card.js outcomePanel) */
.outcome-panel { margin: 8px 0 2px; padding: 8px 10px; background: var(--paper); border-radius: 6px; }
.tag-chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 6px; }
.tag-chip { font-size: 12px; padding: 2px 9px; border: 1px solid #c9c6bb; border-radius: 12px;
  background: #fff; color: #444; cursor: pointer; line-height: 1.5; }
.tag-chip:hover { border-color: #888; }
.tag-chip.on { background: var(--rt, #5B5F54); border-color: var(--rt, #5B5F54); color: #fff; }
.tag-chip.missed.on { background: #8a6a3a; border-color: #8a6a3a; }
.outcome-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0; }
.outcome-row select { font: inherit; font-size: 12px; padding: 2px 4px; }
.ride .missed-flag { color: #8a6a3a; font-weight: 500; }
/* v1.35 — the zone table on the setup page's test-result card */
.zone-table { margin-top: 8px; overflow-x: auto; }
.zone-table table { border-collapse: collapse; font-size: 13px; }
.zone-table th, .zone-table td { padding: 3px 6px; text-align: left; border-bottom: 1px solid #e4e2d8; }
.zone-table input { width: 64px; font: inherit; font-size: 13px; padding: 2px 4px; }
.zone-table .zname { min-width: 120px; }
.sc-upload, .sc-reup { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sc-upload button, .sc-reup button, .sc-reup a.btn { font-size: 12px; padding: 2px 8px; }
.sc-file { font-size: 12px; max-width: 190px; }
.sc-head { display: flex; align-items: center; gap: 12px; }
.sc-num { font-size: 30px; font-weight: 700; line-height: 1; color: var(--sc, var(--ink));
  border: 2px solid var(--sc, var(--line)); border-radius: 8px; padding: 6px 10px; min-width: 54px; text-align: center; }
.sc-verdict { font-weight: 600; }
.sc-comps { margin: 8px 0 4px; display: flex; flex-direction: column; gap: 4px; }
.sc-comp { display: flex; align-items: center; gap: 8px; }
.sc-comp-k { font-size: 12px; width: 74px; color: var(--ink-soft); }
.sc-bar { flex: 1; height: 8px; background: rgba(0,0,0,0.06); border-radius: 4px; overflow: hidden; }
.sc-bar i { display: block; height: 100%; }
.sc-comp-v { width: 96px; text-align: right; }
.sc-reup { margin-top: 6px; }
/* The list of stored activities a session can be scored against. */
.sc-pick { margin-top: 6px; display: flex; flex-direction: column; gap: 4px; }
.sc-pick.hidden { display: none; }
.sc-pick-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sc-pick-txt { flex: 1; min-width: 200px; }
.sc-pick button { font-size: 12px; padding: 2px 8px; }
.sc-pick-more { align-self: flex-start; }
/* v5.4: destructive 'delete the score' button, matching the remove-warn look. */
.sc-del { color: var(--warn); border-color: var(--line); }
.sc-env { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.sc-env-toggle { font-size: 12px; padding: 1px 8px; }
.sc-del:hover { border-color: var(--warn); }

/* v4.4 — hidden file inputs (the button opens the picker; choosing uploads)     */
.hidden-file { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; pointer-events: none; }
.rw-body:not(:empty) { margin-top: 6px; }
.sc-status { margin-left: 6px; }

/* ==========================================================================
   v5.1 — Week view laid out as separated day blocks + current-day highlight
   ========================================================================== */
.week-days { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }

.day-block {
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg);
  padding: 8px 12px 10px;
}
.day-block.is-rest { background: transparent; }

.day-block-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding-bottom: 6px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.day-block-head .db-dow {
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}
.day-block-head .db-date { font-size: 12px; color: var(--ink-soft); }
.day-block-head .db-today {
  margin-left: auto;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--accent);
  color: var(--accent-ink);
  border-radius: 10px;
  padding: 2px 9px;
}

/* current day: green accent frame + soft tint so it reads at a glance */
.day-block.is-today {
  border-color: var(--accent);
  box-shadow: inset 3px 0 0 0 var(--accent);
  background: #EDF2EC;
}
.day-block.is-today .day-block-head { border-bottom-color: var(--accent); }
.day-block.is-today .db-dow { color: var(--accent); }

.day-rest { font-size: 13px; font-style: italic; color: var(--ink-soft); padding: 2px 0; }

/* rides sit snug inside their day block (they already carry the intensity accent) */
.day-block .ride { margin: 8px 0 0; background: var(--paper); }
.day-block .ride:first-of-type { margin-top: 0; }

/* v5.1 — per-day "Add activity" menu (swap-to style) */
.day-add { margin-top: 8px; }
.day-block.is-rest .day-add { margin-top: 6px; }
.day-add-sel {
  font-size: 12px;
  padding: 3px 8px;
  color: var(--ink-soft);
  background: var(--paper);
  border: 1px dashed var(--line);
  border-radius: 2px;
  max-width: 220px;
}
.day-add-sel:hover { border-color: var(--ink-soft); color: var(--ink); }
.day-block.is-today .day-add-sel { background: #fff; }

/* ---- v5.9 Strava panel (Dashboard) ------------------------------------- */
.strava-state { line-height: 1.5; }
.strava-state .dot-ok { color: #6E7F3A; }
#strava details.adv { margin-top: 14px; }
#strava details.adv[open] summary { margin-bottom: 6px; }
#strava label { margin-top: 10px; }
@media (max-width: 460px) { .grid.three.dash-row { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------------------------
   Phase 4 — the coaching bar.

   Deliberately not in the paper palette. Every other surface here is cream and
   muted green because it is one rider's own quiet workspace; the moment the
   week on screen belongs to somebody else, that has to stop being true. The
   strip is warm and solid, it sits above the content rather than inside it,
   and the read-only variant is a different colour again — because "I may look
   at this" and "I may change this" are the two states worth never confusing.
   --------------------------------------------------------------------------- */

/* Full-bleed like the header above it, but its CONTENTS line up with the page's
   940px column, so the sentence starts where the page's first heading does. A
   strip that spans the window while its text floats off to one side reads as
   chrome; this has to read as part of the page it is describing. */
.coach-bar {
  display: flex; align-items: center; gap: 12px 16px; flex-wrap: wrap;
  max-width: 940px; margin: 0 auto; padding: 9px 24px;
  border: 1px solid var(--line); border-left-width: 4px; border-radius: 2px;
  background: var(--paper); color: var(--ink-soft);
  font-size: 0.92em;
}
.coach-bar strong { font-weight: 500; }
.coach-bar select {
  margin-left: auto; font: inherit; padding: 3px 6px;
  flex: 0 1 auto; max-width: 15em; min-width: 9em;
  border: 1px solid var(--line); border-radius: 2px;
  background: var(--paper); color: var(--ink);
}
.coach-bar.busy { opacity: 0.55; pointer-events: none; }

/* Acting as somebody with 'edit' — anything typed here lands in their plan. */
.coach-bar.acting.edit {
  background: #F3E4D8; border-color: var(--running); color: #4A2B18;
  border-left-color: var(--running);
}
/* Acting as somebody with 'view' — a different colour, because it is a
   different permission, not a weaker shade of the same one. */
.coach-bar.acting.view {
  background: #E3E9EE; border-color: var(--phase-taper); color: #22323F;
  border-left-color: var(--phase-taper);
}
.coach-bar.acting select { background: rgba(255,255,255,0.6); }

.coach-notice {
  max-width: 940px; margin: 10px auto 0; padding: 8px 24px;
  border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: 2px; background: var(--paper); font-size: 0.92em;
}
.coach-notice.warn { border-left-color: var(--warn); color: #6A211B; }

/* The bar and its notice belong to the page column, so they take the page's own
   gutter on small screens too — matching `main, .wrap` above rather than
   inventing a second set of breakpoints. */
@media (max-width: 640px) {
  .coach-bar, .coach-notice { padding-left: 16px; padding-right: 16px; }
  .coach-bar select { margin-left: 0; max-width: none; width: 100%; }
}
#coach-bar { margin-top: 14px; }


/* ---- v1.8: Setup → Connections (intervals.icu) ---- */
.conn-row { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.conn-row > .conn-main { flex: 1 1 320px; min-width: 0; }
.conn-state { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.conn-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--line); flex: none; }
.conn-dot.on { background: #3F6B45; }
.conn-dot.err { background: #B3542E; }
.conn-key { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.conn-err { color: #7A2820; }

/* ---- v1.14: the draft, and the way back ---------------------------------
   A drafted session has to be visibly NOT the plan, or "make the changes
   permanent" reads as a button with nothing to do. Dashed accent border and a
   badge, so the difference survives a glance across a seven-day week view. */
.disc-badge.draft {
  color: var(--rt-test);
  border-color: var(--rt-test);
  border-style: dashed;
}
.ride.is-draft {
  border-style: dashed;
  background:
    repeating-linear-gradient(135deg,
      transparent 0 9px,
      rgba(122, 90, 107, 0.045) 9px 18px);
}
/* A drafted removal: still there, clearly on its way out. */
.ride.is-removing { opacity: 0.6; }
.ride.is-removing .t s { text-decoration-thickness: 1.5px; }
.draft-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
  padding: 10px 12px;
  border: 1px solid var(--rt-test);
  border-radius: 2px;
  background: rgba(122, 90, 107, 0.07);
  position: sticky;
  bottom: 0;
  /* clears the phone's home indicator when the bar sticks to the bottom */
  padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
}
.draft-count { font-weight: 500; color: var(--rt-test); }
.draft-bar .primary {
  background: var(--rt-test);
  color: var(--accent-ink);
  border: 1px solid var(--rt-test);
  border-radius: 2px;
  padding: 5px 12px;
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}
.draft-bar .primary:hover { filter: brightness(1.1); }
@media (max-width: 560px) {
  .draft-bar .primary { margin-left: 0 !important; width: 100%; }
  .draft-bar .draft-discard { width: 100%; }
}
.rv-undo { color: var(--ink-soft); }
/* v1.14: model prose that outlived the FTP it was written against */
.nar-stale {
  margin-top: 4px;
  padding-left: 8px;
  border-left: 2px solid var(--rt-tempo);
  font-style: italic;
}

/* ---- v1.14: the activity list ------------------------------------------
   One chronological row per activity: date, sport, name, then THAT SPORT'S
   numbers as labelled chips. See Discipline::readout() for who decides which
   numbers those are — it is not this file, and it is not the JS either.

   The old ten-column table put cycling's NP / IF / TSS on every row, which on a
   run is three dashes, and squeezed ten columns into ~880px so the values wrapped
   mid-figure. Chips carry their own label, so they wrap as units instead. */
.wide-out {
  /* break out of main's 940px without escaping its 24px side padding */
  width: min(1180px, calc(100vw - 48px));
  margin-inline: calc(470px - min(590px, 50vw - 24px));
}
@media (max-width: 1000px) { .wide-out { width: 100%; margin-inline: 0; } }

.act-list { display: flex; flex-direction: column; }
.act-row {
  display: grid;
  /* minmax(0, Nfr), not minmax(px, Nfr): a content-based minimum makes each row
     size its own tracks, so the metric chips started at a different x on every
     line. Zero-minimum fr tracks are the same width on every row, which is what
     turns this back into something you can read down. The last track is fixed
     for the same reason. */
  grid-template-columns: 46px 26px minmax(0, 1.5fr) minmax(0, 2fr) 178px;
  align-items: center;
  gap: 14px;
  padding: 9px 2px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.act-row:last-child { border-bottom: 0; }
.act-row:hover { background: rgba(0, 0, 0, 0.018); }

.act-date { display: flex; flex-direction: column; align-items: center; line-height: 1.1; }
.act-dom { font-size: 19px; font-weight: 500; }
.act-mon { font-size: 11px; color: var(--ink-soft); text-transform: lowercase; }
.act-sport { display: flex; justify-content: center; opacity: 0.85; }

.act-main { min-width: 0; }
.act-name {
  display: block;
  font-size: 14px;
  /* a Strava title can be a paragraph; one line, then ellipsis */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.act-sub { margin-top: 1px; }

.act-mets { display: flex; flex-wrap: wrap; gap: 6px; }
.act-met {
  display: inline-flex; align-items: baseline; gap: 4px;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--paper);
  white-space: nowrap;
}
.act-met .am-v { font-size: 13px; }
.act-met .am-k { font-size: 10px; color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.04em; }
.act-met.strong { border-color: var(--ink-soft); }
.act-met.strong .am-v { font-weight: 500; }

/* Two fixed sub-columns so "strava ↗" and "analyse →" line up down the list,
   including on the manual rows that have no analysis link. */
.act-acts {
  display: grid;
  grid-template-columns: 92px 86px;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}

/* Non-cycling rows carry their sport's accent on the left edge and on the sport
   glyph, so a run is distinguishable at a glance down the column — WITHOUT
   repainting its numbers, which should read like any other numbers. */
.act-row.act-d-run      { box-shadow: inset 3px 0 0 -1px var(--running); }
.act-row.act-d-strength { box-shadow: inset 3px 0 0 -1px var(--strength); }
.act-row.act-d-swim     { box-shadow: inset 3px 0 0 -1px var(--phase-taper); }
.act-row.act-d-run      .act-sport, .act-row.act-d-run      .act-dom { color: var(--running); }
.act-row.act-d-strength .act-sport, .act-row.act-d-strength .act-dom { color: var(--strength); }
.act-row.act-d-swim     .act-sport, .act-row.act-d-swim     .act-dom { color: var(--phase-taper); }

@media (max-width: 860px) {
  .act-row {
    grid-template-columns: 42px 24px 1fr;
    grid-template-areas: "date sport main" ".  .  mets" ".  .  acts";
    row-gap: 6px;
  }
  .act-date { grid-area: date; } .act-sport { grid-area: sport; }
  .act-main { grid-area: main; } .act-mets { grid-area: mets; }
  .act-acts { grid-area: acts; grid-template-columns: max-content max-content; }
}

/* v1.14: what the fitness curve is made of, and what it is missing */
.curve-note { margin-top: 10px; max-width: none; border-left: 2px solid var(--line); padding-left: 10px; }
.curve-note-warn { border-left-color: var(--rt-tempo); }

/* ---- v1.14: the workout editor ------------------------------------------
   The canvas is the workout: a fixed 0–150 % FTP vertical scale so two
   workouts are comparable by eye, and every block clickable straight into the
   inspector beneath it. */
.wo-titlebar { align-items: baseline; gap: 12px; flex-wrap: wrap; }
.wo-disc {
  font-family: inherit; font-size: 13px;
  padding: 5px 8px;
  border: 1px solid var(--line); border-radius: 2px; background: var(--paper); color: var(--ink);
}
.wo-name {
  flex: 1 1 260px;
  font-family: inherit; font-size: 16px;
  padding: 6px 8px;
  border: 1px solid var(--line); border-radius: 2px; background: var(--paper); color: var(--ink);
}
.wo-stats { font-size: 13px; white-space: nowrap; }

.wo-canvas-wrap { margin: 12px 0 4px; }
.wo-canvas {
  border: 1px solid var(--line); border-radius: 2px;
  background: var(--paper);
  /* the SVG stretches; the wrapper owns the height so the aspect is stable
     from a phone to a wide desktop */
  height: 220px;
}
.wo-canvas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wo-svg { width: 100%; height: 100%; display: block; }
.wo-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.wo-grid-ftp { stroke: var(--rt-threshold); stroke-dasharray: 4 4; }
.wo-seg { cursor: pointer; opacity: 0.88; }
.wo-seg:hover { opacity: 1; }
.wo-seg.is-sel { stroke: var(--ink); stroke-width: 2; vector-effect: non-scaling-stroke; opacity: 1; }
.wo-empty { display: flex; align-items: center; justify-content: center; height: 100%; font-size: 13px; }
.wo-axis { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-soft); }

.wo-add, .wo-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.wo-sep { width: 1px; height: 18px; background: var(--line); }

.wo-inspector {
  margin-top: 12px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 2px; background: var(--paper);
}
.wo-fields { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.wo-kind {
  padding: 2px 8px; border-radius: 2px;
  background: var(--k); color: var(--paper);
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
}
.wo-field { display: inline-flex; align-items: center; gap: 6px; }
.wo-num {
  width: 5.5em;
  font-family: inherit; font-size: 13px; padding: 4px 6px;
  border: 1px solid var(--line); border-radius: 2px; background: var(--bg); color: var(--ink);
}
.wo-suffix { white-space: nowrap; }

.file-btn { display: inline-block; padding: 5px 10px; border: 1px solid var(--line); border-radius: 2px; cursor: pointer; }
/* .hidden-file is already defined above, for the plan card's file input */
.wo-actions a.ghost { display: inline-block; padding: 5px 10px; border: 1px solid var(--line); border-radius: 2px; }
.wo-actions a.is-off { opacity: 0.45; pointer-events: none; }
.wo-sched { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }

.wo-list { display: flex; flex-direction: column; }
.wo-row {
  display: grid;
  /* v1.15: a discipline badge joined the ride-type badge, so this is now name,
     discipline, ride-type, stats, actions — five columns, not four. */
  grid-template-columns: minmax(0, 1.3fr) auto auto minmax(0, 1.2fr) auto;
  align-items: center; gap: 12px;
  padding: 8px 2px; border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.wo-row:last-child { border-bottom: 0; }
.wo-row.is-open { box-shadow: inset 3px 0 0 -1px var(--accent); }
.wo-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wo-row-acts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.wo-row-acts a.ghost { padding: 3px 8px; border: 1px solid var(--line); border-radius: 2px; font-size: 12px; }
/* The library list sits outside any .ride card, so it needs its own badge
   look rather than relying on the `.ride .rt-badge` rule above. */
.wo-row .rt-badge {
  font-size: 9px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-soft);
  border: 1px solid currentColor; border-radius: 2px; padding: 1px 6px; opacity: 0.85;
}
.wo-row .disc-badge.disc-run { color: var(--running); }

@media (max-width: 720px) {
  .wo-row { grid-template-columns: 1fr auto; row-gap: 4px; }
  .wo-row-acts { grid-column: 1 / -1; justify-content: flex-start; }
  .wo-canvas { height: 170px; }
}

/* v1.33 — the climbs a ride rode, in the camp side's own climb-list idiom
   (one more column: what the rider did on it). */
.grade-legend { display: flex; flex-wrap: wrap; gap: 10px; font-size: 10px; color: var(--ink-soft); margin: 6px 0 10px; }
.grade-legend-key { letter-spacing: 0.06em; text-transform: uppercase; }
.grade-swatch { display: inline-flex; align-items: center; gap: 4px; }
.grade-swatch i { display: inline-block; width: 12px; height: 8px; border-radius: 1px; }
.climb-list { border: 1px solid var(--line); border-radius: 2px; font-size: 12px; margin: 8px 0 12px; overflow: hidden; }
.climb-head { display: flex; justify-content: space-between; gap: 10px; padding: 6px 12px; background: var(--bg);
              border-bottom: 1px solid var(--line); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-soft); }
.climb-head-sum { font-weight: 600; color: var(--ink); }
.climb-row { display: grid; grid-template-columns: 2rem 1fr 4.5rem 4rem 3.5rem 4.5rem 5rem 5.5rem 4.5rem 2.8rem; gap: 0 8px;
             align-items: center; padding: 7px 12px; border-bottom: 1px solid var(--line); }
.climb-row:last-child { border-bottom: none; }
.climb-row:hover { background: var(--bg); }
.climb-cat { display: inline-flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem; border-radius: 2px;
             font-size: 10px; font-weight: 700; color: var(--paper); background: var(--ink-soft); }
.climb-cat.cat-4 { background: #8fb08a; } .climb-cat.cat-3 { background: #d8c14a; color: #2b2718; }
.climb-cat.cat-2 { background: #dd9038; } .climb-cat.cat-1 { background: #c4552f; } .climb-cat.cat-hc { background: #7a2820; }
.climb-cat.cat-none { background: none; color: var(--line); font-size: 14px; }
.climb-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.climb-at, .climb-max, .climb-top, .climb-did { color: var(--ink-soft); }
.climb-avg { font-weight: 700; } .climb-gain { font-weight: 600; }
.climb-fiets { justify-self: end; padding: 2px 5px; border: 1px solid var(--line); border-radius: 2px; background: var(--paper); }
@media (max-width: 760px) { .climb-row { grid-template-columns: 2rem 1fr 4rem 4rem 4.5rem; } .climb-max, .climb-top, .climb-did, .climb-fiets, .climb-at { display: none; } }

/* v1.33 — the ride's elevation profile by distance: the camp side's chart,
   its classes and its colours, so a planned route and the ride that rode
   it look like the same road. */
.elevation-wrap { border: 1px solid var(--line); border-radius: 2px; overflow: hidden; background: var(--bg); margin-top: 10px; }
.elevation-chart { display: block; width: 100%; height: auto; }
.elev-grid { stroke: var(--line); stroke-width: 1; stroke-dasharray: 3 6; vector-effect: non-scaling-stroke; }
.elev-grid-label { font-family: 'IBM Plex Mono', monospace; font-size: 10px; fill: var(--ink-soft); }
.elev-line { fill: none; stroke: var(--ink); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.elev-axis { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.elev-tick { stroke: var(--ink-soft); stroke-width: 1; vector-effect: non-scaling-stroke; }
.elev-dist-label { font-family: 'IBM Plex Mono', monospace; font-size: 10px; fill: var(--ink-soft); }
.elev-crosshair { stroke: var(--ink-soft); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.elev-dot { fill: var(--ink); stroke: var(--paper); stroke-width: 2; vector-effect: non-scaling-stroke; }
.elev-tooltip-bg { fill: var(--ink); }
.elev-tooltip-text { font-family: 'IBM Plex Mono', monospace; font-size: 16px; fill: var(--paper); }
.elev-marker-line { stroke: var(--ink); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.35; vector-effect: non-scaling-stroke; }
.elev-marker-dot { stroke: var(--ink); stroke-width: 2; vector-effect: non-scaling-stroke; }
.elev-marker-bg { fill: var(--ink); }
.elev-marker-label { font-family: 'IBM Plex Mono', monospace; font-size: 11px; fill: var(--paper); }
.elev-climb-band { fill: var(--ink); fill-opacity: 0; transition: fill-opacity 0.15s; pointer-events: none; }
.elev-climb-band.is-active { fill-opacity: 0.10; }
.climb-row.is-active { background: var(--bg); }

/* ---- power-duration: the surface and the landscape (js/surface3d.js) ----
   Canvas colours are tokens, read on every render: the surface in the
   app's power orange, the best-effort curve and the selected window in two
   violets used nowhere else, the power plane in the FTP blue. */
:root {
  --surface-rgb: 179,84,46;
  --surface-wire-rgb: 246,222,210;
  --surface-curve: #5b52c9;
  --surface-select: #7F77DD;
  --surface-halo: rgba(255,255,255,.75);
  --surface-ftp: #4A6D8C;
  --surface-cp: #6E7F3A;
  --surface-plane-rgb: 24,95,165;
  --surface-plane: #185FA5;
  --surface-muted: #8c8a84;
}
.sf-tabs { display: inline-flex; border: 1px solid var(--line); border-radius: 2px; overflow: hidden; }
.sf-tabs button {
  font-size: 12px; padding: 5px 14px; border: 0; border-radius: 0;
  background: var(--paper); color: var(--ink-soft);
}
.sf-tabs button[aria-selected="true"], .sf-tabs button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }
.sf-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
.sf-caption { font-size: 11px; color: var(--ink-soft); }
.sf-wrap { padding: 10px 12px 0; }
.sf-ctl { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center; margin: 0 0 6px; font-size: 11px; color: var(--ink-soft); }
.sf-ctl label {
  display: inline-flex; align-items: center; gap: 6px; margin: 0;
  font-size: 11px; font-weight: 400; color: var(--ink-soft); text-transform: none; letter-spacing: 0;
}
.sf-ctl input[type=range] { padding: 0; border: 0; background: none; accent-color: var(--accent); }
.sf-ctl input[type=checkbox] { width: auto; margin: 0; }
.sf-ctl select { width: auto; font-size: 12px; padding: 2px 6px; }
.sf-ctl button.small { font-size: 11px; padding: 3px 9px; }
.sf-num { width: 64px !important; font-size: 12px !important; padding: 2px 4px !important; font-variant-numeric: tabular-nums; }
.sf-unit { color: var(--surface-muted); min-width: 12px; }
.sf-canvas { position: relative; }
.sf-canvas canvas { display: block; width: 100%; aspect-ratio: 680 / 470; cursor: grab; touch-action: none; }
.sf-trace { display: block; width: 100%; aspect-ratio: 680 / 140; margin-top: 4px; }
.sf-tip {
  position: absolute; display: none; pointer-events: none; z-index: 5;
  background: var(--ink); color: var(--paper); font-size: 11px; line-height: 1.5;
  padding: 6px 9px; border-radius: 2px; white-space: nowrap;
}
.sf-tip b { font-weight: 500; }
.sf-tip-muted { opacity: .7; }
.sf-info { font-size: 11px; color: var(--ink-soft); margin: 6px 0 10px; }
.sf-wrap .chart-legend { margin: 0 -12px; }
.ch-band { fill: #7F77DD; opacity: .14; }
