/* Tempo — built to be read from a few feet away, sweaty, mid-set.
   Oversized numerals, and one accent doing all the signaling.

   The voice is the public page's, since 7 September 2026: its type ladder
   (--fs-0..7), one label register, display in caps at a tight leading, and
   sections separated by a rule on the ground rather than boxed. The surface
   is spent on three things — the exercise you are logging, the inputs, and a
   sheet — and asides take a left rule. Controls keep their own ground.
   What is deliberately NOT carried over: card titles stay sentence case (a
   quarter of them are whole sentences), the set inputs and the clocks stay
   monospaced (read at arm's length), and nothing is numbered.

   The palette now comes from design/tokens.css, which index.html loads first.
   What remains here is a compatibility layer: this stylesheet names colors
   `--accent`, `--clay` and so on in a few hundred places, and renaming them
   all would be a large diff with no behavior change. Mapping them onto the
   new tokens re-colors the whole app in one edit, and each rule can be
   migrated properly as its screen is rebuilt. */

:root {
  /* Names this stylesheet uses -> tokens the design system defines. */

  /* --line-soft used to be an alias for --bg-2, which made one value serve as
     both a page ground and the 1px border of every card, exercise row, entry
     and picker item — twenty-nine borders. On the dark palettes that works by
     luck: a ground one step darker than the surface reads as a shadow line. On
     a light ground it cannot, and there is no single value that is both a
     usable rail ground and a visible card edge. It is its own token now, set
     per palette, and each palette below gives it the value it used to compute
     to. */

  /* `--accent` carries the brand lime. */
  --accent:        var(--lime);
  --accent-dim:    var(--lime-dim);
  --accent-wash:   var(--lime-wash);
  --green-dim:     var(--lime-dim);

  /* Semantic colors were the accent before; they are their own now, which is
     what lets a Red screening finding stop reading as brand green. */
  --clay:          var(--red);
  --clay-dim:      color-mix(in srgb, var(--red) 45%, transparent);

  --brand-lime:    var(--lime);
  --brand-olive:   var(--lime-dim);
  --brand-brown:   var(--surface-2);

  --tab-h: 62px;
}

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

html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}
body { min-height: 100dvh; overscroll-behavior-y: none; }
.hidden { display: none !important; }

.app {
  max-width: 520px; margin: 0 auto;
  min-height: 100dvh; display: flex; flex-direction: column;
}

/* ============================= Topbar ============================= */
.topbar {
  position: sticky; top: 0; z-index: 30;
  /* Derived from the palette, not baked in. These three surfaces are the
     app's chrome — header, Ask button, Ask panel — and a hardcoded near-black
     left all of them dark when the light palette was switched on. The plain
     `var(--bg)` is the fallback for anything without `color-mix`; it loses the
     translucency and nothing else. */
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  padding-top: env(safe-area-inset-top);
}
.topbar-inner {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px 10px; gap: 12px;
}
.brand-name {
  font-family: var(--f-display);
  font-size: 21px; font-weight: 800;
  letter-spacing: -0.03em;
}
.topbar-right { display: flex; align-items: center; gap: 10px; }

.icon-btn {
  width: 38px; height: 38px; flex-shrink: 0;
  display: grid; place-items: center;
  background: transparent; border: 1px solid var(--line-strong);
  border-radius: var(--r-pill); color: var(--text-2); cursor: pointer;
}
.icon-btn svg { width: 19px; height: 19px; }
.icon-btn:active { background: var(--surface-3); color: var(--text); }
.icon-btn.light { background: var(--surface-3); border-color: var(--line-strong); color: var(--text); }

.profile-switch {
  display: flex; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-pill); padding: 3px; gap: 2px;
}
.profile-btn {
  border: none; background: none; color: var(--text-3);
  font-family: var(--f-body); font-size: 13px; font-weight: 600;
  padding: 7px 14px; border-radius: var(--r-pill); cursor: pointer;
  transition: background .15s, color .15s;
}
.profile-btn.active { background: var(--accent); color: var(--ink); }

.block-strip {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 0 16px 11px;
}
.pill {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  padding: 4px 10px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
}
/* On the strip it is the screen's eyebrow, not a badge. */
.block-strip .pill { background: none; border: 0; padding: 0; color: var(--text-3); }
.block-strip .pill.accent { background: none; border: 0; color: var(--accent); }
.pill.accent { background: var(--lime-wash); border-color: var(--lime-dim); color: var(--lime); }
.pill.done   { background: var(--red-wash); border-color: var(--red); color: var(--red); }
.strip-note { font-size: 13px; color: var(--text-3); }

/* ============================= Layout ============================= */
/* The outer frame, from the --s1..--s8 ladder tokens.css has been shipping all
   along and this file used exactly zero times. Only the frame: the page gutter,
   the gap between cards and the padding inside one. Chasing all 350 literal
   pixel values through the sheet would be a rewrite dressed up as tidying, and
   most of those numbers are fine. */
.screen {
  flex: 1; padding: var(--s5) var(--s4) calc(var(--tab-h) + var(--s7));
  display: flex; flex-direction: column; gap: var(--s5);
}
.screen-head { padding: 2px 2px 0; }
/* Caps, but only here. The ten titles this reaches are one- and two-word
   screen names — Program, Progress, Schedule, Clients, Settings — which is the
   one shape caps flatter rather than shout in.

   The same treatment was proposed for .card-title and is deliberately NOT
   taken: a quarter of the 59 card titles are whole sentences ("Spend your
   first session moving, not talking", "Too close to a round"), and uppercasing
   a sentence destroys word-shape, which is the opposite of hierarchy. The
   proposed accent eyebrow is dropped too — every screen title already IS the
   screen name, so an eyebrow naming the screen prints the same word twice, and
   the app already ships three eyebrow recipes. */
.screen-title {
  font-family: var(--f-display); font-size: var(--fs-6); font-weight: 800;
  letter-spacing: -0.01em; line-height: 1.06; margin: 0;
  text-transform: uppercase; text-wrap: balance;
}

/* A person's name, in the case they wrote it. #clientName is a .screen-title
   only because it sits where a screen title sits — app.js fills it from
   openClient.name — so the caps rule above must not reach it. Three real
   people use this app; this is not hypothetical. */
#clientName {
  text-transform: none;
  letter-spacing: -0.02em;
}
.screen-sub { font-size: 15px; color: var(--text-2); margin: 8px 0 0; line-height: 1.5; max-width: 46ch; }

.stack { display: flex; flex-direction: column; gap: var(--s4); }
.stack.tight { gap: var(--s2); }

/* A section, not a box. A rule above, the ground behind, and the content
   running to the screen's own gutter. A bordered card around every group
   flattened the hierarchy — everything was equally an object — and on paper
   it read as a form nobody had filled in. What still gets a surface is what
   you type into (.exercise, inputs) and what floats (a sheet, a toast). */
.card {
  background: none;
  border: 0; border-top: 1px solid var(--line);
  border-radius: 0;
  padding: var(--s5) 0 var(--s3);
}
.card-title {
  font-family: var(--f-display); font-size: var(--fs-3); font-weight: 700;
  letter-spacing: -0.01em; line-height: 1.2; margin: 0 0 12px;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 12px;
}
.card-head .card-title { margin: 0; }
.muted { font-size: var(--fs-1); color: var(--text-2); line-height: 1.55; margin: 0 0 12px; }
.hint { font-size: 13px; color: var(--text-3); line-height: 1.5; margin: 8px 0 0; }
.divider { height: 1px; background: var(--line-soft); margin: 20px 0 16px; }

/* ============================= Day header ============================= */
/* The session is the hero: eyebrow, display title, a rule. It was a gradient
   banner with a glow in the corner, which is a box saying "look at me" around
   the one thing on the screen that did not need one. */
.day-header {
  padding: var(--s2) 0 var(--s5);
  border-bottom: 1px solid var(--line);
}
.day-eyebrow {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--accent); margin-bottom: var(--s3);
}
.day-title {
  font-family: var(--f-display); font-size: var(--fs-7); font-weight: 800;
  letter-spacing: -0.02em; line-height: 1.02; margin: 0;
  text-transform: uppercase; text-wrap: balance;
}
.day-focus { font-size: 15px; color: var(--text-2); margin: var(--s3) 0 0; line-height: 1.5; max-width: 46ch; }
.override-note {
  margin-top: 12px; padding: 9px 11px;
  background: var(--lime-wash); border: 1px solid var(--lime-dim);
  border-radius: var(--r-sm); font-size: 12.5px; color: var(--accent); line-height: 1.4;
}

/* ============================= Forms ============================= */
form { display: flex; flex-direction: column; gap: 14px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field-row { display: flex; gap: 10px; }
.field-row .field { flex: 1; }
label {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3);
}
/* The surface, and the 3:1 edge — a control whose border is its only shape.
   The tint they sat on was the page's second ground, and the public page has
   exactly one. */
input, select, textarea {
  font-family: var(--f-body); font-size: 16px;
  padding: 12px 13px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--text);
  width: 100%;
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
textarea { resize: vertical; font-family: var(--f-body); }
select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23A8B2A6' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 17px;
  padding-right: 34px;
}
/* The open list, which inherits none of the above.
   Every `select` here is styled light-text-on-dark, but the popup is drawn by
   the browser and takes only what it is given explicitly: the pale text was
   landing on the platform's white list, which is why the options could not be
   read until one was highlighted and the highlight forced the contrast.
   `background-color` rather than the `background` shorthand — the shorthand is
   ignored on the option element in several browsers. */
option, optgroup {
  background-color: var(--surface);
  color: var(--text);
}
option:disabled { color: var(--text-3); }
input[type="date"] { min-height: 46px; }

/* The public page's pill. Same height as before, so every tap target the
   browser tests measured still measures. */
.btn {
  font-family: var(--f-body); font-size: var(--fs-2); font-weight: 700;
  padding: 14px 22px; border-radius: var(--r-pill); border: 1px solid transparent;
  cursor: pointer; width: 100%; transition: transform .1s, background .15s;
  line-height: 1.2;
}
.btn:active { transform: scale(0.985); }
/* `--ink` rather than a hardcoded near-black: it is whatever reads on the
   accent in the current palette, and in daylight that is white. Thirteen rules
   had the dark value baked in, which put 3.57:1 text on every primary button
   the moment the light palette was switched on. */
.btn-primary { background: var(--accent); color: var(--ink); }
.btn-ghost {
  background: transparent; border-color: var(--line-strong); color: var(--text);
  margin-top: 4px;
}
.btn-ghost.danger { color: var(--clay); border-color: var(--clay-dim); }
.btn-row { display: flex; gap: 7px; }
.field-wrap[hidden] { display: none; }

/* Four numbers that decide what somebody is asked to lift in their first
   session, and the ones most likely to be left blank. Chips first because most
   people recognize their number faster than they can type it; a box under them
   for everybody else; and "Don't know" as a real answer, because an empty box
   and an abandoned form look identical from the coach's side. */
/* Named steps, scrollable, with the ones already answered marked. */
.step-strip {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
  padding: 10px 16px 0;
}
.step-strip::-webkit-scrollbar { display: none; }
.step-chip {
  background: none; border: 1px solid var(--line); color: var(--text-3);
  font-family: var(--f-body); font-size: 12px; font-weight: 600;
  padding: 7px 12px; border-radius: var(--r-pill); cursor: pointer; white-space: nowrap;
}
.step-chip.done { color: var(--text-2); border-color: var(--line-strong); }
.step-chip.on {
  background: var(--accent-wash); border-color: var(--accent); color: var(--accent);
}

.height-pick { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

.weight-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 9px; }
.weight-chips .chip { font-family: var(--f-mono); min-width: 52px; }
.weight-chips .chip.unknown { font-family: var(--f-body); min-width: 0; }
.weight-other:disabled { opacity: 0.4; }
.weight-says {
  margin: 8px 0 0; font-size: 12.5px; color: var(--accent);
  line-height: 1.45;
}
.weight-says[hidden] { display: none; }

/* The shortcut above a chip group, on its own line so it never reads as one
   of the choices. */
.facility-fill { margin-bottom: 9px; }

.chip-btn {
  background: var(--lime-wash); border: 1px solid var(--lime-dim);
  color: var(--accent); font-family: var(--f-body);
  font-size: 12.5px; font-weight: 600;
  padding: 7px 12px; border-radius: var(--r-pill); cursor: pointer; white-space: nowrap;
}
.chip-btn.ghost { background: var(--surface-2); border-color: var(--line); color: var(--text-2); }
/* The one chip in a row of chips that does not come back. Same weight as its
   neighbors — it is not more important than "Make a client" — but it should
   not be mistaken for them at a glance either. */
.chip-btn.ghost.danger { color: var(--clay); border-color: var(--clay-dim); }
.status { font-size: 13px; color: var(--green); text-align: center; min-height: 17px; }
.status.error { color: var(--clay); }

.switch-row {
  display: flex; align-items: center; gap: 11px; cursor: pointer;
  text-transform: none; letter-spacing: 0;
}
.switch-row input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 44px; height: 26px; border-radius: var(--r-pill); flex-shrink: 0;
  background: var(--surface-3); border: 1px solid var(--line);
  position: relative; transition: background .18s;
}
.switch-thumb {
  position: absolute; top: 2px; left: 2px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--text-3); transition: transform .18s, background .18s;
}
.switch-row input:checked + .switch-track { background: var(--accent); border-color: var(--accent); }
.switch-row input:checked + .switch-track .switch-thumb { transform: translateX(18px); background: var(--ink); }
.switch-label { font-size: 13.5px; color: var(--text-2); font-weight: 400; line-height: 1.4; }
.switch-row.compact .switch-label { font-size: 13px; }

.option-stack { display: flex; flex-direction: column; gap: 8px; }
.option {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 12px 13px; text-align: left;
  cursor: pointer; font-family: var(--f-body);
  display: flex; flex-direction: column; gap: 3px;
}
.option.active { background: var(--accent-wash); border-color: var(--accent); }
.option-name { font-size: 14px; font-weight: 600; color: var(--text); }
.option-desc { font-size: 12px; color: var(--text-3); }

/* ============================= Prescribed session ============================= */
/* The one surface on the Today screen: the thing you type into. */
.exercise {
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: 16px; padding: 15px 15px 13px;
  box-shadow: var(--shadow);
}
.exercise.skipped { opacity: 0.45; }
.exercise-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; margin-bottom: 3px;
}
.exercise-name {
  font-family: var(--f-display); font-size: var(--fs-3); font-weight: 700;
  letter-spacing: -0.01em; line-height: 1.2;
}
/* Data, not a label: "4 × 13 @ RPE 7 · last 40 lbs" in the body face with
   tabular figures. It was 11.5px mono, the same recipe as an eyebrow. */
.exercise-target {
  font-family: var(--f-body); font-size: var(--fs-1); color: var(--text-2);
  font-variant-numeric: tabular-nums;
  margin-top: 4px; line-height: 1.45;
}
/* Everything in this screen is pressed one-handed, standing over a bar, often
   with something on the fingers. A 21px-tall text button is not a target.
   The hit area grows and the drawing does not: padding would change the pill,
   so an absolutely-positioned ::after does the reaching instead. Each one is
   sized to clear its neighbors — the how-to link grows vertically only,
   because Skip is 10px to its right. */
.skip-toggle {
  background: none; border: 1px solid var(--line); color: var(--text-3);
  border-radius: var(--r-pill); font-size: 11px; font-weight: 600; font-family: var(--f-body);
  padding: 5px 10px; cursor: pointer; flex-shrink: 0;
  position: relative;
}
.skip-toggle::after { content: ""; position: absolute; inset: -10px -7px; }
.exercise.skipped .skip-toggle { background: var(--surface-3); color: var(--text-2); }

.sets { display: flex; flex-direction: column; gap: 7px; margin-top: 12px; }
.set-line {
  display: grid; grid-template-columns: 22px 1fr 14px 1.25fr 44px;
  gap: 7px; align-items: center;
}
/* An effort box, on the movements that were prescribed one. The reps and
   weight columns give up the room rather than the row growing a sixth track at
   full width — at 390px there is none spare, and the tick must stay 44px. */
.set-line.with-rpe { grid-template-columns: 20px 1fr 12px 1fr 0.72fr 44px; gap: 5px; }
.set-line.with-rpe.unloaded { grid-template-columns: 20px 1fr auto 0.72fr 44px; }
.set-line .s-rpe { color: var(--amber); }
.set-line .s-rpe::placeholder { color: var(--text-3); font-size: 12px; }
.set-num {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3); text-align: center;
}
/* The set inputs keep the mono. They are read at arm's length, mid-set, and
   a tabular monospaced numeral is the one thing a garage at six in the
   morning asks of a typeface. */
.set-line input {
  padding: 11px 6px; font-size: 16px; text-align: center;
  font-family: var(--f-mono); font-weight: 500;
}
.set-x { color: var(--text-3); font-size: 13px; text-align: center; }
/* A plank, a push-up, a bear crawl: nothing to load, so the weight box and the
   × that introduced it are gone and the count gets the room instead. The word
   replaces them so the number still says what it is. */
.set-line.unloaded { grid-template-columns: 22px 1fr auto 44px; }
.set-unit {
  color: var(--text-3); font-size: 12px; letter-spacing: 0.02em;
  text-align: left; white-space: nowrap;
}
.set-done {
  width: 44px; height: 44px; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line-strong);
  color: var(--text-3); cursor: pointer; display: grid; place-items: center;
}
.set-done svg { width: 17px; height: 17px; }
/* A confirmed set has to read as confirmed. It used to gray its own numbers —
   the same gray the app uses for a placeholder — so the work that was done
   looked like the work that wasn't, and the untouched rows below it were the
   ones that read as filled in. The done row is now the loud one and the rows
   still to come are the quiet ones. */
.set-line.logged input {
  color: var(--text); border-color: var(--green);
  background: var(--green-wash);
}
.set-line.logged .set-done { background: var(--green); border-color: var(--green); color: var(--ink); }

/* One tap for a session done as written. Sized and placed as the primary
   thing to press, because for most sessions it is the only thing to press. */
/* Said once, at the top of the card that saves. A session broken across the
   day is one session, and the form is holding the earlier half. */
.field-hint.resumed-note {
  margin: 0 0 14px; padding-left: 12px;
  border-left: 3px solid var(--green); color: var(--text-2);
}

.all-sets {
  width: 100%; margin: 2px 0 10px;
  font-family: var(--f-body); font-size: 14px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  padding: 13px 16px; border-radius: var(--r-pill);
  background: transparent;
  border: 1px solid var(--accent); color: var(--accent);
  cursor: pointer;
}
.all-sets[hidden] { display: none; }
@media (hover: hover) {
  .all-sets:hover { background: var(--accent); color: var(--ink); border-style: solid; }
}

/* What a client reported, on the coach's screen. One row per thing that moved,
   because "they changed their answers" is not the report — "they have flagged
   their knee and no longer have a squat rack" is. */
.changed-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.changed-row {
  padding: 8px 0; border-top: 1px solid var(--line-soft);
}
.changed-row:first-child { border-top: none; }
.changed-what { font-size: 13.5px; font-weight: 600; }
.changed-detail { font-size: 13px; color: var(--text-2); line-height: 1.45; margin-top: 2px; }

/* The plate math, under the weights it is about. It used to be a toast: it
   covered those weights for seven seconds and then took the answer away, which
   is the wrong shape for a number you read while loading a bar. */
/* Why a movement is in somebody's program, inside the guide that already
   opens on its name. The reasoning was on the coach's screen and nowhere the
   client could see it, so a program arrived as an instruction. */
.howto-step.why { border-left: 3px solid var(--accent); padding-left: 12px; }
.why-lede { color: var(--text-2); margin: 0 0 10px; }
.why-need { margin-bottom: 12px; }
.why-need:last-child { margin-bottom: 0; }
.why-need-name { font-weight: 600; font-size: 14.5px; }
.why-because {
  margin: 4px 0 0; padding-left: 18px;
  font-size: 13px; color: var(--text-2); line-height: 1.5;
}
.why-because li { margin-bottom: 2px; }
.why-because li::marker { color: var(--accent); }
.why-hint { margin: 10px 0 0; font-size: 12.5px; color: var(--text-3); }

.plate-out {
  margin-top: 10px; padding: 4px 0 4px 12px;
  border-left: 3px solid var(--line);
  display: flex; flex-direction: column; gap: 5px;
}
.plate-out[hidden] { display: none; }
.plate-line {
  display: flex; gap: 10px; align-items: baseline;
  font-family: var(--f-mono); font-size: 12px; color: var(--text-2);
}
.plate-line b { color: var(--accent); font-weight: 500; min-width: 44px; }

.exercise-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-top: 11px;
}
.text-btn {
  background: none; border: none; color: var(--accent);
  font-family: var(--f-body); font-size: 12.5px; font-weight: 600;
  cursor: pointer; padding: 4px 0;
  position: relative;
}
/* The two of them sit at opposite ends of the foot, so they can reach wide
   without meeting in the middle. */
.text-btn::after { content: ""; position: absolute; inset: -11px -14px; }
.note-input {
  margin-top: 9px; font-size: 14px; padding: 10px 11px;
}

/* A cardio row is a set row with different words in it, so it inherits the
   grid rather than defining a second one — the two used to disagree about
   where the columns were. */
.cardio-line { margin-top: 12px; }
.cardio-line .set-x { font-family: var(--f-body); font-size: 12px; }

.check-list { display: flex; flex-direction: column; gap: 2px; }
.check-item {
  display: flex; align-items: center; gap: 12px; cursor: pointer;
  padding: 11px 2px; text-transform: none; letter-spacing: 0;
  font-size: 15.5px; color: var(--text); font-weight: 400;
  border-bottom: 1px solid var(--line-soft);
}
.check-item:last-child { border-bottom: none; }
.check-item input { position: absolute; opacity: 0; width: 0; height: 0; }
.check-box {
  width: 23px; height: 23px; border-radius: 7px; flex-shrink: 0;
  border: 1.5px solid var(--line-strong); background: var(--surface);
  display: grid; place-items: center; transition: background .15s, border-color .15s;
}
.check-box svg { width: 14px; height: 14px; opacity: 0; color: var(--ink); }
.check-item input:checked + .check-box { background: var(--green); border-color: var(--green); }
.check-item input:checked + .check-box svg { opacity: 1; }
.check-meta { font-size: 13px; color: var(--text-3); margin-left: auto; font-variant-numeric: tabular-nums; }

.section-label {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3);
  padding: 6px 2px 0;
}

.cooldown-list {
  display: flex; flex-direction: column; gap: 7px;
  font-size: 15px; color: var(--text-2);
}
.cooldown-list div { padding-left: 14px; position: relative; }
.cooldown-list div::before {
  content: ""; position: absolute; left: 0; top: 8px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--green-dim);
}

.switcher { border-top-style: dashed; }
.switcher-row { display: flex; gap: 8px; margin-top: 10px; }
.switcher-row select { flex: 1; }

/* ============================= Program ============================= */
.week-tabs { display: flex; gap: 7px; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.week-tabs::-webkit-scrollbar { display: none; }
/* Chips, with the chosen week as the ink pill. The week's name stays in the
   chip — Foundation, Build, Intensify — because it is information, and the
   row scrolls anyway. */
.week-tab {
  flex: 0 0 auto;
  background: transparent; border: 1px solid var(--line-strong);
  border-radius: var(--r-pill); padding: 9px 14px; cursor: pointer;
  display: flex; flex-direction: row; gap: 7px; align-items: baseline;
  font-family: var(--f-body); white-space: nowrap;
}
.week-tab.active { background: var(--accent); border-color: var(--accent); }
.week-num { font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text); }
.week-name { font-size: 12px; color: var(--text-3); }
.week-tab.active .week-num, .week-tab.active .week-name { color: var(--ink); }

/* A ruled row. The outcome word on the right — Done, 1 of 3, Missed — is
   what carries the state now, so the colored left edge that used to say
   "editable" has nothing left to say. */
.program-day {
  background: none; border: 0; border-top: 1px solid var(--line-soft);
  border-radius: 0; padding: var(--s4) 0; text-align: left; width: 100%;
  cursor: pointer; font-family: var(--f-body);
  display: flex; flex-direction: column; gap: 4px;
}
.program-day.readonly { cursor: default; }
.program-day-top { display: flex; justify-content: space-between; align-items: center; }
.program-weekday {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3);
}
.edit-hint { font-size: 12.5px; color: var(--accent); font-weight: 600; }
.program-day.readonly .edit-hint { display: none; }
.program-day-type {
  font-family: var(--f-display); font-size: var(--fs-3); font-weight: 700;
  letter-spacing: -0.01em; color: var(--text); line-height: 1.2;
}
.program-day-items { font-size: var(--fs-1); color: var(--text-2); line-height: 1.55; }
.deload-notice {
  border: 1px dashed var(--green-dim); background: color-mix(in srgb, var(--green) 5%, transparent);
  border-radius: var(--r); padding: 13px 15px; margin-bottom: 2px;
}
.deload-notice p { margin: 0; }

/* ===================== A proposed replacement day ===================== */
/* Below Save in the day editor, and separated from it by a rule: everything
   above is the coach editing rows himself, everything below is him asking for
   a different day. The two should not read as one form. */
.day-ai {
  margin-top: 22px; padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.day-ai .btn-row { margin-top: 12px; }

/* The proposal itself is a card and not a list, because it is one object the
   coach accepts or throws away. A dashed edge, like the deload notice, says
   the same thing that one does: this is not on the board yet. */
.proposal-card {
  margin-top: 16px; padding: 16px;
  border: 1px dashed var(--accent-dim); border-radius: var(--r);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}
.proposal-head { display: flex; justify-content: space-between; align-items: flex-start; }
.proposal-title { margin: 2px 0 10px; font-size: var(--fs-3); }
.proposal-items { display: flex; flex-direction: column; gap: 7px; margin: 12px 0; }
.proposal-item {
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  padding-bottom: 7px; border-bottom: 1px solid var(--line-soft);
}
.proposal-item:last-child { border-bottom: 0; padding-bottom: 0; }
.proposal-move { font-weight: 600; }
.proposal-note { flex-basis: 100%; font-size: 12px; }
.proposal-why {
  margin: 12px 0 0; padding-left: 12px;
  border-left: 3px solid var(--accent-dim);
  color: var(--text-2); font-size: 14px; line-height: 1.5;
}
/* What the validator still objects to. Shown rather than hidden — a day the
   app had to complain about is one to read harder, not one to trust more. */
.proposal-problems {
  margin: 12px 0 0; padding-left: 18px;
  color: var(--clay); font-size: 13px; line-height: 1.5;
}

/* ============================= History ============================= */
/* Ruled rows. The left edge survives only where it says something — a
   session that came from a watch rather than from this app. */
.history-item {
  background: none; border: 0; border-top: 1px solid var(--line-soft);
  border-radius: 0; padding: var(--s4) 0;
}
.history-item.source-apple_watch { border-left: 3px solid var(--clay); padding-left: 12px; }
.history-item.is-rest { opacity: 0.7; }
.history-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.history-type {
  font-family: var(--f-display); font-size: 17px; font-weight: 700; letter-spacing: -0.01em;
}
.history-date { font-size: 13px; color: var(--text-3); margin-top: 3px; font-variant-numeric: tabular-nums; }
.history-meta {
  display: flex; gap: 6px; flex-wrap: wrap; margin-top: 9px;
}
.history-meta span {
  font-size: 12px; color: var(--text-2); font-variant-numeric: tabular-nums;
  border: 1px solid var(--line); padding: 3px 9px; border-radius: var(--r-pill);
}
.history-detail {
  margin-top: 10px; font-size: var(--fs-1); color: var(--text-2); line-height: 1.6;
}
.history-detail em { color: var(--text-3); font-style: normal; }
/* What the plan said, where it differs from what happened. Quiet: the record
   is the session, and this is the margin note beside it. */
.history-asked { color: var(--text-3); }
.history-notes {
  margin-top: 9px; font-size: 13px; color: var(--text-2);
  padding-left: 11px; border-left: 2px solid var(--line);
}
.tag {
  font-family: var(--f-body); font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 4px 9px; border-radius: var(--r-pill);
  background: var(--surface-2); color: var(--text-3); white-space: nowrap;
}
.tag.watch { background: var(--red-wash); color: var(--red); }
.tag.bonus { background: var(--lime-wash); color: var(--lime); }
.history-actions { display: flex; gap: 8px; margin-top: 12px; }
.mini-btn {
  background: transparent; border: 1px solid var(--line-strong); color: var(--text-2);
  font-family: var(--f-body); font-size: 12.5px; font-weight: 600;
  padding: 7px 14px; border-radius: var(--r-pill); cursor: pointer;
}
.mini-btn.danger { color: var(--clay); }

.empty {
  text-align: center; padding: 40px 24px; color: var(--text-3);
  font-size: var(--fs-1); line-height: 1.6;
}

/* ============================= Tiles ============================= */
/* The number is the point, so it takes the display face at a ladder step
   and the box around it goes. */
.tile-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--s5); }
.tile {
  border-top: 1px solid var(--line-soft);
  padding: var(--s4) 0;
}
.tile.wide { grid-column: 1 / -1; }
.tile-label {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3); margin-bottom: 8px;
}
.tile-value {
  font-family: var(--f-display); font-size: var(--fs-5); font-weight: 800;
  letter-spacing: -0.02em; line-height: 1; color: var(--text);
  font-variant-numeric: lining-nums tabular-nums;
}
.tile-value .unit {
  font-family: var(--f-body); font-size: var(--fs-1); font-weight: 500;
  color: var(--text-3); letter-spacing: 0;
}
.tile-sub { font-size: 13px; color: var(--text-2); margin-top: 8px; line-height: 1.45; }
.tile-sub.up { color: var(--green); }
.tile-sub.down { color: var(--clay); }

.pr-list { display: flex; flex-direction: column; gap: 7px; margin-top: 4px; }
.pr-item {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 15px; padding: 9px 0;
  border-top: 1px solid var(--line-soft);
}
.pr-item .w { font-size: 14px; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; }

.stall-list { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 4px; }
.stall-item {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--line-soft);
}
.stall-name { font-size: 15px; font-weight: 600; }
.stall-weight { font-size: var(--fs-1); color: var(--text-2); font-variant-numeric: tabular-nums; }

.inline-select {
  width: auto; max-width: 165px; font-size: 12.5px;
  padding: 8px 30px 8px 11px; border-radius: var(--r-pill);
}

.entry {
  border-top: 1px solid var(--line-soft); padding: 12px 0;
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center;
}
.entry-date { font-size: 14px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.entry-note { font-size: 13px; color: var(--text-3); margin-top: 3px; }
.entry-values { display: flex; gap: 10px; align-items: baseline; }
.entry-values span { font-size: 14px; color: var(--text-2); font-variant-numeric: tabular-nums; }
.entry-values .score { font-family: var(--f-display); font-size: 20px; font-weight: 800; color: var(--accent); }
.holes-tag {
  font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  border: 1px solid var(--line); color: var(--text-3); padding: 2px 8px; border-radius: var(--r-pill);
}

/* ============================= Tabbar ============================= */
.tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  max-width: 520px; margin: 0 auto;
  background: var(--bg);
  border-top: 1px solid var(--line);
  display: flex; padding-bottom: env(safe-area-inset-bottom);
}
.tab {
  flex: 1; background: none; border: none; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 11px 2px 9px; font-size: 11px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-3); font-family: var(--f-body);
}
.tab svg { width: 21px; height: 21px; }
.tab.active { color: var(--accent); }

/* ============================= Timer ============================= */
.timer-screen {
  position: fixed; inset: 0; z-index: 90;
  background: var(--bg);
  display: flex; flex-direction: column;
  padding: calc(14px + env(safe-area-inset-top)) 18px calc(18px + env(safe-area-inset-bottom));
  max-width: 520px; margin: 0 auto;
}
.timer-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.timer-modes { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; }
.timer-modes::-webkit-scrollbar { display: none; }
.mode-chip {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-3);
  font-family: var(--f-body); font-size: 12.5px; font-weight: 600;
  padding: 8px 14px; border-radius: var(--r-pill); cursor: pointer; white-space: nowrap;
}
.mode-chip.active { background: var(--accent); border-color: var(--accent); color: var(--ink); }

.timer-stage {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px;
  min-height: 0;
}
.timer-phase {
  font-family: var(--f-body); font-size: 13px; font-weight: 600; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--text-3);
  transition: color .2s;
}
.timer-clock {
  font-family: var(--f-display);
  font-size: clamp(76px, 26vw, 132px);
  font-weight: 800; letter-spacing: -0.05em; line-height: 0.95;
  font-variant-numeric: tabular-nums;
  color: var(--text); transition: color .2s;
}
.timer-round {
  font-family: var(--f-mono); font-size: 14px; color: var(--text-2);
  min-height: 20px;
}
.timer-progress {
  width: 100%; max-width: 300px; height: 5px; margin-top: 18px;
  background: var(--surface-2); border-radius: var(--r-pill); overflow: hidden;
}
.timer-progress-fill {
  height: 100%; width: 0%; border-radius: var(--r-pill);
  background: var(--accent); transition: width .25s linear, background .2s;
}

/* Phase color carries the state — readable across a garage */
.timer-screen.work  .timer-phase { color: var(--green); }
.timer-screen.work  .timer-clock { color: var(--green); }
.timer-screen.work  .timer-progress-fill { background: var(--green); }
.timer-screen.rest  .timer-phase { color: var(--accent); }
.timer-screen.rest  .timer-clock { color: var(--accent); }
.timer-screen.prep  .timer-phase { color: var(--text-2); }
.timer-screen.done  .timer-clock { color: var(--clay); }
.timer-screen.done  .timer-phase { color: var(--clay); }
.timer-screen.ending .timer-clock { animation: pulse .5s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.45; } }

.timer-config { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
.config-row { display: flex; gap: 10px; }
.config-item { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.config-item label { text-align: center; }
.stepper {
  display: grid; grid-template-columns: 40px 1fr 40px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); overflow: hidden; align-items: stretch;
}
.stepper button {
  background: none; border: none; color: var(--text-2);
  font-size: 20px; cursor: pointer; font-family: var(--f-body); line-height: 1;
}
.stepper button:active { background: var(--surface-3); color: var(--text); }
.stepper .val {
  display: grid; place-items: center;
  font-family: var(--f-mono); font-size: 17px; font-weight: 500;
  padding: 11px 0; color: var(--text);
}
.config-note { font-size: 12px; color: var(--text-3); text-align: center; line-height: 1.5; }

.timer-controls { display: flex; gap: 10px; align-items: stretch; }
.timer-ctl {
  border-radius: var(--r); cursor: pointer; font-family: var(--f-body);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; font-size: 11.5px; font-weight: 600; padding: 15px 8px;
  transition: transform .1s;
}
.timer-ctl:active { transform: scale(0.97); }
.timer-ctl svg { width: 22px; height: 22px; }
.timer-ctl.primary {
  flex: 2; background: var(--accent); border: none; color: var(--ink); font-size: 14px;
}
.timer-ctl.primary svg { width: 26px; height: 26px; }
.timer-ctl.secondary {
  flex: 1; background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
}
.ctl-plus { font-family: var(--f-mono); font-size: 17px; font-weight: 700; color: var(--text); }
.timer-mute { display: flex; justify-content: center; margin-top: 14px; }

/* Mini bar shown when the timer runs behind the app */
.mini-timer {
  position: fixed; left: 12px; right: 12px;
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 10px);
  z-index: 40; max-width: 496px; margin: 0 auto;
  background: var(--accent); color: var(--ink); border: none;
  border-radius: var(--r); padding: 13px 16px; cursor: pointer;
  display: flex; align-items: center; gap: 12px;
  box-shadow: 0 8px 28px rgba(0,0,0,0.4);
  font-family: var(--f-body);
}
.mini-timer.work { background: var(--green); }
.mini-timer.done { background: var(--clay); color: var(--ink); }
.mini-phase {
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; opacity: 0.75;
}
.mini-clock {
  font-family: var(--f-display); font-size: 22px; font-weight: 800;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
}
.mini-open { margin-left: auto; font-size: 12px; font-weight: 600; opacity: 0.75; }

/* ============================= Sheets ============================= */
.sheet-backdrop {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(0,0,0,0.62);
  backdrop-filter: blur(3px);
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet {
  background: var(--surface); width: 100%; max-width: 520px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-top: 1px solid var(--line);
  padding: 10px 16px calc(20px + env(safe-area-inset-bottom));
  max-height: 90dvh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 14px;
  animation: sheet-up .22s cubic-bezier(.2,.8,.3,1);
}
@keyframes sheet-up { from { transform: translateY(18px); opacity: 0.6; } }
.sheet-grip {
  width: 38px; height: 4px; border-radius: var(--r-pill);
  background: var(--line); margin: 0 auto 4px; flex-shrink: 0;
}
.sheet-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.sheet-eyebrow {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3); margin-bottom: 6px;
}
.sheet-title {
  font-family: var(--f-display); font-size: var(--fs-4); font-weight: 800;
  letter-spacing: -0.02em; line-height: 1.1; margin: 0; text-wrap: balance;
}
.sheet-section {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-top: 12px; border-top: 1px solid var(--line-soft);
}
.sheet-section > span {
  font-family: var(--f-display); font-size: 16px; font-weight: 700; letter-spacing: -0.01em;
}

.editor-row {
  /* name · kind · sets · reps · to · RPE · min · lbs · remove */
  display: grid;
  grid-template-columns: 1.4fr 0.85fr 0.42fr 0.42fr 0.42fr 0.5fr 0.45fr 0.55fr 30px;
  gap: 5px; align-items: center;
}
.editor-row input, .editor-row select {
  padding: 9px 7px; font-size: 13px; border-radius: var(--r-sm);
}
/* Each number in its own cell so it can carry its own label. On a desktop the
   column heading does that job and the cell is invisible; on a phone the
   heading is gone and the label is the only thing saying which box is which. */
.e-cell { display: block; min-width: 0; }
.e-cell::before { content: none; }
.editor-row select { background-size: 14px; padding-right: 22px; background-position: right 5px center; }
.row-x {
  background: none; border: none; color: var(--clay);
  font-size: 19px; cursor: pointer; line-height: 1; padding: 4px;
}

.wedit-ex {
  padding: 12px 0; border-bottom: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 8px;
}
.wedit-name { font-size: 14px; font-weight: 600; }
.wedit-ex .set-line { grid-template-columns: 22px 1fr 14px 1.25fr 0.8fr; }
/* A tick-list movement edits as sets × reps on the same five-column grid:
   blank, sets, ×, reps, weight. */
.wedit-ex .summary-line .w-weight { grid-column: 5; }
.wedit-cardio {
  display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 7px;
  align-items: center; padding: 10px 0; border-bottom: 1px solid var(--line-soft);
}

.chip-scroll { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; padding-bottom: 3px; }
.chip-scroll::-webkit-scrollbar { display: none; }
.cat-chip {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-2);
  font-family: var(--f-body); font-size: 12.5px; font-weight: 600;
  padding: 8px 13px; border-radius: var(--r-pill); cursor: pointer; white-space: nowrap;
}
.cat-chip.active { background: var(--accent); border-color: var(--accent); color: var(--ink); }

.picker-results {
  overflow-y: auto; display: flex; flex-direction: column; gap: 15px;
  min-height: 150px; padding-bottom: 4px;
}
.picker-group { display: flex; flex-direction: column; gap: 5px; }
.picker-cat {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3);
  position: sticky; top: 0; background: var(--surface); padding: 4px 0 6px;
}
.picker-item {
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); padding: 12px 13px; text-align: left; width: 100%;
  cursor: pointer; font-family: var(--f-body);
  display: flex; flex-direction: column; gap: 3px;
}
.picker-item:active { background: var(--accent-wash); border-color: var(--accent); }
.picker-name { font-size: 14.5px; font-weight: 600; color: var(--text); }
.picker-meta { font-size: 11.5px; color: var(--text-3); line-height: 1.4; }

/* ============================= Toasts ============================= */
.toast-host {
  /* Below the header, not on top of it. At `top: 0` a toast covered 67px of a
     92px header — every icon in it, including the way out of whatever the
     toast was complaining about. `--topbar-h` is measured at runtime, because
     the header's height moves with the safe-area inset. */
  position: fixed; left: 0; right: 0; top: var(--topbar-h, 0px); z-index: 100;
  max-width: 520px; margin: 0 auto;
  padding: 10px 14px 0;
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.toast {
  /* Opaque. These sit over content and, previously, over a blurred header —
     anything translucent turned the text into a smear. */
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 13px 15px;
  font-size: 13.5px; line-height: 1.45;
  box-shadow: 0 10px 34px rgba(0,0,0,0.5);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  /* Transparent to the finger. A toast is a notice, and for its five or six
     seconds it sits directly over the first set row — "Already logged today"
     used to swallow the tick underneath it, which on a phone reads as the app
     ignoring you. Only the action inside one is worth pressing, so only the
     action takes the tap. */
  pointer-events: none;
  animation: toast-in .2s cubic-bezier(.2,.8,.3,1);
}
.toast .toast-action { pointer-events: auto; }
@keyframes toast-in { from { transform: translateY(-10px); opacity: 0; } }
.toast-pr {
  background: var(--accent); color: var(--ink); border-color: var(--accent); font-weight: 600;
}
/* `--clay-dim` is a 14%-opacity red. As a background it left the text sitting
   on whatever was behind the toast, which is how "Already logged today" became
   unreadable. The color now carries in the edge, where it costs no contrast. */
.toast-warn {
  background: var(--surface-3); color: var(--text);
  border-color: var(--line); border-left: 3px solid var(--red);
}
.toast-action {
  background: rgba(0,0,0,0.22); border: none; color: inherit;
  font-family: var(--f-body); font-size: 12px; font-weight: 700;
  padding: 7px 13px; border-radius: var(--r-sm); cursor: pointer; flex-shrink: 0;
}

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ============================= Practice block ============================= */
.practice { border-top-color: var(--green-dim); }
.practice-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.practice-eyebrow {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--green); margin-bottom: 6px;
}
#practiceCollapse { transition: transform .2s; }

.practice-drill { align-items: flex-start; padding: 12px 2px; }
.practice-drill .check-box { margin-top: 1px; }
.drill-text { display: flex; flex-direction: column; gap: 3px; flex: 1; }
.drill-name { font-size: 15.5px; font-weight: 500; }
.drill-cue { font-size: 13px; color: var(--text-3); line-height: 1.45; }
.practice-drill .check-meta { align-self: flex-start; margin-top: 2px; }

/* The cool down's rows. The name is a button now — every row is a library
   movement with a written guide — so it has to shed the button chrome and
   read as the text it replaced. */
.cool-howto {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; font-size: 15.5px; font-weight: 500;
  color: inherit; text-align: left; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.cool-howto:hover { color: var(--accent-ink); }

/* Why this row is this client's rather than everybody's. */
.cool-why {
  font-size: 12.5px; color: var(--text-3);
  letter-spacing: 0.01em;
}

/* A rep count is a prescription, not a clock. It reads like the hold beside
   it and does nothing when tapped, because there is nothing to time. */
.cool-count { cursor: default; opacity: 0.85; }

/* The coach's version of a cool down row: the same shape the client sees, plus
   the two controls that change it. */
.cool-edit-row { gap: 8px; }
.cool-edit-row .drill-text { min-width: 0; }
.cool-swap, .cool-drop { flex: none; }

/* Picking a replacement, inline under the rows rather than in a sheet over
   them — so what is being replaced stays on screen while the choice is made. */
.picker-inline {
  margin-top: 12px; padding: 12px 14px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r);
}
.picker-inline .check-list { max-height: 320px; overflow-y: auto; }
.picker-inline .cool-pick {
  width: 100%; text-align: left; background: none; border: 0;
  font: inherit; color: inherit; cursor: pointer;
}
.picker-inline .cool-pick:hover .drill-name { color: var(--accent-ink); }

/* The coach's sub-tabs on a client's Program tab. Same pills as the tabs
   above them, one step quieter, because they are a division of one
   destination rather than four destinations. */
.prog-tabs { margin-bottom: 14px; }

/* Approve, where it can be pressed.
   It was the last control on the longest card at the bottom of the tab — 6.9
   phone screens down with a real draft on screen. Fixed above the tab bar,
   following `.mini-timer`, which solved the same problem for the clock. */
.approve-bar {
  position: fixed; left: 0; right: 0;
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom));
  z-index: 34;
  max-width: 520px; margin: 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 96%, transparent);
  backdrop-filter: blur(18px) saturate(140%);
  border-top: 1px solid var(--line);
  box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.16);
}
.approve-count {
  flex: 1; min-width: 0;
  font-size: 11.5px; line-height: 1.35; color: var(--text-3);
}
.approve-count b {
  display: block; font-size: 12.5px; font-weight: 600; color: var(--text);
}
.approve-bar .btn { white-space: nowrap; }
/* Room for the bar, so the last thing in the plan is not sitting under it. */
body.approve-docked #ptab-plan { padding-bottom: 84px; }

.practice-note {
  margin-top: 14px; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--green-dim);
  font-size: 14px; color: var(--text-2); line-height: 1.6;
}

/* The public page's section names, folded away under one line. Same shape as
   .sim-tips; kept separate because these two will drift. */
.more-copy { margin: 18px 0 6px; border-top: 1px solid var(--line); padding-top: 6px; }
.more-copy > summary {
  font-size: 13px; font-weight: 600; color: var(--accent);
  cursor: pointer; padding: 8px 0; list-style: none;
}
.more-copy > summary::-webkit-details-marker { display: none; }
.more-copy > summary::before { content: "\25B8 "; color: var(--text-3); }
.more-copy[open] > summary::before { content: "\25BE "; }

.sim-tips { margin-top: 12px; }
.sim-tips summary {
  font-size: 13px; font-weight: 600; color: var(--accent);
  cursor: pointer; padding: 8px 0; list-style: none;
}
.sim-tips summary::-webkit-details-marker { display: none; }
.sim-tips summary::before { content: "▸ "; color: var(--text-3); }
.sim-tips[open] summary::before { content: "▾ "; }
.sim-tips ul {
  margin: 4px 0 0; padding-left: 18px;
  font-size: 12.5px; color: var(--text-2); line-height: 1.65;
}
.sim-tips li { margin-bottom: 5px; }

.practice-actions { margin-top: 14px; }
.practice #practiceLengths { margin: 14px 0 6px; }

/* ============================= Schedule conflicts ============================= */
.conflict {
  border-left: 3px solid var(--text-3); padding: 4px 0 4px 12px;
  display: flex; flex-direction: column; gap: 7px;
}
.conflict.sev-high { border-left-color: var(--clay); }
.conflict.sev-moderate { border-left-color: var(--accent); }
.conflict.sev-low { border-left-color: var(--text-3); }
.conflict-top {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; flex-wrap: wrap;
}
.conflict-day { font-size: 14px; font-weight: 600; }
.conflict-when { font-size: 13px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.conflict.sev-high .conflict-when { color: var(--clay); }
.conflict-why { font-size: 12.5px; color: var(--text-2); line-height: 1.55; }
.conflict-hint { font-size: 12px; color: var(--text-3); font-style: italic; }
.conflict-swap { align-self: flex-start; margin-top: 2px; }

/* ============================= How-to guides ============================= */
button.exercise-name {
  background: none; border: none; padding: 0; text-align: left;
  font-family: var(--f-display); font-size: var(--fs-3); font-weight: 700;
  letter-spacing: -0.01em; line-height: 1.2; color: var(--text);
  cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
  position: relative;
}
/* Vertical only. Reaching sideways would put this under the Skip pill, and
   downward it lands on the target line, which is not a control. */
button.exercise-name::after { content: ""; position: absolute; inset: -12px 0; }
.howto-mark {
  width: 17px; height: 17px; border-radius: 50%; flex-shrink: 0;
  background: var(--surface-3); color: var(--text-3);
  font-family: var(--f-body); font-size: 10.5px; font-weight: 700;
  display: inline-grid; place-items: center; line-height: 1;
}
button.exercise-name:active .howto-mark { background: var(--accent); color: var(--ink); }
/* "i" rather than "?": the reason and the video are behind it, not a guide. */
.howto-mark.why { font-style: italic; font-family: var(--f-display); }
.howto-mark.standalone {
  border: none; cursor: pointer; width: 24px; height: 24px; font-size: 11.5px;
  position: relative;
}
/* Narrower than it is tall on purpose: this one sits inside the label that
   ticks the movement off, and a square 44px reach would swallow taps meant
   for the name beside it. */
.howto-mark.standalone::after { content: ""; position: absolute; inset: -10px -5px; }
.howto-mark.standalone:active { background: var(--accent); color: var(--ink); }
.mob-name { flex: 1; }
.check-item .howto-mark.standalone { margin-left: 8px; }

.picker-row { display: flex; align-items: stretch; gap: 7px; }
.picker-row .picker-item { flex: 1; }
.picker-howto { align-self: center; flex-shrink: 0; }

.howto-step {
  padding: 2px 0 2px 12px;
  border-left: 3px solid var(--line);
  margin-bottom: 14px;
}
.howto-step.watch { border-left-color: var(--accent); }
.howto-step.danger { border-left-color: var(--clay); }
.howto-label {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3); margin-bottom: 6px;
}
.howto-step.watch .howto-label { color: var(--accent); }
.howto-step.danger .howto-label { color: var(--clay); }
.howto-step p {
  margin: 0; font-size: 15px; line-height: 1.6; color: var(--text-2);
}
.howto-link { /* tappable exercise title — styling comes from button.exercise-name */ }

/* ============================= Desktop ============================= */
/* The app is phone-first, but it gets used on a PC too. On a wide screen a
   bottom-anchored sheet reads as an accident, so center it as a dialog. */
@media (min-width: 640px) and (min-height: 620px) {
  .sheet-backdrop { align-items: center; }
  .sheet {
    border-radius: var(--r-lg);
    border: 1px solid var(--line);
    max-height: 82dvh;
    padding-bottom: 20px;
    animation: sheet-in .2s cubic-bezier(.2,.8,.3,1);
  }
  .sheet-grip { display: none; }
  @keyframes sheet-in { from { transform: translateY(10px) scale(.99); opacity: 0; } }

  .mini-timer { bottom: calc(var(--tab-h) + 14px); }
}

/* Hover states only where there's a real pointer */
@media (hover: hover) {
  .picker-item:hover { background: var(--accent-wash); border-color: var(--accent); }
  .program-day:not(.readonly):hover { border-color: var(--accent-dim); }
  .btn-primary:hover { filter: brightness(1.06); }
  .btn-ghost:hover, .mini-btn:hover { background: var(--surface-3); }
  .howto-mark:hover, button.exercise-name:hover .howto-mark { background: var(--accent); color: var(--ink); }
  .set-done:hover { border-color: var(--accent); color: var(--accent); }
  .set-line.logged .set-done:hover { background: var(--green); color: var(--ink); }
  .tab:hover { color: var(--text-2); }
  .tab.active:hover { color: var(--accent); }
  .icon-btn:hover { color: var(--text); border-color: var(--line); }
}

/* Exercise titles read as tappable on a desktop pointer */
@media (hover: hover) {
  button.exercise-name:hover { color: var(--accent); }
}


/* ---------------------------------------------------------------------------
   Sign-in gate
   Sits above everything until there's a session. Same dark surface as the app
   so signing in doesn't feel like a different product.
--------------------------------------------------------------------------- */
.gate {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  /* Centered with auto margins on the card rather than align-items, and
     scrollable. A centered flex item taller than its container overflows in
     both directions and the top and bottom become unreachable — which made
     the waiver panel impossible to submit on a short screen. */
  overflow-y: auto;
  padding: 24px;
  background: var(--bg);
}
.gate[hidden] { display: none; }

.gate-card {
  width: 100%;
  max-width: 380px;
  margin: auto;
}

.gate-brand {
  font-family: "Bricolage Grotesque", system-ui, sans-serif;
  font-weight: 800;
  font-size: 32px;
  letter-spacing: -0.02em;
  margin-bottom: 28px;
  color: var(--accent);
}

.gate-panel[hidden] { display: none; }

.gate-title {
  font-family: var(--f-display);
  font-size: var(--fs-5);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.06;
  text-transform: uppercase;
  text-wrap: balance;
  margin: 0 0 8px;
}

.gate-hint {
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-2);
  margin: 0 0 16px;
}

.gate-label {
  display: block;
  font-size: var(--fs-0);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--text-2);
  margin: 16px 0 6px;
}

.gate-input {
  width: 100%;
  box-sizing: border-box;
  font-size: 16px; /* 16px stops iOS zooming the page on focus */
  padding: 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: inherit;
}
.gate-input:focus {
  outline: none;
  border-color: var(--accent);
}

.gate-btn {
  width: 100%;
  margin-top: 22px;
  padding: 15px;
  font-size: 16px;
  font-weight: 700;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--ink);
  cursor: pointer;
}
.gate-btn:disabled { opacity: 0.5; cursor: default; }

/* "Forgot your password?" and "Back to sign in". A button because it does
   something on this page rather than going anywhere, and styled as a link
   because that is what it looks like it should be. Full width and a real tap
   height, since the panel it sits under is a phone screen. */
.gate-link {
  display: block; width: 100%; margin: 16px 0 0; padding: 12px 0; min-height: 44px;
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: 14px; color: var(--accent);
  text-decoration: underline; text-underline-offset: 3px;
}
.gate-link:hover, .gate-link:focus-visible { color: var(--text); }

/* The sentence above a gate form that needs one. The sign-in panel has none —
   everybody knows what a sign-in is — and these two do. */
.gate-note {
  margin: 0 0 20px; font-size: 14.5px; line-height: 1.55; color: var(--text-2);
}

.gate-error {
  margin: 14px 0 0;
  font-size: 14px;
  color: var(--clay);
}
.gate-error[hidden] { display: none; }

/* A coach viewing a client — makes it obvious whose data is on screen. */
.viewing-as {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-2);
  margin-right: 8px;
}


/* Google sign-in. White button per Google's branding requirements — it has to
   look like their button, not like ours. */
.gate-google {
  width: 100%;
  margin-top: 18px;
  padding: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: #1F1F1F;
  background: #FFFFFF;
  border: 1px solid #DADCE0;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.gate-google[hidden] { display: none; }
@media (hover: hover) {
  .gate-google:hover { background: #F5F5F5; }
}

.gate-or {
  position: relative;
  text-align: center;
  margin: 20px 0 4px;
}
.gate-or[hidden] { display: none; }
.gate-or::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line-strong);
}
.gate-or span {
  position: relative;
  padding: 0 12px;
  background: var(--bg);
  font-size: var(--fs-0);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--text-3);
}


/* ---------------------------------------------------------------------------
   Coach: roster, intake, review
--------------------------------------------------------------------------- */
.roster-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  padding: var(--s4) 0;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--line-soft);
  background: none;
  color: inherit;
  font-family: inherit;
  cursor: pointer;
}
.roster-main { flex: 1; min-width: 0; }
.roster-name { font-family: var(--f-display); font-size: var(--fs-3); font-weight: 700; letter-spacing: -0.01em; line-height: 1.2; }
.roster-meta { font-size: var(--fs-1); color: var(--text-2); margin-top: 3px; }
.roster-chevron { color: var(--text-3); }

.field-label {
  display: block;
  font-size: var(--fs-0);
  text-transform: uppercase;
  letter-spacing: var(--track-label);
  color: var(--text-2);
  font-weight: 600;
  margin: 14px 0 6px;
}
/* --text-2, not --text-3. This is the smallest running text in the app and the
   one that most needs to be readable, and --text-3 measures 3.0:1 on the light
   palettes — under AA, and tests/browser/contrast.py fails it.

   Worth knowing why that was invisible until now: this rule used `opacity: 0.5`
   with no color, and a computed style carries no opacity, so the contrast sweep
   has been reading the undimmed inherited color and passing this at 15.6:1 for
   as long as it has existed. Naming the color is what made the sweep able to
   see it. --text-3 failing AA on a light palette is a real finding about the
   palette, not about this rule. */
.field-hint { font-size: 13px; color: var(--text-2); margin: 4px 0 0; line-height: 1.5; }
.field-input, .field-select, .field-textarea {
  width: 100%;
  box-sizing: border-box;
  font-family: inherit;
  font-size: 16px;
  padding: 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  /* `background-color`, never the shorthand: this reaches every .field-select,
     and the shorthand resets the chevron the base `select` rule draws. Found
     by tests/test_the_chevron_survives.py — the third select to lose its
     arrow this way, and the first one caught before a person noticed. */
  background-color: var(--surface);
  color: inherit;
}
.field-textarea { min-height: 74px; resize: vertical; }
.field-input:focus, .field-select:focus, .field-textarea:focus {
  outline: none;
  border-color: var(--accent);
}
.field-unit { font-size: 12px; color: var(--text-3); margin-left: 6px; }

.chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 4px; }
.chip {
  padding: 9px 13px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 14px;
  cursor: pointer;
}
.chip.on {
  background: var(--accent-wash);
  border-color: var(--accent);
  color: var(--accent);
}
.chip .rank-num { font-family: var(--f-mono, monospace); opacity: 0.7; margin-right: 5px; }

/* A generated program that needed patching. Never hidden — a program the app
   had to correct is one to read more carefully, not less. */
.warn-box {
  border: 1px solid var(--clay-dim);
  background: var(--red-wash);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  margin: 12px 0;
}
.warn-box .warn-title { font-weight: 600; color: var(--clay, #E0705C); margin-bottom: 6px; }
.warn-box ul { margin: 0; padding-left: 18px; font-size: 13px; color: var(--text-2); }

/* A day of a draft, closed until you open it.
   Seven expanded days was 2.4 phone screens; seven rows is half of one, which
   is the only shape in which the whole week is on screen at once. */
.review-day {
  margin-bottom: 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  overflow: hidden;
}
.review-day-head {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.review-day-head:hover .review-day-name { color: var(--accent-ink); }
/* Drawn rather than typed: a glyph renders as a tofu box wherever the symbol
   font is missing, and this one sits on every row of every draft. */
.day-chev {
  flex: none; width: 0; height: 0;
  border-left: 5px solid var(--text-3);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--fast) var(--ease);
}
.review-day.open .day-chev { transform: rotate(90deg); }
.review-day-head .review-weekday { flex: none; width: 30px; }
.review-day-count {
  flex: none;
  font-family: var(--f-mono); font-size: 11.5px;
  color: var(--text-3);
  background: var(--surface-2);
  border-radius: var(--r-pill);
  padding: 4px 8px;
}
/* What the validator objected to, on the closed row. Without it a collapsed
   day can hide the one thing worth opening it for. */
.review-day-flag {
  flex: none; width: 0; height: 0;
  border-bottom: 9px solid var(--amber);
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}
.review-day-body {
  padding: 0 12px 8px;
  border-top: 1px solid var(--line-soft);
}
.review-day-name { font-weight: 600; flex: 1; min-width: 0; }
.review-weekday { font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label); color: var(--text-3); }
.review-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-soft);
}
.review-item-main { flex: 1; min-width: 0; }
.review-item-name { font-size: 15px; }
.review-item-note { font-size: 12px; color: var(--text-3); margin-top: 2px; }
.review-item-dose { font-size: 13.5px; color: var(--text-2); font-variant-numeric: tabular-nums; }
/* The dose as a button, because it opens the numbers behind it. Looks like the
   text it replaced until you need it. */
.review-item-dose {
  border: 0; background: transparent; color: var(--text-2);
  font: inherit; font-size: 13px; padding: 4px 6px; cursor: pointer;
  border-bottom: 1px dashed var(--line-strong); border-radius: 0;
  text-align: right; white-space: nowrap;
}
.review-item-dose:hover, .review-item-dose:focus-visible { color: var(--accent); }

/* One row's prescription, open under it. Wraps on a phone rather than
   squeezing five number boxes onto a line nobody can hit. */
.dose-editor {
  display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: flex-end;
  padding: 12px 0 14px; border-bottom: 1px solid var(--line-soft);
}
.dose-field { display: flex; flex-direction: column; gap: 3px; flex: 0 1 auto; }
.dose-field span {
  font-size: 10.5px; font-weight: 600; letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--text-3);
}
.dose-field input {
  width: 72px; min-height: 40px; padding: 6px 8px; font-size: 15px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); color: inherit;
}

/* Said once the coach has changed a draft, because the alternative is a screen
   that quietly disagrees with what was generated and no way back. */
.review-edited {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center;
  justify-content: space-between; margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--line); font-size: 13.5px; color: var(--text-2);
}

/* Swapping a movement rather than only removing it. Quieter than the drop
   beside it — replacing something is the ordinary edit and removing it is the
   irreversible one, so the destructive control keeps the clay. */
.review-swap {
  border: 1px solid var(--line); background: transparent; color: var(--text-2);
  border-radius: var(--r-pill); padding: 4px 10px;
  font: inherit; font-size: 11.5px; font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  cursor: pointer; white-space: nowrap;
}
.review-swap:hover, .review-swap:focus-visible { border-color: var(--accent); color: var(--accent); }

/* Adding a movement, beside swapping one. The same shape on purpose — both are
   "change this day", and neither should shout over the other. The one on an
   empty day stands alone and needs the room a row would have given it. */
.review-day-body > .review-add { margin: 10px 0 4px; }

.review-drop {
  border: 0;
  background: transparent;
  color: var(--clay, #E0705C);
  font-size: 19px;
  line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
}

.copy-row { display: flex; gap: 7px; align-items: center; margin-top: 8px; }
.copy-row input {
  flex: 1;
  font-family: var(--f-mono, monospace);
  font-size: 12px;
  padding: 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: inherit;
}
.status-line { font-size: 13px; margin-top: 10px; min-height: 18px; }
/* The reserved height stops a form jumping when "Saved." appears, which is
   worth it mid-form and is just a hole when the line is the last thing in a
   card and has nothing to say. */
.card > .status-line:last-child:empty { min-height: 0; margin-top: 0; }
.status-line.ok { color: var(--accent); }
.status-line.bad { color: var(--clay, #E0705C); }


/* ---------------------------------------------------------------------------
   Ask — the coaching assistant
--------------------------------------------------------------------------- */
.chat-log { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }

.chat-msg {
  max-width: 88%;
  padding: 11px 13px;
  border-radius: var(--r);
  font-size: 15px;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.chat-msg.user {
  align-self: flex-end;
  background: var(--accent-wash);
  border: 1px solid var(--accent-dim);
}
.chat-msg.assistant {
  align-self: flex-start;
  background: var(--surface);
  border: 1px solid var(--line);
}
.chat-msg.pending { opacity: 0.55; font-style: italic; }
.chat-msg.error {
  align-self: flex-start;
  border-color: var(--clay-dim);
  background: var(--red-wash);
  color: var(--clay, #E0705C);
}

.chat-composer { display: flex; gap: 8px; align-items: flex-end; }
.chat-input {
  flex: 1;
  font-family: inherit;
  font-size: 16px;
  padding: 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: inherit;
  resize: none;
  max-height: 140px;
}
.chat-input:focus { outline: none; border-color: var(--accent); }
.chat-send { white-space: nowrap; }

.chat-suggestions { display: flex; flex-wrap: wrap; gap: 7px; margin: 4px 0 14px; }
.chat-suggestion {
  padding: 9px 13px;
  border-radius: var(--r-pill);
  border: 1px dashed var(--line-strong);
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}


/* ---------------------------------------------------------------------------
   Assistant dock
   Persistent across screens. Sits in the same band as the rest timer, so when
   that appears the launcher steps up out of its way rather than under it.
--------------------------------------------------------------------------- */
/* Bottom left, not bottom right, and the reason is the set row.
   `.set-line` puts the tick in a 44px column hard against the right edge, so a
   button floating over the bottom right corner covered a tick — completely,
   because the tick is narrower than the button. At any scroll position one of
   them could not be pressed, and a client presses eighteen of them a session.
   On the left it overlaps the reps input instead, which is three times wider
   and still tappable beside it. Checked by `tests/browser/tap_targets.py`,
   which asks the browser what is under each control rather than trusting
   this comment. */
.ask-fab {
  position: fixed; left: 12px;
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 10px);
  z-index: 35;
  display: flex; align-items: center; gap: 7px;
  /* Narrower than it was. It covers something at every scroll position — that
     is what fixed means — so the thing to minimize is how much. */
  padding: 11px 16px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--ink);
  font-family: inherit; font-size: 14px; font-weight: 700;
  cursor: pointer;
  box-shadow: var(--shadow-lg);
}
.ask-fab[hidden] { display: none; }
.ask-fab svg { width: 18px; height: 18px; }
/* Set by showMini()/hideMini() so the two never stack on each other */
body.timer-docked .ask-fab {
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 72px);
}
/* And the same for the approve bar, which is the third fixed thing down
   there. Both at once stacks. */
body.approve-docked .ask-fab {
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 78px);
}
body.approve-docked.timer-docked .ask-fab {
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 140px);
}

.ask-panel {
  position: fixed; left: 0; right: 0;
  bottom: calc(var(--tab-h) + env(safe-area-inset-bottom));
  z-index: 45;
  max-width: 520px; margin: 0 auto;
  max-height: min(72vh, 620px);
  display: flex; flex-direction: column;
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 98%, transparent);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  border: 1px solid var(--line);
  border-bottom: none;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -10px 40px rgba(0,0,0,0.5);
  padding: 14px 14px 12px;
}
.ask-panel.hidden { display: none; }

.ask-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; padding-bottom: 10px;
  border-bottom: 1px solid var(--line-soft);
}
.ask-title {
  font-family: var(--f-display);
  font-size: var(--fs-3); font-weight: 700; letter-spacing: -0.01em;
}
.ask-sub { font-size: 13px; color: var(--text-2); margin-top: 2px; max-width: 34ch; }

.ask-panel .chat-log {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: 12px 2px; margin-bottom: 10px;
}
/* The two jobs, side by side. `.pills` already exists as the segmented
   control on Progress and in the client workspace; this only places it. */
.ask-tabs { margin: 10px 0 0; align-self: flex-start; }
.ask-tabs[hidden] { display: none; }
/* Said before the first message arrives, because until one did there was
   nothing on this screen to suggest a coach could be reached at all. */
.dm-empty {
  padding: 18px 2px; color: var(--text-2); font-size: 14.5px; line-height: 1.55;
}


/* ---------------------------------------------------------------------------
   A photo or video in the thread

   The bubble stops being a line of text and becomes a frame around the thing
   that was sent, so the media sets the height and the note sits under it.
--------------------------------------------------------------------------- */
.chat-msg.dm.file { display: flex; flex-direction: column; gap: 8px; max-width: 82%; }
.dm-media {
  display: block; width: 100%; max-width: 320px; max-height: 260px;
  height: auto; border-radius: var(--r-2); background: var(--surface-2);
  object-fit: contain;
}
.dm-note { font-size: 14px; line-height: 1.5; white-space: pre-wrap; }
/* Quiet, and on the sender's own side of the exchange as much as the
   recipient's — either of them may delete a file, and the notice promises it
   goes for good when they do. */
.dm-del {
  align-self: flex-start; background: none; border: 0; padding: 0;
  font: inherit; font-size: 11.5px; font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3); cursor: pointer; text-decoration: underline;
}
.dm-del:disabled { opacity: 0.5; cursor: default; }
/* The paperclip beside Send, not above the box. `form` is a column — see the
   base rule — so the two controls need a row of their own to share one line,
   and Send takes what the paperclip leaves. Only on the Messages tab: see
   showAskTab, since a file goes to a person and not to the assistant. */
/* Full width, because the composer aligns its children to the end and a
   shrink-to-fit row would have pulled Send in from the edge it has
   always sat on. */
.chat-actions { display: flex; gap: 8px; align-items: center; width: 100%; }
.chat-actions .chat-send { flex: 1; }
.chat-attach { flex: none; }

/* ---------------------------------------------------------------------------
   Hours asked for and not yet answered
--------------------------------------------------------------------------- */
.req-tag {
  display: inline-block; margin-top: 4px;
  font-size: 11px; font-weight: 600; letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--warn, var(--text-3));
  border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 8px;
}
.req-row {
  border-top: 1px solid var(--line-soft); padding: 12px 0;
  display: flex; flex-wrap: wrap; gap: 8px 12px;
  align-items: center; justify-content: space-between;
}
.req-who { display: flex; flex-direction: column; gap: 2px; }
.req-name { font-size: 15px; font-weight: 600; color: var(--text); }
.req-when {
  font-size: 13px; color: var(--text-2); font-variant-numeric: tabular-nums;
}

/* An unfinished session from earlier in the week, offered on Today.
   Accent-edged rather than warning-colored: it is an invitation to train,
   not a telling-off for having missed one. */
.catch-up {
  display: flex; flex-wrap: wrap; gap: 10px 14px;
  align-items: center; justify-content: space-between;
  border-left: 3px solid var(--accent);
}
.catch-up-text { flex: 1 1 190px; font-size: 14.5px; line-height: 1.5; }

/* Said when the session changed under somebody who is part-way through it.
   A line rather than a re-render: their numbers would survive either way, but
   redrawing the form under a person mid-set is its own kind of wrong. */
.day-changed {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center;
  justify-content: space-between;
  border: 1px solid var(--accent); border-radius: var(--r-2);
  background: var(--accent-wash, var(--surface-2));
  padding: 12px 14px; margin: 0 0 14px;
  font-size: 14px; line-height: 1.5; color: var(--text);
}

/* ---------------------------------------------------------------------------
   The coach's journal

   A list of dated notes, so rules rather than boxes: each entry is separated
   from the next by a hairline and nothing else. Boxing them would have made
   fifteen cards out of fifteen sentences.
--------------------------------------------------------------------------- */
.jrn-empty {
  padding: 16px 2px 4px; color: var(--text-2); font-size: 14.5px; line-height: 1.55;
  margin: 0;
}
.jrn-entry {
  border-top: 1px solid var(--line-soft); padding: 12px 0;
  display: flex; flex-direction: column; gap: 4px;
}
/* The date, whose entry it is when that is not obvious, and the way out — all
   on one line above the note, so the note itself gets the full column width.
   Beside the text it cost three lines of wrapping on a phone to say one word. */
.jrn-meta {
  display: flex; gap: 10px; align-items: center; justify-content: flex-start;
}
/* Smaller than the note it sits above. Deleting is available on every entry
   and interesting on almost none of them, and at full button weight it was the
   first thing the eye found in a list of sentences. */
.jrn-meta .jrn-del {
  margin-left: auto; font-size: 11.5px; padding: 3px 11px;
  color: var(--text-3);
}
.jrn-date {
  font-size: 12px; font-weight: 600; letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--text-2);
  font-variant-numeric: tabular-nums;
}
.jrn-who {
  font-size: 12px; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3);
}
.jrn-body {
  margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
/* Where the generator stops reading, said on the list rather than in a rule the
   coach has to hold in his head. */
.jrn-cutoff {
  border-top: 1px solid var(--line); margin: 0; padding: 14px 0 2px;
  font-size: 12px; font-weight: 600; letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--text-3);
}

/* ---------------------------------------------------------------------------
   The plan document
--------------------------------------------------------------------------- */
.plan-section { margin-top: 16px; }
.plan-heading {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-2); margin-bottom: 5px;
}
.plan-body { font-size: 14.5px; line-height: 1.55; margin: 0 0 4px; }
.plan-goal {
  border-left: 2px solid var(--accent-dim);
  padding-left: 12px; margin: 12px 0;
}
.plan-goal-name { font-weight: 600; font-size: 15px; margin-bottom: 6px; }
.plan-toggle {
  background: none; border: 0; color: var(--accent);
  font-family: inherit; font-size: 14px; cursor: pointer; padding: 8px 0;
}


/* ---------------------------------------------------------------------------
   The `hidden` attribute only sets display:none in the UA stylesheet, so any
   component rule with its own `display` silently wins and the element stays on
   screen. That's how a coach-only tab ended up visible to clients. This makes
   `hidden` mean hidden, whatever a component sets.
--------------------------------------------------------------------------- */
[hidden] { display: none !important; }


/* --- Note of the day, above today's session ---
   The one reversed band. The public page allows itself exactly one — ink
   ground, paper text — and in the app it goes to the coach's own words, which
   is the one thing on the screen the app did not write. Ink-on-ground is the
   palette's own pair reversed, so it holds on the dark schemes too. */
.motd {
  background: var(--text);
  color: var(--bg);
  border-radius: var(--r);
  padding: 16px 18px;
  margin-bottom: 14px;
}
.motd-label {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: inherit; opacity: 0.72; margin-bottom: 6px;
}
.motd-text { font-size: 16px; line-height: 1.5; margin: 0; }

/* --- Help sheet --- */
.help-group { margin-top: 4px; }
.help-group + .help-group { margin-top: 18px; }
.help-group h4 {
  font-family: var(--f-display, inherit);
  font-size: 15px; font-weight: 700; margin: 0 0 8px;
}
.help-item { display: flex; gap: 10px; padding: 7px 0; align-items: baseline; }
.help-where {
  flex: 0 0 96px;
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--accent); padding-top: 2px;
}
.help-what { flex: 1; font-size: 14.5px; line-height: 1.5; }
.help-what b { font-weight: 600; }
.help-intro { font-size: 14.5px; line-height: 1.55; color: var(--text-2); margin: 0 0 6px; }
@media (max-width: 460px) {
  .help-item { flex-direction: column; gap: 2px; }
  .help-where { flex: none; }
}


/* --- Messages between coach and client, inside the Ask panel --- */
.dm-block {
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 12px; margin-bottom: 4px;
  display: flex; flex-direction: column; gap: 8px;
}
.dm-label {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-3);
}
/* Today's three tabs: the session, the cooldown, the golf work. `.pills` is
   the segmented control this app already uses in three other places. */
.today-tabs { align-self: flex-start; }
.today-tabs[hidden] { display: none; }
.today-pane[hidden] { display: none; }
.today-pane { display: flex; flex-direction: column; gap: var(--s5); }
.tab-state[hidden] { display: none; }
/* The count, on the tab you are on. */
.tab-state.count {
  font-family: var(--f-body); font-variant-numeric: tabular-nums;
  font-size: 11.5px; font-weight: 700; padding: 2px 7px; margin-left: 7px;
  border-radius: var(--r-pill); background: var(--lime-wash); color: var(--lime);
}
/* And a dot on the ones you are not, because three counts do not fit on a
   390px phone and the third tab loses its state off the right edge. */
.tab-state.dot {
  width: 8px; height: 8px; border-radius: 50%; display: inline-block;
  margin-left: 7px; vertical-align: 1px;
}
.tab-state.dot.all  { background: var(--green); }
.tab-state.dot.some { background: var(--amber); }
.tab-state.dot.none { background: var(--line-strong); }

/* Between the two ratings of an area that moved. Small, and it takes the
   row's own color rather than a green or a red of its own — which way it went
   is already in the two words either side of it. */
.was-now { color: var(--text-3); margin: 0 6px; flex-shrink: 0; }

/* Plain. The dash was there to tell a person's message apart from the
   assistant's when the two shared one scroll; they no longer do, and inside
   its own pane every bubble is a person. */
.chat-msg.dm { border-style: solid; }
/* The app telling the client their program changed, in the coach's name.
   Same bubble, a label, and the lines kept — one note may carry several. */
.chat-msg.program { white-space: pre-line; border-left: 3px solid var(--accent-dim); }
.dm-kind { font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase;
           color: var(--text-3); margin-left: 6px; }
.dm-from {
  display: block; font-size: 11px; color: var(--text-2);
  text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 3px;
}
.chat-msg.dm-sent { font-size: 13px; color: var(--text-2); font-style: italic; }


/* --- Brand marks --- */
/* The header carries the mark, three controls and the profile switch. On a
   phone that is already full, so the wordmark only appears once there is room
   for it — a clipped brand reads worse than no brand. */
.brand-lockup { display: flex; align-items: center; gap: 9px; min-width: 0; flex-shrink: 0; }
/* Vector, and drawn to the edges of its box — so no radius and no border,
   which were there to tidy the square photograph this replaced. */
.brand-mark {
  width: 25px; height: 32px; flex-shrink: 0;
  color: var(--brand-lime);
}
.brand-words { display: none; flex-direction: column; line-height: 1.05; }
.brand-ri, .brand-gf { white-space: nowrap; }
/* The public page's wordmark: a tracked line over a bold one. */
.brand-ri {
  font-family: var(--f-display);
  font-size: 10px; font-weight: 500; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--text-3);
}
.brand-gf {
  font-family: var(--f-display);
  font-size: 15px; font-weight: 700; letter-spacing: -0.01em;
  text-transform: uppercase; color: var(--text);
}
@media (min-width: 430px) {
  .brand-words { display: flex; }
}

/* The card itself, on the way in */
.gate-card-image {
  display: block; width: 100%; max-width: 168px;
  margin: 0 auto 18px; border-radius: var(--r-sm);
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.45);
}
.gate-tagline {
  text-align: center; font-size: 11.5px; line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--brand-lime); margin: 0 0 18px;
}


/* Screening and agreements. Wider than the sign-in card because it is read,
   not filled in — a waiver set at 380px is a waiver nobody reads. */
.gate-card.is-wide { max-width: 580px; }

.consent-section { margin: 22px 0 0; }
.consent-section h2 {
  font-family: var(--f-display);
  font-size: var(--fs-3); font-weight: 700; letter-spacing: -0.01em; margin: 0 0 4px;
}
.consent-section .consent-lede {
  font-size: 14px; line-height: 1.55; color: var(--text-2); margin: 0 0 12px;
}

/* The document text scrolls inside its own box, so the agree button stays
   reachable without scrolling past several hundred words on a phone. */
.consent-doc {
  max-height: 240px;
  overflow-y: auto;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  font-size: 13.5px;
  line-height: 1.62;
  color: var(--text-2);
}
.consent-doc p { margin: 0 0 10px; }
.consent-doc p:last-child { margin-bottom: 0; }
.consent-doc strong { color: var(--text); font-weight: 600; }
.consent-doc ol { margin: 0 0 10px; padding-left: 20px; }
.consent-doc li { margin-bottom: 7px; }

.consent-q {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-soft);
}
.consent-q:last-of-type { border-bottom: 0; }
.consent-q-text { flex: 1; font-size: 14px; line-height: 1.5; }
.consent-yn { display: flex; gap: 6px; flex-shrink: 0; padding-top: 1px; }
.consent-yn label {
  cursor: pointer;
  font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em;
  padding: 7px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  color: var(--text-3);
  user-select: none;
}
.consent-yn input { position: absolute; opacity: 0; pointer-events: none; }
.consent-yn input:checked + span {
  /* Set on the label via :has below; this keeps older engines from showing
     nothing at all when a choice is made. */
  color: var(--text);
}
.consent-yn label:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--accent);
}
.consent-yn label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A "yes" is not a failure, but the client should see that it was noticed. */
.consent-flag-note {
  margin: 12px 0 0; padding: 12px 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--clay-dim);
  background: var(--red-wash);
  font-size: 13px; line-height: 1.55; color: var(--text);
}
.consent-flag-note[hidden] { display: none; }

.consent-notes {
  width: 100%; box-sizing: border-box;
  margin-top: 12px; padding: 12px;
  min-height: 72px; resize: vertical;
  font: inherit; font-size: 15px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: inherit;
}

.consent-agree {
  display: flex; align-items: flex-start; gap: 11px;
  margin-top: 14px; font-size: 14px; line-height: 1.5;
  cursor: pointer;
}
.consent-agree input { width: 20px; height: 20px; flex-shrink: 0; accent-color: var(--accent); }


.toggle-row { display: flex; align-items: center; gap: 10px; font-size: 14.5px; }
.toggle-row input { width: 20px; height: 20px; accent-color: var(--accent); }
.settings-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--line-soft);
  font-size: 14.5px;
}
.settings-row:last-child { border-bottom: none; }
.settings-row .grow { flex: 1; min-width: 0; }
.settings-row .sub { font-size: 12px; color: var(--text-2); }


/* --- Booking --- */
.book-day { margin-top: 16px; }
.book-day-head {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-2); margin-bottom: 7px;
}
.book-slots { display: flex; flex-wrap: wrap; gap: 7px; }
.book-slot {
  padding: 10px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: transparent;
  color: inherit; font-family: var(--f-mono); font-size: 14px; cursor: pointer;
}
.book-slot:disabled { opacity: 0.35; cursor: default; }

/* How long and where. Two fields side by side on anything wider than a small
   phone, stacked below that — a select squeezed to half of a 320px screen
   truncates the one place name that distinguishes it from the other. */
.book-shape { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0 4px; }
.book-shape-field { display: flex; flex-direction: column; gap: 5px; flex: 1 1 150px; }
.book-shape-field > span {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-2);
}
.book-shape-field select {
  width: 100%; min-height: 44px; padding: 10px 34px 10px 12px;
  border-radius: var(--r-sm); border: 1px solid var(--line-strong);
  /* `background-color`, not the `background` shorthand: the shorthand resets
     background-image, which is where the base `select` rule draws the chevron.
     Written as the shorthand first, and the dropdowns rendered as plain text
     boxes with no sign they could be opened. */
  background-color: var(--surface); color: inherit; font-size: 15px;
}
/* One place is not a choice. Hidden rather than disabled: a select somebody
   can open and find nothing in is an invitation to wonder what is missing. */
.book-shape-field.hidden { display: none; }
@media (hover: hover) { .book-slot:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); } }

/* A card that is telling you something is wrong rather than showing you
   something. Amber rather than red: nothing is broken, but a coach with no
   published hours has clients being told to book and finding nothing. */
/* Still a box: it is a notice, not a section. */
.warn-card {
  border: 1px solid var(--amber); background: var(--amber-wash);
  border-radius: var(--r); padding: var(--s4);
}
/* The title stays ordinary text. Amber on an amber wash measured 3.86:1 in
   the light palette — the same lesson the toast learned: the color carries
   in the edge and the ground, where it costs no contrast, and not in the
   words, where it does. */
.warn-card .card-title { color: var(--text); }
/* The bar is the first flex item, so `space-between` would put the title at
   the far end of the row — which it did. */
.warn-card .card-head { justify-content: flex-start; }
.warn-card .card-head::before {
  content: ""; width: 4px; align-self: stretch; border-radius: var(--r-pill);
  background: var(--amber); margin-right: 10px;
}

/* The one line of diary that stays above the workout. A button rather than a
   card, because it is a way to get somewhere and not a thing to read. */
.next-up {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  margin: 0 0 4px; padding: 12px 0;
  background: none; border: 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  color: var(--text);
  font-family: var(--f-body); font-size: 14px; text-align: left;
  cursor: pointer;
}
.next-up[hidden] { display: none; }
.next-up-label {
  font-family: var(--f-body); font-size: var(--fs-0); font-weight: 600;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-3); flex-shrink: 0;
}
.next-up-body { flex: 1; min-width: 0; color: var(--text-2); }
.next-up-body b { color: var(--text); font-weight: 600; }
.next-up-go {
  font-size: 12.5px; font-weight: 600; color: var(--accent); flex-shrink: 0;
}
@media (hover: hover) { .next-up:hover .next-up-go { text-decoration: underline; } }

/* Three parts, and the middle one wins the argument over width. `.btn` is
   width:100% by default, so an un-inlined Cancel here asked for the whole row
   and shrank the date and the name into two-word columns — five wrapped lines
   of "First session · Waiting Booker" beside a button the width of the card. */
.session-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--line-soft);
}
.session-row:last-child { border-bottom: none; }
.session-when {
  font-family: var(--f-mono); font-size: 14px; color: var(--accent);
  flex: 0 0 auto; min-width: 108px;
}
.session-what { flex: 1 1 auto; min-width: 0; font-size: 14.5px; }
/* An hour asked for and not yet answered. A line of its own, because the row
   is a flex line ending in two buttons: inside the description the pill had
   about eight characters of width, wrapped to three words tall, and sat on
   top of Move. `flex-basis: 100%` makes the row wrap and gives it the width
   of the card instead. */
.session-row { flex-wrap: wrap; }
/* Full basis so it takes the line; max-content so the pill is the width of
   its words rather than the width of the card. */
.session-tag { flex: 0 0 100%; max-width: max-content; margin: 2px 0 0; }

/* Why this person is on the coach's list: a name with the reason under it,
   rather than a name and a reason fighting for one line. The reason is a
   sentence, so it does not take `.session-when`'s mono and its accent green —
   "hasn't moved in three sessions" in the same green as a personal best is the
   wrong news in the right color. */
.row-main { flex: 1; min-width: 0; }
.row-name { font-size: 14.5px; }
.row-note {
  font-size: 12.5px; color: var(--text-2); line-height: 1.4; margin-top: 2px;
}
.row-note.accent { color: var(--accent); }
/* Only the clock is mono. The signal notes are sentences. */
.row-note.since { font-family: var(--f-mono); color: var(--accent); }
.row-note.amber { color: var(--amber); }
.row-note.red   { color: var(--red); }
.row-note.green { color: var(--green); }

.avail-row { display: flex; align-items: center; gap: 8px; padding: 7px 0; }
.avail-day { flex: 0 0 46px; font-size: 13px; }
.avail-row input[type="number"] {
  width: 66px; font-family: var(--f-mono); text-align: center;
  padding: 8px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface); color: inherit;
}
.avail-row input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); }

/* Unread/awaiting-reply count on the Ask launcher */
.ask-badge {
  min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: var(--r-pill); background: var(--clay); color: var(--ink);
  font-family: var(--f-mono); font-size: 11px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.ask-badge[hidden] { display: none; }


/* --- Goals --- */
/* Separators *between* goals, drawn on the top of each one after the first.
   The obvious `border-bottom` plus `:last-child { border: none }` did not work
   here and neither did `:last-of-type`: the card ends with a status-line div,
   so the last child is not a goal and the last div is not a goal either. CSS
   has no `:last-of-class`, and a rule between siblings needs neither. */
.goal { padding: 14px 0; }
.goal + .goal { border-top: 1px solid var(--line-soft); }
.goal-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.goal-name { font-size: 15.5px; font-weight: 600; }
.goal-now { font-size: 15px; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; }
.goal-bar { height: 5px; border-radius: var(--r-pill); background: var(--surface-3); margin: 9px 0 6px; overflow: hidden; }
.goal-fill { height: 100%; background: var(--accent); border-radius: var(--r-pill); }
.goal-meta { font-size: 12px; color: var(--text-2); }
.goal-log { display: flex; gap: 7px; margin-top: 10px; }
.goal-log input {
  flex: 1; font-family: var(--f-mono); text-align: center; font-size: 16px;
  padding: 10px; border-radius: var(--r-sm); border: 1px solid var(--line-strong);
  background: var(--surface); color: inherit;
}


/* --- Coach View --- */
.coach-view-select {
  max-width: 148px;
  font-family: inherit; font-size: 13px;
  padding: 8px 9px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--text);
}
.coach-view-select[hidden] { display: none; }

.as-client-banner {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 14px;
  background: var(--accent); color: var(--ink);
  font-size: 13px; font-weight: 600;
}
.as-client-banner.hidden { display: none; }
.as-client-banner button {
  border: 0; border-radius: var(--r-pill); padding: 6px 12px;
  background: rgba(0,0,0,0.16); color: inherit;
  font-family: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  /* Pushed right whatever sits to its left, so adding the picker didn't
     redistribute the banner's spacing. */
  margin-left: auto;
}
/* Whose screen this is. On the lime banner so it reads as part of the
   sentence rather than as a form control dropped into one. */
.as-client-banner select {
  border: 0; border-radius: var(--r-pill); padding: 5px 24px 5px 10px;
  background: rgba(0,0,0,0.16); color: inherit;
  font-family: inherit; font-size: 12.5px; font-weight: 700;
  cursor: pointer; max-width: 46vw;
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2314170F' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 7px center;
  background-size: 11px;
}
.as-client-banner select[hidden] { display: none; }
.as-client-note { font-weight: 600; opacity: 0.72; }
/* On a narrow screen the name and the way out matter; the explanation does
   not, and it is the first thing that would push the button off the edge. */
@media (max-width: 460px) { .as-client-note { display: none; } }


/* The one part of the account sheet that can't be undone, set apart so it
   doesn't read as another row of settings. */
.danger-group {
  border-top: 1px solid var(--clay-dim);
  margin-top: 22px;
  padding-top: 18px;
}
.danger-group h4 { color: var(--clay); }
.danger-group[hidden] { display: none; }

.agreement-row {
  display: flex; align-items: baseline; gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 14px;
}
.agreement-row:last-child { border-bottom: 0; }
.agreement-row .grow { flex: 1; }
.agreement-row .when { font-size: 12.5px; color: var(--text-3); }


/* A flagged screening. The card gets an edge so it reads as "look at this"
   from across the roster, not as another gray panel. */
.pill.clay { background: var(--red-wash); border-color: var(--clay-dim); color: var(--clay); }
.card-flagged { border-color: var(--clay-dim); }

.screening-flags { margin-top: 4px; }
.screening-flag {
  padding: 11px 13px;
  margin-top: 8px;
  border-radius: var(--r-sm);
  border: 1px solid var(--clay-dim);
  background: var(--red-wash);
  font-size: 14px; line-height: 1.5;
}
.screening-notes { margin-top: 16px; }


/* This month's AI bill. The figure is the point of the card, so it is set
   like a number rather than like a sentence. */
.spend-total {
  display: flex; align-items: baseline; gap: 10px;
  padding: 4px 0 14px;
}
.spend-figure {
  font-family: var(--f-display);
  font-size: var(--fs-6); font-weight: 800; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.spend-total.over .spend-figure { color: var(--clay); }
.spend-of { font-size: 13px; color: var(--text-3); }
.spend-amount { font-variant-numeric: tabular-nums; font-size: 14.5px; }

/* ===================== Coach at fifty clients =====================
   The Clients screen used to stack the roster, an add-client form, eight
   sections of Settings and the API key. At fifty clients that meant scrolling
   past everyone you coach to change an SMTP password. Settings is its own
   screen now; what follows is what replaced it. */

.screen-head-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
}
.btn-inline { width: auto; flex-shrink: 0; padding: 11px 18px; font-size: 14px; }

.roster-tools { margin: 0 0 12px; }
.roster-tools[hidden] { display: none; }
.roster-tools .field-input { margin: 0; }

/* --- Enquiries ------------------------------------------------------- */
.card-leads { border-color: var(--accent-dim); }
.lead-row {
  padding: 14px 0;
  border-bottom: 1px solid var(--line-soft);
}
.lead-row:last-of-type { border-bottom: 0; }
.lead-name { font-weight: 600; font-size: 15.5px; display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.lead-flag {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--clay); border: 1px solid var(--clay-dim);
  border-radius: var(--r-pill); padding: 2px 8px;
}
.lead-meta { font-size: 13px; color: var(--text-2); margin-top: 3px; }
.lead-contact { font-size: 13px; color: var(--text-3); margin-top: 2px; }
.lead-note {
  margin-top: 9px; padding: 2px 0 2px 12px;
  border-left: 3px solid var(--line);
  font-size: 14px; line-height: 1.5; color: var(--text-2);
}
.lead-actions { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 11px; }

/* --- Settings as its own screen -------------------------------------- */
.settings-screen {
  position: fixed; inset: 0; z-index: 120;
  background: var(--bg);
  overflow-y: auto;
  padding: 0 0 40px;
}
.settings-screen.hidden { display: none; }
body.settings-open { overflow: hidden; }
.settings-head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 18px 16px;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.settings-head .screen-title { margin: 0; }
.settings-body { max-width: 520px; margin: 0 auto; padding: 16px; }

.testimonial-edit {
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 14px; margin-bottom: 12px;
}
.testimonial-edit .field-input { margin-top: 8px; }

/* --- Desktop width, coaches only -------------------------------------
   The training screens stay a phone column: they are used on a phone, in a
   gym, mid-set. Only the two screens where a coach does desk work widen, and
   only when they are not viewing the app as a client. */
/* Superseded by the rail below, which owns the coach's desktop layout. What
   is left here is the roster's two-column grid, which the rail doesn't
   affect. */
@media (min-width: 900px) {
  body.is-coach #rosterList {
    display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  }
  body.is-coach #rosterList .roster-row { margin: 0; }
  .settings-body { max-width: 880px; }
  .settings-body .card { max-width: 620px; }
}

/* ===================== Progress, and the new tab bar =====================
   Three tabs instead of five. History, Body and Trends are panes behind pills
   rather than destinations of their own — they were always three views of the
   same question. */

#screen-progress .pills { margin-bottom: var(--s5, 24px); }
#screen-progress .pane.hidden { display: none; }
#screen-progress .screen-head { margin-bottom: var(--s4, 16px); }

/* The panes carry their original markup, which opened with its own heading
   back when each was a separate screen. The pill above now says the same
   thing.

   #pane-body has TWO direct-child heads though — "Body" and "On the course" —
   and only the first is the redundant one. The second introduces the rounds
   cards, and it had been hidden along with it, leaving that section with no
   heading at all. */
#pane-trends > .screen-head,
#pane-body > .screen-head:not(#golfHead),
#pane-history > .screen-head { display: none; }

/* Segmented pills — the navigation control, not the status badge.
   `.pill` was already taken in this stylesheet for status badges, so these are
   scoped under `.pills`. Higher specificity, no rename, no collision. */
.pills {
  display: inline-flex; gap: 4px; padding: 4px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-pill); max-width: 100%;
  overflow-x: auto; scrollbar-width: none;
}
.pills::-webkit-scrollbar { display: none; }
.pills .pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 17px; border: 0; border-radius: var(--r-pill);
  background: transparent;
  font-family: var(--f-body); font-size: 13.5px; font-weight: 600;
  letter-spacing: 0; text-transform: none;
  color: var(--text-2); white-space: nowrap; cursor: pointer;
  transition: background .14s ease, color .14s ease;
}
.pills .pill:hover { color: var(--text); }
.pills .pill[aria-selected="true"] { background: var(--surface-3); color: var(--text); }
.pills .pill .count {
  font-family: var(--f-body); font-variant-numeric: tabular-nums;
  font-size: 11.5px; font-weight: 700; padding: 2px 7px;
  border-radius: var(--r-pill); background: var(--lime-wash); color: var(--lime);
}
.pills .pill[aria-selected="true"] .count { background: var(--lime); color: var(--ink); }

/* --- Roster rows, with signals ------------------------------------------
   Status moved from the meta line into tags on the right, so the scan for
   "who needs me" runs down one column instead of through a sentence. */
.roster-row .disc {
  display: grid; place-items: center; flex-shrink: 0;
  width: 40px; height: 40px; border-radius: var(--r-pill);
  background: var(--text); color: var(--bg);
  font-family: var(--f-display); font-size: 13px; font-weight: 700;
}
.roster-row.flagged .disc { background: var(--lime-wash); color: var(--lime); }
.roster-row .tag {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: 5px 11px; border-radius: var(--r-pill);
  font-family: var(--f-body); font-size: 11.5px; font-weight: 600;
  /* `.tag` already existed here as a small uppercase mono chip. These are
     sentences a coach reads — "12 days quiet" — so the inherited casing and
     tracking are reset rather than lived with. Third collision of this kind;
     see also `.pill`. */
  text-transform: none; letter-spacing: 0.02em; white-space: nowrap;
  border: 1px solid var(--line-strong); color: var(--text-2);
  /* These are buttons now — each one opens the client at the section it is
     about. Undo the UA button styling, keep the chip. */
  appearance: none; cursor: pointer; min-height: 32px;
  transition: filter .12s ease, box-shadow .12s ease;
}
/* Below 44px, deliberately: a tag this size is a chip a coach reads first and
   taps second, and 44px chips would turn a roster row into a wall of buttons.
   A tap that misses lands on the row, which opens the same client at their
   usual section — the wrong destination, never a dead tap. */
.roster-row .tag:hover { filter: brightness(1.12); }
.roster-row .tag:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.roster-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.roster-row .tag.amber { background: var(--amber-wash); border-color: transparent; color: var(--amber); }
.roster-row .tag.red   { background: var(--red-wash);   border-color: transparent; color: var(--red); }

.tab-badge {
  position: absolute; top: 6px; right: 50%; transform: translateX(20px);
  min-width: 17px; padding: 1px 5px; border-radius: var(--r-pill);
  background: var(--amber); color: var(--ink);
  font-size: 10.5px; font-weight: 700; line-height: 15px;
  font-variant-numeric: tabular-nums;
}
.tab { position: relative; }

/* One control where there were two. The "Viewing" name row and the "Coach
   view" dropdown both chose a client from the same list, differing only in
   whether coach chrome stayed on screen — and the roster now chooses the
   client. What survives is the mode. */
.coach-view-btn {
  font-family: var(--f-body); font-size: 13px; font-weight: 600;
  padding: 9px 15px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--text-2); cursor: pointer; white-space: nowrap;
  transition: color .14s ease, border-color .14s ease, background .14s ease;
}
.coach-view-btn:hover { color: var(--text); border-color: var(--line-strong); }
.coach-view-btn[aria-pressed="true"] {
  background: var(--lime-wash); border-color: transparent; color: var(--lime);
}
.coach-view-btn[hidden] { display: none; }

/* --- The client workspace ------------------------------------------------
   Was one column holding review, screening, a 25-field intake, messages, the
   assistant transcript, contact and access all at once. Same sections, behind
   pills. */
#clientTabs { margin-bottom: 20px; }
.ctab.hidden { display: none; }
.ctab > .card + .card { margin-top: 14px; }

/* On a wide screen the coach's two-column reading holds; on a phone the pills
   scroll horizontally rather than wrapping into two rows. */
@media (max-width: 560px) {
  #clientTabs { width: 100%; }
}

/* `.btn` is full-width by default in this stylesheet, which is right for a
   primary action on a phone and wrong for a back link. */
.btn-back {
  width: auto; padding: 9px 16px; font-size: 13.5px;
  margin-bottom: 14px; border-radius: var(--r-pill);
}

/* ===================== The coach's rail =====================
   Not a second navigation. The same tab bar markup, laid out as a left rail
   once there is room for one — so there is one set of destinations in the
   HTML, one set of click handlers, and no second layout to keep in step.

   Two columns, and the right one flows normally: topbar, then whichever screen
   is showing. Naming both a single grid area stacked them instead, and a
   sticky topbar sitting on top of the page silently swallowed clicks on
   anything near the top of a screen.

   Only for coaches, and only when they are not viewing as a client — in client
   view they should see exactly the client's screen, bottom bar included. Below
   900px everything falls back to the bottom bar, which is right on a phone for
   both roles. */
@media (min-width: 900px) {
  body.is-coach .app {
    max-width: none; margin: 0;
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    align-items: start;
    min-height: 100dvh;
  }
  body.is-coach .app > * { grid-column: 2; }
  body.is-coach .as-client-banner { grid-column: 1 / -1; }

  /* The Ask bubble moved to the left to keep it off the set rows' tick
     column, which on a phone is the right thing. On the coach's desktop the
     left is where the navigation rail lives, so it clears that instead — and
     goes back to the edge when the rail is collapsed. */
  body.is-coach .ask-fab { left: calc(232px + 12px); }
  body.is-coach.rail-hidden .ask-fab { left: 12px; }

  /* On a wide screen the tab bar is a left rail rather than a bottom bar, so
     the offset that clears it on a phone would leave the approve bar floating
     62px above the floor with the rail behind its left end. */
  body.is-coach .approve-bar {
    bottom: 0; left: 232px; right: 0;
    max-width: none; margin: 0;
    padding: 12px 24px;
  }
  body.is-coach.rail-hidden .approve-bar { left: 0; }

  body.is-coach .tabbar {
    grid-column: 1; grid-row: 1 / 100;
    /* The bottom bar is fixed, centered and width-capped. All three have to go,
       or the rail floats in the middle of its own column — it was rendering
       145px wide inside a 232px track, centered, and looked like a bug nobody
       could name. */
    position: sticky; inset: 0 auto auto 0; top: 0;
    margin: 0; max-width: none; width: auto;
    align-self: start; height: 100dvh;
    display: flex; flex-direction: column; align-items: stretch;
    gap: 4px; padding: 22px 12px;
    border-right: 1px solid var(--line); border-top: 0;
    background: var(--bg-2); backdrop-filter: none;
  }
  body.is-coach .tabbar .tab {
    /* `flex: 1` divides a bottom bar evenly; in a column it stretches five
       destinations across a thousand pixels of height. */
    flex: 0 0 auto;
    flex-direction: row; align-items: center; justify-content: flex-start;
    gap: 12px; height: auto; padding: 11px 12px;
    border-radius: var(--r-sm); font-size: 14.5px; font-weight: 600;
    /* A rail is a list of places, not a row of labels. */
    text-transform: none; letter-spacing: 0;
  }
  body.is-coach .tabbar .tab svg { width: 18px; height: 18px; }
  body.is-coach .tabbar .tab span { font-size: 14.5px; }
  body.is-coach .tabbar .tab:hover { background: var(--surface); color: var(--text); }
  body.is-coach .tabbar .tab.active { background: var(--lime-wash); color: var(--lime); }

  /* The badge sat centered above an icon in a bottom bar; in a rail it belongs
     at the end of the row. */
  body.is-coach .tab-badge { position: static; transform: none; margin-left: auto; }

  /* The two screens a coach works in get the room. The training screens stay a
     column, because that is what they are for. */
  body.is-coach .screen { padding-left: 32px; padding-right: 32px; }
  body.is-coach #screen-today,
  body.is-coach #screen-program,
  body.is-coach #screen-progress { max-width: 620px; }
  body.is-coach #screen-clients,
  body.is-coach #screen-schedule { max-width: 1160px; }
}

/* ===================== The movement screen =====================
   A mode of its own, like the timer. Filled in standing up, on a tablet held
   in one hand, with a client waiting — so: one item at a time, targets big
   enough to hit without looking, and every tap saved. */
.screening {
  position: fixed; inset: 0; z-index: 140;
  background: var(--bg);
  display: flex; flex-direction: column;
}
.screening.hidden { display: none; }

.screening-bar {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 20px;
}
.screening-bar .label { flex: 1; }
.screening-bar .tag { opacity: 0; transition: opacity .2s ease; }
.screening-bar .tag.on { opacity: 1; }

.screening-progress { height: 3px; background: var(--surface-2); margin: 0 20px; border-radius: var(--r-pill); }
.screening-progress > div {
  height: 100%; width: 0; background: var(--lime); border-radius: var(--r-pill);
  transition: width .25s ease;
}

.screening-body {
  flex: 1; overflow-y: auto;
  padding: 40px 20px 20px;
  max-width: 760px; width: 100%; margin: 0 auto;
}
.screening-title {
  font-family: var(--f-display); font-weight: 800;
  font-size: var(--fs-6); letter-spacing: -0.02em; line-height: 1.06;
  margin: 8px 0 12px;
}
.screening-how { color: var(--text-2); font-size: 16px; line-height: 1.6; margin: 0 0 28px; max-width: 60ch; }
.screening-notes { margin-top: 20px; }

/* Green / Yellow / Red. Deliberately large — this is tapped without looking
   down, mid-conversation. */
.gyr { display: flex; gap: 10px; margin-bottom: 8px; }
.gyr button {
  flex: 1; padding: 18px 10px;
  border-radius: var(--r-sm); border: 1.5px solid var(--line);
  background: transparent; cursor: pointer;
  font-family: var(--f-body); font-size: 13.5px; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-3);
  transition: all .14s ease;
}
.gyr button:hover { border-color: var(--line-strong); color: var(--text-2); }
.gyr button[aria-pressed="true"].g { background: var(--green-wash); border-color: var(--green); color: var(--green); }
.gyr button[aria-pressed="true"].y { background: var(--amber-wash); border-color: var(--amber); color: var(--amber); }
.gyr button[aria-pressed="true"].r { background: var(--red-wash);   border-color: var(--red);   color: var(--red); }

.gyr-side { margin-bottom: 18px; }
.gyr-side > .label { margin-bottom: 8px; }

.measure-row { display: flex; gap: 12px; margin-bottom: 18px; }
.measure-row .field { flex: 1; }
.measure-row .field-input { font-size: 20px; padding: 16px; text-align: center; font-variant-numeric: tabular-nums; }
.measure-unit { font-size: var(--fs-0); font-weight: 600; color: var(--text-3); text-transform: uppercase; letter-spacing: var(--track-label); }
.asym {
  font-size: 13px; color: var(--amber); margin: -8px 0 18px;
  font-variant-numeric: tabular-nums;
}

.screening-checks { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.screening-checks label {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 10px 15px; border: 1px solid var(--line); border-radius: var(--r-pill);
  font-size: 13.5px; color: var(--text-2); user-select: none;
}
.screening-checks input { position: absolute; opacity: 0; pointer-events: none; }
.screening-checks label:has(input:checked) {
  border-color: var(--lime); background: var(--lime-wash); color: var(--lime);
}

.screening-foot {
  display: flex; gap: 12px; align-items: center;
  padding: 16px 20px calc(16px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  max-width: 760px; width: 100%; margin: 0 auto;
}
.screening-foot .btn { width: auto; }
.screening-foot .grow { flex: 1; }

/* Finding → need → movements, on the coach's Assess tab. */
.derivation {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 14px; padding: 7px 0;
}
.derivation .grow { flex: 1; min-width: 140px; }
.derivation .arrow { color: var(--text-3); }
.derivation .tag { text-transform: capitalize; }
.code {
  font-family: var(--f-mono); font-size: 11px; font-weight: 700;
  letter-spacing: 0.06em; padding: 4px 8px; border-radius: var(--r-sm);
  background: var(--lime-wash); color: var(--lime);
}
.stack-2 { display: flex; flex-direction: column; gap: 4px; }
.row.wrap { display: flex; flex-wrap: wrap; gap: 7px; }
.tag.lime { background: var(--lime-wash); border-color: transparent; color: var(--lime); }

/* ===================== Design-system primitives =====================
   `list`, `stat` and the tag tones were written in design/ui.css for the
   preview, then used from app.js on real screens — where ui.css isn't loaded,
   so they rendered as unstyled text. Rather than load ui.css and fight this
   stylesheet for `.pill`, `.tag`, `.card` and `.btn` (all already taken here),
   the handful of primitives the app actually uses are defined once, here,
   where they can be scoped against what already exists.

   Fifth collision of this kind. The pattern is settled: new components either
   scope under a parent or reset what they inherit. */

/* One container, hairline rows — instead of a bordered card per item. */
.list { border-top: 1px solid var(--line-soft); }
.list-row {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 14px 0; text-align: left;
  border-bottom: 1px solid var(--line-soft);
  background: none; color: inherit; font-family: var(--f-body);
}
.list-row:last-child { border-bottom: 0; }
.list-row .grow { flex: 1; min-width: 0; }
.list-row .name { display: block; font-size: 15px; font-weight: 600; line-height: 1.35; }
.list-row .meta { display: block; font-size: 13px; color: var(--text-3); margin-top: 3px; }

/* `.tag` is a small uppercase mono chip here. In a list row it carries a
   sentence, so casing and tracking are reset — same fix as the roster. */
.list-row .tag {
  flex-shrink: 0; font-family: var(--f-body); font-size: 11.5px; font-weight: 600;
  text-transform: none; letter-spacing: 0.02em;
  padding: 5px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--line-strong); background: transparent; color: var(--text-2);
}
/* Not a signal — nothing is wrong. Somebody is training right now, which is
   the one roster fact that is true for the next hour and then isn't. */
.tag.live {
  background: var(--accent-wash); border-color: transparent; color: var(--accent);
}
/* `signals.TONES` has said "accent" for the client-filled-in-their-own-profile
   tag since it was written, and there was no rule for it — so the one signal
   deliberately colored as good news rendered in the same gray outline as a
   tag with no tone at all. Found by building the legend, which puts every tone
   side by side for the first time. */
.tag.accent { background: var(--accent-wash); border-color: transparent; color: var(--accent); }
.tag.green { background: var(--green-wash); border-color: transparent; color: var(--green); }
.tag.amber { background: var(--amber-wash); border-color: transparent; color: var(--amber); }
.tag.red   { background: var(--red-wash);   border-color: transparent; color: var(--red); }

/* Numbers get to be big — this is a fitness app and the number is the point. */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 0 var(--s5); }
.stat {
  border-top: 1px solid var(--line-soft);
  padding: var(--s4) 0;
}
.stat .k {
  font-size: var(--fs-0); font-weight: 600; letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--text-3);
}
.stat .v {
  font-family: var(--f-display); font-size: var(--fs-5); font-weight: 800; line-height: 1;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums; margin-top: 8px;
}
.stat .v small { font-size: 14px; font-weight: 600; color: var(--text-3); margin-left: 3px; }

/* The topbar at phone width.
   A coach carries six controls up here — help, settings, account, timer, the
   view-as toggle and sign out — and at 390px they overflow, which pushed the
   whole page sideways. The brand gives up its space first, then the view-as
   button truncates rather than the row breaking out of the viewport.

   Nothing is hidden: a control a coach can't find is worse than a narrow one,
   and this is the same set of actions either way. */
@media (max-width: 560px) {
  .topbar-inner { gap: 8px; padding-left: 12px; padding-right: 12px; }
  .brand-lockup { min-width: 0; flex-shrink: 1; overflow: hidden; }
  .topbar-right { gap: 6px; min-width: 0; }
  .icon-btn { width: 34px; height: 34px; }
  .icon-btn svg { width: 17px; height: 17px; }
  .coach-view-btn {
    padding: 8px 11px; font-size: 12px;
    max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
}

/* A warning that has to be read, not noticed. Used for mail that is
   configured and failing — the state where everything looks set up and
   nothing is arriving. */
.panel-warn {
  border: 1px solid var(--red); border-radius: var(--r-sm);
  background: var(--red-wash); padding: 14px; margin: 10px 0 4px;
  font-size: 14px; line-height: 1.55;
}
.panel-warn .field-hint { color: var(--text-2); }

/* The client's own notice that their block is running down. Amber when it is
   close, red when it has gone — the same two meanings the roster uses, so a
   color means one thing whichever side of the app you are on. */
.access-banner {
  margin: 0 0 12px; padding: 11px 13px; border-radius: var(--r);
  font-size: 13.5px; line-height: 1.45;
  background: var(--surface); border: 1px solid var(--line-soft); color: var(--text);
}
.access-banner.soon   { border-color: var(--amber); background: var(--amber-wash); }
.access-banner.lapsed { border-color: var(--red);   background: var(--red-wash); }
.access-banner b { font-weight: 700; }

/* --- Accounts (Settings) --- */
.admin-row .pill { font-size: 10.5px; padding: 1px 7px; vertical-align: middle; }
.admin-reset {
  padding: 10px 0 10px 12px; margin: 0 0 8px;
  border-left: 3px solid var(--line);
}
.admin-reset.hidden { display: none; }
.admin-add { margin-top: 16px; }
.admin-add summary {
  cursor: pointer; font-weight: 600; font-size: 14px;
  padding: 9px 0; list-style: none;
}
.admin-add summary::-webkit-details-marker { display: none; }
.admin-add summary::before { content: "+ "; color: var(--accent); font-weight: 700; }
.admin-add[open] summary::before { content: "\2013 "; }

/* The demonstration, under the written guide. Two states, and the difference
   is the point: a link Dan chose looks like an instruction, a search looks
   like a search. */
.howto-watch { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.howto-watch .btn { display: block; text-align: center; text-decoration: none; }
.howto-watch .field-hint { margin-top: 8px; }

/* --- Coaches corner: exercise videos --- */
.video-row { padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.video-row:last-child { border-bottom: 0; }
.video-name { font-size: 14.5px; font-weight: 600; margin-bottom: 8px; }
.video-open {
  font-size: 12px; font-weight: 600; color: var(--accent);
  text-decoration: none; margin-left: 6px;
}
.video-row .field-input { margin: 0; }

/* --- The rail toggle ---------------------------------------------------
   Hidden by default and only shown where a rail exists: a coach, wide screen,
   not in Coach View. On a phone the tab bar is at the bottom and there is
   nothing to collapse, so the button would be a control for a thing that
   isn't there. */
.rail-toggle { display: none; }

@media (min-width: 900px) {
  body.is-coach .rail-toggle {
    display: flex; align-items: center; justify-content: center;
    position: fixed; z-index: 40; top: 14px; left: 14px;
    width: 36px; height: 36px; border-radius: var(--r-sm);
    background: var(--surface-2); border: 1px solid var(--line);
    color: var(--text-2); cursor: pointer;
  }
  body.is-coach .rail-toggle:hover { color: var(--text); border-color: var(--line-strong); }
  body.is-coach .rail-toggle svg { width: 18px; height: 18px; }

  /* Collapsed: the rail column goes to zero and the content takes the width.
     The tab bar is only hidden, not removed, so nothing below it reflows. */
  body.is-coach.rail-hidden .app { grid-template-columns: 0 minmax(0, 1fr); }
  body.is-coach.rail-hidden .tabbar { display: none; }
  /* The toggle sits where the rail was; without the rail it needs the room the
     header would otherwise start in. */
  body.is-coach.rail-hidden .topbar-inner { padding-left: 58px; }
}

/* The pack prompt, inside the booking card. Set apart because it is a
   different question from "book one" — it is the one that empties the pack. */
.pack-prompt {
  margin-top: 14px; padding: 14px; border-radius: var(--r-sm);
  background: var(--lime-wash); border: 1px solid var(--lime-dim);
}
.pack-prompt .field-input { margin: 0; }
.pack-prompt .btn-row { gap: 8px; }


/* The roster legend.
   Under the list, shut, and quiet — a coach who has learned the tags should
   not have to scroll past an explanation of them every morning. Open, it is a
   two-column read: the tag exactly as it appears on the row, and what to do. */
.legend {
  margin-top: 18px; border-top: 1px solid var(--line);
}
.legend > summary {
  padding: 13px 0; cursor: pointer; color: var(--text-2);
  font-size: 14px; font-weight: 600; list-style: none;
  display: flex; align-items: center; gap: 8px; min-height: 44px;
}
.legend > summary::-webkit-details-marker { display: none; }
.legend > summary::before {
  content: "›"; display: inline-block; transition: transform .15s ease;
  font-size: 17px; line-height: 1; color: var(--text-3);
}
.legend[open] > summary::before { transform: rotate(90deg); }
.legend > summary:hover { color: var(--text); }
.legend > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.legend-lede { padding: 0 0 10px; margin: 0; }
.legend-body {
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px 0 8px; border-top: 1px solid var(--line);
}
.legend-row {
  display: grid; grid-template-columns: 118px 1fr; gap: 12px;
  align-items: start; padding: 11px 8px; border-radius: var(--r-sm);
}
/* Marked, not shouted: the reason to open this is a tag you can see. */
.legend-row.present { background: var(--surface-2); }
.legend-row .tag { justify-self: start; margin: 1px 0 0; }
.legend-text { font-size: 13.5px; line-height: 1.5; color: var(--text-2); }
.legend-do { color: var(--text); margin-top: 3px; }
@media (max-width: 560px) {
  .legend-row { grid-template-columns: 1fr; gap: 6px; }
}

/* The RPE scale, in the movement-guide sheet. */
.rpe-scale { display: flex; flex-direction: column; gap: 2px; margin-top: 14px; }
.rpe-row {
  display: grid; grid-template-columns: 62px 1fr; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
  font-size: 14px; line-height: 1.5; color: var(--text-2);
}
.rpe-row:last-child { border-bottom: 0; }
.rpe-n {
  font-family: var(--f-mono); font-size: 13px; font-weight: 600;
  color: var(--amber); text-align: right;
}
/* The effort target, on the line that states it. It reads as part of the
   sentence rather than as a control bolted to the end of it, which is why it
   is underlined rather than boxed — and the tappable area is grown past the
   ink, because the ink has to stay the size of the text around it. */
.rpe-help {
  position: relative; padding: 0; border: 0; background: none;
  color: var(--amber); font: inherit; cursor: pointer;
  text-decoration: underline; text-decoration-style: dotted;
  text-underline-offset: 3px;
}
.rpe-help::after { content: ""; position: absolute; inset: -13px -6px; }
.rpe-help:hover { color: var(--amber); text-decoration-style: solid; }
.rpe-help:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }

/* The plan editor got two more columns when the coach started prescribing
   effort, and nine of them inside a 520px sheet left the exercise name with
   115px — narrower than most of the names that go in it. This is the one
   sheet a coach works in at a desk rather than a client reads on a phone, so
   it gets the width when there is width to give. Below 640px it stays exactly
   as it was and the row scrolls the way the rest of the app's wide content
   does. */
@media (min-width: 640px) {
  #editorSheet { max-width: 720px; }
}
/* And on a phone nine columns cannot fit at all — sharing 390px between them
   gave every number box 24px, which is narrower than the two digits that go
   in it. The row keeps its real width and the list scrolls sideways instead,
   which is what the rest of the app does with content that will not shrink.
   The alternative was hiding columns on small screens, and a coach editing a
   plan on his phone needs the same fields as a coach editing it at a desk. */
@media (max-width: 639px) {
  /* `flex: none`, and the reason is that this list lives inside a flex-column
     sheet capped at 90dvh. Giving it a min-height of zero made it the one
     child that could shrink when the sheet's content ran past the cap — and it
     shrank to 0px, clipping every exercise row out of existence. The coach
     opened a day on his phone and found a name box, a Save button, and nothing
     to edit between them. The sheet scrolls as a whole; nothing inside it
     should give up height. */
  #editorItems { flex: none; }

  /* The row becomes a card. It used to keep its nine columns and scroll
     sideways — the comment here said the alternative was hiding columns on a
     small screen, and that a coach editing a plan on his phone needs the same
     fields as one editing it at a desk. Both true, and there was a third
     option: keep every field and stack them. A coach reading his own roster on
     a phone is the ordinary case here, not the degraded one.

     Laid out as the app already says a dose out loud — 4 × 12 to 15 at RPE 7,
     then minutes and pounds — so the shape of the line matches the shape of
     the sentence it produces. */
  #editorItems .editor-row {
    grid-template-columns: repeat(4, 1fr) 34px;
    grid-template-areas:
      "name name name name x"
      "kind kind kind kind kind"
      "sets reps to rpe rpe"
      "min min lbs lbs lbs";
    gap: 8px 6px;
    padding: var(--s3) 0;
    border-top: 1px solid var(--line-soft);
    align-items: end;
  }
  #editorItems .editor-row .e-name { grid-area: name; }
  #editorItems .editor-row .e-kind { grid-area: kind; }
  #editorItems .editor-row .row-x  { grid-area: x; justify-self: end; }
  #editorItems .e-cell:nth-of-type(1) { grid-area: sets; }
  #editorItems .e-cell:nth-of-type(2) { grid-area: reps; }
  #editorItems .e-cell:nth-of-type(3) { grid-area: to; }
  #editorItems .e-cell:nth-of-type(4) { grid-area: rpe; }
  #editorItems .e-cell:nth-of-type(5) { grid-area: min; }
  #editorItems .e-cell:nth-of-type(6) { grid-area: lbs; }
  /* The column heading is what a desktop reads; here each box says its own
     name, and keeps saying it once the box is full. A placeholder does not. */
  #editorItems .e-cell::before {
    content: attr(data-label);
    display: block; margin-bottom: 3px;
    font-size: var(--fs-0); font-weight: 600; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--text-3);
  }
  #editorItems .e-cell input { width: 100%; }
  /* A header for columns that are no longer columns. */
  #editorItems .editor-row.editor-head { display: none; }
  #editorItems .editor-row .pair-toggle { grid-column: 1 / -1; justify-self: start; }
  #editorItems .editor-gear-missing { grid-column: 1 / -1; }
}

/* Column headings for the plan editor. Not a table header — the rows are a
   grid, so this is a row that shares their track sizes and carries no inputs.
   Every query that reads a row as an exercise excludes it explicitly. */
.editor-row.editor-head {
  font: var(--t-label); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-2); padding: 0 7px 2px;
}
.editor-row.editor-head span { overflow: hidden; text-overflow: ellipsis; }


/* --- Working with -----------------------------------------------------------
   The coach's own Today, which is a diary rather than a workout. One row: who,
   and the two things worth doing about them. Wraps rather than shrinks on a
   phone — a client name truncated to "Jam…" is the one part that must stay
   readable. */
/* Outside the screens since 9 Sep 2026, so it supplies its own horizontal
   padding — it used to borrow `.screen`'s. Same gutter as the screens it sits
   above, so the name lines up with the content it names. No top rule: the
   topbar below it is already an edge, and two hairlines a few pixels apart
   read as a mistake. */
.working-with {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
  padding: 8px var(--s4);
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
/* One line where it fits. It is on every screen now, so the vertical space it
   takes is space every screen loses. */
.working-with-actions { display: flex; gap: 6px; margin-left: auto; }
.working-with-actions .btn { padding: 7px 11px; font-size: 12.5px; }
.working-with-label {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-2);
}
.working-with select {
  /* Greedy, and measured: sizing it to its content made the strip taller
     rather than shorter, because the arrow's padding pushes the intrinsic
     width out and the buttons wrapped anyway. */
  flex: 1 1 150px; min-width: 0; min-height: 44px; padding: 9px 34px 9px 12px;
  border-radius: var(--r-sm); border: 1px solid var(--line-strong);
  /* See `.book-shape-field select` — background-color, never the shorthand. */
  background-color: var(--surface); color: inherit; font-size: 15px;
}
.working-with-actions { display: flex; gap: 8px; flex: 0 0 auto; }


/* Tick-list of training types. Chips rather than a column of checkboxes: five
   of them fit on one phone row and read as a set of choices about one thing,
   which a stacked list does not. */
.checks { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 0; }
.checks label {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  min-height: 44px; padding: 9px 15px; font-size: 14px;
  /* The base `label` rule is uppercase and tracked, which is right above an
     input and wrong on a chip. "BARBELL AND PLATES" set a fourteen-item list
     shouting down a card whose actual instruction is one sentence. */
  font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface-2);
}
.checks label:has(input:checked) { border-color: var(--accent); color: var(--accent); }
.checks input { accent-color: var(--accent); width: 16px; height: 16px; }


/* --- Paired work and missing kit -------------------------------------------
   A paired movement is done in the rest between sets of the one above it. The
   left edge is the whole signal: it ties the row visually to its partner
   without nesting it, so the log stays the flat list it actually is. */
.exercise.paired {
  border-left: 2px solid var(--accent);
  border-top-left-radius: 4px; border-bottom-left-radius: 4px;
}
.paired-tag {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--accent); margin-bottom: 6px;
}
/* Amber, not red. Nothing is broken — the coach has prescribed something this
   person cannot load today, which is a substitution to make, not a failure. */
.gear-missing {
  margin-top: 5px; font-size: 12.5px; color: var(--amber);
}
/* What to have out before you start. Reads as a heading for the session rather
   than as another row in it. */
.gear-list {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px;
}
.gear-list span {
  font-size: 12px; padding: 4px 10px; border-radius: var(--r-pill);
  border: 1px solid var(--line); color: var(--text-2);
}
.gear-list span.missing { border-color: var(--amber); color: var(--amber); }


/* Pairing, as a state rather than a question.
   This was a full-width labeled checkbox under every row: eight repetitions
   of "Do this between sets of the movement above" down a day, all of them off,
   asking a question about seven movements it was never going to be true of.
   A rarely-used option should read as an affordance you can find, not as a
   form field you have to answer — so off it is one muted glyph, and on it says
   what it is and indents the row under its partner. */
.pair-toggle {
  grid-column: 1 / -1; justify-self: start;
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; cursor: pointer; color: var(--text-3);
  font-size: 12px; line-height: 1;
  /* Small to look at, 44px to hit — the size everything else in this app is
     reachable at, and a glyph is exactly the control most likely to be missed
     with a thumb. */
  min-height: 44px; min-width: 44px; padding: 4px 10px; margin: -6px 0 -2px;
}
.pair-glyph { font-size: 14px; }
.pair-word { display: none; }
.pair-toggle[aria-pressed="true"] { color: var(--accent); }
.pair-toggle[aria-pressed="true"] .pair-word { display: inline; }
@media (hover: hover) { .pair-toggle:hover { color: var(--text-2); } }
.pair-toggle:hover .pair-word { display: inline; }

/* The pairing itself, shown on the row rather than described under it. */
.editor-row.is-paired .e-name { border-left: 2px solid var(--accent); padding-left: 8px; }
/* Full width under the row it belongs to, so it reads as a note about that
   movement rather than as another field in the grid. */
.editor-gear-missing {
  grid-column: 1 / -1; font-size: 12.5px; color: var(--amber); margin-top: -2px;
}

/* Two fields, one above the other. `.field-row` puts them side by side, which
   is right for two short numbers and wrong for anything holding an address. */
.field-stack { display: flex; flex-direction: column; gap: 10px; }

/* Pairing and missing kit on a mobility row, which is a tick-list line rather
   than a card and has no room for a line of its own. Small, inline, after the
   name it qualifies. */
.paired-inline, .gear-inline {
  display: inline-block; margin-left: 7px;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
  padding: 2px 7px; border-radius: var(--r-pill); vertical-align: 1px;
}
.paired-inline { color: var(--accent); border: 1px solid var(--accent); }
.gear-inline { color: var(--amber); border: 1px solid var(--amber); }
.check-item.paired { border-left: 2px solid var(--accent); padding-left: 8px; }

/* The equipment list on the week-rework card, folded away. Most weeks are
   answered by the two buttons above it — everything, or nothing — and a
   fourteen-item list open by default buries the button that submits. */
.kit-fold > summary {
  cursor: pointer; list-style: none; padding: 10px 0; min-height: 44px;
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--text-2);
}
.kit-fold > summary::-webkit-details-marker { display: none; }
.kit-fold > summary::after { content: "▾"; font-size: 11px; color: var(--text-3); }
.kit-fold[open] > summary::after { content: "▴"; }

/* Permanently deleting a client, folded away on their Access card.
   Deliberately not a fifth button in the row above it: the four up there are
   routine and reversible, and a permanent delete a tap away from "New invite
   link" is a mis-tap waiting to happen. Closed by default, and the clay edge
   says what kind of thing is inside before it is opened. */
.danger-fold {
  margin-top: 16px; border-top: 1px solid var(--clay-dim); padding-top: 4px;
}
.danger-fold > summary {
  cursor: pointer; list-style: none; padding: 12px 0; min-height: 44px;
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 600; color: var(--clay);
}
.danger-fold > summary::-webkit-details-marker { display: none; }
.danger-fold > summary::after { content: "\25BE"; font-size: 11px; }
.danger-fold[open] > summary::after { content: "\25B4"; }
.danger-fold[open] { padding-bottom: 16px; }

/* How many people are actually doing this movement. The number is why the row
   is near the top, so it sits beside the name rather than at the end. */
.video-count {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-3); margin-left: 6px;
}

/* The coach's own clip, played in place. Capped in height so a portrait phone
   video does not push the cues off the screen — the guide is what somebody is
   reading between sets and the video is beside it, not instead of it. */
.howto-clip {
  width: 100%; max-height: 46vh; border-radius: var(--r-sm);
  background: #000; display: block; margin-bottom: 8px;
}
.howto-clip-label {
  font-size: var(--fs-0); font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--accent); margin-bottom: 6px;
}
/* The upload control on a video row in Coaches Corner. */
.clip-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.clip-row .field-hint { margin: 0; }
.clip-have { color: var(--accent); font-size: 12.5px; }
input[type="file"].clip-input { display: none; }

/* The one-line explanation of the pairing glyph, under the Exercises heading. */
.editor-note { margin: 2px 0 10px; }
.editor-note .pair-glyph { color: var(--accent); }

/* Settings in named sections. The panel held seventeen things in one column
   spanning four unrelated concerns, so finding anything meant reading all of
   it. The grouping is the whole change — nothing was removed. */
/* Settings as its own index.
   The panel was 13,295px — 14.3 phone screens — because three named sections
   labeled the scroll without shortening it. Every unit is a fold now, so it
   opens as thirteen named rows at about 1,100px, and exactly one fold can be
   open at a time, which is what stops it growing back. */
.settings-search {
  flex: 1 0 100%; order: 3; margin: 0;
}
.settings-head { flex-wrap: wrap; }

.sset { border-bottom: 1px solid var(--line-soft); }
.sset[hidden], .settings-section[hidden] { display: none; }

.sset > summary {
  list-style: none; cursor: pointer;
  min-height: 44px; padding: 10px 2px;
  display: grid; grid-template-columns: 1fr auto;
  align-items: center; row-gap: 2px;
}
.sset > summary::-webkit-details-marker { display: none; }
.sset > summary::after {
  content: "\25BE"; grid-column: 2; grid-row: 1 / span 2;
  font-size: 11px; color: var(--text-3); padding-left: 12px;
}
.sset[open] > summary::after { content: "\25B4"; }

/* The moved heading keeps whatever status pill sat beside it, so a closed row
   still says whether the API key is set. */
.sset-head .card-head { grid-column: 1; margin: 0; }
.sset-head .card-title, .sset-head h4 { margin: 0; }
/* The h4 has left .help-group, so that rule no longer reaches it. */
.sset-head h4 { font-family: var(--f-display); font-size: 17px; font-weight: 700; letter-spacing: -0.01em; }
/* Closed, a fold is an index row; the section title's 20px would make
   thirteen of them a wall. */
.sset-head .card-title { font-size: 17px; }
.sset-note {
  grid-column: 1; grid-row: 2;
  font-size: 12.5px; color: var(--text-3); line-height: 1.4;
}

/* The heading of the thing you are inside stays pinned under the panel head,
   and tapping it is the way out. The offset cannot be a constant — the search
   box wraps the head onto two rows on a narrow phone. */
.sset[open] > summary {
  position: sticky; top: var(--settings-head-h, 61px); z-index: 1;
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.sset[open] > .card, .sset[open] > .help-group { margin: 6px 0 16px; }
/* The open summary already draws a rule under itself; a section rule
   directly beneath it made two. */
.sset[open] > .card { border-top: 0; padding-top: var(--s3); }

.sset-none { padding: 18px 2px; color: var(--text-3); font-size: 13.5px; }

/* The guide gets the same fold. It was ten groups and about eight thousand
   pixels inside a sheet capped at 90dvh — ten sheet-heights of scrolling in
   the one panel whose entire job is answering a question quickly. The first
   group is open, so it still reads as a document rather than a menu. */
.help-fold[open] > summary { top: 0; }
.help-fold > .help-group { margin: 0 0 4px; }

/* The segmented control on the video worklist. It has had NO rules at all —
   `.seg` appears in none of the three stylesheets the app loads, so the filter
   on the largest card in the panel has been rendering as three default browser
   buttons since the day it was written. */
.seg {
  display: inline-flex; gap: 4px; padding: 4px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-pill);
  max-width: 100%; overflow-x: auto; scrollbar-width: none;
}
.seg::-webkit-scrollbar { display: none; }
.seg button {
  min-height: 44px; padding: 9px 17px;
  border: 0; border-radius: var(--r-pill); background: transparent;
  font-family: var(--f-body); font-size: 13.5px; font-weight: 600;
  color: var(--text-2); white-space: nowrap; cursor: pointer;
}
.seg button[aria-pressed="true"] { background: var(--surface-3); color: var(--text); }

.settings-section { margin: 0 0 4px; }
.settings-section + .settings-section { margin-top: 20px;
  padding-top: 22px; border-top: 1px solid var(--line); }
.settings-section-title {
  font-family: var(--f-display); font-size: var(--fs-3); font-weight: 700; letter-spacing: -0.01em; margin: 0 0 2px;
}
.settings-section-note {
  margin: 0 0 12px; font-size: 13px; color: var(--text-3);
}
