/* ==========================================================================
   GymBro design tokens
   Light, airy, data-focused. Big type where it matters, generous spacing.

   Brand palette (exact swatch values -- do not approximate):
     navy #37537D · medium blue #6E8BC4 · light blue #AFBCDE
     pink/rose #D171A6 · cream #E1D9D0 · charcoal #37373C
     slate #474851 · gray #A9AAB2
   Trend colors (good/warn/bad) are semantically separate from the brand
   palette on purpose -- they mean improving/plateau/declining and must
   never be reassigned to a brand role. Pink is used deliberately and
   sparingly: the load-mode toggle's selected state, and the recommendation
   callout's left accent stripe -- not spread across everyday chrome.
   ========================================================================== */

:root {
  /* Raw palette -- reach for these only when introducing a new semantic
     token below, not directly in component rules. */
  --palette-navy: #37537d;
  --palette-medium-blue: #6e8bc4;
  --palette-light-blue: #afbcde;
  --palette-pink: #d171a6;
  --palette-cream: #e1d9d0;
  --palette-charcoal: #37373c;
  --palette-slate: #474851;
  --palette-gray: #a9aab2;

  /* Light is the app's real theme -- airy and cool rather than the flat warm
     beige of the first light pass. See the dormant [data-theme="dark"] block
     below for the parked dark palette from the previous iteration. */
  --bg: #f7f5f2;
  --surface: #ffffff;
  /* Softened fill from the first pass (#F2F0EC) -- lighter and lower-contrast
     so the "last time" box and stepper buttons recede behind the numbers
     instead of reading as heavy flat-gray blocks. Border kept close to its
     original value on purpose: the fill needed softening, not the button's
     outline, which is still doing the job of showing it's tappable. */
  --surface-raised: #f5f3f0;
  --surface-hover: #ede9e3;
  --border: #e2ded6;
  --border-soft: #edebe6;

  --text: var(--palette-charcoal);
  /* Two-tier secondary text: slate for readable secondary copy (exercise meta,
     body text inside callouts), gray for the lowest-emphasis decorative
     labels (small-caps section labels). Note: raw gray on this light surface
     is genuinely low-contrast for text (~2:1) -- kept as specified since it's
     always paired with bold weight + letter-spacing + short label strings,
     but flagging it rather than quietly overriding your call. */
  --text-muted: var(--palette-slate);
  --text-faint: var(--palette-gray);

  --accent: var(--palette-medium-blue);
  --accent-soft: #6e8bc41a;
  --accent-text: #3f5c8c;
  --on-accent: #12151c;

  /* One or two deliberate touches only (toggle selected-state, recommendation
     callout stripe) -- never everyday chrome. */
  --highlight: var(--palette-pink);
  --highlight-soft: #d171a61a;
  --on-highlight: #12151c;
  --tag-bg: #afbcde40;
  --tag-text: #3f5c8c;

  --good: #5fb488;
  --good-soft: #5fb48818;
  --warn: #d18b4a;
  --warn-soft: #d18b4a18;
  --bad: #c26a5f;
  --bad-soft: #c26a5f18;

  --header-blue: var(--palette-navy);
  --header-blue-text: #eef1f7;
  --header-blue-meta: var(--palette-light-blue);

  /* Primary CTA: deepened/high-contrast, distinct from the medium-blue
     interactive accent used for toggles/links, so it reads unambiguously as
     THE action even glanced at mid-set. */
  --cta-bg: var(--palette-navy);
  --cta-text: #ffffff;
  --cta-bg-active: #2c4265;

  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 22px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  /* Technical/scoreboard voice for numerals and small-caps labels -- system
     stack only, no external font loading. */
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;

  /* Unified type scale (introduced with the cross-screen consistency pass) --
     one set of sizes shared by every screen instead of each renderer picking
     its own px value inline. */
  --type-caption: 12px;
  --type-meta: 13px;
  --type-body: 15px;
  --type-control: 16px;
  --type-section: 17px;
  --type-title: 28px;
  --type-value: 32px;

  --control-height: 48px;
  --target-height: 44px;
  --border-control: var(--border);
  --on-semantic: #12151c;
  --focus-ring: var(--accent);
  --scrim: rgb(0 0 0 / .65);

  --nav-height: 72px;
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Single source of truth for the stepper cluster's footprint, so the
     1-value/2-values toggle above it can be sized to match exactly. */
  --stepper-btn: 44px;
  --stepper-gap: 10px;
  --stepper-value-w: 76px;
  --stepper-cluster-w: calc(var(--stepper-btn) * 2 + var(--stepper-gap) * 2 + var(--stepper-value-w));
}

/* Near-black, navy-tinted technical dark theme -- matches the reference's
   "scoreboard" aesthetic more closely than the earlier lighter-charcoal
   dormant version this replaced. Activated via data-theme="dark" on <html>
   in index.html; :root (light) stays the fallback/dormant alternate. */
:root[data-theme="dark"] {
  --bg: #0c0e14;
  --surface: #141826;
  --surface-raised: #1b2030;
  --surface-hover: #232939;
  --border: #262b3a;
  --border-soft: #1e2230;

  --text: #f2f3f6;
  --text-muted: #8b93a8;
  --text-faint: #5c6478;

  --accent-soft: #6e8bc426;
  --accent-text: #a9bce2;

  --highlight-soft: #d171a626;
  --tag-bg: #6e8bc433;
  --tag-text: var(--palette-light-blue);

  --good-soft: #5fb48822;
  --warn-soft: #d18b4a22;
  --bad-soft: #c26a5f22;

  /* Darker than the shared --palette-navy so the exercise-card header sits
     closer to the rest of this near-black theme instead of standing out as
     a lighter navy block. */
  --header-blue: #263a57;

  /* Lighter than the structural --border so an input/select's outline still
     reads as "interactive boundary" against this near-black surface, not
     just a divider. */
  --border-control: #68748c;

  /* Today's own bespoke palette -- kept distinct from the app-wide navy/pink
     tokens on purpose (from the original Today redesign reference this was
     built against). Applied only to Today's own classes (.log-set-btn,
     .field-box, .exercise-active, its disclosure) -- never given as the
     default for .btn-primary/.card elsewhere. */
  --today-bg: #0d1118;
  --today-card: #171d28;
  --today-field: #10161f;
  --today-raised: #202836;
  --today-border: #2b3444;
  --today-text: #f1f4fa;
  --today-muted: #a0aabe;
  --today-action: #a9c2ff;
  --today-action-text: #172641;
  --today-action-tint: #222e46;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

button, input { font-family: inherit; }
a { color: inherit; }

/* Deliberate focus treatment everywhere: no default UA outline (which reads as
   an unstyled blue browser ring on tap/click), but keyboard users still get a
   clear focus-visible ring in the accent color. Covers every focusable
   element type, including the ones added by the cross-screen pass
   (select/textarea/summary) that weren't in the original scope. */
button, input, a, select, textarea, summary { outline: none; }
button:focus-visible, input:focus-visible, a:focus-visible,
select:focus-visible, textarea:focus-visible, summary:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--focus-ring);
}

h1, h2, h3, p { margin: 0; }

.app-shell {
  max-width: 560px;
  margin: 0 auto;
  min-height: 100%;
  padding-bottom: calc(var(--nav-height) + var(--safe-bottom) + var(--space-4));
}

.screen { display: none; padding: var(--space-4) var(--space-4) 0; }
.screen.active { display: block; }

/* ---------------------------------------------------------------- topbar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
  /* GPU-composited layer so the sticky reposition itself is smooth; the
     dominant source of jank here is actually Chrome-for-Android's
     collapsing address bar resizing the viewport mid-scroll, which is
     outside what CSS on the page can control -- installing via "Add to
     Home Screen" removes that bar entirely and avoids it. */
  will-change: transform;
  /* Horizontal: 0, not a stray extra inset -- .screen already gives every
     header the same 16px edge every card below it uses; a second inset
     here was quietly indenting titles 4px further than their own content.
     Bottom: bumped from 8px so the header has real breathing room before
     whatever comes next, instead of the next section crowding right in. */
  padding: var(--space-5) 0 var(--space-4);
  transition: padding 0.2s ease;
}
.topbar .eyebrow {
  font-family: var(--font);
  font-size: var(--type-caption);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 2px;
  /* Opacity is written every scroll frame by app.js (wireCompactHeader),
     not animated by a CSS transition -- a JS-driven value and a CSS
     transition on the same property were fighting each other and causing
     visible jitter mid-scroll. No transition here on purpose. */
  will-change: opacity;
}
.topbar h1 {
  font-size: var(--type-title);
  font-weight: 700;
  letter-spacing: -0.025em;
  margin-top: 2px;
  /* Scaled via transform (compositor-only, no per-frame text reflow) rather
     than animating font-size directly -- font-size transitions force the
     browser to re-shape glyphs on every frame, which is what was janky.
     Written every scroll frame by app.js, same reasoning as .eyebrow above:
     no CSS transition on transform here, so there's nothing to fight. */
  transform-origin: left top;
  will-change: transform;
}
.topbar .sub {
  color: var(--text-muted);
  font-size: 15px;
  margin-top: var(--space-2);
}
/* Toggled by app.js on scroll (wireCompactHeader) with hysteresis -- separate
   enter/exit thresholds so it can't flicker even if scroll holds still right
   at one position. This is a discrete, infrequent toggle (not written every
   frame), so a CSS transition here is fine. */
.topbar--compact { padding-top: var(--space-2); padding-bottom: var(--space-2); }

.progress-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.progress-track {
  flex: 1;
  height: 3px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  background: var(--accent);
  border-radius: 999px;
  transition: width 0.3s ease;
}
.progress-label {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-faint);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- cards */

.card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  box-shadow: none;
  transition: outline-color 0.15s ease;
}
/* Body dashboard's chart card fades between metrics instead of the content
   swapping instantly (see Body.renderChart in body.js). */
#body-chart-card { transition: opacity 0.15s ease; }

.measurement-card { padding: var(--space-4); cursor: pointer; }
/* A real selected-state cue (outline), not just a color change on the
   number -- driven by a class toggle in body.js's click handler, not an
   inline style, and independent of keyboard focus (its own focus-visible
   ring still applies on top when tabbing to it). */
.measurement-card.is-selected { outline: 2px solid var(--accent); outline-offset: 0; }
.measurement-card .section-label { font-size: var(--type-body); color: var(--text-muted); margin: 0; }
.measurement-date { font-size: var(--type-caption); color: var(--text-muted); }
.measurement-value { font-family: var(--font); font-size: var(--type-value); font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
/* Chart title on its own line, the 30d/90d/6m/1y/All picker on the next --
   five options next to a title is too cramped on a phone width. */
.measurement-chart-head { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-3); margin-bottom: var(--space-4); }
.chart-caption { font-size: var(--type-caption); color: var(--text-muted); line-height: 1.5; }

.exercise-card {
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
}

.exercise-card__head {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
  /* Flat navy, separated from the card body below by a cast shadow rather
     than a color-blend gradient (the gradient read as a cheap glossy
     effect). */
  background: var(--header-blue);
  padding: var(--space-4);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: 0 4px 10px rgba(0,0,0,0.28);
}
/* Title reads as the unmistakable anchor of the header; metadata below it is
   deliberately lighter-weight so the two never compete. */
.exercise-card__title { font-size: 23px; font-weight: 800; letter-spacing: -0.02em; text-transform: uppercase; color: var(--header-blue-text); }
.exercise-card__meta {
  color: var(--header-blue-meta);
  font-size: 13px;
  font-weight: 500;
  margin-top: 4px;
}
.exercise-card__target {
  color: var(--header-blue-meta);
  font-size: 13px;
  font-weight: 500;
  margin-top: var(--space-1);
  font-variant-numeric: tabular-nums;
}
/* No top padding here on purpose: the first child's own margin-top (always
   --space-4, see previous-box/suggestion/toggle below) provides the gap from
   the header, so spacing stays consistent regardless of which block happens
   to render first. */
.exercise-card__body { padding: 0 var(--space-4) var(--space-5); }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  background: none;
  border: none;
  color: var(--header-blue-meta);
  font-size: 20px;
  line-height: 1;
  min-width: var(--target-height);
  min-height: var(--target-height);
  margin: 0;
  padding: var(--space-2);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.icon-btn:active { background: rgba(255,255,255,0.12); }
.exercise-card__body .icon-btn { color: var(--text-muted); }
.exercise-card__body .icon-btn:active { background: var(--surface-hover); }

.previous-box {
  margin-top: var(--space-4);
  padding: var(--space-3);
  background: var(--surface-raised);
  border-radius: var(--radius-md);
  cursor: pointer;
}
/* Every small-caps ROW label in the card (LAST TIME, WEIGHT, REPS, LEFT,
   RIGHT...) shares this exact style -- one consistent voice for "this is a
   label, not content." */
.previous-box__label,
.stepper-row__label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
/* SET 1 is the section marker for the whole block below it, not a row label
   like WEIGHT/REPS -- deliberately larger, bolder, tighter-tracked, and
   darker so it reads as a heading rather than blending into the row labels
   it introduces. */
.active-set__label {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.previous-box__value {
  margin-top: 4px;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.previous-box__expanded {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-soft);
  display: none;
  font-size: 14px;
  color: var(--text-muted);
}
.previous-box__expanded.open { display: block; }
.previous-box__expanded div { padding: 2px 0; font-variant-numeric: tabular-nums; }

.suggestion {
  margin-top: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: var(--space-3) var(--space-3) var(--space-3) 10px;
  border: 1px solid var(--border);
  /* Deliberate pink touch #2: a left accent stripe marks this box as "a
     recommendation" structurally, distinct from the neutral "last time" box
     above it -- separate from the badge's own good/muted/bad state color. */
  border-left: 3px solid var(--highlight);
  border-radius: var(--radius-md);
}
.suggestion__headline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.suggestion__badge {
  flex-shrink: 0;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  background: transparent;
  border: 1.5px solid currentColor;
}
.suggestion__weight {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
}
.suggestion--increase .suggestion__badge { color: var(--good); }
.suggestion--maintain .suggestion__badge { color: var(--text-muted); }
.suggestion--consider_reducing .suggestion__badge { color: var(--bad); }
.suggestion--insufficient_data .suggestion__badge { color: var(--text-faint); }
.suggestion__text { color: var(--text-muted); font-size: 13px; }

/* ---------------------------------------------------------------- sets */

.set-list { margin-top: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }

/* Compact, content-sized segmented control -- a secondary, infrequently-
   changed setting shouldn't visually compete with the primary recommendation
   content above it. iOS-style: pill track, a lighter "raised" capsule marks
   the selected segment (no separate accent fill, no focus-ring artifact). */
.load-mode-toggle {
  display: inline-flex;
  background: var(--surface-raised);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
}
.load-mode-toggle button {
  flex: none;
  min-height: var(--target-height);
  border: none;
  background: none;
  color: var(--text-faint);
  font-size: var(--type-meta);
  font-weight: 600;
  padding: 7px 12px;
  border-radius: 999px;
  cursor: pointer;
  text-align: center;
  white-space: nowrap;
}
.load-mode-toggle button.selected {
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
/* Full-width variant for page-level filters (Progress/Body period selectors)
   where a wider, more prominent control is appropriate. */
.load-mode-toggle--full { display: flex; width: 100%; }
.load-mode-toggle--full button { flex: 1; }

/* ---------------------------------------------------------------- active set */

.active-set {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
/* Inside the active exercise card, the set inputs don't need their own
   nested frame -- one border/card per level of nesting reads as clutter,
   and the fields (.field-box) are already visually distinct controls on
   their own. Recovers space without losing any information. */
.exercise-active .active-set { border: 0; padding: 0; border-radius: 0; gap: var(--space-3); }
.exercise-active .active-set .field-grid { margin-top: 0; }

.set-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
}
.set-row.is-done { opacity: 0.55; }
.set-row.is-done ~ .set-row.is-done { border-top: 1px solid var(--border-soft); }

.set-num {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--surface-raised);
  color: var(--text-faint);
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

/* Every loggable value (weight, weight-B in 2-value mode, reps) is its own
   full-width row -- never sharing a line with another value's steppers, so
   nothing has to shrink and nothing can visually collide. The label is plain
   read-only text pinned left (it doesn't need to be reachable); the whole
   interactive [-][value][+] cluster is grouped tight and pushed to the right
   side of the card via space-between, so a right-hand thumb finds every
   stepper in the same horizontal zone instead of traveling to a far-left
   minus button. */
.stepper-rows { display: flex; flex-direction: column; gap: var(--space-3); }
.stepper-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.stepper-row__label { flex-shrink: 0; }

.stepper {
  display: flex;
  align-items: center;
  gap: var(--stepper-gap);
  flex: 0 0 auto;
}
.stepper__btn {
  width: var(--stepper-btn);
  height: var(--stepper-btn);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}
.stepper__btn:active { background: var(--surface-hover); }
.stepper__value {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 0 0 auto;
  width: var(--stepper-value-w);
}
.stepper__value input {
  width: 100%;
  min-width: 0;
  border: none;
  background: none;
  text-align: center;
  color: var(--text);
  /* Visual anchor of the card: bigger, bolder, monospaced tabular figures so
     the width never shifts as a digit changes (e.g. 9 -> 10). */
  font-family: var(--font-mono);
  font-size: 23px;
  font-weight: 800;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
  padding: 0;
}
.stepper__value input::-webkit-outer-spin-button,
.stepper__value input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper__unit {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-top: -2px;
}

.rir-picker {
  display: flex;
  gap: 4px;
}
.rir-picker.collapsed { display: none; }
.rir-pill {
  flex: 1;
  height: 38px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.rir-pill.selected { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* ------------------------------------------------------------- medicine */

.dose-row { display: flex; gap: var(--space-2); margin-top: var(--space-3); flex-wrap: wrap; }
/* A row of repeated secondary controls, not a single primary action --
   sized down from the shared 48px control-height (right for a lone button,
   too roomy once 2-3 of these sit side by side in one card). */
.dose-pill {
  flex: 1;
  min-width: 72px;
  min-height: var(--target-height);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-control);
  background: var(--surface-raised);
  color: var(--text-muted);
  font-size: var(--type-meta);
  font-weight: 700;
  cursor: pointer;
  text-align: center;
}
/* Dark ink, not white -- white text on this light green is low contrast. */
.dose-pill.taken { background: var(--good); border-color: var(--good); color: var(--on-semantic); }
.dose-pill:disabled { opacity: 0.6; cursor: default; }

/* Bottom action zone: RIR and the confirm CTA live in one row in the card's
   lower-right, the natural resting zone for a one-handed thumb. This row is
   always the last element regardless of 1-value/2-value mode, so the CTA
   never shifts position depending on which mode is active. */
.action-row { display: flex; align-items: center; gap: var(--space-3); }
.rir-chip {
  flex-shrink: 0;
  min-width: 44px;
  height: 44px;
  padding: 0 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border-radius: 999px;
  cursor: pointer;
}
.rir-chip.has-value { color: var(--accent-text); border-color: var(--accent); }
.rir-chip svg { width: 18px; height: 18px; flex-shrink: 0; }

/* Solid, high-contrast CTA -- deliberately distinct from the medium-blue
   interactive accent used for toggles/links, so it reads unambiguously as
   THE action to take even glanced at mid-set. */
.log-set-btn {
  flex: 1;
  border: none;
  border-radius: var(--radius-sm);
  /* Today's own bespoke light-blue action color -- not the app-wide navy
     .btn-primary -- with dark text for contrast, per the preferred Today
     reference this was built against. */
  background: var(--today-action);
  color: var(--today-action-text);
  font-size: var(--type-control);
  font-weight: 700;
  min-height: var(--control-height);
  padding: var(--space-3);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.log-set-btn:active { background: #93b3f5; }

.add-set-btn {
  margin-top: var(--space-3);
  background: none;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  color: var(--text-muted);
  padding: var(--space-3);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  width: 100%;
}

.save-status {
  font-size: 12px;
  color: var(--text-faint);
  margin-top: var(--space-2);
  height: 16px;
}
.save-status.error { color: var(--bad); }

/* ------------------------------------------------- today: header progress */

.workout-title-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.workout-title-btn svg { width: 20px; height: 20px; color: var(--text-muted); flex-shrink: 0; }

.exercise-progress {
  display: flex;
  gap: 4px;
  margin-top: var(--space-3);
}
.exercise-progress__seg {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.exercise-progress__fill { height: 100%; background: var(--accent); border-radius: 999px; }
/* A skipped exercise's segment fills fully but in a muted tone -- reads as
   "resolved" without being confused for real training progress. */
.exercise-progress__fill--skipped { background: var(--border-soft); }

/* --------------------------------------------- today: active exercise card */

/* Flat, no navy header band -- the muscle label carries the same "what is
   this" job the navy header used to, at far less visual weight, so one
   exercise reads as the clear focus of the screen instead of every
   exercise looking equally loud. Exclusively used by workout.js. */
/* Today keeps its own bespoke dark-navy palette (--today-*) throughout this
   card, distinct from the app-wide --surface/--border tokens -- see the
   "preferred Today design" tokens at the top of this file. */
.exercise-active {
  background: var(--today-card);
  border: 1px solid var(--today-border);
  border-radius: 20px;
  padding: var(--space-4);
  box-shadow: none;
}
.exercise-active__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
}
.exercise-active__muscle-label {
  color: var(--today-action);
  font-size: var(--type-caption);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 4px;
}
/* 23px per the preferred Today reference specifically -- distinct from the
   shared 20px exercise-title/sheet-title scale used elsewhere. */
.exercise-active__name { color: var(--today-text); font-size: 23px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.35; cursor: pointer; }
.exercise-active__meta { color: var(--today-muted); font-size: var(--type-meta); line-height: 1.5; margin-top: 4px; }
.exercise-active__last {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  color: var(--today-muted);
  font-size: var(--type-meta);
  line-height: 1.5;
  margin-top: var(--space-3);
}
.exercise-active__last svg { width: 14px; height: 14px; flex-shrink: 0; }
.exercise-active__last strong { font-weight: 600; color: var(--today-text); }

.exercise-active__disclosure {
  margin-top: var(--space-3);
  background: var(--today-action-tint);
  border-radius: var(--radius-sm);
  color: var(--today-action);
}
.exercise-active__disclosure summary {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--target-height);
  padding: var(--space-3);
  color: var(--today-text);
  font-size: var(--type-meta);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.exercise-active__disclosure summary::-webkit-details-marker { display: none; }
.exercise-active__disclosure summary svg { width: 15px; height: 15px; flex-shrink: 0; }
.exercise-active__disclosure summary .disclosure-chevron { margin-left: auto; color: var(--today-muted); transition: transform 0.15s ease; }
.exercise-active__disclosure[open] summary .disclosure-chevron { transform: rotate(180deg); }
.exercise-active__disclosure p { font-size: var(--type-meta); line-height: 1.5; color: var(--today-muted); margin: 0; padding: 0 var(--space-3) var(--space-3); }

/* Small text trigger that opens the load-mode picker modal below, instead
   of an inline segmented toggle -- matches the redesign reference. */
.load-mode-trigger {
  display: block;
  margin-left: auto;
  margin-top: var(--space-4);
  background: none;
  border: none;
  color: var(--today-muted);
  font-size: var(--type-meta);
  font-weight: 600;
  min-height: var(--target-height);
  padding-block: var(--space-2);
  cursor: pointer;
}
.load-mode-trigger:active { color: var(--today-action); }

/* Selectable rows inside the load-mode picker modal -- title + one-line
   explanation each, with a filled/accented state on whichever is active.
   This is the shared modal sheet, not Today's own card -- app-wide tokens. */
.load-mode-choice {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  padding: 13px;
  margin: 8px 0;
  cursor: pointer;
}
.load-mode-choice.active { border-color: var(--accent); background: var(--accent-soft); }
.load-mode-choice > span:first-child { flex: 1; }
.load-mode-choice strong { display: block; font-size: 14px; font-weight: 600; }
.load-mode-choice small { display: block; font-size: 12px; line-height: 1.5; margin-top: 3px; color: var(--text-muted); }
.load-mode-choice svg { color: var(--accent); flex-shrink: 0; }

/* ------------------------------------------------------ today: field grid */

/* Weight/reps (or left/right weight) side by side -- replaces the old
   full-width .stepper-row for Today only; .stepper-row itself stays as-is
   since medicine.js/reports.js still use it elsewhere. */
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); margin-top: var(--space-4); }
.field-grid--span-2 { grid-column: 1 / -1; }

.field-box {
  min-width: 0;
  /* Recessed/dark input surface, distinct from the raised card behind it --
     part of Today's own token set, not the app-wide --surface-raised. */
  background: var(--today-field);
  border: 1px solid var(--today-border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-2) var(--space-2);
}
.field-box__label { display: flex; justify-content: space-between; font-size: var(--type-caption); color: var(--today-muted); font-weight: 600; }
.field-box__label span { font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; }
.field-box__input {
  display: block;
  width: 100%;
  min-height: 48px;
  border: none;
  background: none;
  text-align: center;
  color: var(--today-text);
  font-family: var(--font);
  font-size: var(--type-value);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  padding: 4px 0;
  -moz-appearance: textfield;
}
.field-box__input::-webkit-outer-spin-button,
.field-box__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.field-box__stepper { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-top: 4px; }
.field-box__stepper button {
  height: var(--target-height);
  border: none;
  border-radius: var(--radius-sm);
  background: var(--today-raised);
  color: var(--today-text);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.field-box__stepper button:active { background: var(--today-border); }

/* ------------------------------------------------------- today: queue rows */

.queue-row {
  width: 100%;
  min-height: 64px;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 2px;
  border: none;
  border-bottom: 1px solid var(--border-soft);
  background: none;
  text-align: left;
  cursor: pointer;
}
.queue-row:last-child { border-bottom: none; }
.queue-row:active { background: var(--surface-hover); }
.queue-row__badge {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.queue-row__badge--done { background: var(--good-soft); border-color: transparent; color: var(--good); }
.queue-row__badge--skipped { color: var(--text-faint); border-style: dashed; }
.queue-row__name { flex: 1; min-width: 0; font-size: var(--type-body); line-height: 1.4; font-weight: 600; }
.queue-row__meta { display: block; font-size: 12px; font-weight: 400; color: var(--text-muted); margin-top: 2px; }
.queue-row__count { flex: none; font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.queue-row > svg { width: 14px; height: 14px; color: var(--text-faint); flex: none; }

/* One small muscle highlight per group heading in the "+ Extra work"
   picker -- deliberately narrow (the shared .musclemap component defaults
   to full-width, sized for a hero card), since every exercise inside a
   group would otherwise repeat an identical full-size diagram. */
.extra-picker-map { max-width: 220px; margin: 8px auto 12px; }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: var(--radius-sm);
  font-size: var(--type-control);
  font-weight: 600;
  min-height: var(--control-height);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}
/* One primary-action color app-wide (navy) -- previously .btn-primary used
   the blue interactive accent while Today's .log-set-btn separately used
   navy, so the same visual role (THE action) read as two different colors
   depending on screen. Today keeps its own bespoke light-blue below. */
.btn-primary { background: var(--cta-bg); color: var(--cta-text); width: 100%; }
.btn-primary:active { background: var(--cta-bg-active); }
.btn-secondary { background: var(--surface-raised); color: var(--text); border: 1px solid var(--border-control); }
.btn-ghost { background: none; color: var(--text-muted); }
.btn-full { width: 100%; }
/* Session-completion action -- deliberately pink, distinct from the navy/blue
   used for in-progress actions like Log Set, so finishing a workout feels
   like its own celebratory moment rather than just another primary button.
   Dark ink (--on-highlight), not white -- white-on-pink is low contrast. */
.btn-finish { background: var(--highlight); color: var(--on-highlight); font-weight: 800; }
.btn-finish:active { background: #b85a8d; }
.btn-sm { min-height: var(--target-height); padding: var(--space-2) var(--space-3); font-size: var(--type-meta); border-radius: var(--radius-sm); }
.btn:disabled { opacity: 0.5; cursor: default; }

/* ---------------------------------------------------------------- nav */

.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  display: flex;
  padding-bottom: var(--safe-bottom);
  z-index: 40;
}
.bottom-nav__inner {
  max-width: 560px;
  margin: 0 auto;
  display: flex;
  gap: var(--space-1);
  width: 100%;
  padding: var(--space-2);
  min-height: var(--nav-height);
  box-sizing: border-box;
}
.nav-item {
  flex: 1;
  min-width: 0;
  min-height: 56px;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-faint);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  padding: 4px 0;
  font-size: var(--type-caption);
  font-weight: 600;
  cursor: pointer;
}
.nav-item svg { width: 22px; height: 22px; flex: none; }
/* A rounded background pill, not just a text-color change -- the selected
   state needs a cue that isn't color alone, and Reports' pink accent must
   never leak into nav (this stays the app's blue accent everywhere,
   including on the Insights tab while viewing Day/Week/Month). */
.nav-item.active { background: var(--accent-soft); color: var(--accent-text); }

/* ---------------------------------------------------------------- rest timer */

.rest-timer {
  position: fixed;
  left: var(--space-4);
  right: var(--space-4);
  bottom: calc(var(--nav-height) + var(--safe-bottom) + var(--space-3));
  max-width: 528px;
  margin: 0 auto;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  display: none;
  align-items: center;
  gap: var(--space-4);
  z-index: 50;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35);
}
.rest-timer.visible { display: flex; }
.rest-timer__time {
  font-size: 24px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-width: 88px;
}
.rest-timer__time.over { color: var(--highlight); }
.rest-timer__label { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; }
.rest-timer__actions { display: flex; gap: var(--space-2); margin-left: auto; }

/* ---------------------------------------------------------------- misc */

.empty-state {
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  margin-top: var(--space-4);
  color: var(--text-muted);
}
.empty-state h3 { font-size: var(--type-section); color: var(--text); margin-bottom: var(--space-2); }
.empty-state p { font-size: var(--type-body); line-height: 1.55; }

.section-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  font: 600 var(--type-section)/1.4 var(--font);
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: var(--space-5) 0 var(--space-3);
}
/* A second span is a low-emphasis context caption (units, a date range) --
   only that one shrinks down; a lone span is the label itself and keeps
   full section-heading weight. */
.section-label > span { font-size: var(--type-caption); color: var(--text-muted); letter-spacing: 0; font-weight: 400; }
.section-label > span:first-child:not(:last-child) { font: inherit; color: inherit; letter-spacing: inherit; }

.list-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  min-height: 56px;
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--border-soft);
  font-size: var(--type-body);
}
.list-row:last-child { border-bottom: none; }
.list-row > :first-child { min-width: 0; overflow-wrap: anywhere; }
.list-row__meta { flex: none; color: var(--text-muted); font-size: var(--type-meta); }

textarea.note-input, input.text-input, select.text-input {
  width: 100%;
  min-height: var(--control-height);
  background: var(--surface-raised);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: var(--space-3);
  font-size: var(--type-control);
  line-height: 1.4;
  resize: vertical;
}
/* Day tab's date trigger: same look as .text-input, but it's a <button>
   that opens the calendar sheet, not a native form control -- laid out to
   put the calendar icon at the right edge like a select's own arrow did. */
.day-date-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--control-height);
  background: var(--surface-raised);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: var(--space-3);
  font-family: var(--font);
  font-size: var(--type-control);
  text-align: left;
  cursor: pointer;
}
.day-date-trigger svg { width: 18px; height: 18px; flex: none; color: var(--text-muted); }

.modal-backdrop {
  position: fixed; inset: 0;
  background: var(--scrim);
  display: none;
  align-items: flex-end;
  z-index: 100;
}
.modal-backdrop.visible { display: flex; }
.modal-sheet {
  background: var(--surface);
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: var(--space-5) var(--space-4);
  padding-bottom: calc(var(--space-5) + var(--safe-bottom));
  max-height: 85dvh;
  overflow-y: auto;
}
.modal-sheet h2 { font-size: 1.25rem; line-height: 1.35; margin-bottom: var(--space-4); }
/* For a modal sheet long enough to need scrolling internally (the extra-work
   exercise picker, up to 11 muscle groups deep) -- keeps the title + a close
   icon reachable at all times instead of requiring a scroll to the bottom
   "Cancel" every other, shorter modal uses. The negative margin/top pair
   cancels the sheet's own top padding so this sits flush with the sheet's
   visual top edge while pinned. */
.modal-sticky-header {
  position: sticky;
  top: calc(-1 * var(--space-5));
  margin: calc(-1 * var(--space-5)) 0 0;
  padding-top: var(--space-5);
  background: var(--surface);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.modal-option {
  display: block;
  width: 100%;
  min-height: 56px;
  text-align: left;
  background: var(--surface-raised);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-2);
  color: var(--text);
  font-size: var(--type-body);
  cursor: pointer;
}
.modal-option:active { background: var(--surface-hover); }
.modal-close-row { margin-top: var(--space-4); }

.toast {
  position: fixed;
  top: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 13px;
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.toast.visible { opacity: 1; }

.summary-hero { text-align: center; padding: var(--space-6) 0 var(--space-5); }
.summary-hero .eyebrow { color: var(--text-muted); font-size: 13px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; }
.summary-hero h1 { font-size: 26px; margin: var(--space-2) 0; }
.summary-stats { display: flex; justify-content: center; gap: var(--space-6); margin-top: var(--space-4); }
.summary-stats div { text-align: center; }
.summary-stats .num { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.summary-stats .lbl { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

.trend-summary-row {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px 18px;
  font-weight: 700;
  font-size: 15px;
}
.trend-summary-row span { white-space: nowrap; }

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

/* No max-width here on purpose -- it should fill its card exactly like
   every other card's content (list rows, stat rows, etc.). An earlier
   version capped this at 340px, which left it narrower than every card
   around it once the card itself is wider than ~340+padding. */
.musclemap { width: 100%; }
.musclemap svg { display: block; }
.musclemap-caption {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  fill: var(--text-faint);
}
.musclemap-legend { margin-top: var(--space-2); }
.musclemap-legend .list-row { font-size: 14px; }

/* Deliberately NOT another pill-group toggle -- this sits directly under the
   Muscles/Reports pill toggle, and two stacked pill rows read as a single
   confusing control. Minimal text tabs with an underline instead. */
.period-tabs {
  display: flex;
  gap: 22px;
  margin-top: var(--space-4);
}
.period-tabs button {
  background: none;
  border: none;
  padding: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
  border-bottom: 2px solid transparent;
  cursor: pointer;
}
.period-tabs button.selected { color: var(--accent-text); border-bottom-color: var(--accent); }

/* ------------------------------------------------------------------ calendar */

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-grid--head { margin-bottom: 4px; }
.cal-headcell {
  text-align: center;
  font-family: var(--font);
  font-size: var(--type-caption);
  font-weight: 700;
  color: var(--text-muted);
  padding-bottom: 4px;
}
.cal-cell {
  min-width: 0; /* same 7-column grid overflow fix as .report-week-day */
  min-height: var(--target-height);
  height: auto;
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border-radius: var(--radius-sm);
}
/* Reports use the pink report accent, not the app's blue -- Body's own
   charts stay blue, so a filled/dotted day here reads as "a report thing"
   at a glance. */
.cal-cell--has-session { cursor: pointer; background: var(--highlight-soft); }
.cal-cell--has-session:active { background: var(--surface-hover); }
.cal-daynum { font-size: var(--type-caption); font-variant-numeric: tabular-nums; color: var(--text-muted); }
.cal-daynum--today { color: var(--text); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.cal-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; background: var(--highlight); }
/* Month report: a date with a planned session but no log -- dashed outline
   instead of the solid "logged" fill, distinct at a glance without a
   separate legend row for each. */
.cal-cell--planned-unlogged { border: 1px dashed var(--warn); }
.cal-cell--planned-unlogged .cal-daynum { color: var(--warn); }
/* The Day tab's date-picker sheet reuses this same grid, but its cells are
   real <button>s (a picker, not a read-only calendar) -- reset button
   chrome so they still look like plain .cal-cell boxes. */
.cal-grid button.cal-cell { border: none; background: none; font: inherit; color: inherit; cursor: pointer; }
.cal-grid button.cal-cell:disabled { cursor: default; opacity: 0.35; }
.cal-cell--selected { outline: 2px solid var(--highlight); }

/* ---------------------------------------------------------------- reports */

.report-heading { font-size: 1.5rem; font-weight: 700; line-height: 1.25; letter-spacing: -0.02em; margin-bottom: var(--space-4); }

.report-takeaway { background: var(--surface); border: 1px solid var(--border-soft); border-left: 3px solid var(--highlight); }
.report-takeaway__label {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--highlight);
  font-family: var(--font);
  font-size: var(--type-caption);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: none;
  margin-bottom: var(--space-2);
}
.report-takeaway__label svg { width: 15px; height: 15px; flex-shrink: 0; }
.report-takeaway h3 { font-size: var(--type-section); line-height: 1.4; margin-bottom: var(--space-2); }
.report-takeaway p, .report-next p { font-size: var(--type-body); color: var(--text-muted); line-height: 1.55; margin: 0; }

/* Prominent, stacked front/back pair above full-width bars -- the map is
   the headline evidence for "where the sets went," not a thumbnail next to
   it, and this is the same layout in Day, Week and Month. */
.report-muscle-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-5); align-items: start; }
.report-muscle-layout > :first-child { width: 100%; max-width: 220px; margin: 0 auto; }
.report-muscle-layout > :last-child { width: 100%; min-width: 0; }
.report-muscle-map--large { max-width: 220px; margin: var(--space-3) auto 0; }

.report-bar-row { margin-bottom: var(--space-3); }
.report-bar-row:last-child { margin-bottom: 0; }
.report-bar-row__label { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); font-size: var(--type-meta); font-weight: 600; margin-bottom: var(--space-2); }
.report-bar-row__label > :first-child { min-width: 0; overflow-wrap: anywhere; }
.report-bar-row__label span:last-child { flex: none; color: var(--text-muted); font-weight: 500; font-variant-numeric: tabular-nums; }
.report-bar-track { height: 6px; border-radius: 999px; background: var(--surface-hover); overflow: visible; position: relative; }
.report-bar-fill { height: 100%; background: var(--highlight); border-radius: inherit; max-width: 100%; }
/* Weekly/monthly plan-vs-actual: a thin tick over the same track marking the
   planned target, so logged vs. planned reads as one bar + one marker
   rather than two separate bars competing for attention. translateX(-100%)
   keeps the tick fully inside the track even at exactly 100% -- the track
   itself is overflow:visible so the tick is never clipped at either edge. */
.report-bar-target {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 2px;
  background: var(--text-muted);
  transform: translateX(-100%);
}

.report-zero-list { margin-top: var(--space-3); }

.report-disclosure { margin-top: var(--space-3); }
.report-disclosure summary {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--target-height);
  font-size: var(--type-meta);
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  list-style: none;
}
.report-disclosure summary::-webkit-details-marker { display: none; }
.report-disclosure summary .disclosure-chevron { margin-left: auto; transition: transform 0.15s ease; }
.report-disclosure[open] summary .disclosure-chevron { transform: rotate(180deg); }
.report-disclosure--card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  padding-block: var(--space-2);
  margin-top: var(--space-4);
}

.report-exercise { border-bottom: 1px solid var(--border-soft); padding-block: var(--space-4); }
.report-exercise:last-child { border-bottom: none; }
.report-exercise summary { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: var(--space-3); cursor: pointer; list-style: none; }
.report-exercise summary::-webkit-details-marker { display: none; }
.report-exercise summary > :first-child { min-width: 0; flex: 1 1 140px; }
.report-exercise__name { font-weight: 600; font-size: var(--type-body); line-height: 1.45; }
.report-exercise__meta { font-size: var(--type-meta); color: var(--text-muted); margin-top: var(--space-1); }
.report-exercise__detail { font-size: var(--type-meta); color: var(--text-muted); line-height: 1.6; padding-top: var(--space-3); }
.report-exercise__detail strong { color: var(--text); }

.report-delta {
  flex: none;
  max-width: 100%;
  font-size: var(--type-caption);
  font-weight: 700;
  padding: var(--space-1) var(--space-2);
  border-radius: 999px;
  background: var(--surface-raised);
  color: var(--text-muted);
  white-space: normal;
}
/* Amber, not red, for a lower number -- a quieter session isn't a failure
   (matches the calm, matter-of-fact tone the redesign asked for). */
.report-delta.up { color: var(--good); background: var(--good-soft); }
.report-delta.down { color: var(--warn); background: var(--warn-soft); }

.report-next { border-left: 3px solid var(--highlight); padding: var(--space-3) 0 var(--space-3) var(--space-4); margin: var(--space-5) 0; }
.report-next strong { display: block; font-size: var(--type-section); line-height: 1.4; margin-bottom: var(--space-2); }

.report-kv { display: flex; justify-content: space-between; gap: var(--space-3); font-size: 14px; padding: 6px 0; }
.report-kv span:last-child { font-weight: 600; font-variant-numeric: tabular-nums; }

.report-stats-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); margin-block: var(--space-4) var(--space-5); }
.report-stats-row__item { min-width: 0; }
.report-stats-row__item + .report-stats-row__item { border-left: 1px solid var(--border); padding-left: var(--space-4); }
.report-stats-row__num { font-size: var(--type-value); font-weight: 800; line-height: 1.15; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.report-stats-row__num span { font-size: var(--type-section); color: var(--text-muted); font-weight: 700; }
.report-stats-row__lbl { font-size: var(--type-meta); color: var(--text-muted); margin-top: 4px; }

/* --------------------------------------------------------- week day strip */

.report-week-strip { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 5px; }
.report-week-day {
  /* Grid items default to min-width:auto, i.e. their own content's natural
     width -- with 7 columns that's wider than 1fr can give each on a narrow
     phone, so the row overflows its card instead of actually shrinking.
     min-width:0 lets the track win, same fix as the exercise-progress bar
     segments elsewhere. min-height (not aspect-ratio) sets the height, so a
     narrow 1fr column doesn't also squash the row short. */
  min-width: 0;
  min-height: 64px;
  padding-block: var(--space-2);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text-muted);
  cursor: pointer;
}
.report-week-day[aria-pressed="true"] { border-color: var(--highlight); background: var(--highlight-soft); }
.report-week-day__letter { font-size: var(--type-caption); text-transform: uppercase; color: var(--text-muted); }
.report-week-day__num { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); }
.report-week-day--done .report-week-day__num { color: var(--good); }
.report-week-day--gap .report-week-day__num { color: var(--warn); }
.report-week-day svg { width: 12px; height: 12px; }
.report-week-detail { margin-top: var(--space-3); font-size: var(--type-body); line-height: 1.5; color: var(--text-muted); }

/* Below 360px, seven equal columns get too narrow for a legible day + date
   + icon -- wrap into a 4-then-3 layout instead so each tile keeps a real
   tap target, same relief valve the muscle map below it uses. */
@media (max-width: 359px) {
  .report-week-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); }
}

.reflection {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-muted);
  font-style: italic;
  padding: var(--space-4) 0;
  text-align: center;
}

/* ------------------------------------------------------------- login gate */

.login-screen {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--bg);
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}
.login-screen.visible { display: flex; }
.login-card {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.login-card__title {
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -0.02em;
  text-align: center;
  margin-bottom: var(--space-3);
  color: var(--text);
}
.login-error {
  color: var(--bad);
  font-size: 14px;
  text-align: center;
}

/* Respect the OS-level reduced-motion preference. Doesn't touch the compact-
   header scroll effect (js/app.js writes transform/opacity directly per
   frame, no CSS transition to disable), just the handful of real CSS
   transitions elsewhere (disclosure chevrons, card outlines, toast). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
