/* lift — styles.css. Light parchment palette (SPEC.md "Design system",
   owner decision 08/15 00:29 — supersedes the original dark-first draft).
   All UI text lowercase by default; .preserve-case opts out (exercise/
   routine names read better in their natural case). */

:root {
  --bg: #f2ede3;
  --surface: #fbf8f2;
  --surface-raised: #e8dcc8;
  --text: #3d2218;
  /* P10 contrast pass: owner requires 5:1 on EVERY text pairing, no
     large-text/UI-component exemptions. #7a6a5f measured 4.89:1 and
     #6b584c measured 4.96:1 on --surface-raised (the darkest ground any
     text sits on in this app) — both below the 5:1 floor. #69574b clears
     it at 5.06:1 there, which means it clears 5:1 everywhere lighter too
     (5.87 on --bg, 6.46 on --surface). Verified via
     scripts/check-contrast.mjs (npm run check:contrast). */
  --text-dim: #69574b;
  /* P10: --accent/--highlight/--cool/--success are NON-TEXT tokens from
     here on — borders, chart marks, fills, the checkbox accent-color, the
     rest-timer progress bar. None of them clear 5:1 as TEXT against this
     palette's surfaces (verified), so any element that colors a text/icon
     label with one of these uses the matching -ink (transparent/bordered
     context) or -strong (colored-fill context, paired with a light label)
     token below instead. Two exceptions that already clear 5:1 as-is and
     so were NOT migrated: .chip-active and .set-type-dropset, both dark
     ink (--on-accent) text on raw --highlight fill (5.41:1, verified). */
  --accent: #b85c45;
  --highlight: #c9943e;
  --cool: #6b7c80;
  --success: #7a8c5e;
  --danger: #b8453f;
  --border: #e3d9c8;

  /* P10 ink tokens: hue-preserving darkened, text-safe versions of
     accent/highlight/cool/success. Each is >=5:1 against --surface-raised
     (#e8dcc8, the darkest ground any text sits on here), which means each
     also clears 5:1 against --surface and --bg (both lighter). Use for ANY
     colored text/label/icon-on-transparent — links, "suggested:" numeral,
     chip text/border pairs, evidence-tier chips. Ratios verified via
     scripts/check-contrast.mjs. */
  --accent-ink: #8d4635;
  --highlight-ink: #745421;
  --cool-ink: #505c5f;
  --success-ink: #525f3f;

  /* P10 "strong" ground tokens: for the few elements that fill a colored
     background AND put a text/icon label directly on top of it (buttons,
     .log-btn, .superset-badge). Text-on-fill needs a darker ground than
     text-on-border does, because the label has to clear 5:1 against the
     fill itself. Each is the minimal hue-preserving darkening of its base
     token that clears 5:1 paired with the light label color (--surface,
     #fbf8f2) — verified via scripts/check-contrast.mjs. */
  --accent-strong: #a5533e; /* 5.07:1 with --surface label */
  --success-strong: #616f4b; /* 5.10:1 with --surface label */
  --cool-strong: #5e6d71; /* 5.08:1 with --surface label */

  /* foreground for text sitting on --highlight (the two exceptions noted
     above) — kept as a distinct token from --bg since --bg's role flipped
     from near-black to light parchment; dark ink reads better there than
     light text would. */
  --on-accent: #3d2218;
  /* --danger is dark enough that dark ink fails contrast on it — light text
     instead (5.00:1 with --on-danger, verified — the floor, not a margin). */
  --on-danger: #fbf8f2;

  --radius: 12px;
  --radius-sm: 8px;
  --tap: 48px;
  /* P10 breathing-room pass (owner ask): raised ~25-35% across card
     padding, inter-card gap, row padding, and section margins for a
     measurably less dense UI. Font sizes are untouched — only spacing.
     Re-run the rect-sweep overflow check after touching any of these (see
     scripts/*rect-sweep* QA note) — vertical-only changes, but verify. */
  --gap: 16px; /* was 12px, +33% — space between stacked cards/blocks */
  --card-pad: 21px; /* was .card's flat 16px, +31% */
  --row-pad-v: 10px; /* was set-row/detail-row's 8px vertical padding, +25% */
  --section-gap-top: 26px; /* was .section-title's 20px top margin, +30% */

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  overflow-x: hidden;
  max-width: 100vw;
}

body {
  text-transform: lowercase;
  padding-bottom: env(safe-area-inset-bottom, 0);
}

/* P9 A1: true-modal bottom sheet — body scroll-lock while any sheet
   (public/js/components/modal-sheet.js) is open. */
body.scroll-locked {
  overflow: hidden;
}

.preserve-case {
  text-transform: none;
}

button,
input,
select {
  font-family: inherit;
}

a {
  color: var(--highlight-ink);
}

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

/* big numerals: weight/reps read at arm's length mid-set */
.numeral {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.02em;
}

#app-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

#app-header .wordmark {
  font-size: 20px;
  font-weight: 800;
  color: var(--accent-ink);
  letter-spacing: 0.02em;
}

#app-header nav {
  display: flex;
  align-items: center;
  gap: 4px;
}

.nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 8px;
  color: var(--text-dim);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  border-radius: var(--radius-sm);
}

.nav-link:active {
  background: var(--surface);
}

.icon-btn {
  min-width: var(--tap);
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: var(--text);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 18px;
}

.icon-btn:active {
  background: var(--surface);
}

#app {
  padding: 16px;
  padding-bottom: 140px; /* room above the fixed rest-timer bar */
  max-width: 480px;
  margin: 0 auto;
  width: 100%;
}

/* ---------- generic components ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--card-pad);
  margin-bottom: var(--gap);
}

.btn {
  min-height: var(--tap);
  padding: 0 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text);
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  text-transform: lowercase;
}

.btn:active {
  transform: scale(0.98);
}

/* P10: the export-csv download link is the only <a class="btn"> in the app
   — a real <button> centers its single-line label fine on min-height alone,
   but an anchor is `display: inline` by default (no automatic vertical
   centering) AND keeps the browser's default underline (`a` only sets
   `color`, not `text-decoration`, elsewhere in this file) — together that
   read as "neither button nor link" (cramped, top-aligned text with a
   stray underline inside a button-colored box). Flex-centers the label and
   kills the underline; explicit vertical padding is redundant with
   min-height/flex-centering here but kept per spec ("real padding") and as
   a safety margin if the label ever wraps. */
a.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: 14px;
  padding-bottom: 14px;
  text-decoration: none;
}

.btn-primary {
  /* P10 contrast fix: dark ink on raw --accent measured 3.23:1, and light
     label on raw --accent measured 4.26:1 — both below the 5:1 floor at
     ANY size (this does NOT rely on the WCAG large-text 3:1 exemption the
     previous pass used — owner wants 5:1 everywhere, no exemptions).
     --accent-strong is --accent darkened just enough that the light label
     clears 5:1 (5.07:1, verified via scripts/check-contrast.mjs). */
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--surface);
  font-size: 18px;
  font-weight: 700;
}

.btn-ghost {
  background: transparent;
}

.btn-block {
  width: 100%;
}

.btn-sm {
  min-height: 44px; /* P9 A5 tap target */
  padding: 0 12px;
  font-size: 14px;
}

.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--on-danger);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.chip-active {
  background: var(--highlight);
  border-color: var(--highlight);
  color: var(--on-accent);
}

/* settings' club picker — a selected-club chip that's also a remove button
 * (tap anywhere on the pill to remove, same "whole element is the target"
 * convention as the day/muscle-group filter chips elsewhere in this file). */
.chip-removable {
  cursor: pointer;
}

.chip-removable span[aria-hidden] {
  margin-left: 2px;
  font-weight: 700;
}

/* P9 B2: a selected-club chip that also carries an inline commute-minutes
   input — unlike .chip-removable (the whole pill is one click target), this
   chip has a real interactive input inside it, so remove is now its own
   small button rather than the whole pill. */
.chip-editable {
  padding: 4px 6px 4px 10px;
  gap: 6px;
}

.chip-commute-input {
  width: 40px;
  min-height: 28px;
  font-size: 13px;
  padding: 0 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  text-align: center;
}

.chip-remove-btn {
  background: none;
  border: none;
  color: var(--text-dim);
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
  padding: 0 2px;
  min-width: 20px;
}

/* P9 B2 work-schedule block: one row per weekday. */
.work-hours-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.work-hours-row .toggle-row {
  flex: 0 0 auto;
  min-width: 64px;
  min-height: auto;
}

.work-hours-row input[type="time"] {
  flex: 1;
}

/* P9 C3: session-header club chip — bumped to the 44px tap-target floor
   (not named in the A5 audit list since it's a new element, but built to
   the same standard from the start). */
.club-chip {
  min-height: 44px;
  padding: 0 14px;
}

.muted {
  color: var(--text-dim);
  font-size: 13px;
}

.section-title {
  font-size: 13px;
  color: var(--text-dim);
  font-weight: 700;
  letter-spacing: 0.04em;
  margin: var(--section-gap-top) 0 10px; /* bottom: was 8px, +25% */
}

.section-title:first-child {
  margin-top: 0;
}

input[type="number"],
input[type="text"],
input[type="password"],
input[type="time"] {
  min-height: var(--tap);
  font-size: 16px; /* prevents iOS zoom-on-focus; P9 B2 also requires this floor for the work-schedule time inputs */
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 0 10px;
  width: 100%;
}

input:focus {
  outline: 2px solid var(--highlight);
  outline-offset: -1px;
}

.spinner-text {
  text-align: center;
  color: var(--text-dim);
  padding: 40px 0;
}

.error-banner {
  background: var(--danger);
  color: var(--on-danger);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  margin-bottom: var(--gap);
  font-size: 14px;
}

/* ---------- home view ---------- */

/* P10: was a single flex row (title left, club-chip + date right,
   space-between) — at 375-390px a longer title ("mon · upper push") plus
   the club chip plus the date collided/wrapped mid-word (see .qa/p9-10).
   Title now gets its own full-width line; the chip + date move to a second
   line below it, so neither ever has to fight the other for horizontal
   room regardless of title length. */
.today-status {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

.routine-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.routine-card .meta {
  flex: 1;
  min-width: 0;
}

.routine-card .name {
  font-size: 16px;
  font-weight: 700;
}

/* P12: card-body tap target for "preview this routine" — a real <button>
   reset to look like plain content (free Enter/Space + tab-focus, no custom
   keydown wiring). Sits to the left of the existing "start" button inside
   the same .routine-card row; .meta stacks its children in a column here
   (they're <span>s now, not <div>s — button content can't hold flow/block
   elements per the HTML5 content model, browsers render it anyway but this
   keeps the markup valid) so name/count/chip still read top-to-bottom. */
.routine-card-body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  padding: 6px 4px;
  margin: -6px -4px;
  border-radius: var(--radius-sm);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.routine-card-body .meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* P12: shared hover/active/focus-visible feedback for every "the whole
   element is a <button> that navigates/opens something" tap target —
   .routine-card-body above and .history-row below both carry this class
   (previously each had its own copy of the identical three rules). */
.tap-row:hover,
.tap-row:active {
  background: var(--surface-raised);
}

.tap-row:active {
  transform: scale(0.99);
}

.tap-row:focus-visible {
  outline: 2px solid var(--highlight);
  outline-offset: 2px;
}

/* P12: shared "this is tappable, here's where it goes" signifier — the
   routine card body above and history rows below both use it. */
.chevron {
  font-size: 22px;
  line-height: 1;
  color: var(--text-dim);
  flex-shrink: 0;
}

.recent-strip {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}

.recent-strip .card {
  min-width: 150px;
  flex: 0 0 auto;
  margin-bottom: 0;
}

/* ---------- session view ---------- */

.exercise-card {
  padding: 0;
  overflow: hidden;
}

.exercise-card-header {
  padding: 14px 16px 10px;
  display: flex;
  flex-direction: column;
  gap: 8px; /* was 6px, +33% — breathing room around the exercise-name heading */
}

.exercise-card-header .title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.exercise-card-header .name {
  font-size: 17px;
  font-weight: 700;
}

/* P9 A5: the "suggested:" words are ink (>12:1). P10: the numeral value
   now uses --accent-ink instead of raw --accent — 5.07:1+ on any surface
   in this app (verified), not relying on the large-text exemption the
   previous pass used. */
.suggested {
  font-size: 13px;
  color: var(--text);
}

.suggested .numeral {
  font-size: 18px;
  font-weight: 700;
  color: var(--accent-ink);
}

.set-rows {
  border-top: 1px solid var(--border);
}

.set-row {
  display: grid;
  /* minmax() is load-bearing here: a bare `1.5fr` track's implicit minimum
     is its content's min-content size, which competes unpredictably with
     the other tracks and was clipping the weight input to ~15px at 375px
     wide (verified via getBoundingClientRect during browser QA). minmax()
     pins a guaranteed floor per column so the weight numeral always has
     room to show a full "123.5"-style value.
     P9 A5 tap targets: col1 (type chip) and col5 (log button) are 44px to
     match their elements' min-width; col3 (stepper-group: minus + input +
     plus, each stepper now 44px wide) floors at 126px = 44+44+34(input)+
     2x2px gaps, so the two 44px steppers don't get squeezed below their
     own min-width at 375px. */
  grid-template-columns: 44px minmax(34px, 0.55fr) minmax(126px, 1.6fr) minmax(32px, 0.6fr) 44px;
  align-items: center;
  gap: 4px;
  /* Vertical only raised (var(--row-pad-v), was flat 8px, +25%) — horizontal
     stays 4px, untouched: the grid-template-columns minmax() floors above
     are already tuned to the narrowest supported viewport (375px); widening
     horizontal padding here would eat into that budget and risk the exact
     input-clipping bug the comment above this rule documents fixing. */
  padding: var(--row-pad-v) 4px;
  border-bottom: 1px solid var(--border);
}

.set-row:last-child {
  border-bottom: none;
}

/* P7 offline queue: a set logged without connectivity, waiting to replay. */
.set-row.pending-sync {
  background: rgba(107, 124, 128, 0.1); /* --cool tint */
}

.pending-sync-caption {
  padding: 0 4px 8px;
  margin-top: -6px;
  font-size: 11px;
}

.set-index-chip {
  min-width: 44px; /* P9 A5 tap target */
  min-height: 44px;
  border-radius: 999px;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
  background: var(--surface-raised);
  color: var(--text-dim);
}

.set-type-normal {
  background: var(--surface-raised);
  color: var(--text-dim);
}

.set-type-warmup {
  background: transparent;
  border: 1px solid var(--cool);
  color: var(--cool-ink);
}

/* P10: was a raw --accent fill with dark-ink text (3.23:1, fails) —
   --accent-strong + light label clears 5:1 (verified). */
.set-type-failure {
  background: var(--accent-strong);
  color: var(--surface);
}

/* Dark ink on raw --highlight already clears 5:1 (5.41:1, verified) —
   left as-is, no --highlight-strong needed. */
.set-type-dropset {
  background: var(--highlight);
  color: var(--on-accent);
}

.ghost-value {
  color: var(--text-dim);
  font-size: 13px;
  text-align: center;
}

.set-row input {
  min-height: 40px;
  text-align: center;
  padding: 0 2px;
}

/* remove native number-input spin arrows — their intrinsic min-content
   width was stealing space from the grid column at narrow viewports. */
.set-row input[type="number"]::-webkit-inner-spin-button,
.set-row input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.set-row input[type="number"] {
  -moz-appearance: textfield;
}

.stepper-group {
  display: flex;
  align-items: stretch;
  gap: 2px;
  min-width: 0;
}

.stepper-group input {
  flex: 1 1 auto;
  min-width: 34px;
}

.stepper-btn {
  flex: 0 0 auto;
  min-width: 44px; /* P9 A5 tap target */
  min-height: 44px;
  border: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text);
  border-radius: 6px;
  font-size: 16px;
  cursor: pointer;
}

/* P10: was a raw --success fill — neither dark ink (3.99:1) nor a light
   label (3.45:1) cleared 5:1 against it. --success-strong + light label
   does (5.10:1, verified). */
.log-btn {
  min-width: 44px;
  min-height: 44px;
  border-radius: 999px;
  border: none;
  background: var(--success-strong);
  color: var(--surface);
  font-size: 18px;
  font-weight: 800;
  cursor: pointer;
}

.logged-row,
.set-row[data-role="edit-row"] {
  grid-template-columns: 44px minmax(50px, 1fr) minmax(50px, 1fr) minmax(64px, auto);
}

.logged-row {
  cursor: pointer;
}

.set-row[data-role="edit-row"] {
  cursor: default;
}

/* P9 A2 duration exercises: a 3-4 column variant of the above (no separate
   reps column — duration is the row's only measure). */
.set-row.set-row-duration[data-role="unlogged-row"] {
  grid-template-columns: 44px minmax(34px, 0.6fr) minmax(142px, 1.8fr) 44px;
}

.set-row.set-row-duration.logged-row,
.set-row.set-row-duration[data-role="edit-row"] {
  grid-template-columns: 44px minmax(60px, 1fr) minmax(64px, auto);
}

.set-row-duration .logged-value {
  text-align: center;
}

.logged-row .logged-value {
  text-align: center;
}

.logged-row .numeral {
  font-size: 16px;
}

/* P10: was a raw --cool fill — neither dark ink (3.35:1) nor a light label
   (4.11:1) cleared 5:1 against it. --cool-strong + light label does
   (5.08:1, verified). */
.superset-badge {
  align-self: flex-start;
  background: var(--cool-strong);
  color: var(--surface);
}

.add-set-row {
  padding: 10px;
  text-align: center;
  border-top: 1px dashed var(--border);
}

.add-exercise-btn {
  margin-top: 4px;
}

.finish-bar {
  display: flex;
  gap: 10px;
  margin-top: 16px;
}

.finish-bar .btn {
  flex: 1;
}

.workout-summary {
  text-align: center;
}

.workout-summary .big {
  font-size: 28px;
}

.summary-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 10px;
}

.summary-grid .stat-label {
  font-size: 11px;
  color: var(--text-dim);
}

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

#rest-timer-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0));
  display: none;
}

#rest-timer-bar.visible {
  display: block;
}

/* Timer end is silent by default (owner decision 08/15) — this pulse plus
   vibration is the actual "rest's over" signal. 3 flashes over 1.2s. */
@keyframes rest-timer-pulse {
  0%,
  100% {
    background: var(--surface);
  }
  50% {
    background: var(--highlight);
  }
}

#rest-timer-bar.pulse {
  animation: rest-timer-pulse 0.4s ease-in-out 3;
}

.rest-timer-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.rest-timer-countdown {
  font-size: 26px;
}

.rest-timer-actions {
  display: flex;
  gap: 8px;
}

.rest-timer-progress {
  height: 6px;
  border-radius: 3px;
  background: var(--border);
  overflow: hidden;
}

.rest-timer-progress-fill {
  height: 100%;
  background: var(--highlight);
  width: 100%;
  transition: width 1s linear;
}

/* ---------- exercise search sheet ---------- */

/* P9 A1: swap sheet -> true modal bottom sheet. Ink scrim (not plain black)
   at ~0.35 opacity, sheet slides up from the bottom — both driven by the
   .open class public/js/components/modal-sheet.js adds after append (a
   layout flush before adding it gives the transition a starting state to
   animate from). Transform/opacity only, and the transition itself only
   exists under prefers-reduced-motion:no-preference below — with reduced
   motion, .open still applies (so dismiss/scroll-lock behavior is
   unaffected), it just snaps to the final state instead of animating. */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(61, 34, 24, 0.35); /* --text (ink) scrim, not black */
  z-index: 30;
  display: flex;
  align-items: flex-end;
  opacity: 0;
}

.sheet-backdrop.open {
  opacity: 1;
}

.sheet {
  background: var(--bg);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  width: 100%;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-bottom: none;
  transform: translateY(100%);
}

.sheet-backdrop.open .sheet {
  transform: translateY(0);
}

@media (prefers-reduced-motion: no-preference) {
  .sheet-backdrop {
    transition: opacity 250ms ease-out;
  }

  .sheet {
    transition: transform 250ms ease-out;
  }
}

.sheet-header {
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid var(--border);
}

.sheet-header input {
  flex: 1;
}

.sheet-filters {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: 10px 16px;
  -webkit-overflow-scrolling: touch;
}

.sheet-list {
  overflow-y: auto;
  padding: 0 16px 16px;
}

.sheet-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--tap);
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}

.sheet-item .name {
  font-weight: 600;
}

/* ---------- settings view ---------- */

.field-group {
  margin-bottom: 16px;
}

.field-group label {
  display: block;
  font-size: 13px;
  color: var(--text-dim);
  margin-bottom: 6px;
}

.field-row {
  display: flex;
  gap: 8px;
}

.field-row input {
  flex: 1;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap);
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
}

.toggle-row input[type="checkbox"] {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  accent-color: var(--accent);
}

/* No <400px override: the base .set-row grid-template-columns already uses
   minmax() to guarantee a legible weight-input width at any viewport down
   to 320px — a bare-1fr override here previously reintroduced the exact
   truncation bug minmax() was added to fix (verified via computed-style
   inspection at 375px: this rule was winning the cascade and resolving to
   34px/77px/77px/77px/44px, clipping "77.5" to "77."). Do not re-add a
   fixed-fr override without re-testing computed widths, not just visual
   spot-checks — a short 2-digit value can look fine while a longer one
   (e.g. "127.5") still clips. */

/* ---------- P4: history + workout/exercise detail + stats ---------- */

.title-row-chips {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

.base-weight-chip {
  color: var(--cool-ink);
  border-color: var(--cool);
}

/* P6.5 double progression — target rep-range chip on the session card header. */
.range-chip {
  color: var(--accent-ink);
  border-color: var(--accent);
}

/* P9 A4 "squiggle" audit finding, real fix landed P10: this is the real
   e1rm trend sparkline (public/js/components/charts.js sparklineSvg), not
   a decorative artifact — SPEC.md PWA feature #3 mandates it. The actual
   bug wasn't spacing, it was rendering a "chart" from <3 points at all: a
   2-point polyline's y-scale always stretches to fill the full 24px box
   regardless of how small the real e1rm change is, so its endpoints touch
   the box's top AND bottom edges, reading as a diagonal strikethrough
   through the "suggested" text below. Per the dataviz rule (fewer than 3
   points -> not a chart), exercise-card.js now renders this wrapper at all
   ONLY when the series has >=3 points — so this rule only ever has to lay
   out a real, legible sparkline, no dead/empty-state band to account for. */
.sparkline-wrap {
  height: 24px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}

/* P12: this is a real <button class="card history-row"> now (was a plain
   div + manual role/tabindex/keydown — see views/history.js), so it needs
   the usual button-content resets on top of .card's own background/border/
   padding: full width (a button's block-level width:auto already fills its
   container the same as a div's would, but stated explicitly rather than
   assumed), inherited font/color instead of the UA button defaults, and
   left-aligned text. Hover/active/focus-visible feedback comes from the
   shared .tap-row class (also used by .routine-card-body) rather than a
   second copy of the same three rules. */
.history-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.history-row-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.history-row-stats {
  text-align: right;
  white-space: nowrap;
  font-size: 13px;
}

.history-row-stats .numeral {
  font-size: 14px;
}

.detail-set-list {
  margin-top: 8px;
}

.detail-set-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* P9 B3: classes.js rows can carry up to 3 trailing chips (club, "conflicts
     with work", studio) alongside the name — flex-wrap lets the chip group
     drop to a second line instead of overflowing the viewport (verified via
     getBoundingClientRect, not just scrollWidth — see commit notes: this
     project's overflow-x:hidden on html/body clips rather than scrolls, so
     scrollWidth alone doesn't reveal this class of bug). Harmless no-op for
     workout-detail.js's simpler 2-child usage of this same class, which
     already fits on one line. */
  flex-wrap: wrap;
  row-gap: 4px;
  column-gap: 8px;
  padding: 8px 0; /* was 6px, +33% — vertical only, horizontal stays 0 */
  border-bottom: 1px solid var(--border);
}

.detail-set-row:last-child {
  border-bottom: none;
}

/* ---------- routine detail / preview (P12) ---------- */
/* components/routine-preview.js's shared list renderer — used by both
   #/routine/:id and the home-screen preview sheet. */

.routine-item-row {
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.routine-item-row:last-child {
  border-bottom: none;
}

.routine-item-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* Superset pairing: components/routine-preview.js wraps a paired first/
   second item in one .superset-pair container (border lives here, once —
   not split across two row classes that had to be hand-kept in sync) so
   the pair reads as one visual bracket instead of two unrelated rows, on
   top of the existing "superset X" badge chip.
   border-left + padding-left - margin-left must net to 0 (3 + 10 - 13 = 0
   here) to keep the pair's text flush with every other row's left edge —
   CSS can't enforce that invariant, so: if you change border-left's width,
   update padding-left/margin-left to match. */
.superset-pair {
  border-left: 3px solid var(--cool-strong);
  padding-left: 10px;
  margin-left: -13px;
}

.superset-pair .routine-item-row:first-child {
  border-bottom: none;
  padding-bottom: 4px;
}

.superset-pair .routine-item-row:last-child {
  padding-top: 4px;
}

textarea {
  font-size: 16px; /* prevents iOS zoom-on-focus */
  font-family: inherit;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 8px 10px;
  width: 100%;
  resize: vertical;
}

/* ---------- accordions (exercise detail: steps/cues/mistakes) ---------- */

.accordion {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 4px 16px;
  margin-bottom: var(--gap);
}

.accordion summary {
  padding: 12px 0;
  font-weight: 700;
  cursor: pointer;
  min-height: 24px;
}

.accordion ul {
  margin: 0 0 12px;
  padding-left: 18px;
  color: var(--text-dim);
  font-size: 14px;
  line-height: 1.5;
}

/* ---------- technique videos ---------- */

.video-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.video-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
}

.video-row img,
.video-thumb-placeholder {
  width: 80px;
  height: 45px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--surface-raised);
  flex: 0 0 auto;
}

.video-meta {
  min-width: 0;
}

/* ---------- P7: exercise alternatives ---------- */

.alt-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.alt-row {
  display: block;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--text);
}

.alt-row-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.alt-row .name {
  font-weight: 700;
  font-size: 15px;
}

.alt-tradeoff {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.4;
}

.alt-evidence {
  margin-top: 4px;
  font-size: 12px;
  color: var(--cool-ink);
}

/* honest, not decorative: meta/rct/emg carry a real trial or measurement,
   inference is the author's mechanistic reasoning with no direct trial —
   two visibly different chip treatments so that distinction survives a
   skim, not just a close read of the evidence line underneath. */
.tier-chip-evidence {
  color: var(--success-ink);
  border-color: var(--success);
}

.tier-chip-reasoning {
  color: var(--text-dim);
  border-style: dashed;
}

/* card header's swap (⇄) button sits inline with the small muscle/base/range
   chips — the header isn't tall enough for a full 48px tap target there, so
   this is sized like the set-row stepper buttons (40px) instead of
   .icon-btn's 48px default. */
.swap-btn {
  min-width: 44px; /* P9 A5 tap target */
  min-height: 44px;
  font-size: 16px;
}

/* ---------- P7: session swap sheet ---------- */

.swap-alt-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  border-left: none;
  border-right: none;
  border-top: none;
  cursor: pointer;
  color: var(--text);
}

.swap-alt-item:last-child {
  border-bottom: none;
}

.swap-alt-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.swap-alt-tradeoff {
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-dim);
}

/* ---------- stats hero tile ---------- */

.stat-hero-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 6px;
}

/* ---------- charts (public/js/components/charts.js) ---------- */

.chart {
  position: relative;
}

.chart-empty {
  padding: 24px 0;
  text-align: center;
  font-size: 13px;
}

.chart svg {
  display: block;
  width: 100%;
  height: auto;
  touch-action: pan-y;
}

.trend-chart svg {
  height: 220px;
}

.chart-tooltip {
  position: absolute;
  transform: translateX(-50%);
  background: var(--text);
  color: var(--surface);
  font-size: 12px;
  font-weight: 600;
  padding: 4px 8px;
  border-radius: 6px;
  white-space: nowrap;
  pointer-events: none;
  z-index: 5;
}

/* Auth failure banner (settings). Uses the danger ground + its paired ink so
   it clears the 5:1 floor — see scripts/check-contrast.mjs. */
.auth-banner {
  background: var(--danger);
  color: var(--on-danger);
  padding: 12px 14px;
  border-radius: 8px;
  margin-bottom: 14px;
  font-weight: 600;
}
