@import url('https://fonts.googleapis.com/css2?family=Archivo:wdth,wght@62..125,300..900&display=swap');

/* ============ TOKENS ============ */
:root {
  /* Surfaces — warm graphite, gym rubber-crumb, not pure black */
  --rack:    #14161a;
  --bar:     #1c1f26;
  --collar:  #262a33;
  --knurl:   #333844;

  /* Text */
  --chalk:   #f2f0eb;
  --steel:   #8d939f;
  --dim:     #5c6270;

  /* IPF calibrated plate colors → muscle groups */
  --p-red:    #d6252b;  /* chest      (25kg) */
  --p-blue:   #2e7fd9;  /* back       (20kg) */
  --p-yellow: #f0be1e;  /* legs       (15kg) */
  --p-green:  #2aa85c;  /* shoulders  (10kg) */
  --p-white:  #e8e5de;  /* arms       (5kg)  */
  --p-chrome: #a8aeb8;  /* core       (1.25) */

  --good:    #2aa85c;
  --warn:    #f0be1e;
  --bad:     #d6252b;

  /* v59: one token per job, not per hue (vibes/defs/v1.js, the colour roles).
     In v1 each is a literal copy of the colour that did the job before, so
     nothing moves; a vibe can part them. The plate colours above are DATA —
     legs, carbs, the fuel and weight subject, the maintain zone — and these
     are the app's own chrome: --accent is the call to action (the primary
     button, the dock mark, today, the FAB, "on", Coach's voice, the PR),
     --focus the focus ring and every focused field's border, --danger the
     destructive and the refused, --done a set done and the rest line running.
     --well is the recessed ground inside a card (fields, chips, tiles), and
     --knockout / --inverse the ink cut out of a bright fill and that fill.
     --cal-mark is the marks on the calorie bar (its head, the zone ticks, the
     dashed target) and their swatches in the bar guide: the copy calls the
     head "the white head", so it stays white where --chalk may not.
     --collar is left the hairline (borders, dividers, grid lines) and --knurl
     a raised thing's border: --raised is the fill of a control that sits up
     off the card, --track a meter's empty part, --grip a fill one step above
     --raised (the grab handle, a toggle off) and --faint the faintest ink. */
  --accent:  #f0be1e;
  --focus:   #f0be1e;
  --accent-press: #d9a90f;
  --ink:     #141414;   /* on the accent */
  --danger:  #d6252b;
  --on-danger: #fff;
  --done:    #2aa85c;
  --ink-go:  #0d1a11;   /* on done */
  --ink-plate: #14161a; /* the number on a plate chip */
  --well:    #14161a;
  --knockout: #14161a;
  --inverse: #f2f0eb;
  --cal-mark: #f2f0eb;
  --raised:  #262a33;
  --track:   #262a33;
  --grip:    #333844;
  --faint:   #333844;
  --video-bg: #000;

  /* Channels, for a colour at an alpha: rgba(var(--x-rgb), a), the pattern
     .kpi's --kpi-rgb set. Each is its colour token's r,g,b. */
  --rack-rgb:     20,22,26;
  --shade-rgb:    0,0,0;
  --lift-rgb:     255,255,255;
  --accent-rgb:   240,190,30;
  --p-yellow-rgb: 240,190,30;
  --warn-rgb:     240,190,30;
  --p-red-rgb:    214,37,43;
  --bad-rgb:      214,37,43;
  --danger-rgb:   214,37,43;
  --p-blue-rgb:   46,127,217;
  --p-green-rgb:  42,168,92;
  --done-rgb:     42,168,92;
  --good-rgb:     42,168,92;
  --p-white-rgb:  232,229,222;
  --steel-rgb:    141,147,159;

  --font: 'Archivo', system-ui, -apple-system, sans-serif;
  --font-mono: ui-monospace, monospace;
  /* The stacks a vibe sets its heads, italic and numerals in; v1's are --font. */
  --font-display: 'Archivo', system-ui, -apple-system, sans-serif;
  --font-italic: 'Archivo', system-ui, -apple-system, sans-serif;
  --font-num: 'Archivo', system-ui, -apple-system, sans-serif;

  /* Engine v2 (vibes/defs/index.js ROLES). Each holds v1's value, so a rule
     that spends one draws what it drew. --tag-ink-* is the set badge's
     letter and --ink-of-* the ink of small text in a data colour, each the
     colour of the role it names; --tint-runway(-edge) the calorie runway's
     hatching and edge. There is no --band here: v1 draws no strip under the
     status bar, and a vibe that wants one declares it. */
  --tag-ink-w: #f0be1e;
  --tag-ink-f: #d6252b;
  --tag-ink-d: #2e7fd9;
  --ink-of-p-red: #d6252b;
  --ink-of-p-blue: #2e7fd9;
  --ink-of-p-yellow: #f0be1e;
  --ink-of-p-green: #2aa85c;
  --ink-of-p-white: #e8e5de;
  --ink-of-p-chrome: #a8aeb8;
  --tint-runway: rgba(20,22,26,.55);
  --tint-runway-edge: rgba(20,22,26,.7);
  /* Engine v3: the toggle's off knob and the name in the You greeting, each
     the colour that drew it (steel, the accent). The type.tag tokens
     (--type-tag-*) are not here on purpose: v1's preset is null, and every
     site that reads one falls back to its own literal — the small caps
     outside the blocks (.sync-pip, .trial-bar, .add-tile .tag, .adm-flag,
     .conf, .ai-cost, .group-pill, .mini-stat-l, .chart-sub, .you-since,
     .cal-legend-item; auth.css's .gate-sep, .ob-kicker, .ob-item-t). The
     sites that have no v1 literal to fall back to — a preset site marked
     [data-tag], a figure that takes the tag's size as a floor — get their
     rule only in a vibe that sets the preset, generated after its tokens
     (tools-check/vibes-css.mjs). */
  --knob: #8d939f;
  --greet-name: #f0be1e;
  /* The look params (vocab.js `params`) at their defaults. v1 names no look,
     so nothing in v1 reads them; a list is one token per entry. */
  --shape-rule-ink: #333844;
  --shape-rule-hair: 1px;
  --shape-rule-head-0: 2px;
  --shape-rule-head-1: 0;
  --shape-rule-head-2: 0;
  --shape-rule-sub-0: 2px;
  --shape-rule-sub-1: 0;
  --shape-rule-sub-2: 0;
  --shape-rule-total-0: 2px;
  --shape-rule-total-1: 0;
  --shape-rule-total-2: 0;
  --shape-leader-ink: #8d939f;
  --shape-leader-dot: 1.5px;
  --shape-leader-pitch: 4px;
  --shape-leader-min: 16px;
  --shape-band-fill: #262a33;
  --shape-band-ink: #f2f0eb;
  --shape-band-height: 30px;
  --shape-gutter: 2px;
  --shape-keyline-ink: #f2f0eb;
  --shape-keyline-width: 1px;
  --shape-lead-keyline: 0;
  --shape-cue-ink: #f2f0eb;
  --shape-chosen-tick: 0;
  --shape-rank-column: 0;

  /* Shadows and glass. Each token is the whole value, spelled as the rule
     that spends it spelled it, with its colour written out rather than a
     var(), so each is one level deep like every token here bar --app-top
     (tools-check/touch-target.mjs resolves var() from :root once). A vibe's
     block is generated from its definition by tools-check/vibes-css.mjs, so
     a vibe that changes --well or --accent gets the rings drawn in them
     regenerated with it. */
  --shadow-peek: 0 10px 30px rgba(0,0,0,.45);
  --shadow-rest: 0 6px 24px rgba(0,0,0,.45);
  --shadow-toast: 0 8px 28px rgba(0,0,0,.5);
  --shadow-fab: 0 8px 26px rgba(0, 0, 0, .58), 0 1px 0 rgba(0, 0, 0, .35);
  --shadow-fab-pressed: 0 3px 10px rgba(0, 0, 0, .5);
  --shadow-tour-card: 0 18px 50px rgba(0,0,0,.55);
  --shadow-cal-tick: 0 0 0 1px rgba(20,22,26,.55);
  --shadow-flame: 0 0 0 1px rgba(240,190,30,.35) inset;
  --shadow-kpi-day: inset 0 0 0 1.2px #333844;
  --shadow-kpi-day-on: none;
  --shadow-kpi-today: 0 0 0 1.5px #14161a, 0 0 0 2.5px #8d939f;
  --shadow-kpi-today-on: 0 0 0 1.5px #14161a, 0 0 0 2.5px #f2f0eb;
  --shadow-guide-eaten: inset 0 0 0 1px #333844;
  --shadow-traj-good: 0 0 0 4px rgba(42,168,92,.18);
  --shadow-traj-warn: 0 0 0 4px rgba(240,190,30,.18);
  --shadow-traj-bad: 0 0 0 4px rgba(214,37,43,.18);
  --shadow-tour-lit: 0 0 0 2px #f0be1e;
  --shadow-cal-head: none;     /* rings a vibe may put round the calorie head */
  --shadow-cal-target: none;   /* and the dashed target; rack-v58 draws none */
  --blur-sheet: blur(3px);
  --blur-dock: blur(18px) saturate(140%);
  --blur-wk-bar: blur(16px);
  --scrim-tour: linear-gradient(to bottom, rgba(20,22,26,.55) 0%, rgba(20,22,26,.94) 42%);

  --dock-h: 64px;
  /* The notch / status bar. Zero in Safari, where the browser chrome already
     sits above the page — and 47-59px once the app is installed to the home
     screen, because `apple-mobile-web-app-status-bar-style: black-translucent`
     plus `viewport-fit=cover` hands us the whole screen, notch included. The
     bottom inset was handled everywhere; this one was only ever handled by the
     sync pip and the workout bar, which is why an installed Rack had its
     header cropped into the top of the screen. Named so it can be faked in a
     desktop browser for testing. */
  --safe-top: env(safe-area-inset-top, 0px);
  /* Breathing room between the status bar and the first line of the page.
     Nudge this one number if the header still sits too close. */
  --top-gap: 10px;
  --app-top: calc(var(--safe-top) + var(--top-gap));
  --pad: 16px;
  --r: 12px;
  --r-sm: 8px;
  /* The rest of the radii, named for where each first appeared. */
  --r-sheet: 18px;
  --r-tile: 10px;
  --r-pill: 999px;
  --r-plate: 2px;
  --r-chip: 3px;
  --r-mark: 4px;
  --r-idx: 5px;
  --r-round: 50%;
  --r-hair: 1px;
  --r-bubble: 14px;
  --r-badge: 9px;

  --ease: cubic-bezier(.22,.61,.36,1);
  --fast: 140ms;
  --med: 240ms;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0; padding: 0;
  background: var(--rack);
  color: var(--chalk);
  font-family: var(--font);
  font-variation-settings: 'wdth' 100, 'wght' 400;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

body {
  min-height: 100dvh;
  font-size: 15px;
  line-height: 1.45;
}

/* Numerals align in columns everywhere. Non-negotiable in a log app. */
.num, input[type=number], .set-row, .stat-val, .timer {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
input, select, textarea { font-family: inherit; font-size: 16px; } /* 16px stops iOS zoom-on-focus */

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ============ APP SHELL ============ */
#app {
  max-width: 560px;
  margin: 0 auto;
  min-height: 100dvh;
  padding-top: var(--app-top);
  padding-bottom: calc(var(--dock-h) + env(safe-area-inset-bottom));
  position: relative;
}

.view { display: none; animation: viewIn var(--med) var(--ease); }
.view.active { display: block; }

/* Fade, no slide. The 6px translate this used to run promoted the whole view
   to its own GPU layer, and Safari does not always hand it back afterwards —
   text on a leftover composited layer is rasterised half a pixel off and reads
   very slightly soft, which is exactly what "Training log" was doing in the
   top-left corner. Nothing else on the page shows it because nothing else is
   small, bold and letter-spaced enough to give the softness away.
   The Fuel tab was already on a fade for a different reason (its fixed Log food
   button can't live inside a transformed ancestor); this brings the other three
   into line and fixes both problems with one rule. */
@keyframes viewIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.screen-pad { padding: var(--pad); }

/* ============ DOCK ============ */
.dock {
  position: fixed;
  bottom: 0; left: 50%;
  transform: translateX(-50%);
  width: 100%; max-width: 560px;
  height: calc(var(--dock-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: rgba(var(--rack-rgb),.82);
  backdrop-filter: var(--blur-dock);
  -webkit-backdrop-filter: var(--blur-dock);
  border-top: 1px solid var(--collar);
  display: flex;
  z-index: 60;
}

.dock button {
  flex: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 3px;
  color: var(--dim);
  font-size: 10px;
  letter-spacing: .09em;
  text-transform: uppercase;
  font-variation-settings: 'wdth' 88, 'wght' 600;
  transition: color var(--fast) var(--ease);
  position: relative;
}
.dock button svg { width: 22px; height: 22px; stroke-width: 1.9; }
.dock button.active { color: var(--chalk); }
.dock button.active::before {
  content: ''; position: absolute; top: 0; left: 50%;
  transform: translateX(-50%);
  width: 26px; height: 2px; background: var(--accent);
  border-radius: 0 0 var(--r-plate) var(--r-plate);
  animation: dockMark var(--med) var(--ease);
}
@keyframes dockMark { from { width: 0; } to { width: 26px; } }

/* ============ TYPE ============ */
h1, h2, h3 { margin: 0; font-variation-settings: 'wdth' 78, 'wght' 800; letter-spacing: -.01em; }
h1 { font-size: 26px; }
h2 { font-size: 18px; }

.eyebrow {
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--dim);
  font-variation-settings: 'wdth' 88, 'wght' 700;
}

/* The signature numeral treatment: wide + heavy, stamped like a plate */
.load-num {
  font-variation-settings: 'wdth' 118, 'wght' 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
  line-height: .95;
}

/* ============ CARDS ============ */
.card {
  background: var(--bar);
  border: 1px solid var(--collar);
  border-radius: var(--r);
  padding: 14px;
  margin-bottom: 12px;
}
.card-hd { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }

/* ============ BUTTONS ============ */
/* min-height, not padding: the touch target is 44px and a border-box min-height
   is exactly that whatever the padding, border, type size or font metrics —
   `line-height: normal` on Archivo made the old box 39px (primary) or 41px
   (ghost, danger), and the compact variants below went as low as 26px. It only
   ever adds height: no width, no padding and no wrap moves, and a button that
   was already taller (.btn-lg, the qty-row and water buttons) is untouched.
   Every site was measured before and after in v47 (report/btn-44.md). */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 18px;
  min-height: 44px;
  border-radius: var(--r-sm);
  background: var(--raised);
  color: var(--chalk);
  font-size: 14px;
  font-variation-settings: 'wdth' 92, 'wght' 700;
  letter-spacing: .02em;
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease), opacity var(--fast);
}
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .4; cursor: default; }
.btn-primary { background: var(--accent); color: var(--ink); }
.btn-danger  { background: transparent; color: var(--danger); border: 1px solid var(--danger); }
.btn-ghost   { background: transparent; border: 1px solid var(--collar); color: var(--steel); }
.btn-block   { width: 100%; }
.btn-lg      { padding: 16px; font-size: 16px; letter-spacing: .06em; text-transform: uppercase; }

/* ============ AUTH ============ */
#auth {
  position: fixed; inset: 0; z-index: 200;
  background: var(--rack);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 24px; gap: 14px;
}
#auth.hidden { display: none; }
.auth-box { width: 100%; max-width: 320px; }
.auth-mark {
  display: flex; gap: 3px; margin-bottom: 24px; justify-content: center;
}
.auth-mark i {
  display: block; width: 7px; height: 40px; border-radius: var(--r-plate);
  animation: plateDrop 560ms var(--ease) backwards;
}
@keyframes plateDrop {
  from { transform: translateY(-14px) scaleY(.4); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.field { margin-bottom: 10px; }
.field label {
  display: block; font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim); margin-bottom: 5px;
  font-variation-settings: 'wdth' 88, 'wght' 700;
}
.field input, .field select {
  width: 100%; padding: 12px;
  background: var(--bar);
  border: 1px solid var(--collar);
  border-radius: var(--r-sm);
  color: var(--chalk);
  transition: border-color var(--fast) var(--ease);
}
.field input:focus, .field select:focus { border-color: var(--focus); outline: none; }
/* Weight → "Weighed earlier?" (weight.js). WebKit sizes a date-and-time
   control from its formatted value rather than from `width`, so on a narrow
   phone this box ran past the card's right edge — and iOS then shrank the
   whole page to fit it, which is what squashed the Log button above it. The
   box sits in its own block under the Log row, not beside it, so nothing here
   touches Log: min-width: 0 and a max-width let it take the card's width, and
   appearance: none is what makes iOS honour that width at all. The picker
   still opens on tap. The value is pinned left, where iOS otherwise centres
   it inside the box. Not seen on a phone from here — see COACH-REPORT §24. */
.field input[type=datetime-local] {
  display: block; min-width: 0; max-width: 100%;
  -webkit-appearance: none; appearance: none;
}
.field input[type=datetime-local]::-webkit-date-and-time-value { text-align: left; }
.auth-err { color: var(--danger); font-size: 13px; min-height: 18px; }

/* ============ CALENDAR — the signature ============ */
.cal-hd {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 14px;
}
.cal-nav { display: flex; gap: 6px; }
.cal-nav button {
  width: 34px; height: 34px; border-radius: var(--r-sm);
  background: var(--bar); border: 1px solid var(--collar);
  color: var(--steel);
  display: grid; place-items: center;
  transition: background var(--fast), color var(--fast);
}
.cal-nav button:active { background: var(--raised); color: var(--chalk); }

.cal-dow {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 4px; margin-bottom: 6px;
}
.cal-dow span {
  text-align: center; font-size: 9px; letter-spacing: .1em;
  color: var(--dim); text-transform: uppercase;
  font-variation-settings: 'wdth' 88, 'wght' 700;
}

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }

.cal-day {
  aspect-ratio: 1 / 1.18;
  border-radius: var(--r-sm);
  background: var(--bar);
  border: 1px solid transparent;
  display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start;
  padding: 5px 3px 4px;
  gap: 3px;
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.cal-day.empty { background: transparent; }
.cal-day.pad { visibility: hidden; }
.cal-day:active { transform: scale(.94); }
.cal-day.today { border-color: var(--accent); }
.cal-day.has-work { background: var(--raised); }

.cal-daynum {
  font-size: 12px;
  font-variation-settings: 'wdth' 92, 'wght' 600;
  color: var(--dim);
  line-height: 1;
}
.cal-day.has-work .cal-daynum { color: var(--chalk); }
.cal-day.today .cal-daynum { color: var(--accent); font-variation-settings: 'wdth' 92, 'wght' 800; }

/* the plates stacked on the bar */
.cal-plates { display: flex; flex-direction: column; gap: 2px; width: 100%; padding: 0 2px; }
.cal-plates i {
  display: block; height: 3px; border-radius: var(--r-hair);
  animation: plateIn 300ms var(--ease) backwards;
}
@keyframes plateIn { from { transform: scaleX(0); opacity: 0; } to { transform: none; opacity: 1; } }

/* ============ DAY DETAIL SHEET ============ */
.sheet-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(var(--shade-rgb),.6);
  backdrop-filter: var(--blur-sheet);
  animation: fadeIn var(--fast) var(--ease);
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.sheet {
  position: fixed; bottom: 0; left: 50%;
  transform: translateX(-50%);
  width: 100%; max-width: 560px;
  max-height: calc(86dvh - var(--safe-top)); overflow-y: auto;
  background: var(--bar);
  border-radius: var(--r-sheet) var(--r-sheet) 0 0;
  border-top: 1px solid var(--knurl);
  z-index: 101;
  padding: 10px var(--pad) calc(24px + env(safe-area-inset-bottom));
  animation: sheetUp var(--med) var(--ease);
}
@keyframes sheetUp { from { transform: translate(-50%, 100%); } to { transform: translate(-50%, 0); } }
.sheet-grab {
  width: 36px; height: 4px; border-radius: var(--r-plate);
  background: var(--grip); margin: 0 auto 14px;
}

/* ============ STATS ============ */
.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.stat { background: var(--bar); border: 1px solid var(--collar); border-radius: var(--r-sm); padding: 10px; }
.stat-val { font-size: 20px; font-variation-settings: 'wdth' 108, 'wght' 800; line-height: 1; }
.stat-lbl { font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim); margin-top: 4px;
  font-variation-settings: 'wdth' 88, 'wght' 700; }

/* volume bars by group */
.vol-row { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.vol-name { font-size: 11px; color: var(--steel); width: 62px; flex-shrink: 0;
  font-variation-settings: 'wdth' 88, 'wght' 600; }
.vol-track { flex: 1; height: 6px; background: var(--track); border-radius: var(--r-chip); overflow: hidden; }
.vol-fill { height: 100%; border-radius: var(--r-chip); transition: width 500ms var(--ease); }
.vol-val { font-size: 11px; color: var(--dim); width: 42px; text-align: right;
  font-variant-numeric: tabular-nums; }

/* ---- parked-workout bar (calendar peek) ---- */
.peek-bar {
  position: fixed;
  left: var(--pad); right: var(--pad);
  bottom: calc(var(--dock-h) + env(safe-area-inset-bottom) + 10px);
  z-index: 60;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px 9px 13px;
  background: var(--raised);
  border: 1px solid var(--knurl);
  border-radius: var(--r);
  box-shadow: var(--shadow-peek);
}
.peek-left { min-width: 0; flex: 1; }
.peek-name {
  font-size: 13px; color: var(--chalk);
  font-variation-settings: 'wdth' 92, 'wght' 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.peek-bar .btn { padding: 8px 16px; flex-shrink: 0; }
.peek-bar .timer { font-size: 12px; color: var(--steel); margin-top: 1px; }
/* Room for the bar under the last thing on the page. The bar is 64px since
   .btn's 44px minimum reached Resume (it was 57), so 58px here would put it
   flush against the bottom button; 65 keeps the ~7px gap it always had. */
body.peeking .view.active { padding-bottom: 65px; }
.wk-cal-btn {
  flex-shrink: 0;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid var(--collar);
  border-radius: var(--r-sm);
  color: var(--steel);
}
.wk-cal-btn svg { width: 19px; height: 19px; }
.wk-cal-btn:active { color: var(--chalk); border-color: var(--knurl); }

/* ============ ACTIVE WORKOUT ============ */
.wk-bar {
  position: sticky; top: 0; z-index: 50;
  background: rgba(var(--rack-rgb),.9);
  backdrop-filter: var(--blur-wk-bar);
  -webkit-backdrop-filter: var(--blur-wk-bar);
  border-bottom: 1px solid var(--collar);
  padding: 10px var(--pad);
  margin-top: calc(-1 * var(--app-top));
  padding-top: calc(10px + var(--app-top));
  display: flex; align-items: center; justify-content: space-between;
}
.timer {
  font-size: 22px;
  font-variation-settings: 'wdth' 112, 'wght' 800;
  letter-spacing: -.01em;
}

/* rest timer — thin line across the top, ambient not modal */
.rest-line {
  position: fixed; top: 0; left: 0; height: 3px;
  background: var(--done);
  z-index: 90;
  transition: width 1s linear;
}
.rest-line.over { background: var(--danger); }

.rest-pill {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--dock-h) + env(safe-area-inset-bottom) + 12px);
  background: var(--raised);
  border: 1px solid var(--knurl);
  border-radius: var(--r-pill);
  padding: 8px 14px;
  display: flex; align-items: center; gap: 10px;
  z-index: 70;
  font-size: 13px;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  animation: pillIn var(--med) var(--ease);
  box-shadow: var(--shadow-rest);
}
@keyframes pillIn { from { transform: translate(-50%, 14px); opacity: 0; } to { transform: translate(-50%,0); opacity: 1; } }
.rest-pill .t { font-variant-numeric: tabular-nums; min-width: 42px; }
.rest-pill button { color: var(--steel); font-size: 12px; }

/* exercise block */
.ex-block { background: var(--bar); border: 1px solid var(--collar); border-radius: var(--r); margin-bottom: 12px; overflow: hidden; }
.ex-hd { display: flex; align-items: center; gap: 10px; padding: 12px 12px 8px; }
.ex-tag { width: 4px; height: 30px; border-radius: var(--r-plate); flex-shrink: 0; }
.ex-name { flex: 1; font-size: 15px; font-variation-settings: 'wdth' 88, 'wght' 700; line-height: 1.2; }
.ex-menu { color: var(--dim); padding: 4px 6px; font-size: 18px; }

.ex-prev {
  padding: 0 12px 8px 26px;
  font-size: 11px; color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* set table */
.set-hd, .set-row {
  display: grid;
  grid-template-columns: 30px 1fr 1fr 42px 38px;
  gap: 6px; align-items: center;
  padding: 5px 12px;
}
.set-hd { font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim);
  font-variation-settings: 'wdth' 88, 'wght' 700; }
.set-hd span:nth-child(2), .set-hd span:nth-child(3) { text-align: center; }

.set-row { border-top: 1px solid var(--collar); }
.set-row.done { background: rgba(var(--done-rgb),.07); }
.set-row.flash { animation: setFlash 600ms var(--ease); }
@keyframes setFlash {
  0%   { background: rgba(var(--accent-rgb),.28); }
  100% { background: rgba(var(--done-rgb),.07); }
}

.set-idx {
  font-size: 12px; font-variation-settings: 'wdth' 92, 'wght' 700;
  border-radius: var(--r-idx); text-align: center; padding: 3px 0;
  background: var(--raised); color: var(--steel);
  transition: background var(--fast), color var(--fast);
}
/* The letter is --tag-ink-* (the contract's tagInk: in v1 the plate colour
   itself), so a vibe can ink W in warn where its plate yellow is too light
   for 12px text; the wash stays the plate's. */
.set-idx.t-W { background: rgba(var(--p-yellow-rgb),.16); color: var(--tag-ink-w); }
.set-idx.t-F { background: rgba(var(--p-red-rgb),.16);  color: var(--tag-ink-f); }
.set-idx.t-D { background: rgba(var(--p-blue-rgb),.16); color: var(--tag-ink-d); }

.set-row input {
  width: 100%; padding: 8px 4px; text-align: center;
  background: var(--well); border: 1px solid transparent;
  border-radius: var(--r-sm); color: var(--chalk);
  font-variation-settings: 'wdth' 100, 'wght' 700;
  transition: border-color var(--fast);
}
.set-row input:focus { border-color: var(--focus); outline: none; }
.set-row input::placeholder { color: var(--dim); font-variation-settings: 'wght' 400; }

.set-e1rm { font-size: 10px; color: var(--dim); text-align: right; }

.set-check {
  width: 30px; height: 30px; border-radius: var(--r-sm);
  border: 1.5px solid var(--knurl);
  display: grid; place-items: center;
  color: var(--dim);
  transition: all var(--fast) var(--ease);
}
.set-check.on { background: var(--done); border-color: var(--done); color: var(--ink-go); }
.set-check:active { transform: scale(.88); }

/* First-workout coach mark: the box people did not realise completes a set.
   The steady yellow is what carries it and the fill pulsing under it is on top
   of that, so that anyone who asked for less movement still gets the hint. The
   global reduced-motion rule is not enough on its own here: it shortens every
   animation but never stops a repeating one, and an animation in effect beats
   the plain background below it — so without the block underneath, the steady
   fill would never paint and the box would strobe for exactly the people it is
   meant to be calm for. It stays inside the button on
   purpose: each set row is clipped by its own swipe-to-delete wrapper, so a
   ring drawn outside would come out as a band. Nothing draws a ✓ — the one
   thing worse than not knowing how to complete a set is a box that looks
   already ticked. */
.set-check.coach {
  border-color: var(--accent); color: var(--accent);
  background: rgba(var(--accent-rgb),.14);
  animation: coachPulse 1.6s var(--ease) infinite;
}
@keyframes coachPulse {
  0%, 100% { background: rgba(var(--accent-rgb),.07); }
  50%      { background: rgba(var(--accent-rgb),.38); }
}
@media (prefers-reduced-motion: reduce) {
  .set-check.coach { animation: none; }
}

.ex-actions { display: flex; gap: 8px; padding: 10px 12px 12px; }
.ex-actions .btn { flex: 1; padding: 9px; font-size: 12px; }

/* v55: a drop set. Its drops sit indented under the set he changed to a drop
   set, on a rail in the drop badge's blue, and "+ Drop" under the last one
   adds the next. A second drop set stacked under it starts back at the left,
   so two are visibly two groups. Only the badge moves in, inside its own
   column: the weight and reps boxes are exactly as wide as every other row's,
   so a drop in a lifting block at 320px wide has as much room as a set. */
.set-row.drop { position: relative; }
.set-row.drop::before {
  content: ''; position: absolute; left: 15px; top: 0; bottom: 0;
  width: 2px; border-radius: var(--r-hair); background: rgba(var(--p-blue-rgb),.45);
}
.set-row.drop .set-idx { margin-left: 10px; }
.drop-add-row { padding: 4px 12px 6px 22px; }
/* 12px words in the drop set's blue: the ink of small text in that colour
   (--ink-of-p-blue, the contract's inkOf; v1's is the blue itself). */
.drop-add { padding: 6px 14px; font-size: 12px; color: var(--ink-of-p-blue); border-color: rgba(var(--p-blue-rgb),.35); }

/* lifting block — the container the exercise cards above sit inside. It has no
   set rows of its own on purpose: a block repeats whole exercises, it is not a
   new kind of set. */
.wk-block {
  border: 1px solid var(--knurl);
  border-radius: var(--r);
  background: rgba(var(--accent-rgb),.03);
  margin-bottom: 12px;
}
.wk-block-hd { display: flex; align-items: center; gap: 8px; padding: 10px 12px 6px; }
.wk-block-title {
  flex: 1; font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent);
  font-variation-settings: 'wdth' 88, 'wght' 800;
}
.wk-block-acts { display: flex; align-items: center; gap: 4px; }
/* Deliberately the set row's box, one size down: it does the same thing to the
   same rows, so it should not look like a different control. */
.wk-block-chk { width: 26px; height: 26px; font-size: 13px; }
.wk-block-chk:disabled { opacity: .4; }
.wk-block-btn { padding: 6px 10px; font-size: 11px; }
.wk-block-x { color: var(--dim); padding: 4px 8px; font-size: 13px; line-height: 1; }
.wk-block-x:active { color: var(--danger); }
.wk-block-body { padding: 0 8px 8px; }
.wk-block-add { padding: 9px; font-size: 12px; }

/* Add exercise and Add Lifting Block, half the width each. */
.add-row { display: flex; gap: 8px; }
.add-row .btn { flex: 1; min-width: 0; padding: 12px 6px; font-size: 12px; letter-spacing: 0; }

/* plate math strip */
.plate-strip {
  display: flex; align-items: center; gap: 3px;
  padding: 8px 12px 10px 26px;
  flex-wrap: wrap;
}
.plate-strip .lbl { font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim); margin-right: 4px;
  font-variation-settings: 'wdth' 88, 'wght' 700; }
.plate-chip {
  font-size: 10px; padding: 2px 6px; border-radius: var(--r-chip);
  font-variation-settings: 'wdth' 92, 'wght' 800;
  font-variant-numeric: tabular-nums;
  color: var(--ink-plate);
}

/* ============ EXERCISE PICKER ============ */
.picker-search {
  position: sticky; top: 0; background: var(--bar); padding-bottom: 10px; z-index: 5;
}
.picker-search input {
  width: 100%; padding: 12px; background: var(--well);
  border: 1px solid var(--collar); border-radius: var(--r-sm); color: var(--chalk);
}
.filter-row { display: flex; gap: 6px; overflow-x: auto; padding: 10px 0 2px; scrollbar-width: none; }
.filter-row::-webkit-scrollbar { display: none; }
.chip {
  flex-shrink: 0; padding: 6px 12px; border-radius: var(--r-pill);
  background: var(--well); border: 1px solid var(--collar);
  font-size: 11px; color: var(--steel); white-space: nowrap;
  font-variation-settings: 'wdth' 92, 'wght' 600;
  transition: all var(--fast) var(--ease);
}
.chip.on { background: var(--inverse); color: var(--knockout); border-color: var(--inverse); }
/* v56: a custom exercise's Movement and Angle (picker.js movementRows). Not
   the scrolling .filter-row above: every choice is a 44px chip that wraps
   whole, so the whole list is in view at 320px, set in the type Your goal's
   chips use — tools-check/touch-target.mjs G measures each label against its
   chip in Archivo. */
.move-opts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.move-opt {
  min-height: 44px; padding: 8px 14px;
  border: 1px solid var(--collar); border-radius: var(--r-sm);
  background: var(--well); color: var(--steel);
  font-size: 12px; line-height: 1.25; white-space: nowrap;
  font-variation-settings: 'wdth' 92, 'wght' 600;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.move-opt.on { border-color: var(--accent); background: var(--raised); color: var(--chalk); }

.ex-item {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 4px; border-bottom: 1px solid var(--collar);
  width: 100%; text-align: left;
}
.ex-item .dot { width: 8px; height: 8px; border-radius: var(--r-plate); flex-shrink: 0; }
.ex-item .nm { flex: 1; font-size: 14px; font-variation-settings: 'wdth' 92, 'wght' 600; }
.ex-item .eq { font-size: 10px; color: var(--dim); text-transform: uppercase; letter-spacing: .06em; }
.ex-item.sel { background: rgba(var(--accent-rgb),.08); }

/* ============ EMPTY STATES ============ */
.empty-state { text-align: center; padding: 44px 20px; color: var(--dim); }
.empty-state h3 { color: var(--steel); margin-bottom: 6px; }
.empty-state p { font-size: 13px; margin: 0 0 16px; }

/* ============ TOAST ============ */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--dock-h) + env(safe-area-inset-bottom) + 16px);
  background: var(--inverse); color: var(--knockout);
  padding: 10px 18px; border-radius: var(--r-pill);
  font-size: 13px; font-variation-settings: 'wdth' 92, 'wght' 700;
  z-index: 300;
  animation: pillIn var(--med) var(--ease);
  box-shadow: var(--shadow-toast);
}

/* ============ SYNC PIP ============ */
.sync-pip {
  position: fixed; top: calc(8px + var(--safe-top)); right: 10px;
  z-index: 80; font-size: var(--type-tag-size, 9px); letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase);
  color: var(--dim); display: flex; align-items: center; gap: 5px;
  font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
}
.sync-pip i { width: 6px; height: 6px; border-radius: var(--r-round); background: var(--good); display: block; }
.sync-pip.off i { background: var(--warn); }

/* ============ TRIAL COUNTDOWN ============
   The sync pip's opposite corner and the sync pip's manners: a fact available
   at a glance, never a thing to be got past. One tap puts it away until the
   next launch, and it is not drawn at all for a trial with no end date — a
   countdown from nothing would be the one number on screen nobody could act on. */
.trial-bar {
  position: fixed; top: calc(8px + var(--safe-top)); left: 10px;
  z-index: 80;
  padding: 3px 9px; border-radius: var(--r-pill);
  background: rgba(var(--warn-rgb),.10);
  border: 1px solid rgba(var(--warn-rgb),.34);
  color: var(--warn);
  font-size: var(--type-tag-size, 9px); letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase);
  font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
}

/* ============ DESKTOP ============ */
@media (min-width: 900px) {
  #app { max-width: 720px; }
  .dock { max-width: 720px; }
  .sheet { max-width: 720px; border-radius: var(--r-sheet); bottom: 24px; }
  .cal-day { aspect-ratio: 1 / .92; }
}

/* ============ FUEL + WEIGHT (phase 2/3) ============ */
.tool-row { display: flex; gap: 6px; overflow-x: auto; margin: 0 0 12px; scrollbar-width: none; }
.tool-row::-webkit-scrollbar { display: none; }

.food-entry {
  width: 100%; text-align: left;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--collar);
}
.fe-name { font-size: 14px; font-variation-settings: 'wdth' 92, 'wght' 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fe-sub  { font-size: 11px; color: var(--dim); margin-top: 2px; }
.fe-cal  { font-size: 15px; font-variation-settings: 'wdth' 108, 'wght' 800; flex-shrink: 0; }

.micro-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 8px; margin-bottom: 10px; }

.qty-row { display: flex; gap: 8px; align-items: stretch; margin-top: 10px; }
.qty-row input {
  flex: 1; width: 100%; min-width: 0; text-align: center; padding: 12px;
  background: var(--well); border: 1px solid var(--collar); border-radius: var(--r-sm);
  color: var(--chalk); font-variation-settings: 'wdth' 108, 'wght' 800; font-size: 18px;
}
.qty-row input:focus { border-color: var(--focus); outline: none; }
.qty-row .btn { flex: 0 0 54px; padding: 0; }

.save-check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--steel); margin: 6px 0 10px; }
.save-check input { width: 18px; height: 18px; accent-color: var(--accent); }

.paste-box {
  width: 100%; min-height: 110px; padding: 12px; margin-top: 10px;
  background: var(--well); border: 1px solid var(--collar); border-radius: var(--r-sm);
  color: var(--chalk); font-family: var(--font-mono); font-size: 12px; resize: vertical;
}
.paste-box:focus { border-color: var(--focus); outline: none; }

.scan-video { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; border-radius: var(--r); background: var(--video-bg); margin-top: 10px; }

.ex-list { max-height: 48dvh; overflow-y: auto; -webkit-overflow-scrolling: touch; margin-top: 4px; }

.wchart { width: 100%; height: auto; display: block; margin-top: 4px; }
.wc-dot { fill: var(--steel); opacity: .75; }
.wc-avg { fill: none; stroke: var(--p-yellow); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.cal-nav button:disabled { opacity: .3; pointer-events: none; }

/* ================================================================
   ============ ADDED: SHARED PRIMITIVES ============
   Everything below this line was added alongside the statistics
   page, the post-workout summary, swipe-to-delete and the Fuel
   settings sheet. Nothing above it was changed.
   ================================================================ */

/* small explanatory text — replaces the inline styles that used to
   be repeated in six places */
.note {
  font-size: 12px;
  color: var(--dim);
  line-height: 1.5;
  margin-top: 8px;
}

.field-lbl {
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--dim); font-variation-settings: 'wdth' 88, 'wght' 700;
  margin: 16px 0 2px;
}

.row-split { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.card-sub { font-size: 11px; color: var(--dim); }

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

.back-btn {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--steel);
  padding: 4px 0 8px;
  font-variation-settings: 'wdth' 90, 'wght' 700;
  letter-spacing: .04em;
}
.back-btn span { font-size: 17px; line-height: 1; }
.back-btn:active { color: var(--chalk); }

/* segmented control */
.seg {
  display: flex; gap: 3px; padding: 3px;
  background: var(--bar); border: 1px solid var(--collar);
  border-radius: var(--r-pill); margin-bottom: 14px;
}
.seg-btn {
  flex: 1; padding: 8px 4px; border-radius: var(--r-pill);
  font-size: 11px; color: var(--steel);
  font-variation-settings: 'wdth' 92, 'wght' 700;
  letter-spacing: .06em; text-transform: uppercase;
  transition: all var(--fast) var(--ease);
}
.seg-btn.on { background: var(--inverse); color: var(--knockout); }

/* gear button in a header nav — inherits size and chrome from
   `.cal-nav button`, this only handles the icon and the press state */
.cal-nav .gear-btn svg { width: 16px; height: 16px; }
.cal-nav .gear-btn { margin-right: 2px; }
.cal-nav .gear-btn:active { transform: scale(.9); }

/* ============ SWIPE TO DELETE ============ */
.swipe { position: relative; overflow: hidden; }
.swipe-back {
  position: absolute; inset: 0;
  display: flex; justify-content: flex-end; align-items: stretch;
  background: var(--danger);
}
.swipe-del {
  width: 88px;
  color: var(--on-danger); font-size: 12px;
  font-variation-settings: 'wdth' 92, 'wght' 800;
  letter-spacing: .08em; text-transform: uppercase;
}
.swipe-front {
  position: relative;
  background: var(--bar);
  transition: transform var(--med) var(--ease);
  touch-action: pan-y;
}
.swipe-front.no-anim { transition: none; }

.swipe-hint {
  font-size: 10px; color: var(--dim); opacity: .65;
  padding: 6px 12px 0 26px;
  letter-spacing: .04em;
}

/* ============ CALENDAR LEGEND ============ */
.cal-legend { display: flex; flex-wrap: wrap; gap: 10px; margin: 14px 0 18px; }
.cal-legend-item {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--type-tag-size, 10px); color: var(--dim);
  letter-spacing: var(--type-tag-ls, .06em); text-transform: var(--type-tag-upper, uppercase);
}
.cal-legend-item i { width: 12px; height: 3px; border-radius: var(--r-hair); display: block; }

/* ============ DAY SHEET ============ */
.day-title { font-size: 14px; font-variation-settings: 'wdth' 90, 'wght' 700; }
.day-ex {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 6px 0; border-top: 1px solid var(--collar);
}
.day-ex-tag { width: 3px; align-self: stretch; border-radius: var(--r-plate); flex-shrink: 0; }
.day-ex-body { flex: 1; min-width: 0; }
.day-ex-name { font-size: 13px; font-variation-settings: 'wdth' 90, 'wght' 700; }
.day-ex-sets { font-size: 11px; color: var(--steel); margin-top: 2px; }
.day-actions { display: flex; gap: 8px; margin-top: 12px; }
.day-actions .btn { flex: 1; padding: 10px; font-size: 12px; }

/* ============ ACTIVE WORKOUT BAR ============ */
.wk-bar-left { min-width: 0; flex: 1; }
.wk-name {
  background: none; border: none; color: var(--chalk);
  font-size: 14px; font-variation-settings: 'wdth' 88, 'wght' 700;
  padding: 0; width: 100%;
}
.wk-name:focus { outline: none; color: var(--focus); }

/* ============ POST-WORKOUT SUMMARY ============ */
.summary-page { animation: summaryIn var(--med) var(--ease); }
@keyframes summaryIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.summary-hero { margin-bottom: 16px; }
/* The three tiles under the hero. .stat-row carries no margin of its own —
   every screen that uses it spaces it where it sits — and every card in this
   recap is spaced by its own margin-bottom of 12px, so the tiles sat flush on
   whichever card came next ("Against your last 4 weeks", or a record). Scoped
   to the recap so no other screen's tiles move. */
.summary-page .stat-row { margin-bottom: 12px; }
.summary-hero h1 { margin-top: 2px; }
/* v53: the finish line's one true sentence under the headline, in body text —
   the win is the headline's, this only says what earned it. */
.summary-line { font-size: 15px; color: var(--chalk); margin-top: 4px; }
.summary-date { font-size: 12px; color: var(--dim); margin-top: 4px; }
/* v53: "How did that feel?" — ten energy chips in two rows of five, five
   strength steps, Save and Skip; once saved, one line. Every chip is a 44px
   target (the sheet's .coach-chip is smaller, and stays so there). */
.feel-cap { font-size: 12px; color: var(--steel); margin: 10px 0 6px; }
.feel-card .card-hd + .feel-cap { margin-top: 2px; }
.feel-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.feel-card .coach-chip { min-height: 44px; }
.feel-grid .coach-chip { padding: 9px 0; }
.feel-card .coach-chip.on { border-color: var(--accent); color: var(--accent); }
.feel-acts { display: flex; gap: 8px; margin-top: 14px; }
.feel-acts .btn { flex: 1; }
.feel-line { font-size: 15px; color: var(--chalk); }
.feel-mark { margin-top: 12px; }
.feel-ask { font-size: 14px; color: var(--chalk); }
.feel-sub { font-size: 11px; color: var(--dim); margin: 2px 0 8px; }
.feel-ack { font-size: 13px; color: var(--steel); margin-top: 8px; }
/* v53: "Compared with sessions like this" — two plain lines in body colour,
   deliberately no big number and no green/steel verdict. */
.summary-like-row { font-size: 13px; color: var(--chalk); line-height: 1.45; }
.summary-like-row + .summary-like-row { margin-top: 4px; }

.pr-card {
  border-color: rgba(var(--accent-rgb),.4);
  background: linear-gradient(180deg, rgba(var(--accent-rgb),.09), transparent 70%), var(--bar);
}
.pr-hit, .pr-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--collar);
  width: 100%; text-align: left;
}
.pr-card .pr-hit:first-of-type { border-top: none; }
.pr-tag { width: 3px; align-self: stretch; min-height: 30px; border-radius: var(--r-plate); flex-shrink: 0; }
.pr-body { flex: 1; min-width: 0; }
.pr-name {
  font-size: 14px; font-variation-settings: 'wdth' 90, 'wght' 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pr-sub { font-size: 11px; color: var(--dim); margin-top: 2px; }
.pr-right { text-align: right; flex-shrink: 0; }
.pr-val {
  font-size: 19px; font-variation-settings: 'wdth' 110, 'wght' 800;
  color: var(--accent); line-height: 1;
}
.pr-delta { font-size: 10px; color: var(--good); margin-top: 3px; }

/* ============ PERSONAL BEST ROWS ============ */
.pb-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--collar);
}
.pb-row:first-of-type { border-top: none; }
.pb-lbl { font-size: 13px; font-variation-settings: 'wdth' 90, 'wght' 600; }
.pb-sub { font-size: 11px; color: var(--dim); margin-top: 2px; }
.pb-val {
  font-size: 15px; font-variation-settings: 'wdth' 108, 'wght' 800;
  flex-shrink: 0; text-align: right;
}

/* ============ RANK / LEADERBOARD ROWS ============ */
.rank-row {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 0; border-top: 1px solid var(--collar);
  width: 100%; text-align: left;
}
.rank-row:first-of-type { border-top: none; }
.rank-no {
  width: 18px; flex-shrink: 0;
  font-size: 13px; color: var(--dim);
  font-variation-settings: 'wdth' 108, 'wght' 800;
}
.rank-tag { width: 3px; align-self: stretch; min-height: 28px; border-radius: var(--r-plate); flex-shrink: 0; }
.rank-body { flex: 1; min-width: 0; }
.rank-name {
  font-size: 14px; font-variation-settings: 'wdth' 90, 'wght' 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rank-sub { font-size: 11px; color: var(--dim); margin-top: 2px; }
.rank-val {
  font-size: 15px; font-variation-settings: 'wdth' 108, 'wght' 800;
  flex-shrink: 0;
}

/* ============ SESSION LIST ROWS ============ */
.sess-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 0; border-top: 1px solid var(--collar);
}
.sess-row:first-of-type { border-top: none; }
.sess-row.is-pr { background: linear-gradient(90deg, rgba(var(--accent-rgb),.09), transparent 60%); }
.sess-date { font-size: 13px; font-variation-settings: 'wdth' 90, 'wght' 600; }
.sess-sub { font-size: 11px; color: var(--dim); margin-top: 2px; }
.sess-right { text-align: right; flex-shrink: 0; }
.sess-e1 { font-size: 16px; font-variation-settings: 'wdth' 108, 'wght' 800; line-height: 1; }
.sess-e1lbl { font-size: 9px; color: var(--dim); letter-spacing: .1em; text-transform: uppercase; margin-top: 2px; }

.group-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: var(--r-pill);
  background: var(--bar); border: 1px solid var(--collar);
  font-size: var(--type-tag-size, 10px); color: var(--steel);
  letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase);
  font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
  margin-bottom: 14px;
}
.group-pill i { width: 7px; height: 7px; border-radius: var(--r-plate); }

/* ================================================================
   ============ CHARTS ============
   ================================================================ */
/* The hooks chart · ink needs are already here, with nothing added to the
   pinned analytics.js: the area wash under lineChart's line and a sparkline's
   is the one unclassed path[fill^="url(#"] in its svg (a look with no area
   sets fill: none on it); the caps are .chart-line / .spark-line /
   .ring-fill / .donut-seg's stroke-linecap, which CSS beats as an attribute;
   a legend key is .legend-item i, 9 × 9 here, 12 × 3 as a stroke. */
.chart { width: 100%; height: auto; display: block; overflow: visible; }

.chart-grid { stroke: var(--collar); stroke-width: 1; stroke-dasharray: 2 5; opacity: .7; }
.chart-line { fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.chart-dot  { opacity: .9; }
.chart-scatter { fill: var(--steel); opacity: .42; }

.chart-peak-ring { fill: none; stroke-width: 1.6; opacity: .95; }
.chart-peak-lbl {
  font-size: 10px; font-family: inherit;
  font-variation-settings: 'wdth' 100, 'wght' 800;
  font-variant-numeric: tabular-nums;
}

.chart-axis {
  font-size: 9px; fill: var(--dim); font-family: inherit;
  letter-spacing: .04em;
  font-variation-settings: 'wdth' 88, 'wght' 600;
}

.chart-bar-bg { fill: var(--track); opacity: .35; }
.chart-bar { transition: opacity var(--fast); }
.chart-barval {
  font-size: 9px; fill: var(--steel); font-family: inherit;
  font-variant-numeric: tabular-nums;
  font-variation-settings: 'wdth' 100, 'wght' 700;
}

.chart-empty {
  padding: 26px 10px; text-align: center;
  font-size: 12px; color: var(--dim);
  border: 1px dashed var(--collar); border-radius: var(--r-sm);
}

.chart-foot {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--dim); margin-top: 6px;
}

/* donut */
.donut-wrap { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.chart-donut { width: 132px; height: 132px; flex-shrink: 0; }
.donut-seg { transition: opacity var(--fast); }
.donut-top {
  font-size: 22px; fill: var(--chalk); font-family: inherit;
  font-variant-numeric: tabular-nums;
  font-variation-settings: 'wdth' 112, 'wght' 800;
}
.donut-sub {
  font-size: 9px; fill: var(--dim); font-family: inherit;
  letter-spacing: .14em; text-transform: uppercase;
  font-variation-settings: 'wdth' 88, 'wght' 700;
}

/* legend */
.legend { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 120px; }
.legend-item { display: flex; align-items: center; gap: 7px; font-size: 11px; }
.legend-item i { width: 9px; height: 9px; border-radius: var(--r-plate); flex-shrink: 0; }
.legend-lbl { flex: 1; color: var(--steel); }
.legend-val { color: var(--dim); font-size: 10px; }

/* consistency heat strip */
.heat { max-width: 100%; margin: 2px 0 12px; }

/* ================================================================
   ============ FUEL TWEAKS ============
   ================================================================ */
.fuel-top { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }

/* ---- calorie meter: deficit / maintenance / surplus ---- */
.btn-split { display: flex; gap: 8px; }
.btn-split > .btn { flex: 1; }

.cal-meter { margin: 0 0 16px; }
.cal-track {
  position: relative;
  height: 18px;
  border-radius: var(--r-mark);
  background: var(--track);
  overflow: hidden;
}
.cal-zone { position: absolute; top: 0; bottom: 0; }
.cal-zone.cut  { background: rgba(var(--p-blue-rgb),.16); }
.cal-zone.hold { background: rgba(var(--p-yellow-rgb),.18); }
.cal-zone.gain { background: rgba(var(--p-red-rgb),.16); }
.cal-fill {
  position: absolute; left: 0; top: 0; bottom: 0;
  border-radius: var(--r-mark) 0 0 var(--r-mark);
  opacity: .92;
  transition: width 500ms var(--ease);
}
.cal-tick {
  position: absolute; top: -1px; bottom: -1px; width: 2px;
  margin-left: -1px;
  background: var(--cal-mark);
  opacity: .85;
  box-shadow: var(--shadow-cal-tick);
}
.cal-head {
  position: absolute; top: -2px; bottom: -2px; width: 3px;
  margin-left: -1.5px; border-radius: var(--r-plate);
  background: var(--cal-mark);
  /* a ring a vibe may draw round the white head, for a page it would vanish
     on (the contract's shadow.calHead); none in v1 */
  box-shadow: var(--shadow-cal-head);
  transition: left 500ms var(--ease);
}
.cal-bands { position: relative; height: 13px; margin-top: 5px; }
.cal-band-lab {
  position: absolute; top: 0;
  font-size: 9.5px; text-align: center; color: var(--dim);
  text-transform: uppercase; letter-spacing: .08em;
  font-variation-settings: 'wdth' 86, 'wght' 650;
  white-space: nowrap; overflow: hidden;
}
.cal-status {
  display: flex; align-items: center; gap: 6px;
  margin-top: 7px; font-size: 11.5px; color: var(--steel);
}
.cal-status i { width: 7px; height: 7px; border-radius: var(--r-round); flex-shrink: 0; }
.cal-status .cal-maint { margin-left: auto; color: var(--dim); font-size: 11px; }

.fuel-eaten { font-size: 12px; color: var(--dim); }

.meal-right { display: flex; align-items: center; gap: 8px; }
.meal-kcal { font-size: 11px; color: var(--dim); }

.fe-body { min-width: 0; }
.micro-cov { color: var(--faint); }

.entry-readout {
  font-size: 15px; color: var(--chalk);
  font-variation-settings: 'wdth' 104, 'wght' 700;
  margin: 12px 0 4px;
}

.settings-summary {
  font-size: 13px; color: var(--steel);
  margin-top: 8px;
  font-variation-settings: 'wdth' 100, 'wght' 600;
}

.portion-preview { font-size: 13px; color: var(--steel); margin: 6px 0 12px; }

.ex-del { margin-left: 8px; }

.import-list { margin-top: 8px; }
.import-bar { height: 6px; border-radius: var(--r-chip); overflow: hidden; display: flex; margin-top: 12px; }
.import-status { margin-top: 10px; }
.file-pick { margin-top: 14px; width: 100%; color: var(--steel); font-size: 13px; }

.picker-foot {
  position: sticky; bottom: 0; background: var(--bar);
  padding-top: 10px; display: flex; gap: 8px;
}

/* keep the calendar nav row tidy now that it can hold a gear too */
.cal-nav { display: flex; align-items: center; gap: 6px; }

/* ============================================================
   WATER
   ============================================================ */
.water-pct { font-size: 12px; color: var(--steel); font-variation-settings: 'wdth' 92, 'wght' 700; }

.water-row { display: flex; align-items: center; gap: 16px; margin-top: 4px; }
.water-vessel { width: 76px; height: auto; flex: 0 0 76px; display: block; }
.water-right { flex: 1; min-width: 0; }
.water-left { font-size: 13px; color: var(--steel); margin-top: 6px;
  font-variation-settings: 'wdth' 96, 'wght' 600; }

.water-pips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
.water-pip {
  width: 13px; height: 13px; border-radius: var(--r-mark);
  background: var(--track); border: 1px solid var(--knurl);
  position: relative; overflow: hidden;
}
.water-pip.on { background: var(--p-blue); border-color: var(--p-blue); }
.water-pip.part::after {
  content: ''; position: absolute; inset: 0 auto 0 0;
  width: var(--fill, 50%); background: var(--p-blue);
}

.water-ctl { display: flex; gap: 8px; margin-top: 14px; }
.water-ctl .water-add { flex: 1; font-size: 13px; }
.water-ctl .water-mini { flex: 0 0 48px; padding: 10px 0; font-size: 17px; }

.water-preset-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin: 12px 0 4px;
}
.water-preset {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 11px 12px; border-radius: var(--r-sm);
  background: var(--bar); border: 1px solid var(--collar); color: var(--chalk);
  text-align: left; cursor: pointer;
}
.water-preset:active { border-color: var(--p-blue); }
.wp-label { font-size: 12px; font-variation-settings: 'wdth' 92, 'wght' 700; }
.wp-amt { font-size: 12px; color: var(--steel); }

.water-entries { margin-top: 8px; }
.water-entry {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 12px; background: var(--bar); border-top: 1px solid var(--collar);
}
.we-time { font-size: 11px; color: var(--dim); }

.water-preset-edit { margin-top: 8px; }
.wpe-row { display: grid; grid-template-columns: 1fr 92px 34px; gap: 6px; margin-bottom: 6px; }
.wpe-row input {
  padding: 9px 10px; background: var(--well); border: 1px solid var(--collar);
  border-radius: var(--r-sm); color: var(--chalk); font-size: 13px; min-width: 0;
}
.wpe-row input:focus { border-color: var(--p-blue); outline: none; }
.wpe-amt { text-align: center; font-variation-settings: 'wdth' 100, 'wght' 700; }
.wpe-del {
  background: none; border: 1px solid var(--collar); border-radius: var(--r-sm);
  color: var(--dim); font-size: 17px; cursor: pointer;
}
.wpe-del:active { color: var(--danger); border-color: var(--danger); }

/* ============================================================
   TIME-OF-DAY CURVE
   ============================================================ */
.tod-strip {
  display: flex; align-items: flex-end; gap: 2px;
  height: 74px; margin: 6px 0 14px; padding-bottom: 14px; position: relative;
}
.tod-col { flex: 1; height: 100%; display: flex; align-items: flex-end; justify-content: center; position: relative; }
.tod-col i { display: block; width: 100%; border-radius: var(--r-plate) var(--r-plate) 0 0; background: var(--p-yellow); }
.tod-col.empty i { background: var(--track); }
.tod-h {
  position: absolute; bottom: -14px; left: 50%; transform: translateX(-50%);
  font-size: 8px; color: var(--dim); letter-spacing: .04em; white-space: nowrap;
}

/* ============================================================
   ROUTINES
   ============================================================ */
.rt-list { margin-top: 6px; }
.rt-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 12px; margin-bottom: 8px; text-align: left; cursor: pointer;
  background: var(--bar); border: 1px solid var(--collar);
  border-radius: var(--r); color: var(--chalk);
}
.rt-item:active { border-color: var(--knurl); }
.rt-dots { display: flex; flex-direction: column; gap: 3px; flex: 0 0 6px; }
.rt-dots i { width: 6px; height: 6px; border-radius: var(--r-plate); display: block; }
.rt-mid { flex: 1; min-width: 0; }
.rt-name { font-size: 15px; font-variation-settings: 'wdth' 96, 'wght' 700; }
.rt-meta { font-size: 11px; color: var(--steel); margin-top: 3px; }
.rt-go { color: var(--dim); font-size: 20px; }

.rt-preview { margin-top: 10px; }
/* A block in the read-only preview. The editor reuses .wk-block itself, but the
   preview has no cards to put in a container — just rows — so it borrows the
   look at a smaller scale. */
.rt-pv-block {
  border: 1px solid var(--knurl);
  border-radius: var(--r);
  background: rgba(var(--accent-rgb),.03);
  padding: 7px 10px 3px;
  margin: 6px 0;
}
.rt-pv-block .wk-block-title { margin-bottom: 3px; }
.rt-pv-row {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 0; border-top: 1px solid var(--collar);
}
.rt-pv-name { flex: 1; min-width: 0; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-pv-sets { font-size: 11px; color: var(--steel); flex: 0 0 auto; }

.rt-edit { margin-top: 14px; }
/* Four columns instead of five — no e1RM, nothing to tick off. */
.rt-sets .set-hd, .rt-sets .set-row { grid-template-columns: 30px 1fr 1fr 38px; }

/* ============================================================
   STEPS
   ============================================================ */
/* Five dock slots now, not three — tighten the tracking so the longest label
   still fits on a small phone. At 320px that is a 64px slot against WEIGHT,
   which sets about 39px at 10px in this face; the icon is 22px. Nothing
   collides, so nothing else here has to change. */
.dock button { letter-spacing: .07em; }

.st-hero { display: flex; align-items: center; gap: 18px; margin-top: 4px; }
.st-ring { width: 132px; height: 132px; flex: 0 0 132px; display: block; }
.st-ring-n {
  fill: var(--chalk); font-size: 22px;
  font-variation-settings: 'wdth' 104, 'wght' 800;
}
.st-ring-s { fill: var(--steel); font-size: 9.5px; letter-spacing: .06em; }

.st-hero-side { flex: 1; min-width: 0; }
.st-side-lbl { font-size: 12px; color: var(--steel); margin-top: 2px;
  font-variation-settings: 'wdth' 96, 'wght' 600; }
.st-src { font-size: 10.5px; color: var(--dim); margin-top: 10px;
  letter-spacing: .04em; }

.st-ctl { display: flex; gap: 7px; margin-top: 16px; }
.st-ctl .st-inc { flex: 1; padding: 10px 0; font-size: 12px; }
.st-ctl .st-set { flex: 1.5; padding: 10px 0; font-size: 12px; }

/* streak */
.st-streak { display: flex; align-items: center; gap: 14px; margin-top: 4px; }
.st-flame {
  flex: 0 0 56px; height: 56px; border-radius: var(--r-round);
  display: flex; align-items: center; justify-content: center;
  background: var(--raised); color: var(--dim);
  font-size: 21px; font-variation-settings: 'wdth' 104, 'wght' 800;
}
.st-flame.on {
  background: rgba(var(--accent-rgb),.16); color: var(--accent);
  box-shadow: var(--shadow-flame);
}
.st-streak-t { font-size: 15px; font-variation-settings: 'wdth' 96, 'wght' 700; }
.st-streak-s { font-size: 11.5px; color: var(--steel); margin-top: 3px; }

/* recent list */
.st-row {
  display: grid; grid-template-columns: 1fr auto 46px;
  gap: 10px; align-items: center;
  padding: 11px 2px; border-top: 1px solid var(--collar);
  background: var(--bar); cursor: pointer;
}
.st-row-d { font-size: 13px; color: var(--chalk); }
.st-row-n { font-size: 14px; font-variation-settings: 'wdth' 100, 'wght' 700; }
.st-row-p { font-size: 11px; color: var(--dim); text-align: right; }

/* ============================================================
   YOU
   The tab the app opens on: the week in summary, the settings hub
   behind the gear, and the owner's admin panel. It borrows the
   card / stat / chart vocabulary above rather than restating it —
   only the parts that had no home already are named here.
   ============================================================ */

/* Avatar, name, and the gear that is now the app's only way into settings.
   A grid rather than a flex row so the gear stays pinned right whatever
   length the name is; `min-width: 0` on the children is what lets a long
   name ellipsis instead of pushing the gear off the edge. */
.you-hero {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: 13px;
  margin-bottom: 4px;
}
.you-hero > * { min-width: 0; }
.you-avatar {
  width: 52px; height: 52px; border-radius: var(--r-round);
  display: grid; place-items: center;
  background: var(--raised); color: var(--steel);
  font-size: 19px; font-variation-settings: 'wdth' 104, 'wght' 800;
}
.you-name {
  font-size: 19px; line-height: 1.15;
  font-variation-settings: 'wdth' 100, 'wght' 800;
  letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.you-sub { font-size: 12px; color: var(--dim); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.you-gear {
  width: 36px; height: 36px; border-radius: var(--r-sm);
  background: var(--bar); border: 1px solid var(--collar);
  color: var(--steel);
  display: grid; place-items: center;
  transition: background var(--fast), color var(--fast), transform var(--fast);
}
.you-gear svg { width: 17px; height: 17px; }
.you-gear:active { background: var(--raised); color: var(--chalk); transform: scale(.92); }

.you-since {
  font-size: var(--type-tag-size, 10px); letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase);
  color: var(--dim); font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
  margin: 12px 0 16px;
}

/* Week against week. The three classes are a judgement, not arithmetic:
   `.up` means it moved the right way and `.down` the wrong one, so a trend
   weight that fell is `.delta.up`. Nothing here reads the sign of the
   number, because the sign does not know which way is good. The caller
   picks the class; if it can't tell, it uses `.flat`. */
.delta { display: inline-flex; align-items: baseline; gap: 5px; color: var(--steel); }
.delta.up   { color: var(--good); }
.delta.down { color: var(--bad); }
.delta.flat { color: var(--dim); }
.delta-v {
  font-size: 13px; font-variant-numeric: tabular-nums;
  font-variation-settings: 'wdth' 100, 'wght' 700;
}
.delta-l { font-size: 10px; color: var(--dim); letter-spacing: .04em; }

/* `.stat-row` is already three-up, but You lays whole cards and rows into
   these grids as well as `.stat`s, so the pair is named for the shape. */
.you-grid-2, .you-grid-3 { display: grid; gap: 8px; }
.you-grid-2 { grid-template-columns: repeat(2, 1fr); }
.you-grid-3 { grid-template-columns: repeat(3, 1fr); }

/* This is a long screen — the headings are what make it scannable. */
.you-sec { margin-top: 26px; }
.you-sec-t {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 10px;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--dim); font-variation-settings: 'wdth' 88, 'wght' 700;
}
.you-sec-t::after { content: ''; flex: 1; height: 1px; background: var(--collar); }

/* ---------- the settings hub ----------
   `.set-row` above belongs to the workout set table; these are the
   deliberately separate names for a tappable settings row. */
.set-list { margin-top: 4px; }
.set-row-nav {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  padding: 13px 2px; border-top: 1px solid var(--collar);
  color: var(--chalk);
  transition: background var(--fast) var(--ease);
}
.set-list .set-row-nav:first-child { border-top: none; }
.set-row-nav:active { background: rgba(var(--lift-rgb),.04); }
.set-row-l { flex: 1; min-width: 0; font-size: 14px;
  font-variation-settings: 'wdth' 92, 'wght' 600; }
/* The live value on the right — targets kcal, the water goal, AI on or off. */
.set-row-v { flex: none; font-size: 12px; color: var(--steel);
  font-variant-numeric: tabular-nums; }
/* The trailing chevron: the row's only signal that it opens something. */
.set-row-x { flex: none; color: var(--dim); font-size: 17px; line-height: 1; }

/* ---------- admin ----------
   Same bones as `.person` in auth.css — a name over a quieter second line,
   numbers pinned right — but as a dense list rather than boxed cards,
   because the owner is reading a table of accounts, not one profile. */
.adm-table { margin-top: 6px; }
.adm-r {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 2px; border-top: 1px solid var(--collar);
}
.adm-table .adm-r:first-child { border-top: none; }
.adm-who {
  flex: 1; min-width: 0;
  font-size: 13px; font-variation-settings: 'wdth' 92, 'wght' 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.adm-uid {
  font-size: 10px; color: var(--dim); margin-top: 2px;
  font-family: var(--font-mono);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.adm-n {
  flex: none; text-align: right;
  font-size: 14px; font-variant-numeric: tabular-nums;
  font-variation-settings: 'wdth' 104, 'wght' 800;
}
.adm-flag {
  flex: none;
  padding: 2px 7px; border-radius: var(--r-pill);
  border: 1px solid var(--collar); color: var(--dim);
  font-size: var(--type-tag-size, 9px); letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase);
  font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
}
.adm-flag.on  { border-color: rgba(var(--good-rgb),.45);  color: var(--good); }
.adm-flag.off { border-color: rgba(var(--bad-rgb),.45);  color: var(--bad); }
/* Account types get the same three colours the rest of the panel uses — green
   is fine, yellow is running out, red is stopped — plus blue for a paid tier,
   which is neither. Basic keeps the neutral outline: it is the ordinary case
   and it should not draw the eye. */
.adm-flag.lit  { border-color: rgba(var(--p-blue-rgb),.45); color: var(--p-blue); }
.adm-flag.warn { border-color: rgba(var(--warn-rgb),.45); color: var(--warn); }

/* ---------- add to home screen ----------
   Numbered steps in the same shape as the Steps setup walkthrough, on a card
   that is plated like `.pr-card` because it is an offer, not a warning. */
.install-card {
  position: relative;
  border-color: rgba(var(--accent-rgb),.32);
  background: linear-gradient(158deg, rgba(var(--accent-rgb),.10), rgba(var(--accent-rgb),0) 62%), var(--bar);
}
.install-x {
  position: absolute; top: 7px; right: 7px;
  padding: 6px 9px; border-radius: var(--r-sm);
  color: var(--dim); font-size: 13px; line-height: 1;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.install-x:active { color: var(--chalk); background: var(--raised); }
.install-steps { margin-top: 12px; }
.install-step { display: flex; gap: 11px; padding: 9px 0; border-top: 1px solid var(--collar); }
.install-steps .install-step:first-child { border-top: none; }
.install-n {
  flex: 0 0 22px; height: 22px; border-radius: var(--r-round);
  background: var(--raised); color: var(--steel);
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-variation-settings: 'wght' 700;
}

/* ---------- how it fits together ----------
   The maintenance arithmetic, printed a line at a time — operator, number,
   what the number is — so it can be checked rather than believed. The last
   line carries `.thesis-eq` and is the total. */
.thesis { margin-top: 12px; font-variant-numeric: tabular-nums; }
.thesis-line { display: flex; align-items: baseline; gap: 10px; padding: 8px 0; }
.thesis-op {
  flex: 0 0 14px; text-align: center;
  color: var(--dim); font-size: 15px;
  font-variation-settings: 'wdth' 100, 'wght' 700;
}
.thesis-line b {
  font-size: 18px; line-height: 1;
  font-variation-settings: 'wdth' 110, 'wght' 800;
  letter-spacing: -.02em;
}
/* `:not(b)` so a line written without a label doesn't shrink its own number. */
.thesis-line > :last-child:not(b) { flex: 1; min-width: 0; font-size: 12px; color: var(--steel); line-height: 1.4; }
.thesis-eq { border-top: 1px solid var(--collar); margin-top: 2px; padding-top: 12px; }
.thesis-eq .thesis-op, .thesis-eq b { color: var(--p-yellow); }

/* ================================================================
   ============ ADDED: FUEL — LOG BUTTON + AI ESTIMATOR ============
   The Fuel redesign. One floating action opens one menu; everything
   that puts food in the log lives in that menu. Nothing above this
   line was changed except .ex-del, which grew a real tap target.
   ================================================================ */

/* Room under the last card so the button never covers a number. */
#view-food .screen-pad { padding-bottom: 104px; }

/* ---------- the button ---------- */
.fuel-fab {
  position: fixed;
  left: 50%;
  bottom: calc(var(--dock-h) + env(safe-area-inset-bottom) + 14px);
  transform: translateX(-50%);
  z-index: 55;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 15px 30px 15px 25px;
  border-radius: var(--r-pill);
  /* Flat plate yellow, the same face as every other primary button. The
     gradient, the inner highlight and the yellow halo all came off — nothing
     else in Rack is lit from within, and a glowing button in a room of stamped
     metal read like it came from a different app. What is left doing the work
     is a hard drop shadow, which is depth rather than shine: it lifts the
     button off the cards without pretending to emit light. */
  background: var(--accent);
  color: var(--ink);
  font-size: 14px;
  letter-spacing: .09em;
  text-transform: uppercase;
  font-variation-settings: 'wdth' 92, 'wght' 800;
  box-shadow: var(--shadow-fab);
  animation: fabIn 340ms var(--ease) both;
  transition: transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease),
              background var(--fast) var(--ease);
}
.fuel-fab svg { width: 19px; height: 19px; }
/* Pressed, it sits down into the page instead of flashing. */
.fuel-fab:active {
  transform: translateX(-50%) scale(.955);
  background: var(--accent-press);
  box-shadow: var(--shadow-fab-pressed);
}
.fuel-fab.settled { animation: none; opacity: 1; }

@keyframes fabIn {
  from { opacity: 0; transform: translate(-50%, 16px) scale(.9); }
  to   { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

/* An empty meal is one line, not a hollow box. */
.meal-blank { padding-bottom: 12px; }
.meal-blank .card-hd { margin-bottom: 0; }
.meal-blank .meal-kcal { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint); }

/* ---------- the menu ---------- */
.add-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  margin-top: 14px;
}
.add-tile {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: flex-start; gap: 9px;
  padding: 15px 14px 14px;
  border-radius: var(--r);
  background: var(--well);
  border: 1px solid var(--collar);
  text-align: left;
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.add-tile:active { transform: scale(.97); border-color: var(--knurl); }
/* A tile for something this account is not allowed. Dimmed rather than removed:
   the menu keeps its shape, and the line under the title says why. */
.add-tile:disabled { opacity: .42; }
.add-tile:disabled:active { transform: none; border-color: var(--collar); }
.add-tile .ic {
  width: 36px; height: 36px; border-radius: var(--r-tile);
  display: grid; place-items: center;
  background: var(--raised); color: var(--chalk);
}
.add-tile .ic svg { width: 19px; height: 19px; }
.add-tile .t { font-size: 14.5px; font-variation-settings: 'wdth' 92, 'wght' 750; }
.add-tile .d { font-size: 11px; color: var(--dim); line-height: 1.38; }
.add-tile .tag {
  position: absolute; top: 11px; right: 11px;
  font-size: var(--type-tag-size, 8.5px); letter-spacing: var(--type-tag-ls, .14em); text-transform: var(--type-tag-upper, uppercase);
  color: var(--knockout); background: var(--accent);
  padding: 2px 6px; border-radius: var(--r-pill);
  font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 800);
}
/* The photo tile is the reason this menu exists, so it gets the plate. */
.add-tile.hero {
  border-color: rgba(var(--accent-rgb), .42);
  background: linear-gradient(158deg, rgba(var(--accent-rgb), .13), rgba(var(--accent-rgb), 0) 64%), var(--well);
}
.add-tile.hero .ic { background: var(--accent); color: var(--ink); }
.add-tile.lit {
  border-color: rgba(var(--accent-rgb), .22);
  background: linear-gradient(158deg, rgba(var(--accent-rgb), .06), rgba(var(--accent-rgb), 0) 64%), var(--well);
}
.add-tile.lit .ic { background: var(--grip); }
.add-tile.lit .tag { background: var(--grip); color: var(--steel); }

.ai-warn {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  margin-top: 10px; padding: 10px 12px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(var(--warn-rgb), .32);
  background: rgba(var(--warn-rgb), .07);
  color: var(--warn); font-size: 11.5px; line-height: 1.38;
}
.ai-warn svg { width: 16px; height: 16px; flex-shrink: 0; }

.add-rule { height: 1px; background: var(--collar); margin: 18px 0 0; }

.add-secondary { display: flex; gap: 8px; margin-top: 4px; }
.add-secondary .btn { flex: 1; padding: 12px 10px; }
.add-secondary .btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.add-secondary .cnt {
  font-size: 10px; color: var(--dim);
  border: 1px solid var(--collar); border-radius: var(--r-pill);
  padding: 1px 7px; margin-left: 2px;
}

/* Was an 8px margin and nothing else — a 12px glyph is not a tap target,
   and it is the only way to remove a saved food. */
.ex-del {
  margin-left: 6px; padding: 6px 8px;
  color: var(--dim); font-size: 12px; line-height: 1;
  border-radius: var(--r-sm);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.ex-del:active { color: var(--danger); background: rgba(var(--danger-rgb), .12); }

/* ---------- estimator ---------- */
.ai-shot {
  width: 100%; max-height: 230px; object-fit: cover;
  display: block; margin-top: 12px;
  border-radius: var(--r); border: 1px solid var(--collar);
  background: var(--well);
}
/* The description box is prose, not JSON — .paste-box's monospace is
   for pasted payloads. */
.ai-text {
  min-height: 76px;
  font-family: inherit; font-size: 15px; line-height: 1.45;
}

.ai-busy { display: flex; align-items: center; gap: 14px; padding: 26px 2px 18px; }
.ai-busy-t { font-size: 15px; font-variation-settings: 'wdth' 92, 'wght' 700; }
.ai-busy .note { margin-top: 2px; }
.ai-spin {
  width: 24px; height: 24px; flex-shrink: 0;
  border-radius: var(--r-round);
  border: 2.5px solid var(--track);
  border-top-color: var(--accent);
  animation: aiSpin .85s linear infinite;
}
@keyframes aiSpin { to { transform: rotate(360deg); } }

.conf {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 10px; padding: 4px 11px 4px 9px;
  border-radius: var(--r-pill); border: 1px solid var(--collar);
  font-size: var(--type-tag-size, 10.5px); letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase);
  color: var(--steel); font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
}
.conf i { width: 7px; height: 7px; border-radius: var(--r-round); flex-shrink: 0; }

/* A proposed row: tap the left to fix it, tap the ✕ to drop it. The body and
   Save stretch to the row's height, the biggest target it has, and a button
   centres its own content, so their words sit on the row's centre line. The
   number is a div, which does not: stretched, it sat level with the top of
   the name, above Save and the ✕ (Micah, 25 Sep). v58 centres it, and holds
   the ✕ to 44px as well — it was its 12px glyph and 12px of padding. */
.pe-row { align-items: stretch; }
.pe-body { flex: 1; min-width: 0; text-align: left; padding: 2px 0; }
.pe-row .fe-cal { align-self: center; }
.pe-x { align-self: center; flex-shrink: 0; min-height: 44px; }
/* v58: a long name gets two lines before it is cut, not one — "White rice,
   long-grain, enriched, coo…" said too little. overflow-wrap, so one long
   unbroken word wraps inside the body instead of widening it. */
.pe-row .fe-name {
  white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
}
/* v58: "USDA: Beef, steak, sirloin, NS as to fat eaten", under the name box
   on the fix-it sheet — whole, however long, since it is the answer to
   "which row is this?". */
.pe-desc { margin-top: 5px; overflow-wrap: anywhere; }
/* v55: "Save", on an estimate's row — that row as a meal of its own. The
   row's full height and never under 44px, in the row's quiet type. */
.pe-save {
  flex-shrink: 0; align-self: stretch; min-height: 44px; min-width: 44px;
  padding: 0 8px; border-radius: var(--r-sm);
  font-size: 11px; letter-spacing: .04em; color: var(--steel);
  font-variation-settings: 'wdth' 92, 'wght' 700;
}
.pe-save:active { color: var(--chalk); background: var(--raised); }
/* v55: "Which one?" — onboarding's full-width choice rows, one per option and
   "None of these" last, each a 44px target. The Worker writes the label, so a
   long one wraps inside its row rather than running off a 320px screen. */
.ask-opt { min-height: 44px; }
.ask-opt .ob-choice-t { overflow-wrap: anywhere; }

.ai-cost {
  font-size: var(--type-tag-size, 10px); color: var(--dim);
  letter-spacing: var(--type-tag-ls, .08em); text-transform: var(--type-tag-upper, uppercase);
  text-align: center; margin-top: 12px;
}

/* ============================================================
   FUEL — saved foods, the meal builder, the food memory
   ============================================================ */

/* Editing a saved food sits on the row next to deleting it, so the two need
   to read as different actions before they are pressed, not after. */
.ex-edit:active { color: var(--accent); background: rgba(var(--accent-rgb), .12); }

/* The count pill was scoped to the add sheet; the settings rows want it too. */
.cnt {
  font-size: 10px; color: var(--dim);
  border: 1px solid var(--collar); border-radius: var(--r-pill);
  padding: 1px 7px; margin-left: 2px;
  font-variant-numeric: tabular-nums;
}
.btn .cnt { margin-left: auto; }

/* A saved meal is a row you open, so it gets the routine treatment — with the
   delete pushed out to the end where it can't be hit by accident. */
.rt-item .ex-del { flex: 0 0 auto; margin-left: 4px; }

/* ============================================================
   TRAIN — the exercise library manager
   ============================================================ */

.ex-go { color: var(--dim); font-size: 16px; margin-left: 6px; }

/* Hidden exercises still appear in the manager — that is where you put them
   back — but they should not look available. */
.ex-off { opacity: .5; }
.ex-off .nm { text-decoration: line-through; text-decoration-color: var(--dim); }

/* ============================================================
   YOU — the read-outs drawn as pictures
   ============================================================ */

/* A headline number, the way it moved, and the shape of the last fortnight
   under it. The arrow is the number's sign and the colour is the judgement
   (see .delta above) — the two are deliberately independent, because a
   weight that fell on a cut is a down arrow painted green. */
.kpi-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.kpi {
  background: var(--well); border: 1px solid var(--collar); border-radius: var(--r-sm);
  padding: 10px 10px 8px; min-width: 0;
}
.kpi-lbl {
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim);
  font-variation-settings: 'wdth' 88, 'wght' 700;
}
.kpi-val {
  display: flex; align-items: baseline; gap: 4px; margin-top: 5px;
  font-size: 22px; line-height: 1; letter-spacing: -.01em;
  font-variation-settings: 'wdth' 108, 'wght' 800;
}
.kpi-unit {
  font-size: 10px; color: var(--dim); letter-spacing: .06em;
  font-variation-settings: 'wdth' 88, 'wght' 700;
}
.kpi .delta { margin-top: 6px; }
.kpi .chart-spark { margin-top: 8px; }
.delta-a { font-size: 11px; font-variation-settings: 'wdth' 100, 'wght' 800; }

/* sparkline */
.chart-spark { width: 100%; height: auto; }
.spark-line, .spark-line-dim {
  fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.spark-line-dim { stroke: var(--faint); }
.spark-none { stroke: var(--collar); stroke-width: 1.5; stroke-dasharray: 3 4; }

/* a goal line across a bar chart, and a bar for a day that isn't over */
.chart-target { stroke: var(--steel); stroke-width: 1; stroke-dasharray: 4 4; opacity: .8; }
.chart-bar-dim { opacity: .38; }

/* rings — one value against its goal, several side by side */
.ring-row { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px; }
.ring-cell { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; }
.ring-track { fill: none; stroke: var(--track); }
.ring-top {
  font-size: 15px; fill: var(--chalk); font-family: inherit;
  font-variant-numeric: tabular-nums;
  font-variation-settings: 'wdth' 108, 'wght' 800;
}
.ring-sub {
  font-size: 7.5px; fill: var(--dim); font-family: inherit;
  letter-spacing: .1em; text-transform: uppercase;
  font-variation-settings: 'wdth' 88, 'wght' 700;
}
.ring-lbl {
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim);
  font-variation-settings: 'wdth' 88, 'wght' 700; text-align: center;
}
.ring-of { font-size: 10px; color: var(--steel); margin-top: -3px; text-align: center; }

/* one big number with its delta beside it, above a chart */
.headline { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.headline-v {
  font-size: 34px; line-height: 1; letter-spacing: -.02em;
  font-variation-settings: 'wdth' 112, 'wght' 800;
}
.headline-u {
  font-size: 11px; color: var(--dim); letter-spacing: .08em; text-transform: uppercase;
  font-variation-settings: 'wdth' 88, 'wght' 700; margin-left: -6px;
}

/* two cards shoulder to shoulder */
.you-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.you-pair .card { padding: 12px; margin-bottom: 0; }
.you-pair .card .chart-empty { padding: 16px 6px; font-size: 11px; }
.you-pair .ring-cell { margin-top: 8px; }

/* a small heading inside a card, between two pictures */
.chart-sub {
  font-size: var(--type-tag-size, 10px); letter-spacing: var(--type-tag-ls, .12em); text-transform: var(--type-tag-upper, uppercase); color: var(--dim);
  margin: 14px 0 6px; font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700);
}
/* The third judgement, for the weight card's rate: the wrong way for the
   stated goal is a caution, not a failure — a pound up on a cut is a week to
   look at, not a red mark on the screen the app opens on. */
.delta.warn { color: var(--warn); }

/* ---------- You, second pass: the pictures got more to say ----------
   Additions only; the selectors above are untouched. */

/* The avatar is a button now — it opens the photo picker — and a photo fills
   the circle when there is one. The initials stay the fallback, so nothing
   here changes for an account that never adds a picture. */
.you-avatar { overflow: hidden; padding: 0; border: 0; cursor: pointer; transition: transform var(--fast); }
.you-avatar:active { transform: scale(.94); }
.you-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* the profile sheet's photo row */
.photo-row { display: flex; align-items: center; gap: 12px; }
.photo-pv {
  width: 56px; height: 56px; border-radius: var(--r-round); flex-shrink: 0; overflow: hidden;
  background: var(--raised); color: var(--dim);
  display: grid; place-items: center; font-size: 18px;
}
.photo-pv img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-btns { display: flex; gap: 8px; flex-wrap: wrap; }
.photo-btns .btn { padding: 8px 12px; font-size: 12px; }

/* a derived line over a measured one, and a reference line that is not the target */
.chart-line2 { fill: none; stroke-width: 1.6; stroke-dasharray: 5 4; opacity: .8;
  stroke-linecap: round; stroke-linejoin: round; }
.chart-ref { stroke: var(--chalk); stroke-width: 1; stroke-dasharray: 1.5 3; opacity: .55; }

/* a one-line legend under a chart, and the swatch shapes for a dashed line
   and a dot, so the legend looks like the thing it names */
.legend-inline { flex-direction: row; flex-wrap: wrap; gap: 6px 14px; margin-top: 8px; }
.legend-inline .legend-item i.sw-dash { width: 14px; height: 0; border-top: 2px dashed currentColor; background: none !important; color: var(--chalk); border-radius: 0; }
.legend-inline .legend-item i.sw-dot { width: 7px; height: 7px; border-radius: var(--r-round); opacity: .7; }
/* the same legend, two columns, under a full-width bar */
.legend-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; margin-top: 10px; }

/* the week's macros against their targets, one row each with the share at the end */
.macro-rows { margin-top: 14px; }
.macro-rows .vol-val { width: 74px; }
.macro-pct {
  width: 38px; text-align: right; font-size: 11px; color: var(--dim);
  font-variation-settings: 'wdth' 100, 'wght' 700;
}
.macro-pct.good { color: var(--good); }

/* the month's working sets as one bar split by muscle */
.split-bar { display: flex; gap: 2px; height: 14px; border-radius: var(--r-mark); overflow: hidden; }
.split-seg { min-width: 3px; }

/* strongest lifts: each carries a bar scaled to the top one */
.pb-body { flex: 1; min-width: 0; }
.pb-track { height: 4px; border-radius: var(--r-plate); background: var(--track); margin-top: 7px; overflow: hidden; }
.pb-fill { height: 100%; border-radius: var(--r-plate); }

/* two small numbers under the steps and water bars */
.mini-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; }
.mini-stat { background: var(--well); border: 1px solid var(--collar); border-radius: var(--r-sm); padding: 7px 8px; min-width: 0; }
.mini-stat-v { font-size: 14px; line-height: 1; font-variation-settings: 'wdth' 96, 'wght' 800;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-stat-l { font-size: var(--type-tag-size, 8.5px); letter-spacing: var(--type-tag-ls, .1em); text-transform: var(--type-tag-upper, uppercase); color: var(--dim); margin-top: 4px;
  font-variation-settings: 'wdth' var(--type-tag-wdth, 88), 'wght' var(--type-tag-wght, 700); }

/* ---------- Fuel's calorie bar: a window, not a ruler from zero ----------
   The runway is the compressed stretch below the window, the dashed mark is
   the day's target, and the target's word sits on its own line under the
   band words so the two never collide. */
.cal-runway {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: repeating-linear-gradient(135deg, transparent 0 3px, var(--tint-runway) 3px 5px);
  border-right: 1px solid var(--tint-runway-edge);
}
.cal-target {
  position: absolute; top: -2px; bottom: -2px; width: 0;
  margin-left: -1px; border-left: 2px dashed var(--cal-mark);
  box-shadow: var(--shadow-cal-target);
  opacity: .9;
}
.cal-bands { height: 25px; }
.cal-target-lab {
  top: 13px; width: 60px; margin-left: -30px;
  color: var(--steel); overflow: visible;
}

/* ---------- "Against last week", the tiles the app opens on ----------
   Each tile carries a faint tint of its subject colour in the top corner,
   the delta as a pill, last week's figure in small print, and the seven
   days of this week as dots under the picture. */
.kpi {
  --kpi-rgb: var(--steel-rgb);
  position: relative; overflow: hidden;
  background: radial-gradient(130px 90px at 100% 0%, rgba(var(--kpi-rgb), .14), transparent 72%), var(--well);
  padding: 10px 10px 9px;
}
.kpi-hd { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-height: 18px; }
.kpi-hd .delta { margin-top: 0; }
.delta-pill {
  padding: 2px 7px 2px 5px; border-radius: var(--r-pill); gap: 3px;
  background: rgba(var(--lift-rgb),.05);
}
.delta-pill.up   { background: rgba(var(--good-rgb),.16); }
.delta-pill.down { background: rgba(var(--bad-rgb),.16); }
.delta-pill.warn { background: rgba(var(--warn-rgb),.16); }
/* The pill's figure and arrow are type.pill's web site (engine v3). Its web
   side is null, like the other type roles: v1 and a vibe without one keep
   these literals, and a vibe that gives one sets both in its own sheet. */
.delta-pill .delta-v { font-size: 11.5px; }
.delta-pill .delta-a { font-size: 10px; }
.kpi-prev { font-size: 10.5px; color: var(--dim); margin-top: 4px; letter-spacing: .02em; }
.kpi .chart-spark { margin-top: 7px; }
.kpi-days { display: flex; justify-content: space-between; margin-top: 7px; padding: 0 2px; }
.kpi-days i {
  width: 7px; height: 7px; border-radius: var(--r-round);
  background: transparent; box-shadow: var(--shadow-kpi-day);
}
.kpi-days i.on { box-shadow: var(--shadow-kpi-day-on); }
.kpi-days i.today { box-shadow: var(--shadow-kpi-today); }
.kpi-days i.today.on { box-shadow: var(--shadow-kpi-today-on); }

/* the sparkline's target line, end glow, and bars */
.spark-ref { stroke: var(--steel); stroke-width: 1; stroke-dasharray: 3 3; opacity: .7; vector-effect: non-scaling-stroke; }
.spark-glow { opacity: .22; }
.spark-bar { transition: opacity var(--fast); }

/* ---------- the Fuel bar's key ----------
   The dots sit in the card's corner; each row of the sheet carries a swatch
   drawn the way the thing it names is drawn on the bar. */
.fuel-sum { position: relative; }
.fuel-more { position: absolute; top: 8px; right: 10px; z-index: 1; }
.guide { margin-top: 6px; }
.guide-row { display: flex; align-items: flex-start; gap: 12px; padding: 11px 0; border-top: 1px solid var(--collar); }
.guide-row:first-child { border-top: none; }
.guide-sw {
  flex: none; width: 30px; height: 14px; border-radius: var(--r-chip); margin-top: 2px;
  background: var(--track); position: relative;
}
.guide-sw.big    { background: var(--p-blue); }
.guide-sw.eaten  { background: transparent; box-shadow: var(--shadow-guide-eaten); }
.guide-sw.cut    { background: rgba(var(--p-blue-rgb),.45); }
.guide-sw.hold   { background: rgba(var(--p-yellow-rgb),.45); }
.guide-sw.gain   { background: rgba(var(--p-red-rgb),.45); }
.guide-sw.head::after, .guide-sw.tick::after, .guide-sw.target::after {
  content: ''; position: absolute; top: -2px; bottom: -2px; left: 50%;
}
.guide-sw.head::after   { width: 3px; margin-left: -1.5px; border-radius: var(--r-plate); background: var(--cal-mark); box-shadow: var(--shadow-cal-head); }
.guide-sw.tick::after   { width: 2px; margin-left: -1px; background: var(--cal-mark); opacity: .85; }
.guide-sw.target::after { width: 0; margin-left: -1px; border-left: 2px dashed var(--cal-mark); box-shadow: var(--shadow-cal-target); }
.guide-sw.runway { background: repeating-linear-gradient(135deg, var(--grip) 0 3px, var(--track) 3px 5px); }
.guide-sw.status { background: transparent; }
.guide-sw.status::before {
  content: ''; position: absolute; left: 3px; top: 3px; width: 8px; height: 8px; border-radius: var(--r-round);
  background: var(--p-blue);
}
.guide-sw.status::after {
  content: ''; position: absolute; left: 15px; right: 0; top: 6px; height: 2px; border-radius: var(--r-hair); background: var(--steel);
}
.guide-sw.macro { background: linear-gradient(90deg, var(--p-red) 0 34%, var(--p-yellow) 34% 67%, var(--p-blue) 67%); }
.guide-body { flex: 1; min-width: 0; }
.guide-t { font-size: 13px; font-variation-settings: 'wdth' 92, 'wght' 700; }
.guide-d { font-size: 12px; color: var(--steel); line-height: 1.45; margin-top: 3px; }

/* ---------- You, third pass: verdicts, goal, review ----------
   The cards that say something rather than draw something. */
.card-right { display: flex; align-items: center; gap: 6px; }
.card-why { padding: 0 2px; line-height: 1; font-size: 17px; }

/* a finding: subject mark, title, detail — tappable for its rule */
.find-list { display: flex; flex-direction: column; }
.find-row, .review-row {
  display: flex; align-items: flex-start; gap: 11px; width: 100%;
  padding: 10px 0; border-top: 1px solid var(--collar); text-align: left;
}
.find-row:first-child, .review-row:first-child { border-top: none; padding-top: 4px; }
.find-row:active { opacity: .7; }
.find-mark { flex: none; width: 8px; height: 8px; border-radius: var(--r-round); margin-top: 6px; }
.find-body { flex: 1; min-width: 0; }
.find-t { font-size: 13.5px; line-height: 1.35; font-variation-settings: 'wdth' 94, 'wght' 700; }
.find-d { font-size: 12px; color: var(--steel); line-height: 1.45; margin-top: 2px; }
.find-row.good .find-t { color: var(--chalk); }
.find-row.warn .find-t { color: var(--chalk); }
.card-wins { border-left: 3px solid var(--good); }
.card-improve { border-left: 3px solid var(--warn); }

/* goal trajectory */
.traj-dot { width: 10px; height: 10px; border-radius: var(--r-round); background: var(--grip); align-self: center; }
.traj-dot.good { background: var(--good); box-shadow: var(--shadow-traj-good); }
.traj-dot.warn { background: var(--warn); box-shadow: var(--shadow-traj-warn); }
.traj-dot.bad  { background: var(--bad);  box-shadow: var(--shadow-traj-bad); }
.traj-reason { font-size: 12.5px; color: var(--steel); margin-top: 6px; line-height: 1.45; }
.traj-bar { height: 8px; border-radius: var(--r-mark); background: var(--track); overflow: hidden; margin-top: 14px; }
.traj-fill { height: 100%; border-radius: var(--r-mark); transition: width 500ms var(--ease); }
.traj-ends { display: flex; justify-content: space-between; gap: 8px; margin-top: 6px; font-size: 10px; color: var(--dim);
  letter-spacing: .04em; }

/* weekly review */
.review-verdict {
  font-size: 24px; line-height: 1.1; letter-spacing: -.01em;
  font-variation-settings: 'wdth' 104, 'wght' 800; margin: 2px 0 4px;
}
.review-list { display: flex; flex-direction: column; }
.review-row.good .find-t::before, .review-row.warn .find-t::before { content: ''; }
.review-take { margin-top: 14px; padding: 12px; border-radius: var(--r-sm);
  background: rgba(var(--accent-rgb),.07); border: 1px solid rgba(var(--accent-rgb),.18); }
.review-take .chart-sub { margin: 0 0 6px; color: var(--accent); }
.review-take-t { font-size: 13.5px; line-height: 1.45; }
.review-take + .chart-sub { margin-top: 16px; }
.card .heat { margin-bottom: 10px; }

/* ---------- the greeting ----------
   Two lines, large: the app says hello before it says anything else. The
   name line ellipses on its own so a long name never pushes the gear off
   the edge; the greeting line never needs to. */
.you-hero { align-items: center; }
.you-greet {
  display: flex; flex-direction: column;
  font-size: 27px; line-height: 1.05; letter-spacing: -.02em;
  font-variation-settings: 'wdth' 100, 'wght' 800;
}
.you-greet-name { color: var(--greet-name); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.you-hero .you-sub { margin-top: 6px; }

/* Hero alignment with the two-line greeting: avatar and gear sit level
   with the greeting rather than centred on the whole three-line block,
   which read as the avatar having slipped. */
.you-hero { align-items: start; }
.you-avatar { margin-top: 3px; }
.you-gear { margin-top: 11px; }

/* ---------- Coach ----------
   A card that says one true thing about your own log, and the sheet behind it.

   THE HEIGHT IS FIXED AND IT IS LOAD-BEARING, not a style preference. What the
   card says changes every day — a one-line finding today, a two-line one with a
   two-line reason tomorrow — and on Train it sits directly above Start workout.
   A box that grew by a line would move the app's primary button under somebody's
   thumb between one morning and the next. So everything inside clamps, nothing
   wraps past its allowance, and the box cannot resize. box-sizing is explicit
   here rather than inherited from the reset, because that is the line the whole
   guarantee rests on. */
.coach-card {
  display: flex; flex-direction: column;
  width: 100%; height: 190px; box-sizing: border-box;
  overflow: hidden;
  text-align: left; color: inherit;
  background: var(--bar);
  border: 1px solid var(--collar);
  border-radius: var(--r);
  padding: 14px;
  margin-top: 14px;
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.coach-card:active { transform: scale(.985); border-color: var(--knurl); }
/* Train's form: the greeting goes, because what belongs above Start workout is
   the answer and nothing else. Exactly one line's worth shorter. */
.coach-card.tight { height: 164px; margin-bottom: 12px; }
.coach-card.tight .coach-greet { display: none; }
.coach-card.loading .coach-greet { color: var(--dim); }

/* margin-bottom: auto is the whole of the short-finding fix. Flexbox splits
   positive free space equally between every auto margin on the main axis, so
   this one and .coach-go's put half the slack above the greeting and half above
   the row — a one-line finding with a one-line reason sits centred in the box
   instead of stranding 44 measured pixels of void over a pinned tap target,
   which read as a render that had failed. When the card is full there are about
   five pixels going spare, both margins resolve to almost nothing, and the tall
   states lay out exactly as they did. The height never enters into it: the box
   still cannot resize, which is the guarantee the card exists for. */
.coach-hd { display: flex; align-items: center; gap: 7px; margin-bottom: auto; }
.coach-mark { display: flex; color: var(--accent); }
.coach-mark svg { width: 15px; height: 15px; display: block; }
.coach-ttl {
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent);
  font-variation-settings: 'wdth' 88, 'wght' 800;
}
/* Yellow and shut without Pro, grey and open with it. The shackle is the only
   thing that moves between the two, which is what makes them readable at 14px. */
.coach-lock { margin-left: auto; display: flex; color: var(--accent); }
.coach-lock.open { color: var(--steel); }
.coach-lock svg { width: 14px; height: 14px; display: block; }

.coach-greet {
  margin-top: 9px;
  font-size: 15px; line-height: 1.2;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.coach-line {
  margin-top: 7px;
  font-size: 14px; line-height: 1.35;
  font-variation-settings: 'wdth' 96, 'wght' 600;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
.coach-line.caution { color: var(--warn); }
.coach-why {
  margin-top: 5px;
  font-size: 12px; line-height: 1.4; color: var(--steel);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
/* margin-top: auto pins this to the floor of the box however little is above
   it, so a one-line finding does not leave the tap target floating mid-card. It
   is also one of the card's two auto margins: it and .coach-hd's split the
   slack between them, which is what centres a short finding rather than
   dropping all of the space into one lump above this row. */
.coach-go {
  margin-top: auto; padding-top: 10px;
  display: flex; align-items: baseline; gap: 8px;
  border-top: 1px solid var(--collar);
}
.coach-go-t {
  flex: none;
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--accent);
  font-variation-settings: 'wdth' 88, 'wght' 800;
}
.coach-go-q {
  flex: 1; min-width: 0;
  font-size: 12px; color: var(--steel);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.coach-go-x { flex: none; color: var(--accent); font-size: 16px; line-height: 1; }

/* ---------- the COACH ME sheet ----------
   Taller than Log food, because it is a conversation rather than a form. */
.sheet.coach-sheet { max-height: calc(92dvh - var(--safe-top)); }
.coach-thread { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.coach-bub {
  align-self: flex-start; max-width: 90%;
  padding: 11px 13px;
  border-radius: var(--r-bubble) var(--r-bubble) var(--r-bubble) var(--r-mark);
  background: var(--raised);
}
.coach-bub.ask { border-left: 3px solid var(--accent); }
.coach-bub.you {
  align-self: flex-end;
  border-radius: var(--r-bubble) var(--r-bubble) var(--r-mark) var(--r-bubble);
  background: rgba(var(--accent-rgb),.12);
  border: 1px solid rgba(var(--accent-rgb),.26);
}
.coach-bub-t { font-size: 14px; line-height: 1.42; }
.coach-bub.you .coach-bub-t {
  color: var(--accent);
  font-variation-settings: 'wdth' 94, 'wght' 700;
}
.coach-bub-r { font-size: 12px; line-height: 1.45; color: var(--steel); margin-top: 6px; }
.coach-bub .coach-chips { margin-top: 10px; }
/* The Pro panel. It IS a bubble — same box, same voice — because what it says
   belongs in the conversation rather than beside it; the tint is the one the
   lock and the account pill already use. The rows inside are the only new
   thing, and they are a list rather than a paragraph because the engine hands
   them over as a list. */
.coach-bub.pro {
  background: rgba(var(--accent-rgb),.10);
  border: 1px solid rgba(var(--accent-rgb),.24);
}
.coach-adds { display: flex; flex-direction: column; gap: 9px; margin: 11px 0; }
.coach-add-t {
  font-size: 12px; letter-spacing: .04em;
  color: var(--chalk); font-variation-settings: 'wdth' 94, 'wght' 700;
}
.coach-add-n { font-size: 12px; line-height: 1.4; color: var(--steel); margin-top: 1px; }
.coach-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.coach-chip {
  padding: 9px 13px; border-radius: var(--r-pill);
  border: 1px solid var(--collar); background: transparent; color: var(--chalk);
  font-size: 13px; font-variation-settings: 'wdth' 94, 'wght' 600;
  transition: transform var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.coach-chip:active { transform: scale(.96); border-color: var(--accent); }
/* v54: "How was it?" in the live sheet — the set's line and three effort
   chips, each a 44px target (the sheet's other chips stay smaller), the chosen
   one reading as chosen the way the check-in's does; then Coach's answer and
   "Use it for my next set". Laid out as the thread is, and taking no gap in it
   while empty. */
.coach-rate, .coach-rate-after { display: flex; flex-direction: column; gap: 10px; }
.coach-rate:empty, .coach-rate-after:empty { display: none; }
.coach-rate-q { font-size: 14px; color: var(--chalk); }
.coach-effort { min-height: 44px; }
.coach-effort.on { border-color: var(--accent); color: var(--accent); }

/* ---------- the workout builder's proposal ----------
   Drawn with the recap's own list (.day-ex) and the workout screen's block
   label (.wk-block-title) rather than a new component, because it is the same
   thing turned forward: what he did, about to be done again. What is new is
   the dim line of what the log shows under an exercise, the two notes, and
   the column the four buttons stand in. */
.coach-build { display: flex; flex-direction: column; gap: 6px; padding: 2px 1px 0; }
/* flex: none because the label brings `flex: 1` from the workout screen's
   header row, where it fills the width; in this column it would be height. */
.coach-build .wk-block-title { flex: none; margin-top: 6px; }
.coach-build-note { font-size: 12px; line-height: 1.45; color: var(--steel); }
.coach-build-note.mine { color: var(--chalk); font-variation-settings: 'wdth' 94, 'wght' 600; }
.coach-build-w { font-size: 11px; line-height: 1.4; color: var(--dim); margin-top: 2px; }
/* The target for next time (v48): the note's size, in the body colour rather
   than --dim, because it is the line to act on — and never yellow, which is for
   buttons. A button, so a tap opens its evidence, with the UA's padding and
   centring taken off so it reads as a line of the list. */
.coach-build-target {
  display: block; width: 100%; padding: 0; margin-top: 2px; text-align: left;
  font-size: 11px; line-height: 1.4; color: var(--chalk);
}

/* ---------- Coach in a live session ----------
   The chip sits in the session's header row beside the calendar button, at
   that button's height and in its border, so the row keeps its rhythm; the
   mark and the word are the card's own. */
.wk-coach {
  flex-shrink: 0;
  height: 38px; padding: 0 11px; margin: 0 8px;
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--collar);
  border-radius: var(--r-sm);
}
.wk-coach .coach-mark svg { width: 14px; height: 14px; }
.wk-coach:active { border-color: var(--accent); }
/* The one quiet line under a finished exercise. It takes the swipe hint's
   slot and keeps its box — the same padding and the same 10px type, clipped
   to one line rather than wrapping — so appearing and going moves no row. */
.swipe-hint.coach-nudge { display: flex; align-items: center; gap: 8px; opacity: 1; }
.coach-nudge-t {
  flex: 1; min-width: 0; padding: 0; text-align: left;
  font-size: inherit; letter-spacing: inherit; line-height: inherit;
  color: var(--accent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* A thumb-sized target round a 10px glyph without a taller line: the padding
   is handed back as negative margin, so the row sees the glyph's box. Both
   buttons take the hint's own size and line — body's line-height is unitless,
   so a bigger glyph here would inherit a taller line and move the row. */
.coach-nudge-x {
  flex: none; font-size: inherit; line-height: inherit; color: var(--dim);
  padding: 10px 12px; margin: -10px -12px -10px 0;
}
.coach-build-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.coach-build .coach-chips { margin-top: 4px; }

/* ---------- the Coach switches ----------
   The app's first real switch. Everything binary before this was segmented(),
   and seven segmented pairs stacked in the settings sheet is a wall rather than
   a screen. ON is the ABSENCE of a stored key, so a fresh account sees every
   one of these on without a byte having been written anywhere. */
.set-row-tog {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 2px; border-top: 1px solid var(--collar);
}
.set-list .set-row-tog:first-child { border-top: none; }
.set-row-tog .set-row-l { display: flex; flex-direction: column; gap: 2px; }
.set-row-sub { font-size: 11px; color: var(--dim); line-height: 1.4;
  font-variation-settings: 'wdth' 92, 'wght' 400; }
.tog {
  flex: none; position: relative;
  width: 44px; height: 26px; border-radius: var(--r-pill);
  background: var(--grip);
  transition: background var(--fast) var(--ease);
}
.tog::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: var(--r-round);
  background: var(--knob);
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease);
}
.tog.on { background: rgba(var(--accent-rgb),.28); }
.tog.on::after { transform: translateX(18px); background: var(--accent); }

/* ---------- Your goal (v55) ----------
   Settings → Coach → Your goal, and the sheet "Yes, update my goal" opens.
   Every answer is one kind of chip, 44px tall and in one type: the aims two to
   a row, a question of three answers or fewer on one line with each chip as
   wide as its label, the focus groups wrapping whole. No label wraps — each
   fits its chip on one line at 320px wide, which tools-check/touch-target.mjs
   measures in Archivo — and the chosen one reads as chosen the way
   onboarding's choice rows do. */
.coach-goal-opts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.coach-goal-opts.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.coach-goal-opts.line { flex-wrap: nowrap; }
.coach-goal-opt {
  min-height: 44px; padding: 8px 14px;
  border: 1px solid var(--collar); border-radius: var(--r-sm);
  background: var(--well); color: var(--steel);
  font-size: 12px; line-height: 1.25; white-space: nowrap;
  font-variation-settings: 'wdth' 92, 'wght' 600;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.coach-goal-opts.grid .coach-goal-opt, .coach-goal-opts.line .coach-goal-opt { padding: 8px 6px; }
.coach-goal-opts.line .coach-goal-opt { flex: 1 1 auto; }
.coach-goal-opt.on { border-color: var(--accent); background: var(--raised); color: var(--chalk); }

/* Two buttons sharing a row need to be allowed to shrink, or a long label
   pushes its neighbour off the edge instead of ellipsing inside its half.
   .add-row has always carried this; .btn-split did not, and Train's new
   Routines / Exercises pair is the first place that showed. */
.btn-split > .btn { min-width: 0; }

/* ---------- Look → Vibes (V59 §8.3) ----------
   Settings → Look → Vibes: a tall sheet of tiles, one per vibe, each drawn in
   its own vibe. vibes-sheet.js writes a vibe's tokens onto its tile's
   .vibe-in, so every var() from there down is that vibe's whichever one is
   worn; the sheet, the ring and Close spend the vibe worn now. The words
   inherit the sheet's own face; only the number is set in the tile's
   (var(--font) under .vibe-in). Sizes are research track 10 §4.2–4.4. */
.sheet.vibes-sheet { max-height: calc(92dvh - var(--safe-top)); }
/* 5px all round is the ring's well: nothing clips it and nothing moves when it
   moves. 9 + 5 = the house 14 under the title; tiles 12 apart. */
.vibe-list { display: flex; flex-direction: column; gap: 12px; margin-top: 9px; padding: 5px; }
.vibe-tile { display: block; width: 100%; padding: 0; text-align: left; border-radius: 14px; }
/* The vibe worn: a 2px ring 3px clear of its tile, in the sheet's ink on the
   sheet's ground — the worn vibe's own contrast, whatever the tile's ground —
   and the check inside. A shape, never a colour alone. */
.vibe-tile[aria-current="true"] { box-shadow: 0 0 0 3px var(--bar), 0 0 0 5px var(--chalk); }
.vibe-tile:focus-visible { outline-offset: 7px; }
.vibe-in {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  min-height: 112px; padding: 12px;
  border-radius: 14px;
  background: var(--rack); color: var(--chalk);
  transition: transform var(--fast) var(--ease);
}
.vibe-tile:active .vibe-in { transform: scale(.98); }
/* The sample card: the vibe's card colours and corner (at most 12), a fixed
   swatch that does not grow with the words beside it. */
.vibe-sample {
  position: relative; flex: none;
  width: 112px; height: 88px; overflow: hidden;
  background: var(--bar); border: 1px solid var(--collar);
  border-radius: min(var(--r), 12px);
}
.vibe-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Caps 24px tall in Archivo 800 (35px), sitting 34→58 from the card's top.
   Set in the vibe's numeral face (--font-num, v1's is --font's). */
.vibe-num {
  position: absolute; left: 10px; top: 29px;
  font-family: var(--font-num); font-size: 35px; font-weight: 800;
  font-variation-settings: 'wdth' 118, 'wght' 800;
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
  letter-spacing: -.02em; line-height: .95; white-space: nowrap;
  color: var(--chalk);
}
/* Not drawn until its own face has loaded; the card does not move when it is. */
.vibe-num.wait { visibility: hidden; }
.vibe-accent {
  position: absolute; left: 10px; bottom: 15px;
  width: 44px; height: 4px; border-radius: var(--r-plate);
  background: var(--accent);
}
.vibe-words { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.vibe-name {
  padding-right: 28px; font-size: 16px; line-height: 1.2;
  font-variation-settings: 'wdth' 100, 'wght' 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vibe-feel {
  margin-top: 4px; font-size: 13px; line-height: 1.35; color: var(--steel);
  font-variation-settings: 'wdth' 100, 'wght' 400;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.vibe-exp { margin-top: 6px; font-size: 12px; line-height: 1.3; font-variation-settings: 'wdth' 100, 'wght' 600; }
.vibe-check {
  position: absolute; top: 12px; right: 12px; width: 20px; height: 20px;
  fill: none; stroke: var(--chalk); stroke-width: 2.25; stroke-linecap: round; stroke-linejoin: round;
}
.vibes-close { margin-top: 11px; }
