/* The structure follows iOS: a grouped background with raised cells, the system font at 17 pt, 44 pt touch targets,
   inset grouped lists, a tab bar. The colours are the brand's on every screen (docs/brand.md): a black ground with cells of
   crema over black raised above it in dark (the browns on the Espresso background), crema with white cells in light, large
   titles in the brand's heavy capitals. No inline styles
   anywhere: the shell's policy (style-src 'self') would refuse them.

   The brand's colours are named once. Volt is light, so it only ever sits on dark: it is the accent in dark mode, and
   in light mode espresso takes over with volt riding on top of it. Every text pair here measures 4.5:1 or better on
   the ground, the cells and the fills; the bars of a chart, a control's edge (--control-line) and what marks a state (a
   selected segment or tab, a switch that is off) 3:1 or better, in dark, light and on espresso (#223,
   tests/test_service_contrast.py).

   Liquid Glass (docs/brand.md): glass is the controls layer that floats over the content, never the content. A glass
   surface is --glass under a 24 px blur, a --glass-line hairline, a 1 px --glass-highlight top edge and --glass-shadow;
   what is selected inside it is a --glass-lens tint, never a second glass. The sidebar and the docked Chat of a wide screen are
   --glass-regular, the more opaque glass Apple gives larger elements (the thresholds are with the regular-width rules). --edge0 and --edge1 are the ground at 0 %
   and 92 %: the scroll edge that content fades into under the floating tab bar. Cards and lists are --radius-card,
   sheets --radius-sheet at the top, buttons capsules (half their height). */
:root { --espresso: #442C2C; --volt: #E6FF00; --moss: #A9BC2A; --dust: #9A7B7B; --oat: #C9B5B2; --crema: #F6F3EF; --roast: #241616;
        --bg: var(--crema); --cell: #FFFFFF; --fill: #EAE4DD; --fill-strong: #D9CFC6; --line: #D9CFC6; --text: var(--roast); --muted: #6B4A4A;
        --accent: var(--espresso); --on-accent: var(--volt); --warn-bg: #FFF4CC; --warn-text: #5C4300; --danger: #C4271B;
        --control-line: #896E6E; --switch-off: var(--control-line); --badge: #D92D20; --ring-2: var(--dust); --ring-3: #6B4A4A; --ring-4: var(--espresso);
        --tabbar: 62px; --edge-height: 120px; --sidebar: 248px; --inset: 12px; --side: calc(var(--sidebar) + var(--inset) * 2); --radius-window: 32px;
        --topbar: 56px; --edge-top: 84px; --chat-pane: 400px; --detail-min: 360px; --column-min: 340px; --easy: #8A7671; --tempo: var(--dust); --seg-track: #FFFFFF; --seg-on: #E0D8D0;
        --glass: rgba(255, 255, 255, .62); --glass-regular: rgba(255, 255, 255, .78); --glass-line: rgba(36, 22, 22, .14); --glass-shadow: 0 8px 30px rgba(36, 22, 22, .12);
        --glass-lens: rgba(36, 22, 22, .07); --glass-highlight: rgba(255, 255, 255, .9); --backdrop: rgba(36, 22, 22, .45);
        --edge0: rgba(246, 243, 239, 0); --edge1: rgba(246, 243, 239, .92);
        --radius-card: 20px; --radius-sheet: 34px;
        --good: #4E6610; --watch: var(--warn-text); --bad: var(--danger);
        /* The same in both schemes: white on the red of a badge or Delete, a switch's knob, the amber of a late sync (the
           warning text is brown in light), and the camera's dark, so its volt frame reads in either. */
        --on-red: #FFFFFF; --knob: #FFFFFF; --late: #D99A00; --camera: #0A0808;
        color-scheme: light dark; }
/* Appearance follows the phone unless Settings says otherwise. The dark tokens are written twice on purpose:
   once for the phone's own preference (unless the athlete forced light), once for the athlete's own choice. The
   shell's policy refuses inline scripts, so nothing can set data-theme before the first paint — the media query
   is what keeps a dark phone dark while app.js is still loading.
   On the Black ground (the Owner's pick of 2026-09-28) the surfaces are neutral steps of crema over black, each written
   solid: the cell is crema at 10 % over black, the fill crema at 10 % over the cell, the strong fill and a switch that is
   off a step up again each. Solid, so a Meal swiped over its red Delete never shows it through. The glass is a grey of
   the same family (crema at 21 % over black) at 62 %, so no brown pill floats over grey cards, and a sheet dims the page
   in black, not roast, which tinted the grey cards brown behind it. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --bg: #000000; --cell: #191818; --fill: #2F2E2E; --fill-strong: #434241; --line: rgba(246, 243, 239, .18);
          --text: var(--crema); --muted: var(--oat); --accent: var(--volt); --on-accent: var(--espresso);
          --warn-bg: #3A2A10; --warn-text: #FFD60A; --danger: #FF9B92; --switch-off: #555452;
          --ring-2: var(--oat); --ring-3: var(--moss); --ring-4: var(--volt);
          --glass: rgba(52, 51, 50, .62); --glass-regular: rgba(52, 51, 50, .8); --glass-line: rgba(246, 243, 239, .22); --glass-shadow: 0 8px 30px rgba(0, 0, 0, .55);
          --glass-lens: rgba(246, 243, 239, .14); --glass-highlight: rgba(255, 255, 255, .24);
          --edge0: rgba(0, 0, 0, 0); --edge1: rgba(0, 0, 0, .92); --backdrop: rgba(0, 0, 0, .45);
          --easy: #B5A19D; --tempo: var(--moss); --seg-track: #191818; --seg-on: #434241;
          --good: var(--moss); --control-line: #918F8D; }
}
:root[data-theme="dark"] { --bg: #000000; --cell: #191818; --fill: #2F2E2E; --fill-strong: #434241; --line: rgba(246, 243, 239, .18);
          --text: var(--crema); --muted: var(--oat); --accent: var(--volt); --on-accent: var(--espresso);
          --warn-bg: #3A2A10; --warn-text: #FFD60A; --danger: #FF9B92; --switch-off: #555452;
          --ring-2: var(--oat); --ring-3: var(--moss); --ring-4: var(--volt);
          --glass: rgba(52, 51, 50, .62); --glass-regular: rgba(52, 51, 50, .8); --glass-line: rgba(246, 243, 239, .22); --glass-shadow: 0 8px 30px rgba(0, 0, 0, .55);
          --glass-lens: rgba(246, 243, 239, .14); --glass-highlight: rgba(255, 255, 255, .24);
          --edge0: rgba(0, 0, 0, 0); --edge1: rgba(0, 0, 0, .92); --backdrop: rgba(0, 0, 0, .45);
          --easy: #B5A19D; --tempo: var(--moss); --seg-track: #191818; --seg-on: #434241;
          --good: var(--moss); --control-line: #918F8D; }
/* The Background in Settings: black by default, or espresso, in dark mode only (light mode stays crema). On espresso the
   browns step up a tone, as on the sign-in screen, so a cell still sits above its ground; danger, muted and good lighten
   a step to keep 4.5:1 on the fills and a control's edge 3:1 (#223: oat was 3.97:1 on the strong fill, moss 4.16:1 on
   the fill). Its glass stays the brown one: Black's neutral grey is Black's alone (2026-09-28). Written twice like the
   dark tokens: the phone's dark, and a forced one. */
@media (prefers-color-scheme: dark) {
  :root[data-background="espresso"]:not([data-theme="light"]) { --bg: var(--espresso); --cell: #553939; --fill: #604343; --fill-strong: #6B4A4A;
          --line: rgba(246, 243, 239, .2); --danger: #FFA59C; --switch-off: #7A5858; --seg-track: #553939; --seg-on: #6B4A4A;
          --glass: rgba(52, 38, 38, .62); --glass-regular: rgba(52, 38, 38, .8); --backdrop: rgba(36, 22, 22, .45);
          --edge0: rgba(68, 44, 44, 0); --edge1: rgba(68, 44, 44, .92); --muted: #D4C4C1; --good: #C2CE69; --control-line: #AE9F9D; }
}
:root[data-theme="dark"][data-background="espresso"] { --bg: var(--espresso); --cell: #553939; --fill: #604343; --fill-strong: #6B4A4A;
          --line: rgba(246, 243, 239, .2); --danger: #FFA59C; --switch-off: #7A5858; --seg-track: #553939; --seg-on: #6B4A4A;
          --glass: rgba(52, 38, 38, .62); --glass-regular: rgba(52, 38, 38, .8); --backdrop: rgba(36, 22, 22, .45);
          --edge0: rgba(68, 44, 44, 0); --edge1: rgba(68, 44, 44, .92); --muted: #D4C4C1; --good: #C2CE69; --control-line: #AE9F9D; }
/* The sign-in screen is espresso whatever the mode: the first thing anyone sees of the app is its primary colourway. */
body.signed-out { --bg: var(--espresso); --cell: #553939; --fill: #604343; --fill-strong: #6B4A4A; --line: rgba(246, 243, 239, .2);
                  --text: var(--crema); --muted: var(--oat); --accent: var(--volt); --on-accent: var(--espresso);
                  --ring-2: var(--oat); --ring-3: var(--moss); --ring-4: var(--volt); --control-line: #AE9F9D; --switch-off: #7A5858;
                  color-scheme: dark; }
* { box-sizing: border-box; }
[hidden] { display: none !important; }
/* Behind a feature flag (ADR 0016): hidden until /me says the athlete has it, then shown or removed (app.js, Flags). */
[data-flag] { display: none !important; }
body { margin: 0; background: var(--bg); color: var(--text); -webkit-text-size-adjust: 100%;
       font: 17px/22px -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif; }
p, h1, h2, h3, ul, ol, pre { margin: 0; }
/* A focused control never stops under what floats over the page (#212): on a phone, the tab bar and the scroll edge it
   sits in, whose height is the fade's own; from 768 px, the bar row and the top scroll edge. The page's padding keeps the
   last card clear; this keeps clear what Tab or a screen reader brings into view. */
html { scroll-padding-top: env(safe-area-inset-top); scroll-padding-bottom: calc(env(safe-area-inset-bottom) + var(--edge-height)); }
/* A view's heading takes focus when the view opens (#212, app.js Landing), to be read first; beside the list from 1024 px,
   an open group of Settings' column, which its title names. Neither is a control: no ring. */
h1[tabindex="-1"]:focus, h2[tabindex="-1"]:focus, [role="heading"][tabindex="-1"]:focus, #settings-detail[tabindex="-1"]:focus { outline: none; }
button, input, textarea { font: inherit; color: inherit; }
/* Every field shows where typing goes in the brand's ring, not the browser's (#212): the Profile's edits, quiet hours, the
   Owner's picks. Of no weight (:where), so a field that rings its own way (the composer's pill, the slot chip, a field
   with its own offset) keeps it. */
:where(input:not([type=checkbox], [type=radio]), select, textarea):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A placeholder is a hint, never a field's only name (#223): muted at full strength, where the browser's own grey was
   3.5:1 on a dark field and 1.9:1 on espresso's. Firefox would dim it again without the opacity. */
::placeholder { color: var(--muted); opacity: 1; }
button { cursor: pointer; }
button:disabled { opacity: .45; cursor: default; }
.defs { position: absolute; }
.muted { color: var(--muted); font-size: 13px; line-height: 18px; }
.center { text-align: center; }
.grow { flex: 1; min-width: 0; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.between { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.dot { width: 8px; height: 8px; border-radius: 4px; background: var(--accent); flex-shrink: 0; }
.icon { width: 25px; height: 25px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
        flex-shrink: 0; }
/* The logo (#wordmark): the ring, then "coach", 737 by 199. Its letters take the text colour, its four zones the
   --ring tokens of the theme. Never under 120 px wide: below that the ring stands alone (#monogram). */
.wordmark { display: block; width: 126px; height: 34px; color: var(--text); }
.wordmark.large { width: 252px; height: 68px; }
/* Bare classes: a symbol's paths are matched where they are drawn (inside <use>) in some browsers and where they are
   defined in others, so no ancestor in the selector. The tokens resolve the same either way: the defs sit in <body>. */
.zone-2 { fill: var(--ring-2); } .zone-3 { fill: var(--ring-3); } .zone-4 { fill: var(--ring-4); }
.mark { display: block; width: 18px; height: 18px; }
.tagline { font-size: 34px; line-height: 36px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px; text-transform: uppercase; }
.tagline span { display: block; }
.tagline .last { color: var(--accent); }

/* ---------- a page: a large title over grouped lists ---------- */
/* The page ends where the scroll edge begins, so the last card scrolls clear of the fade and the floating tab bar. */
.page { max-width: 600px; margin: 0 auto; display: flex; flex-direction: column; gap: 22px;
        padding: calc(env(safe-area-inset-top) + 16px) 16px calc(env(safe-area-inset-bottom) + var(--edge-height)); }
.title { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; padding: 6px 0 10px; }
.title .muted { margin-top: 2px; }
/* Large titles speak in the brand's headline voice: heavy capitals, tight, as on the sign-in screen. */
.title h1 { font-size: 32px; line-height: 36px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px; text-transform: uppercase; }
/* The Dashboard's title is the day, and "Wednesday 23 Sep" is 365 px at 32 px where the page leaves 358 at
   390 px. It shrinks with the screen to fit on one line, never under 24 or over 32; narrower still, it breaks evenly. */
#view-dashboard .title h1 { font-size: clamp(24px, calc(8.6vw - 8px), 32px); text-wrap: balance; }
/* Until today's first answer, the Dashboard shows its title and nothing else: the cards then appear once, at their
   real size, instead of growing from empty shells and pushing each other down (a layout shift of 0.86 on open). The
   line under the title keeps one line's height whatever it says, so the header never moves either. */
#view-dashboard[data-pending] > :not(.title) { display: none; }
#view-dashboard .title .muted { min-height: 18px; }
/* Update now, the last words of that line: a word in the accent, tappable across the whole line's height and more,
   44 px high and at least as wide (#223: Update now was 42, Got it 43 wide). */
.inline-link { min-width: 44px; border: none; background: transparent; padding: 13px 0; margin: -13px 0; font: inherit; color: var(--accent);
               font-weight: 600; cursor: pointer; }
.inline-link:disabled { color: inherit; font-weight: inherit; cursor: default; }
.inline-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* Ride now: a button on the fill with the bike before its name, as a Day's Log food is (.button.wide), under Workouts'
   strip and at Today's foot on the Dashboard. It opens the sheet of the same name (#218). The Owner's call of 28 Sep:
   the row it was, a badge with a line under its name, read as a link rather than an action; grey, not volt, since the
   accent is each screen's one main action, Approve on Workouts and the Verdict on the Dashboard. */
.ride-now { display: flex; align-items: center; justify-content: center; gap: 8px; }
.ride-now .icon { width: 20px; height: 20px; stroke-width: 2.2; }
.ride-now:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lead { margin-top: 8px; color: var(--muted); }
#view-signin { gap: 24px; padding-top: calc(env(safe-area-inset-top) + 28px); }
/* An Invite (#101): the page before any sign-in, on the same ground as signing in. The days are chips that toggle; the
   two windows are a pair of times each. A chip and a time are 44 px, a chip's edge in --control-line until it is on
   (#223: they were 37 by 34 and 34, a fill 1.2:1 on the cell). */
#view-invite { gap: 24px; padding-top: calc(env(safe-area-inset-top) + 28px); max-width: 680px; margin-left: auto; margin-right: auto; }
#invite-body { display: flex; flex-direction: column; gap: 24px; }
.invite .prose p + p { margin-top: 4px; }
/* What the Coach does and what to have ready (#216): sentences as rows, so a line may wrap; the sub lines are the size of
   the prose below them, since the AI key's are the ones a friend must read before Join. */
ul.invite-lines { padding: 0; list-style: none; }
.invite-lines .row { padding-top: 11px; padding-bottom: 11px; }
.invite-lines .row .sub { margin-top: 2px; font-size: 15px; line-height: 20px; }
.invite-lines .row .sub + .sub { margin-top: 6px; }
/* What is stored, who could read it and leaving (#216): three rows that open, the brand's disclosure (as Nutrition's
   "How 3136 is worked out"): no browser triangle, a chevron in the accent at the row's end that turns down when open,
   and a focus ring drawn inside the card, which clips what lies outside it. */
.invite-data summary { cursor: pointer; list-style: none; }
.invite-data summary::-webkit-details-marker { display: none; }
.invite-data summary::marker { content: ""; }
.invite-data summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 12px; }
.invite-data summary .chevron { width: 18px; height: 18px; stroke-width: 2.2; color: var(--accent); transition: transform .2s ease; }
.invite-data details[open] .chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .invite-data summary .chevron { transition: none; } }
.invite-data .prose { display: flex; flex-direction: column; gap: 8px; padding: 2px 16px 14px; font-size: 15px; line-height: 20px; }
.row.days { flex-wrap: wrap; justify-content: flex-start; gap: 4px; padding: 10px 12px; }
.day-chip { position: relative; display: inline-flex; }
.day-chip input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.day-chip span { display: flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; padding: 0 6px; border-radius: 12px;
                 background: var(--fill); box-shadow: inset 0 0 0 1px var(--control-line); text-align: center; font-size: 14px; font-weight: 600; }
.day-chip input:checked + span { background: var(--accent); color: var(--on-accent); box-shadow: none; }
.day-chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.row.field .times { flex: 1; min-width: 0; display: flex; align-items: center; gap: 4px; }
.row.field .times input { flex: 1 1 0; width: auto; min-width: 0; max-width: 140px; height: 44px; padding: 4px 4px; border-radius: 10px; background: var(--fill);
                          box-shadow: inset 0 0 0 1px var(--control-line); color: inherit; font: inherit; }
.row.field .times input::-webkit-calendar-picker-indicator { display: none; }
.row.field .switch { margin-left: auto; }
/* The link of an Invite just made, shown once in Settings to copy. */
.invite-link { display: flex; gap: 8px; align-items: center; }
.invite-link input { flex: 1; min-width: 0; height: 44px; padding: 6px 10px; border: none; border-radius: 10px; background: var(--fill);
  box-shadow: inset 0 0 0 1px var(--control-line);
  color: inherit; font: 13px ui-monospace, "SF Mono", Menlo, monospace; }

.group, .groups { display: flex; flex-direction: column; }
.group { gap: 8px; }
.groups { gap: 26px; }
#view-settings { gap: 26px; }
/* A section header over a list is title case, bold and in the text colour, as iOS 26 sets them; only the large title
   above it speaks in capitals. */
.group > h2 { padding: 0 4px; font-size: 20px; line-height: 24px; font-weight: 700; color: var(--text); }
.footnote { padding: 0 16px; font-size: 13px; line-height: 18px; color: var(--muted); }
.invite-consent { display: flex; gap: 12px; align-items: flex-start; margin: 20px 4px 16px; font-size: 15px; line-height: 21px; }
.invite-consent input { flex: none; width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--accent); }
.invite-consent a { color: inherit; text-decoration: underline; }
.footnote:empty { display: none; }
.footnote a { color: var(--accent); }
.list { display: flex; flex-direction: column; background: var(--cell); border-radius: var(--radius-card); overflow: hidden; }
.list > * { position: relative; }
.list:not(.padded) > * + *::before { content: ""; position: absolute; top: 0; left: 16px; right: 0; height: 1px; background: var(--line);
                                     transform: scaleY(.5); transform-origin: top; }
.list.padded { padding: 12px 16px; gap: 8px; font-size: 15px; line-height: 20px; }
.list.padded ol, .list.padded ul { padding-left: 22px; }

.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; padding: 6px 16px; }
.row .value { color: var(--muted); }
.row .sub { display: block; font-size: 13px; line-height: 18px; color: var(--muted); }
.row.field .name { width: 96px; flex-shrink: 0; }
.row.field { min-height: 52px; }
.row.field input { flex: 1; min-width: 0; height: 44px; border: none; outline: none; background: transparent; }
.row.field input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }      /* where typing goes (#212) */
/* An editable row of the Profile: the name with its note under it on the left, the control or the value at the end.
   The control is 44 px with its edge in --control-line (#223: 34 px, and a fill 1.13:1 on the cell). */
.row.edit { min-height: 52px; }
.row.edit .name { flex: 1; min-width: 0; }
.row.edit .value { text-align: right; }
.row.edit input, .row.edit select { flex: 0 1 50%; min-width: 0; max-width: 220px; height: 44px; padding: 6px 10px; border: none;
  border-radius: 10px; background: var(--fill); box-shadow: inset 0 0 0 1px var(--control-line); color: inherit; font: inherit; text-align: right; }
.row.edit select { -webkit-appearance: none; appearance: none; text-align: right; text-align-last: right; padding-right: 10px; }
.row.edit input[type=text], .row.edit input[type=url] { flex: 1 1 60%; text-align: left; }
/* A Connection: a link out to intervals.icu, with the source's icon, its name, what it syncs under it, and a chevron. */
.row.connection { justify-content: flex-start; min-height: 60px; color: var(--text); text-decoration: none; }
.row.connection .icon { flex-shrink: 0; width: 22px; height: 22px; color: var(--accent); }
.row.connection .grow { flex: 1; min-width: 0; }
.row.connection .sub.note { color: var(--text); }
.row.connection.more { min-height: 44px; }
/* The catalogue (#151): a source not connected ends in Connect; Apple Health is a sentence, with nothing to tap. A Connect,
   and Connect another without the catalogue, is in the text colour (#222): twelve volt ones in Data outshouted the one
   filled action a screen has. The row's icon keeps the accent, as Log food's plus does. */
.row.connection .connect { color: var(--text); }
.row.connection.sentence { color: var(--text); }
button.row { width: 100%; border: none; background: transparent; text-align: left; color: var(--accent); justify-content: flex-start; }
button.row.danger, .link.danger { color: var(--danger); }
button.row:disabled { color: var(--muted); }
/* Remove and Leave (#102): the second tap is a confirmation under the row, in words, with Cancel beside the button. */
.row.confirm { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px 16px; }
.confirm-words { font-size: 15px; line-height: 20px; }
.confirm-buttons { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.button.small.danger { color: var(--danger); }
/* AI in Settings (#97): the key is typed into a row of its own and never shown again; a refused one says so in red. */
.row.key-entry input { flex: 1 1 auto; max-width: none; text-align: left; }
.row.refused .value, .row.refused .sub { color: var(--danger); }
.row.estimate { display: block; min-height: 0; padding: 10px 16px; font-size: 13px; line-height: 18px; color: var(--muted); }
.row.estimate:empty { display: none; }
.row.admission { justify-content: flex-start; min-height: 56px; }
.row.admission .grow { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.row.admission.failed .sub, .row.admission.stopped .sub { color: var(--danger); }
.row.link:disabled { color: var(--muted); }
/* A new athlete's AI as three steps (B3): each a section whose heading starts with its number in a filled circle, and
   under Test it the line that says where the test stands, in the text colour, since it says what to do next. */
.group.step > h2 { display: flex; align-items: center; gap: 10px; }
.step-num { display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center; width: 28px; height: 28px;
            border-radius: 14px; background: var(--fill-strong); color: var(--text); font-size: 15px; line-height: 20px; font-weight: 700;
            font-variant-numeric: tabular-nums; }
.row.said { display: block; min-height: 0; padding: 10px 16px; font-size: 15px; line-height: 20px; color: var(--text); }
.row.said:empty { display: none; }
.row.said:focus { outline: none; }
.row.said:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* In Settings › AI a Provider's or a model's name is what is read, so its picker takes the room the name needs, up to three
   fifths of the row; the base URL's note is too long to sit beside its field, which goes under it. */
#ai .row.edit select { flex: 0 1 auto; max-width: 62%; }
#ai .row.edit.stacked { flex-wrap: wrap; padding-top: 10px; padding-bottom: 10px; }
#ai .row.edit.stacked .name { flex-basis: 100%; }
#ai .row.edit.stacked input { flex-basis: 100%; max-width: none; }
/* Data sync (#150): a dot per kind in the status colours with its word beside it, so colour is never the only sign; the
   same dot before the Dashboard's "Updated" line when a kind is late or has stopped, its touch target wider than it. */
.sync-dot { display: inline-block; flex-shrink: 0; width: 10px; height: 10px; border-radius: 50%; background: var(--good); }
.sync-dot.late { background: var(--late); }
.sync-dot.not_arriving { background: var(--bad); }
.row.sync { display: block; padding-top: 10px; padding-bottom: 10px; }
.row.sync .sync-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.row.sync .sync-state { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.row.sync .sub.fix { margin-top: 2px; }
.row.sync .sub.fix { color: var(--text); }
#connections { display: flex; flex-direction: column; gap: 26px; }   /* Data sync above Connections, spaced as Settings' sections */
#built a.sync-dot { position: relative; width: 8px; height: 8px; margin-right: 8px; vertical-align: 1px; }
#built a.sync-dot::after { content: ""; position: absolute; inset: -18px -22px -18px -14px; }   /* 44 to touch, on the screen at its edge (#223) */
/* Features (#148): each flag a list of its own, its stage across a row, the overrides folded away beneath; a date past
   its remove-by in red. */
.feature { display: flex; flex-direction: column; gap: 8px; }
.row.feature-stage .segmented { flex: 1; }
.row.feature-stage .segmented button { padding: 2px 4px; }
.segmented button:disabled { opacity: .4; }
.row.feature-note, .row.feature-label, .row.conn-label { display: block; min-height: 0; padding: 10px 16px; font-size: 13px; line-height: 18px; color: var(--muted); }
.row .sub.overdue { color: var(--danger); }
.feature-more summary { display: flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 16px; color: var(--accent); cursor: pointer; }
.feature-more summary .chevron { color: var(--accent); transition: transform .2s ease; }
.feature-more[open] summary .chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .feature-more summary .chevron { transition: none; } }
.feature-more summary::-webkit-details-marker { display: none; }
.feature-more summary::marker { content: ""; }
.feature-more summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The Chat's notice while the Coach cannot answer this athlete: a way to Settings, on every width. */
.notice { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; padding: 12px 16px; border-radius: var(--radius-card);
          background: var(--cell); color: var(--text); text-decoration: none; font-size: 15px; line-height: 20px; }
.notice[hidden] { display: none; }
.notice .grow { flex: 1; }
.notice .chevron { width: 16px; height: 16px; stroke-width: 2.2; color: var(--muted); }
.link { border: none; background: transparent; color: var(--accent); min-height: 44px; padding: 0 4px; flex-shrink: 0; }
/* A link that pages a list, as Workouts' "Show earlier days" (#215): the text colour, since volt marks the one filled
   action on a screen; muted while the page it asked for is read. */
.link.plain { color: var(--text); }
.link.plain:disabled { color: var(--muted); }
/* The brand's disclosure, for any <details class="disclosure"> whose summary holds its label and then the chevron icon
   (#222; drawn first for Meal ideas' "Which days", whose rules keep their own names): no browser triangle, the label in
   the text colour and after it a chevron in the accent that turns down when open, 44 px to touch, a focus ring. */
details.disclosure > summary { display: flex; align-items: center; gap: 6px; width: fit-content; min-height: 44px; margin-left: -4px;
                               padding: 0 8px 0 4px; border-radius: 12px; color: var(--text); font-size: 15px; line-height: 20px;
                               font-weight: 600; cursor: pointer; list-style: none; }
details.disclosure > summary::-webkit-details-marker { display: none; }
details.disclosure > summary::marker { content: ""; }
details.disclosure > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
details.disclosure > summary .chevron { flex-shrink: 0; width: 16px; height: 16px; stroke-width: 2.2; color: var(--accent); transition: transform .2s ease; }
details.disclosure[open] > summary .chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { details.disclosure > summary .chevron { transition: none; } }
input[type=time] { border: none; border-radius: 10px; padding: 6px 10px; background: var(--fill); box-shadow: inset 0 0 0 1px var(--control-line);
                   min-height: 44px; }

/* The iOS switch, 51 by 31: a real checkbox, invisible, over a drawn track and knob. The checkbox is 51 by 44, centred on
   the drawing, so the switch is 44 px to touch while it looks as iOS draws it; the drawing takes no tap (#223). */
.switch { position: relative; flex-shrink: 0; width: 51px; height: 31px; }
.switch input { position: absolute; left: 0; top: 50%; width: 100%; height: 44px; margin: -22px 0 0; opacity: 0; cursor: pointer; }
.switch .track { display: block; width: 100%; height: 100%; border-radius: 16px; background: var(--switch-off); transition: background .2s;
                 pointer-events: none; }
.switch .track::before { content: ""; position: absolute; top: 2px; left: 2px; width: 27px; height: 27px; border-radius: 50%;
                         background: var(--knob); box-shadow: 0 2px 4px rgba(0, 0, 0, .25); transition: transform .2s; }
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track::before { transform: translateX(20px); background: var(--on-accent); }   /* a white knob is lost on volt */
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Buttons are capsules: the radius is half the height, 25 on the 50 px button and 22 on the 44 px one (#223: it was
   36 px, with a ::after reaching 44). */
.button { height: 50px; padding: 0 20px; border: none; border-radius: 25px; background: var(--fill); color: var(--text); font-weight: 600; }
.button.primary { background: var(--accent); color: var(--on-accent); }
/* A main button that cannot be used is a secondary one, dimmed the same way: a dimmed volt read as muddy olive (#214). */
.button.primary:disabled { background: var(--fill); color: var(--text); }
.button.wide { width: 100%; }
a.button { display: flex; align-items: center; justify-content: center; text-decoration: none; }
.button.small { position: relative; height: 44px; padding: 0 16px; border-radius: 22px; font-size: 15px; }
.card .button.small { align-self: flex-start; }

/* ---------- Today ---------- */
.fact { display: flex; flex-direction: column; gap: 3px; padding: 12px 16px; }
.fact .label, .card .label { font-size: 13px; line-height: 18px; font-weight: 400; color: var(--muted); }      /* a card's heading (#212) */
.fact .big, .card .big { font-size: 22px; line-height: 28px; font-weight: 700; letter-spacing: .35px; }
.fact .detail, .card .detail, .card .reasons { font-size: 15px; line-height: 20px; color: var(--muted); }
.measure { display: flex; flex-direction: column; gap: 8px; padding: 12px 16px; }
.measure .value { color: var(--muted); font-size: 15px; }
progress { appearance: none; -webkit-appearance: none; display: block; width: 100%; height: 6px; border: none; border-radius: 3px;
           background: var(--fill); overflow: hidden; }
progress::-webkit-progress-bar { background: var(--fill); border-radius: 3px; }
progress::-webkit-progress-value { background: var(--accent); border-radius: 3px; }
progress::-moz-progress-bar { background: var(--accent); border-radius: 3px; }

.card { display: flex; flex-direction: column; gap: 8px; padding: 16px; background: var(--cell); border-radius: var(--radius-card); }
.card.warn { gap: 4px; padding: 12px 16px; background: var(--warn-bg); color: var(--warn-text); font-size: 15px; line-height: 20px; }
.card.warn h2 { font-size: 13px; line-height: 18px; font-weight: 600; text-transform: uppercase; }
.card.warn ul { display: flex; flex-direction: column; gap: 4px; padding: 0; list-style: none; }
#proposals, .day-proposals { display: flex; flex-direction: column; gap: 12px; }      /* a Day answers its own (review of 28 Sep) */
/* A Proposal (#208): the eyebrow with the Deadline in words beside it, what (the card's heading) and why, one row per date
   it changes on the fill, before struck through in the muted colour and after in the text colour, Approve and Reject,
   then what happens unanswered and, the first time, its note: one line each, muted. */
.proposal-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 2px 12px; }
.proposal .eyebrow { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 18px; font-weight: 600;
                     text-transform: uppercase; color: var(--muted); }
.proposal .by { font-size: 13px; line-height: 18px; color: var(--muted); }
.proposal .by:empty { display: none; }
.proposal .what { font-size: 20px; line-height: 25px; font-weight: 600; }
.proposal .why { font-size: 15px; line-height: 20px; color: var(--muted); }
.proposal .changes { display: flex; flex-direction: column; gap: 8px; padding: 0; list-style: none; }
.proposal .change { padding: 10px 12px; border-radius: 14px; background: var(--fill); }
.proposal .change-words { display: flex; flex-direction: column; gap: 2px; }
.proposal .change-title { font-size: 15px; line-height: 20px; font-weight: 600; }
.proposal .change-diff { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; font-size: 15px; line-height: 20px; }
.proposal .before { color: var(--muted); text-decoration: line-through; text-decoration-thickness: 1px; }
.proposal .after { color: var(--text); font-weight: 600; }
.proposal .change .arrow { width: 16px; height: 16px; color: var(--muted); }
.proposal .deadline, .proposal .status, .proposal .first-note.line { font-size: 13px; line-height: 18px; color: var(--muted); }
.proposal .status:empty { display: none; }
.proposal .actions { display: flex; gap: 10px; margin-top: 6px; }
.proposal .actions .button { flex: 1; }
/* A Method switch's two-week preview (#266): this week as the Proposal leaves it, then next week by the new Method. */
.proposal .preview { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px; border-radius: 14px; background: var(--fill); }
.proposal .preview-title { font-size: 15px; line-height: 20px; font-weight: 600; }
.proposal .preview-week { margin-top: 4px; font-size: 13px; line-height: 18px; font-weight: 600; color: var(--muted); }
.proposal .preview-days { display: flex; flex-direction: column; gap: 2px; padding: 0; list-style: none; }
.proposal .preview-day { display: flex; flex-wrap: wrap; gap: 0 8px; font-size: 15px; line-height: 20px; }
.proposal .preview-date { min-width: 64px; font-weight: 600; }
.proposal .preview-what { flex: 1; min-width: 0; }
.proposal .preview-tag { font-size: 13px; color: var(--muted); }
.proposal .preview-day.proposed .preview-what { font-weight: 600; }
/* Coach Today (#162): the morning's findings as cards, a Proposal's shape with its action and Hide for today. */
.coach-today { display: flex; flex-direction: column; gap: 12px; }
.card.coach-card { gap: 8px; }
.coach-card .eyebrow { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 18px; font-weight: 600;
                       text-transform: uppercase; color: var(--muted); }
.coach-card .what { font-size: 17px; line-height: 22px; font-weight: 600; }
.coach-card .why { font-size: 15px; line-height: 20px; color: var(--muted); }
.coach-card .actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 -10px; }
.coach-card .go { display: flex; align-items: center; gap: 2px; min-height: 44px; color: var(--accent); font-size: 15px; line-height: 20px;
                  font-weight: 600; text-decoration: none; }
.coach-card .go .icon { width: 16px; height: 16px; }
.coach-card .go:focus-visible, .coach-card .hide:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.coach-card .hide { color: var(--muted); font-size: 15px; line-height: 20px; }
/* A first-time note (#106): what a tab, the Ride screen or the first Proposal is for, once per athlete. The Proposal's
   eyebrow and dot, then one or two sentences, then Got it in the accent. */
.card.first-note { gap: 6px; }
.first-note .eyebrow { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 18px; font-weight: 600;
                       text-transform: uppercase; color: var(--muted); }
.first-note .text { font-size: 15px; line-height: 20px; }
.first-note .inline-link { align-self: flex-start; margin-top: -6px; }
#conversation > .first-note { margin-bottom: 18px; }

/* ---------- the week: the arrows to the one before and after ---------- */
.arrows { display: flex; margin-right: -10px; }
.arrow { width: 44px; height: 44px; padding: 0; border: none; background: transparent; color: var(--accent); display: flex; align-items: center;
         justify-content: center; }
.arrow .icon, .back .icon { width: 22px; height: 22px; stroke-width: 2.4; }

.card { gap: 12px; }
.card.proposal { gap: 8px; }
.fact-head { display: flex; flex-direction: column; gap: 3px; }
.card .reasons { padding-left: 20px; }
.card .reasons:empty, .fine:empty, .detail:empty { display: none; }
.fine { font-size: 12px; line-height: 16px; color: var(--muted); }
/* A Session drawn: each bar is a step, as wide as it is long and as high as its share of FTP. */
.profile { display: flex; align-items: flex-end; gap: 2px; height: 36px; }
.profile.tall { position: relative; height: 96px; margin-bottom: 22px; }
.profile .axis { position: absolute; left: 0; right: 0; top: 100%; margin-top: 6px; display: flex; justify-content: space-between;
                 font-size: 12px; line-height: 16px; color: var(--muted); }
.profile.dense { gap: 1px; }
.profile .bar { flex-basis: 0; min-width: 1px; border-radius: 2px; background: var(--easy); }
.profile .bar.tempo, .swatch.tempo { background: var(--tempo); }
.profile .bar.hard, .swatch.hard { background: var(--accent); }
.swatch { display: inline-block; width: 8px; height: 8px; border-radius: 2px; background: var(--easy); }
.targets { display: flex; flex-direction: column; gap: 6px; font-size: 15px; line-height: 20px; }
.targets .heading { margin-top: 4px; font-size: 13px; line-height: 18px; color: var(--muted); }
.targets .heading:first-child { margin-top: 0; }
.targets .repeat { font-weight: 600; }
/* A step is one line that reads "10 min · 90–108 W", at every width (#84). */
.targets .step { display: block; font-variant-numeric: tabular-nums; }

.gauges { display: flex; flex-direction: column; gap: 14px; }
/* What decided the Verdict, one line above its bars (#85). */
.decided { font-size: 15px; line-height: 20px; font-weight: 600; }
/* The athlete's own switch at the Session's foot on a Day (the Owner's ask of 28 Sep, coach/own_change.py): a grey button,
   as Ride now is. Outdoors against the Verdict asks first: the weather's reason in the warning's colours, then the two
   answers one over the other, Keep it indoors first (side by side, each wrapped onto two lines at 390 px, German worse).
   The reason takes focus to be read; it is no control, so it wears no ring. */
.place-switch, .place-ask { display: flex; flex-direction: column; gap: 8px; }
.place-switch { margin-top: 4px; }
.place-ask .caution { padding: 10px 12px; border-radius: 10px; background: var(--warn-bg); color: var(--warn-text);
                      font-size: 15px; line-height: 20px; }
.place-ask .caution:focus { outline: none; }
.place-ask .actions { display: flex; flex-direction: column; gap: 8px; }
.place-switch .status { font-size: 13px; line-height: 18px; color: var(--muted); }
.place-switch .status:empty { display: none; }
.gauge { display: flex; flex-direction: column; gap: 6px; font-size: 15px; line-height: 20px; }
.gauge .value { color: var(--muted); }
.gauge .track { position: relative; }
.gauge .track::after { content: ""; position: absolute; top: -3px; right: 0; width: 2px; height: 12px; border-radius: 1px; background: var(--muted); }

.fuel { display: flex; flex-direction: column; }
.fuel-step { display: flex; gap: 12px; }
.fuel-step .rail { width: 12px; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 4px; padding-top: 5px; }
.fuel-step .rail i { width: 10px; height: 10px; flex-shrink: 0; border-radius: 5px; background: var(--accent); }
.fuel-step .rail b { flex: 1; width: 2px; border-radius: 1px; background: var(--fill-strong); }
.fuel-step .words { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding-bottom: 14px; }
.fuel-step:last-child .rail b { display: none; }
.fuel-step:last-child .words { padding-bottom: 0; }
.fuel-step .when, .fuel-step .how { font-size: 13px; line-height: 18px; color: var(--muted); }
.fuel-step .what { font-weight: 600; }

/* The day's food rings, each with its name and note beside it. Its own class: .ring is the meal ideas' tick (#84). */
.rings { display: flex; flex-direction: column; gap: 14px; }
.ring-col { display: flex; flex-direction: row; align-items: center; gap: 14px; }
.ring-words { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ring-name { font-weight: 600; }
.food-ring { --share: 0; width: 104px; height: 104px; flex-shrink: 0; border-radius: 50%; display: flex; align-items: center; justify-content: center;
             background: conic-gradient(var(--accent) calc(var(--share) * 1turn), var(--fill) 0); }
.food-ring .hole { width: 88px; height: 88px; border-radius: 50%; background: var(--cell); display: flex; flex-direction: column; align-items: center;
                   justify-content: center; }
.ring-big { font-size: 22px; line-height: 26px; font-weight: 700; }
.ring-unit { font-size: 12px; line-height: 16px; color: var(--muted); text-align: center; }

/* ---------- the Dashboard: today, the weather, last week, this week, the tiles ----------
   Cards are solid on --cell, and the title carries no button (#86). Every line is an SVG path from
   view.js, sized by attributes; a bar's length is set through the style object, never a style attribute. */
.round { position: relative; width: 44px; height: 44px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; padding: 0;
         border-radius: 22px; color: var(--text); }
.round .icon { width: 20px; height: 20px; }
.round:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A card's header: an icon in the accent and a label in small capitals, something at the other end. */
.card-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 20px; }
.card-top .head { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em;
                  text-transform: uppercase; color: var(--muted); }
.card-top .head .icon { width: 18px; height: 18px; color: var(--accent); }
.card-top .muted { white-space: nowrap; }
/* The way on at a card's top (Records' Both seasons, Last week's Full review, the Athlete card's Profile) is in the text
   colour, a link underlined: volt is for the screen's one action (#209). */
.card-top .more { display: flex; align-items: center; gap: 2px; min-height: 44px; margin: -12px -4px -12px 0; color: var(--text); font-size: 15px;
                  line-height: 20px; text-decoration: none; white-space: nowrap; }
.card-top a.more { text-decoration: underline; }
.card-top .more .icon { width: 16px; height: 16px; }
.badge-icon { width: 40px; height: 40px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: 20px;
              background: var(--fill); color: var(--accent); }
.badge-icon .icon { width: 22px; height: 22px; }
.badge-icon.small { width: 32px; height: 32px; border-radius: 16px; color: var(--text); }
.badge-icon.small .icon { width: 18px; height: 18px; }

/* Today: the Verdict's band in the accent, the Session under it, last night's three numbers at the foot. */
.card.today { gap: 14px; padding: 12px 12px 16px; }
.today-stack { display: flex; flex-direction: column; gap: 22px; min-width: 0; }      /* Today, and Ride now under it on a day with no ride */
.today .band { display: flex; flex-direction: column; gap: 4px; padding: 14px 14px 16px; border-radius: 12px; background: var(--accent); color: var(--on-accent); }
.today .band-top { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em;
                   text-transform: uppercase; white-space: nowrap; }
.today .band-top span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }       /* one line; the window is in the line below */
/* The headline shrinks with the screen as the large title does, so "Ride indoors." is one line on a phone (review of
   28 Sep: two lines of it made the band taller than its answer), never under 24 or over 32 px; longer, it wraps. */
.today .headline { display: flex; align-items: center; gap: 10px; font-size: clamp(24px, calc(8.6vw - 8px), 32px); line-height: 1.125;
                   font-weight: 800; font-stretch: condensed; letter-spacing: .2px; text-transform: uppercase; }
.today .headline .icon { width: 1em; height: 1em; stroke-width: 2; }
.today .band .line { font-size: 15px; line-height: 20px; }
.today .band .line:empty { display: none; }
/* Readiness on a line of its own at the band's foot (review of 28 Sep: it was a second verdict beside the eyebrow, its
   reason a second paragraph): the word, or the brand's disclosure whose reason opens under it. In the band's colours:
   the chevron and the focus ring take the text's, since the accent is the band itself. */
.today .band .readiness { margin-top: 4px; font-weight: 600; }
.today .band details.disclosure > summary { color: inherit; }
.today .band details.disclosure > summary .chevron { color: currentColor; }
.today .band details.disclosure > summary:focus-visible { outline-color: currentColor; }
.today .band details.disclosure > .line { padding-bottom: 2px; font-weight: 400; }
.today .session { display: flex; flex-direction: column; gap: 10px; padding: 0 4px; }
.session-head { display: flex; align-items: center; gap: 12px; }
.session-head .name { font-size: 20px; line-height: 26px; font-weight: 700; overflow-wrap: anywhere; }
.session-head .sub { font-size: 13px; line-height: 18px; color: var(--muted); }
.today .profile.tall { height: 56px; margin-bottom: 22px; }
/* Last night's tiles. Scoped to their row: `mini` is also the small size of a day row's steps (Workouts' list), which a
   bare `.mini` turned into grey boxes. */
.minis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.minis > .mini { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 10px 12px; border-radius: 10px; background: var(--fill); }
.minis > .mini .label { display: flex; align-items: center; gap: 5px; font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em;
                        text-transform: uppercase; color: var(--muted); }
.minis > .mini .label .icon { width: 14px; height: 14px; }
.minis > .mini .big { font-size: clamp(18px, calc(8vw - 8px), 22px); line-height: 28px; font-weight: 800; }   /* "6 h 48" fits at 320 (#223) */
.minis > .mini .big small { font-size: 12px; font-weight: 600; color: var(--muted); }
.minis > .mini .delta { font-size: 12px; line-height: 16px; color: var(--muted); }
.minis > .mini .delta.good { color: var(--good); }
.minis > .mini .delta.watch { color: var(--watch); }
.minis > .mini .delta.bad { color: var(--bad); }

/* The Athlete card (#152), compact since the review of 28 Sep: the name with Log weight, the card's one action, on its
   top row; FTP, W/kg and weight in one row that is one link to where they are set, its chevron at the end, each column
   as wide as its words so a note keeps to one line on a phone; last night's minis under them. */
.card.athlete { gap: 12px; }
.card.athlete > .card-top .weigh { flex-shrink: 0; align-self: center; }
.athlete-top { display: flex; align-items: center; gap: 8px; color: inherit; text-decoration: none; border-radius: 10px; }
.athlete-top:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.athlete-top > .more { display: flex; flex-shrink: 0; }
.athlete-top > .more .icon { width: 18px; height: 18px; color: var(--muted); }
.athlete-numbers { flex: 1; min-width: 0; display: grid; grid-template-columns: repeat(3, auto); justify-content: space-between; gap: 8px; }
.athlete-numbers .stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.athlete-numbers .label { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.athlete-numbers .big { font-size: 26px; line-height: 30px; font-weight: 800; letter-spacing: -.02em; white-space: nowrap; }
.athlete-numbers .big small { font-size: 12px; font-weight: 600; letter-spacing: 0; color: var(--muted); }
.athlete-numbers .delta { font-size: 12px; line-height: 16px; color: var(--muted); }
.athlete-numbers .delta.good { color: var(--good); }
.athlete-numbers .delta.watch { color: var(--watch); }

/* The weather: now, the hours with the ride's marked, the limits as bars, and tomorrow as a row to its day. */
.card.weather { gap: 14px; }
.weather .now { display: flex; align-items: center; gap: 14px; }
.weather .now .sky { width: 48px; height: 48px; stroke-width: 1.6; color: var(--accent); }
.weather .temp { font-size: 48px; line-height: 48px; font-weight: 800; letter-spacing: -.02em; }
.weather .sky-line { font-size: 15px; line-height: 20px; font-weight: 600; }
.hours { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 4px; }
.hour { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 20px 0 8px; border-radius: 10px; }
.hour.ride { padding-top: 4px; background: var(--fill); }
.hour .tag { font-size: 10px; line-height: 12px; font-weight: 700; letter-spacing: .08em; color: var(--accent); }
.hour .at { font-size: 12px; line-height: 16px; color: var(--muted); }
.hour .icon { width: 22px; height: 22px; }
.hour .deg { font-size: 17px; line-height: 22px; font-weight: 700; }
.fine.end { margin-top: -8px; text-align: right; }
.limits { display: flex; flex-direction: column; gap: 10px; }
/* "Your limits, 18:00–20:00" and Edit at the other end, which opens Settings' Weather rules (review of 28 Sep): a line
   that opens something, its 44 px reaching over the rows round it rather than pushing them apart. */
.limits-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.limits-head .line-link { margin: -14px 0; }
.limit { display: flex; flex-direction: column; gap: 5px; }
.limit .name { font-size: 15px; line-height: 20px; font-weight: 600; }
.limit .track { position: relative; height: 6px; border-radius: 3px; background: var(--fill); }
.limit .track i { position: absolute; top: 0; bottom: 0; min-width: 4px; border-radius: 3px; background: var(--accent); }
.limit .track i.over { background: var(--danger); }
.tomorrow { display: flex; align-items: center; gap: 12px; min-height: 52px; margin: 0 -16px -16px; padding: 8px 16px; border-top: 1px solid var(--line);
            color: var(--text); text-decoration: none; }
.tomorrow > .icon { width: 22px; height: 22px; color: var(--muted); }
.tomorrow .grow { display: flex; flex-direction: column; }
.tomorrow .name { font-size: 15px; line-height: 20px; font-weight: 600; }
.tomorrow .sub { font-size: 13px; line-height: 18px; color: var(--muted); }
/* The forecast's credit at the card's foot (review of 28 Sep: it was the Dashboard's last line): a line that opens
   Open-Meteo's site, under tomorrow's row, which then keeps off the card's edge. */
.tomorrow:not(:last-child) { margin-bottom: -6px; }
.weather > .credit { margin-bottom: -10px; }
.icon.chevron { width: 18px; height: 18px; color: var(--muted); }

/* Last week: the score as a numeral and ten rising bars, the headline, the summary, the rows. */
.card.review { gap: 14px; }
.review .score { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-top: -8px; }
.review .numeral { font-size: 72px; line-height: 64px; font-weight: 800; font-stretch: condensed; letter-spacing: -.02em; }
.review .numeral small { margin-left: 4px; font: 600 15px/20px ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: 0; color: var(--muted); }
.review .bars { display: flex; align-items: flex-end; gap: 4px; height: 56px; }
.review .bars i { width: 10px; border-radius: 2px; background: var(--fill-strong); }
.review .bars i.on { background: var(--accent); }
.review .headline { margin-bottom: -8px; font-size: 24px; line-height: 28px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px;
                    text-transform: uppercase; }
.review .summary { font-size: 15px; line-height: 20px; }
.review .rows { display: flex; flex-direction: column; margin: 0 -16px -16px; border-top: 1px solid var(--line); }
.review-row { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 8px 16px; }
.review-row + .review-row { border-top: 1px solid var(--line); }
.review-row .grow { display: flex; flex-direction: column; }
.review-row .name { font-size: 15px; line-height: 20px; font-weight: 600; }
.review-row .sub { font-size: 13px; line-height: 18px; color: var(--muted); }
.review-row .end { display: flex; flex-direction: column; align-items: flex-end; text-align: right; }
.review-row .value { font-size: 17px; line-height: 22px; font-weight: 700; white-space: nowrap; }
.review-row .grade { display: flex; align-items: center; gap: 5px; font-size: 12px; line-height: 16px; font-weight: 600; white-space: nowrap; }
.review-row .grade i { width: 7px; height: 7px; border-radius: 4px; background: currentColor; }
.grade.good { color: var(--good); }
.grade.watch { color: var(--watch); }
.grade.bad { color: var(--bad); }
.loadbar { display: block; width: 100%; height: 12px; border-radius: 6px; overflow: hidden; }
.loadbar .track { fill: var(--fill); }
.loadbar .done { fill: var(--accent); }
.loadbar .done.over { fill: var(--danger); }
.loadbar .marker { stroke: var(--text); stroke-width: 2; vector-effect: non-scaling-stroke; }
/* The day's busy times (busy_times): the day from early to late, the calendars' busy times in grey, the Session as a thinner
   bar over them in the accent, in the danger colour where it overlaps one. The hour marks under it fall evenly. */
.busybar { display: block; width: 100%; height: 12px; border-radius: 6px; overflow: hidden; }
.busybar .track { fill: var(--fill); }
.busybar .busy { fill: var(--muted); }              /* full strength: 3:1 on the track (#223; 2.0:1 at 45 % in light) */
.busybar .session { fill: var(--accent); }
.busybar .session.clash { fill: var(--danger); }
.busy-ticks { position: relative; height: 13px; margin-top: -6px; font-size: 11px; line-height: 13px; color: var(--muted);
              font-variant-numeric: tabular-nums; }
.busy-ticks span { position: absolute; top: 0; transform: translateX(-50%); }
.busy-ticks span:first-child { transform: none; }
.busy-ticks span:last-child { transform: translateX(-100%); }
svg.lines { display: block; width: 100%; height: 64px; overflow: visible; }      /* the chart only: a row's .lines is text */
svg.lines path { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
svg.lines .ctl { stroke: var(--accent); }
svg.lines .atl { stroke: var(--dust); }
.swatch.ctl { background: var(--accent); }
.swatch.atl { background: var(--dust); }
.vitals { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.vital { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 12px; background: var(--cell); border-radius: var(--radius-card); }
.vital .label { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em;
                text-transform: uppercase; color: var(--muted); }
.vital .label .icon { width: 14px; height: 14px; color: var(--accent); }
.vital .big { font-size: 26px; line-height: 30px; font-weight: 800; letter-spacing: -.02em; }
.vital .big small { margin-left: 4px; font-size: 12px; font-weight: 600; letter-spacing: 0; color: var(--muted); }
.vital .delta, .vital .when, .vital .empty { font-size: 12px; line-height: 16px; color: var(--muted); }
.vital .big.good, .vital .delta.good { color: var(--good); }
.vital .big.watch, .vital .delta.watch { color: var(--watch); }
.vital .big.bad, .vital .delta.bad { color: var(--bad); }
.vital .delta:empty, .vital .when:empty { display: none; }
.spark { display: block; width: 100%; height: 36px; margin-top: auto; padding-top: 6px; overflow: visible; }
.spark path { fill: none; stroke: var(--accent); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.spark .base { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 2 3; }
.spark .dot { stroke-width: 5; }
/* Training status and Fitness (#153): Form's line in the colour of each day's zone (intervals.icu's zones, in the app's
   own tokens), Fitness over its area; while the Form caveat stands, both are muted. */
.spark .z-transition { stroke: var(--watch); }
.spark .z-fresh { stroke: var(--accent); }
.spark .z-grey { stroke: var(--muted); }
.spark .z-optimal { stroke: var(--good); }
.spark .z-high_risk { stroke: var(--bad); }
.spark .z-muted, .spark.area.muted path { stroke: var(--muted); opacity: .7; }
.spark.area .fill { fill: var(--accent); stroke: none; opacity: .14; }
.spark.area.muted .fill { fill: var(--muted); opacity: .12; }
.vital .big.z-transition { color: var(--watch); }
.vital .big.z-fresh { color: var(--accent); }
.vital .big.z-grey { color: var(--muted); }
.vital .big.z-optimal { color: var(--good); }
.vital .big.z-high_risk { color: var(--bad); }
.vital .big.muted { color: var(--muted); }
.vital .big.word { font-size: 22px; line-height: 28px; letter-spacing: -.01em; }
/* The Form caveat, said once (#220): one note under the numbers it qualifies, starred like the marks on the tiles while
   they carry one. A mark is the star alone beside a tile's value, 44 px to touch; the note takes focus when a mark or a
   Ride opens it. The star is read by nobody: the mark has its name, and the note is the sentence. */
.caveat-note.marked::before { content: "* "; content: "* " / ""; font-weight: 700; color: var(--text); white-space: pre; }
.caveat-note:focus { outline: none; }
.caveat-note:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 6px; }
.caveat-mark { position: relative; margin-left: 3px; color: var(--text); font-weight: 800; text-decoration: none; }
.caveat-mark::after { content: ""; position: absolute; left: 50%; top: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
.caveat-mark:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ---------- the segmented control: one, for every choice of a few (#223) ----------
   Workouts' List and Calendar, Settings' radio groups, Features' stages, the meal plan's new ideas, Log food's ways and
   Ride now's Minutes and Where. A capsule of capsules: 3 + 44 + 3, so the track's 25 and the segment's 22 share a
   centre; the track's edge is --control-line. Standing alone the segments share its width; at the end of a row each is
   as wide as its words. A segment's words wrap, and break only where a word cannot fit, never cut with an ellipsis.
   The selected one is its fill and a ring in the text colour, 3:1 and more on the track and on the fill, where the fill
   alone was 1.34:1 in dark and 1.41:1 in light; the focus ring stands 1 px outside it, in the track's padding, so the
   two never read as one (#218). */
.segmented { display: flex; padding: 3px; border-radius: 25px; background: var(--seg-track); box-shadow: inset 0 0 0 1px var(--control-line); }
.segmented button { flex: 1 1 0; min-width: 44px; min-height: 44px; padding: 2px 10px; border: none; border-radius: 22px; background: transparent;
                    color: var(--text); font: inherit; font-size: 15px; line-height: 20px; font-weight: 500; overflow-wrap: break-word; }
.row > .segmented button, .plan-row > .segmented button { flex: 1 1 auto; }
.segmented button.active { background: var(--seg-on); box-shadow: inset 0 0 0 1.5px var(--text); font-weight: 600; }
.segmented button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* A row that ends in a segmented control wraps rather than squeeze it: the control keeps to the row's end, and under
   375 px takes the whole line under the row's name (the narrow rules, below the tabs). */
.row:has(> .segmented) { flex-wrap: wrap; }
.row > .segmented { margin-left: auto; }

/* ---------- Workouts: List or Calendar (the segmented control above), the month ---------- */
/* The month: seven columns, Monday first, a dot under each day that has something. Today is outlined, the tapped day filled,
   the neighbouring months' days muted (#223: the text at 35 % was 2.2:1 in light, and they are buttons). A planned dot is a ring in the accent, a done one is filled in the good green: the
   shape tells them apart where the dark scheme's volt and moss do not. A day can carry both. */
.card.calendar { gap: 10px; padding: 12px 12px 14px; }
.cal-head { display: flex; align-items: center; justify-content: space-between; margin: -6px -10px 0; }
.cal-head h2 { font-size: 17px; line-height: 22px; font-weight: 700; }
/* The grid is its weeks (#224): each a row of seven columns, 4 px apart as the days are, so a row can be a grid's row. */
.cal { display: grid; gap: 4px; }
.cal .week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
/* A day is 44 px from 368 px wide (#223): under 390 the card's sides and the gaps between days give 4 and 2 px back, so
   a 375 px phone's days are 45, not 42. Under 368 seven columns of 44 do not fit (brand.md, Targets). */
@media (max-width: 389px) {
  .card.calendar { padding-left: 8px; padding-right: 8px; }
  .cal, .cal .week { gap: 2px; }
}
.cal .h { padding: 4px 0; text-align: center; font-size: 11px; line-height: 13px; font-weight: 600; letter-spacing: .4px; text-transform: uppercase;
          color: var(--muted); }
.cal .c { width: 100%; aspect-ratio: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; padding: 0; border: none;
          border-radius: 10px; background: transparent; color: var(--text); font: inherit; font-size: 15px; line-height: 20px; }
.cal .c.other { color: var(--muted); }
.cal .c.today { box-shadow: inset 0 0 0 2px var(--accent); }
.cal .c.today span { color: var(--accent); font-weight: 700; }
.cal .c.sel { background: var(--fill-strong); }
.cal .c:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cal .dots { display: flex; gap: 3px; height: 6px; }
.cal-dot, .cal .dots i { display: inline-block; width: 7px; height: 7px; border-radius: 4px; box-shadow: inset 0 0 0 1.5px var(--accent); }
.cal-dot.done, .cal .dots i.done { background: var(--good); box-shadow: none; }
/* A day with busy time in the athlete's calendars (busy_times): a short dash in the muted colour beside its dots, never a
   dot, so it is not read as something to ride; at full strength, 3:1 on the cell (#223: 2.4:1 at 60 % in light). */
.cal-dot.busy, .cal .dots i.busy { width: 9px; height: 3px; border-radius: 2px; background: var(--muted); box-shadow: none; align-self: center; }
.card.calendar .key { justify-content: center; }
.open-day { display: flex; align-items: center; gap: 2px; min-height: 44px; color: var(--accent); font-size: 13px; line-height: 18px; text-decoration: none;
            white-space: nowrap; }
.open-day .chev { border-color: var(--accent); }

/* ---------- Workouts: the seven-day strip, then the rows ---------- */
/* Seven cells of 58 px: on a phone the last one shows its edge, which says the strip scrolls; on a wide screen it fits. */
.strip { display: flex; gap: 6px; margin: 0 -16px; padding: 2px 16px 4px; overflow-x: auto; scrollbar-width: none; list-style: none; }
.strip::-webkit-scrollbar { display: none; }
.strip > li { flex: 0 0 58px; display: flex; }          /* a list of days (#212): the item sizes, its link fills it */
.strip .cell { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 2px 6px; border-radius: 12px;
               background: var(--cell); color: var(--text); text-decoration: none; font-size: 12px; line-height: 16px; }
.strip .cell.today { box-shadow: inset 0 0 0 2px var(--accent); }
.strip small { font-size: 11px; line-height: 13px; font-weight: 600; letter-spacing: .4px; text-transform: uppercase; color: var(--muted); }
.strip b { font-size: 17px; line-height: 22px; font-weight: 700; }
.strip .cell.today b { color: var(--accent); }
.strip .icon { width: 20px; height: 20px; color: var(--muted); }
.strip .temp { min-height: 16px; color: var(--muted); }
.strip .pill { margin-top: 2px; max-width: 100%; padding: 1px 5px; border-radius: 8px; background: var(--fill); font-size: 10px; line-height: 14px;
               font-weight: 600; text-transform: uppercase; letter-spacing: .2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strip .pill.out { background: var(--moss); color: var(--roast); }
.strip .pill.in { background: var(--fill-strong); }
.strip .pill.rest { background: transparent; color: var(--muted); }

/* A group's head and a key wrap rather than run past the screen (#223: with WCAG's text spacing Workouts' key ran to
   366 px at 320); a key's entry stays whole. */
.group-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 8px; padding: 0 4px; }
.group-head h2 { font-size: 20px; line-height: 24px; font-weight: 700; color: var(--text); }
.key { display: flex; flex-wrap: wrap; gap: 2px 10px; font-size: 12px; line-height: 16px; color: var(--muted); }
.key span { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.day-row { display: flex; align-items: center; gap: 8px; padding: 12px 10px 12px 16px; color: var(--text); text-decoration: none; }
.day-row .when { width: 36px; flex-shrink: 0; align-self: flex-start; display: flex; flex-direction: column; align-items: center; gap: 1px; }
.day-row .when small { font-size: 11px; line-height: 13px; font-weight: 600; letter-spacing: .4px; text-transform: uppercase; color: var(--muted); }
.day-row .when b { min-width: 34px; height: 34px; border-radius: 17px; display: flex; align-items: center; justify-content: center; font-size: 22px;
                   line-height: 28px; font-weight: 700; }
.day-row.is-today .when b { background: var(--accent); color: var(--on-accent); font-size: 20px; }
.day-row .main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.day-row .head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.day-row .head strong { font-weight: 600; }
.day-row .lines { margin-top: -6px; font-size: 13px; line-height: 18px; color: var(--muted); }
.day-row .head strong { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; }
.day-row .head strong span { min-width: 0; overflow-wrap: break-word; }
.day-row .head strong .icon { width: 20px; height: 20px; color: var(--accent); }
.day-row .lines .icon.small { width: 14px; height: 14px; margin-right: 4px; vertical-align: -2px; }
.day-row .main > .chip { align-self: flex-start; margin-top: -2px; }
.day-row .profile.mini { width: 48px; height: 28px; align-self: center; }
.list .empty { padding: 12px 16px; font-size: 15px; line-height: 20px; color: var(--muted); }
.chip { flex-shrink: 0; padding: 3px 8px; border-radius: 8px; background: var(--fill); font-size: 12px; line-height: 16px; font-weight: 600; white-space: nowrap; }
.chip.soft { color: var(--muted); }
.chip.accent { background: var(--accent); color: var(--on-accent); }
.chip.outline { background: transparent; box-shadow: inset 0 0 0 1.5px var(--accent); }
.chip.missed { color: var(--danger); }
.chev { flex-shrink: 0; width: 8px; height: 8px; margin: 0 6px 0 2px; border-top: 2px solid var(--muted); border-right: 2px solid var(--muted);
        transform: rotate(45deg); }

/* ---------- one day, pushed over the week ---------- */
.page.pushed { gap: 16px; padding-top: env(safe-area-inset-top); }
.navbar.pushed { justify-content: space-between; margin: 0 -16px; padding: 0 8px; }
.navbar h1, .navbar .bar-title { font-size: 17px; line-height: 22px; font-weight: 600; }
.back { min-width: 76px; height: 44px; display: flex; align-items: center; gap: 2px; color: var(--accent); text-decoration: none; }
.back-spacer { width: 76px; }
.compare { display: flex; flex-direction: column; gap: 5px; font-size: 15px; line-height: 20px; }
.compare .line { display: flex; align-items: center; gap: 8px; }
.compare .line progress { flex: 1; height: 8px; border-radius: 4px; }
.compare .line span { width: 112px; flex-shrink: 0; text-align: right; font-size: 13px; line-height: 18px; }
.compare .line .soft { color: var(--muted); }
progress.soft::-webkit-progress-value { background: var(--easy); }
progress.soft::-moz-progress-bar { background: var(--easy); }
.tiles { display: flex; gap: 10px; }
.tile { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border-radius: 10px; background: var(--fill); }
.tile small { font-size: 12px; line-height: 16px; color: var(--muted); }
.tile b { font-weight: 700; }
a.callout { display: block; min-height: 44px; padding-top: 12px; padding-bottom: 12px; color: var(--text); text-decoration: none; }   /* 44 px (#223) */
.callout { padding: 10px 12px; border-radius: 10px; background: var(--fill); font-size: 15px; line-height: 20px; }
/* A line that opens something (a Ride's pointer to the caveat, a Day's calendars not read): small text, a link by its
   underline, in the text colour and not volt, 44 px to touch. After .footnote a, which it overrides. */
a.line-link { display: flex; align-items: center; min-height: 44px; margin: -6px 0; font-size: 13px; line-height: 18px;
              color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
a.line-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* Under a list, where a setting bites (Workouts' "Riding days and hours"): in from the edge as a footnote is, as wide as
   its words. */
.group > a.line-link.settings-link { align-self: flex-start; margin: -2px 16px 0; }

/* ---------- Nutrition: calories and protein side by side, and the rows of the days behind ---------- */
.food .pair { display: flex; gap: 16px; }
.food .half { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.food .half .big { font-size: 20px; line-height: 26px; }
.food .half .loadbar { height: 8px; margin: 5px 0 3px; border-radius: 4px; }

/* ---------- what the Coach writes: markdown, in a message or in a report ---------- */
.text { overflow-wrap: anywhere; }
.text p { margin: 0 0 8px; } .text p:last-child { margin-bottom: 0; }
.text h3 { font-size: 17px; font-weight: 600; margin: 10px 0 4px; } .text h3:first-child { margin-top: 0; }
.text ul, .text ol { margin: 0 0 8px; padding-left: 24px; }
.text table { display: block; overflow-x: auto; border-collapse: collapse; font-size: 15px; line-height: 20px; margin: 0 0 8px; }
.text th, .text td { border: 1px solid var(--line); padding: 4px 8px; text-align: left; }
.text code, .text pre { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .88em; }
.text pre { white-space: pre-wrap; margin: 0 0 8px; padding: 10px 12px; background: var(--fill); border-radius: 10px; }
.text a { color: inherit; }

/* ---------- Chat: the Coach writes on the page, the athlete in a quiet bubble ---------- */
body.in-chat { overflow: hidden; }
/* The Chat fills the screen, and the thread scrolls under the composer and the floating tab bar, which is the way out
   of it: no header of its own. The composer floats just above the bar; the thread's last message stops above both. */
.view.chat { position: relative; display: flex; flex-direction: column; max-width: 760px; margin: 0 auto; height: 100dvh; }
.navbar { height: 44px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-bottom: .5px solid var(--line); }
.navbar h1, .navbar .bar-title { display: flex; }
/* The Chat's title is for a screen reader until the Chat is docked (#212): hidden as .sr hides, never display: none,
   which would take the view's heading away too. The docked Chat shows it (1280, below). */
#view-chat .navbar { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
#conversation { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
                padding: calc(env(safe-area-inset-top) + 16px) 16px calc(env(safe-area-inset-bottom) + 12px + var(--tabbar) + 76px); }
#conversation:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }      /* scrolled by keyboard (#206) */
#messages { display: flex; flex-direction: column; gap: 16px; }
.day { align-self: center; padding-top: 6px; font-size: 12px; line-height: 16px; font-weight: 600; color: var(--muted); }
/* Relative: the speaker a screen reader hears first (.sr, #206) is placed inside its message, never at its place in the
   scrolled thread below the Chat, where it would lengthen the page; and at its start, where its unwrapped words stay
   inside the thread (at the end of the athlete's row they ran past the screen's edge). */
.message { position: relative; display: flex; flex-direction: column; gap: 4px; }
.message .sr { top: 0; left: 0; }
.message.athlete { align-items: flex-end; }
.message.athlete .text { max-width: min(78%, 520px); padding: 8px 14px; border-radius: 18px; background: var(--fill); white-space: pre-wrap; }
.message.athlete .meta { padding-right: 6px; }
.message.coach { flex-direction: row; align-items: flex-start; gap: 10px; }
.message.coach .body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.message img { display: block; max-width: min(232px, 78%); max-height: 260px; border-radius: 18px; }
.message .meta { font-size: 12px; line-height: 16px; color: var(--muted); }
.message .error { font-size: 13px; line-height: 18px; color: var(--danger); }
.message.failed .text { box-shadow: 0 0 0 1.5px var(--danger); }
/* A setting Proposal's card in the Conversation (#256): the Proposal card, as wide as a message reads well */
.message .card.proposal { max-width: 560px; }
/* A meal told to the Coach, under its answer (#241): the Proposal card's eyebrow, the meal's name, its total, one row per
   part on the fill with its range, the middle Log it writes, then Log it. Logged, the eyebrow says so in the good colour
   and the button is gone. */
.card.estimate { gap: 8px; max-width: 520px; }
.estimate .eyebrow { font-size: 13px; line-height: 18px; font-weight: 600; text-transform: uppercase; color: var(--muted); }
.estimate.logged .eyebrow { color: var(--good); }
.estimate .what { font-size: 17px; line-height: 22px; font-weight: 600; }
.estimate .what[tabindex="-1"]:focus { outline: none; }      /* where the focus stays once Log it is gone */
.estimate .total { font-size: 15px; line-height: 20px; font-weight: 600; font-variant-numeric: tabular-nums; }
.estimate .parts { display: flex; flex-direction: column; gap: 6px; padding: 0; margin: 0; list-style: none; }
.estimate .part { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 8px 12px; border-radius: 12px;
                  background: var(--fill); font-size: 15px; line-height: 20px; }
.estimate .part .name { min-width: 0; overflow-wrap: anywhere; }
.estimate .part .amount { flex-shrink: 0; color: var(--muted); font-variant-numeric: tabular-nums; }
.estimate .note, .estimate .status { font-size: 13px; line-height: 18px; color: var(--muted); }
.estimate .status:empty { display: none; }
.estimate .button { align-self: flex-start; margin-top: 4px; }
.avatar { width: 22px; height: 22px; margin-top: 1px; flex-shrink: 0; border-radius: 11px; background: var(--espresso);
          display: flex; align-items: center; justify-content: center; }
.working { display: flex; align-items: center; gap: 8px; padding: 16px 0 4px; font-size: 15px; line-height: 20px; color: var(--muted); }
.working .avatar { margin: 0 2px 0 0; }
.working .dot { animation: pulse 1.4s ease-in-out infinite; }
.working.problem { color: var(--danger); }
.working.problem .avatar, .working.problem .dot { display: none; }
@keyframes pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .working .dot { animation: none; } .switch .track, .switch .track::before { transition: none; } }
.banner { display: none; }

#composer { position: absolute; z-index: 3; left: 0; right: 0; bottom: calc(env(safe-area-inset-bottom) + 12px + var(--tabbar));
            display: flex; flex-direction: column; padding: 8px 16px; }
.attached { display: flex; flex-wrap: wrap; gap: 14px; padding: 6px 0 10px 52px; }
/* While nothing has been said (#217): the Coach's avatar, larger than the thread's, its name and one line of what it read
   today, on the openers' left edge. */
.chat-intro { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 0 14px 12px; }
.avatar.large { width: 52px; height: 52px; margin: 0 0 10px; border-radius: 26px; }
.avatar.large .mark { width: 42px; height: 42px; }
.chat-intro .name { font-size: 17px; line-height: 22px; font-weight: 600; }
.chat-intro .line { font-size: 15px; line-height: 20px; color: var(--muted); }
/* Openers, shown only while nothing has been said: a line each, 44 pt to touch. The icon takes the text colour: volt
   stays for the one action (#217). */
#suggestions { display: flex; flex-direction: column; gap: 2px; padding: 0 8px 10px; }
#suggestions button { display: flex; align-items: center; gap: 14px; width: 100%; min-height: 44px; padding: 8px 6px;
                      border: none; background: transparent; color: var(--text); text-align: left; }
#suggestions .icon { width: 22px; height: 22px; color: var(--text); }
/* Until a Quick model passed on the athlete's key the openers cannot be answered: one card in their place opens
   Settings › AI (B3). A card on --cell like the notice, its title over where the setup stands, and the chevron. */
.setup-card { display: flex; align-items: center; gap: 12px; min-height: 64px; margin: 0 6px; padding: 12px 16px;
              border-radius: var(--radius-card); background: var(--cell); color: var(--text); text-decoration: none; }
.setup-card .grow { flex: 1; min-width: 0; }
.setup-card strong { display: block; font-size: 17px; line-height: 22px; font-weight: 600; }
.setup-card .sub { display: block; font-size: 15px; line-height: 20px; color: var(--muted); }
.setup-card .chevron { flex-shrink: 0; width: 16px; height: 16px; stroke-width: 2.2; color: var(--muted); }
.setup-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* On a phone the composer floats over the thread. An empty Chat puts it in the flow instead, over the tab bar's room, so
   on a short screen the intro and the openers push the thread's notes up rather than lie over them (#217). */
.view.chat.empty { padding-bottom: calc(env(safe-area-inset-bottom) + 12px + var(--tabbar)); }
.view.chat.empty #conversation { padding-bottom: 8px; }
.view.chat.empty #composer { position: relative; bottom: auto; }
.thumb { position: relative; display: block; }
.thumb img { display: block; width: 56px; height: 56px; object-fit: cover; border-radius: 12px; }
.thumb .remove { position: absolute; top: -8px; right: -8px; width: 24px; height: 24px; padding: 0; border: none; border-radius: 12px;
                 background: var(--fill-strong); color: var(--text); font-size: 16px; line-height: 1; }
.thumb .remove::after, .send::after { content: ""; position: absolute; inset: -10px; }      /* a 44 pt target around a small control */
/* One pill on glass: the photo button, what is being written, and Send. What has been said scrolls under it. */
/* 4 + 44 + 4 = 52: the pill pays for both sides, so a one-line field sits on its centre. */
.compose { display: flex; align-items: flex-end; gap: 4px; min-height: 52px; padding: 4px 6px 4px 2px; border-radius: 26px;
           background: var(--glass); -webkit-backdrop-filter: blur(24px) saturate(140%); backdrop-filter: blur(24px) saturate(140%);
           border: 1px solid var(--glass-line); box-shadow: inset 0 1px 0 var(--glass-highlight), var(--glass-shadow); }
/* Add a photo is a button that opens the hidden file input (#206): reached by Tab, its ring on the circle it draws. */
.attach { width: 44px; height: 44px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; padding: 0;
          border: none; background: transparent; cursor: pointer; }
.attach span { width: 34px; height: 34px; border-radius: 17px; display: flex; align-items: center; justify-content: center; }
.attach .icon { width: 22px; height: 22px; stroke-width: 2; }
.attach:focus-visible { outline: none; }
.attach:focus-visible span { outline: 2px solid var(--accent); outline-offset: 2px; }
#composer .field { flex: 1; min-width: 0; display: flex; align-items: flex-end; gap: 6px; min-height: 44px; padding: 0 2px 0 4px; }
/* 11 + 22 + 11 = 44, exactly the row: the line sits on the pill's centre instead of 7 px under it. */
#composer textarea { flex: 1; min-width: 0; max-height: 140px; padding: 11px 0; line-height: 22px; border: none; outline: none; resize: none; background: transparent; }
/* The field has no edge of its own, so the ring goes round the pill while it has the cursor (#206); Add a photo and Send
   ring themselves. */
.compose:has(textarea:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Send is drawn only when there is something to send; Enter sends either way. */
.send { position: relative; width: 36px; height: 36px; margin-bottom: 4px; padding: 0; flex-shrink: 0; border: none; border-radius: 18px;
        background: var(--accent); color: var(--on-accent); display: none; align-items: center; justify-content: center; }
#composer.has-input .send { display: flex; animation: send-in 140ms ease-out; }
@keyframes send-in { from { opacity: 0; transform: scale(.7); } }
@media (prefers-reduced-motion: reduce) { #composer.has-input .send { animation: none; } }
.send .icon { width: 20px; height: 20px; stroke-width: 2.4; }
.send:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- sheets: Ride now, a vital's history and Log weight here, Log food and Meal ideas with Nutrition ----------
   From the bottom of a phone; a centred card from 768 px. The backdrop dims the whole window: from 1024 px the sidebar,
   and from 1280 the docked Chat, stay beside a sheet but are inert while it is open (#207), so they are dimmed as out of
   reach with the page, and a tap on them closes it as a tap on the page does (#219). The dimming is the backdrop's own
   flat tint (--backdrop: roast at 45 %, black at 45 % on the Black ground): no blur and no motion, so Reduce
   Transparency and Reduce Motion find nothing in it to take away. */
.sheet { position: fixed; inset: 0; z-index: 8; display: flex; align-items: flex-end; justify-content: center; }
.sheet-backdrop { position: fixed; inset: 0; background: var(--backdrop); }
/* One ground on every sheet, Log food's: in dark a cell over the dimmed page with fills on it, in light the crema ground
   with white on it (#219). --sheet-card is what sits on it: a field, an answer, the Proposal card. */
.sheet-body { --sheet: var(--bg); --sheet-card: var(--cell);
              position: relative; width: 100%; max-width: 600px; max-height: calc(100dvh - 40px); overflow-y: auto; display: flex; flex-direction: column;
              gap: 12px; padding: 8px 16px calc(env(safe-area-inset-bottom) + 20px); border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
              background: var(--sheet); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sheet-body { --sheet: var(--cell); --sheet-card: var(--fill); }
}
:root[data-theme="dark"] .sheet-body { --sheet: var(--cell); --sheet-card: var(--fill); }
.sheet .card, .sheet .answer, .sheet textarea, .sheet .weigh-form input { background: var(--sheet-card); }
/* One head on every sheet (#219; sheets.js builds the grabber and Close into each .sheet-head): the grabber, a hint to
   the eye that this is a sheet (nothing drags it), then the title leading and Close trailing. What else a sheet says in
   its head stands in a column with the title (the ideas' eyebrow and asks, Log food's meal slot), and Close stays at the
   top. The title is the dialog's name, in sentence case as the row that opens it says it (#218), one type on every sheet. */
.grabber { align-self: center; flex-shrink: 0; width: 36px; height: 5px; border-radius: 3px; background: var(--fill-strong); }
.sheet-head { display: flex; align-items: flex-start; gap: 12px; }
.sheet-head > .sheet-title, .sheet-head > .grow { flex: 1; min-width: 0; min-height: 44px; display: flex; flex-direction: column; justify-content: center; gap: 4px; }
.sheet-title { font-size: 22px; line-height: 28px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px; }
/* A past day's name, a line of its own under Log food's title; the " · " between them is for a screen reader's one sentence. */
.sheet-title .day { display: block; font-size: 13px; line-height: 18px; font-weight: 600; font-stretch: normal; letter-spacing: 0; color: var(--muted);
                    white-space: nowrap; }
.sheet-title .sep { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Close: a glass circle, 44 px, the cross in the text colour; it closes as Escape and the backdrop do. */
.sheet-close { flex-shrink: 0; width: 44px; height: 44px; min-width: 44px; min-height: 44px; display: flex; align-items: center; justify-content: center;
               padding: 0; border-radius: 22px; color: var(--text); }
.sheet-close .icon { width: 18px; height: 18px; stroke-width: 2; }
.sheet-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A sheet's title takes focus as the sheet opens, to be read first (#207, sheets.js). It is no control: no ring. */
.sheet [tabindex="-1"]:focus, .logfood [tabindex="-1"]:focus { outline: none; }
.sheet-content { display: flex; flex-direction: column; gap: 12px; }
.sheet .hint { font-size: 15px; line-height: 20px; color: var(--muted); }
.sheet .label { font-size: 13px; line-height: 18px; color: var(--muted); }
/* Minutes and Where are radioGroup's 44 px segments (#218), the segmented control every choice of a few is (#223): the
   pick is its ring in the text colour, so volt stays for Ask the Coach. The note shows its edge (#223). */
.sheet textarea { width: 100%; min-height: 66px; padding: 11px 14px; border: none; border-radius: var(--radius-card);
                  box-shadow: inset 0 0 0 1px var(--control-line); resize: none; outline: none; }
.sheet textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.sheet .actions { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.sheet .card.proposal .actions { flex-direction: row; margin-top: 6px; }
.sheet .waiting { display: flex; align-items: center; gap: 8px; padding: 8px 0; font-size: 15px; line-height: 20px; color: var(--muted); }
.sheet .waiting .dot { animation: pulse 1.4s ease-in-out infinite; }
.sheet .answer { padding: 12px 16px; border-radius: var(--radius-card); }
@media (prefers-reduced-motion: reduce) { .sheet .waiting .dot { animation: none; } }

/* Calendar links (#156): each link a row with Remove, a row to add one, and how to find the link, folded in the brand's
   disclosure (#222), whose 44 px rows need no margin of their own. */
.row.calendar-link .grow > span { display: block; }
.row.calendar-link .sub.bad { color: var(--danger); }
/* A row of fields named on the screen (#223): a calendar's link and name, an Invite's who. Each field is a label with its
   words above it, since a placeholder goes once something is typed; 44 px with its edge, the row's button at their foot. */
.row.field-form { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end; padding-top: 10px; padding-bottom: 12px; }
.field-above { flex: 1 1 180px; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.field-above-name { font-size: 13px; line-height: 18px; color: var(--muted); }
.row.field-form input { width: 100%; min-width: 0; height: 44px; padding: 0 12px; border: none; border-radius: 10px; background: var(--fill);
                        box-shadow: inset 0 0 0 1px var(--control-line); }
.row.field-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* The athlete's own Settings by kind (#252-#255): the days as the Invite's chips, a window as its two times. */
.day-picks { display: flex; flex-wrap: wrap; gap: 4px; }
.own-times { display: flex; align-items: center; gap: 6px; }
.row.field-form .own-times input { flex: 1 1 0; width: auto; max-width: 140px; padding: 0 8px; color: inherit; font: inherit; }
.row.field-form input[type="time"]::-webkit-calendar-picker-indicator { display: none; }
.own-number { display: flex; align-items: center; gap: 8px; }
.row.field-form .own-number input { flex: 1 1 auto; max-width: 140px; }
.own-unit { color: var(--muted); white-space: nowrap; }
.calendar-how { padding: 0 4px; font-size: 13px; line-height: 18px; color: var(--muted); }
.calendar-how p { margin: 0 0 8px; }
/* The athlete's own instructions (#155) and feedback for the Owner (#157): a box, its count, Save or Send. */
.row.instructions, .row.feedback { display: block; }
.instructions-text, .feedback-text { width: 100%; min-height: 110px; padding: 10px 12px; border: none; border-radius: 12px; background: transparent;
                                      box-shadow: inset 0 0 0 1px var(--control-line); resize: vertical; outline: none; font-size: 16px; line-height: 22px; }
.instructions-text:focus-visible, .feedback-text:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }      /* #212 */
.instructions-foot, .feedback-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; }
.instructions-count, .feedback-count { margin: 0; padding: 0; }
.list .instructions-count.over, .list .feedback-count.over { color: var(--danger); }        /* over the .list .footnote colour */
/* What the Coach knows (#154): one card per entry, the section as it reads now quoted, Correct and Forget as links. */
.row.memory { display: block; }
.memory-head strong { display: block; font-weight: 600; }
.memory-head .sub { display: block; margin-top: 2px; font-size: 13px; line-height: 18px; color: var(--muted); }
.memory-why { margin-top: 8px; font-size: 15px; line-height: 20px; }
.memory-now { margin: 8px 0 0; padding: 10px 12px; border-left: 3px solid var(--fill-strong); border-radius: 8px; background: var(--fill);
              font-size: 15px; line-height: 20px; white-space: pre-wrap; overflow-wrap: anywhere; }
.memory-note { display: block; margin-top: 8px; font-size: 13px; line-height: 18px; color: var(--muted); }
a.memory-note { padding: 13px 0; margin: -5px 0 -13px; color: var(--accent); }      /* 44 px to touch, where its line was (#223) */
.memory-actions { display: flex; gap: 12px; margin-top: 4px; margin-left: -4px; }
.row.memory.forgotten .memory-head, .row.memory.forgotten .memory-why, .row.memory.forgotten .memory-now { opacity: .6; }
.row.memory .confirm { margin: 8px 0 0; padding: 0; }
.memory-lead { margin-top: -4px; }
#view-memory .navbar.pushed { height: auto; min-height: 44px; }        /* a title longer than a day's: it may take two lines */
#view-memory .navbar.pushed h1 { min-width: 0; font-size: 15px; line-height: 19px; text-align: center; }
/* A vital's history and Log weight (#163): the minis open it, the Athlete card gets a small button. The
   chart is the sparkline grown: the band from the baseline to the readiness line, the baseline dashed, a dot on each day. */
.minis > .mini.tap { border: none; text-align: start; color: inherit; font: inherit; }
.minis > .mini.tap > .big, .minis > .mini.tap > .delta { display: block; }
.minis > .mini.tap:active { background: var(--fill-strong); }
.minis > .mini.tap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.vital .weigh { align-self: flex-start; margin-top: 10px; }
.vital .weigh:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.history-now .big { font-size: 34px; line-height: 40px; font-weight: 800; letter-spacing: -.02em; }
.history-now .big small { font-size: 15px; font-weight: 600; letter-spacing: 0; color: var(--muted); }
.history-now .delta { font-size: 15px; line-height: 20px; color: var(--muted); }
.history-now .big.good, .history-now .delta.good { color: var(--good); }
.history-now .big.watch, .history-now .delta.watch { color: var(--watch); }
.history-now .big.bad, .history-now .delta.bad { color: var(--bad); }
.history-chart { display: block; width: 100%; height: 140px; padding: 8px 0; box-sizing: content-box; overflow: visible; }
.history-chart .band { fill: var(--watch); opacity: .16; }
.history-chart .base { fill: none; stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; }
.history-chart .line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.history-chart .dot { fill: none; stroke: var(--accent); stroke-width: 6; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.history-chart .dot.last { stroke-width: 11; }
.history-ends { display: flex; justify-content: space-between; gap: 12px; margin-top: -6px; font-size: 13px; line-height: 18px; color: var(--muted); }
.weigh-form { display: flex; flex-direction: column; gap: 8px; }
.weigh-form input { width: 100%; height: 50px; padding: 0 14px; border: none; border-radius: var(--radius-card); background: var(--cell); color: var(--text);
                    box-shadow: inset 0 0 0 1px var(--control-line); font: inherit; font-size: 17px; }
.weigh-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.weigh-form .label + input { margin-top: -4px; }

/* ---------- glass: the controls layer, floating over the content ---------- */
/* One recipe for every glass surface: the tab pill, the Chat's circle, and any floating control that takes .glass.
   Never on a card, and never glass on glass: what is selected inside is a --glass-lens tint. */
.glass, #tabs .pill, #tabs a.coach { background-color: var(--glass); -webkit-backdrop-filter: blur(24px) saturate(180%);
       backdrop-filter: blur(24px) saturate(180%); border: 1px solid var(--glass-line);
       box-shadow: inset 0 1px 0 var(--glass-highlight), var(--glass-shadow); }
/* With Reduce Transparency on, the glass turns solid: the cell's ground, no blur. */
@media (prefers-reduced-transparency: reduce) {
  .glass, #tabs .pill, #tabs a.coach, .compose { background-color: var(--cell); -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ---------- the tabs on a phone: a floating glass pill of four, and the Chat's circle beside it ---------- */
/* The pill is 62 high: 1 of hairline and 3 of padding around a 54 px lens, whose 27 shares the pill's centre. Items on
   the pill are all icon over name; the circle is the icon alone and carries its name in aria-label. A name is never cut:
   it breaks between syllables when it must (German, WCAG's text spacing). The current tab is its lens and a ring in the
   text colour, as a selected segment is: the lens alone was 1.45:1 on the pill in dark and 1.2:1 in light (#223). The
   focus ring stands outside the tab, so a focused tab never looks like the current one. */
#tabs { position: fixed; left: 16px; right: 16px; bottom: calc(env(safe-area-inset-bottom) + 12px); z-index: 5; }
#tabs .brand { display: none; }
#tabs .rail-mark { display: none; }                 /* the C alone heads the rail of a wide screen (#221) */
#tabs .tablist { display: flex; align-items: center; gap: 10px; }
#tabs .pill { flex: 1; min-width: 0; display: flex; height: var(--tabbar); padding: 3px; border-radius: calc(var(--tabbar) / 2); }
#tabs a { position: relative; flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;
          gap: 2px; border-radius: 27px; color: var(--text); text-decoration: none; font-size: 10px; line-height: 12px; font-weight: 500; }
#tabs a .icon { width: 24px; height: 24px; }
#tabs a .label { max-width: 100%; text-align: center; overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; }
#tabs a.active { background-color: var(--glass-lens); box-shadow: inset 0 0 0 1.5px var(--text); color: var(--accent); font-weight: 600; }
#tabs a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#tabs a.coach { flex: none; width: var(--tabbar); height: var(--tabbar); border-radius: 50%; }
#tabs a.coach .label { display: none; }
#tabs a.coach.active { background-color: var(--glass); background-image: linear-gradient(var(--glass-lens), var(--glass-lens));
                      box-shadow: inset 0 0 0 1.5px var(--text), inset 0 1px 0 var(--glass-highlight), var(--glass-shadow); }
#tabs .badge { position: absolute; top: 3px; left: 50%; margin-left: 5px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
               background: var(--badge); color: var(--on-red); font-size: 12px; line-height: 18px; font-weight: 600; text-align: center; }
#tabs a.coach .badge { top: -3px; left: auto; right: -3px; margin-left: 0; }

/* The scroll edge: content fades into the ground under the floating bar, so its labels stay readable over anything.
   Above the content, under the composer and the bar; it never takes a tap. In the Chat it reaches over the composer. */
.scroll-edge { position: fixed; left: 0; right: 0; bottom: 0; z-index: 2; height: calc(env(safe-area-inset-bottom) + var(--edge-height));
               pointer-events: none; background: linear-gradient(to bottom, var(--edge0), var(--edge1) 70%); }
#tabs[hidden] + .scroll-edge { display: none; }
.scroll-edge-top { display: none; }
body.in-chat .scroll-edge { height: calc(env(safe-area-inset-bottom) + 12px + var(--tabbar) + 76px); }

/* ---------- narrow: under 375 px (a 320 px phone, or a zoomed one) ----------
   Nothing scrolls sideways and nothing is cut (#223, WCAG 1.4.10). The tab pill takes 8 px from each side and 2 from
   the gap to the circle, so "Dashboard" fits its tab at 320; a row that ends in a segmented control, and the meal plan's
   row of new ideas, give the control the whole line under the row's name. */
@media (max-width: 374px) {
  #tabs { left: 8px; right: 8px; }
  #tabs .tablist { gap: 8px; }
  .row > .segmented, .plan-row > .segmented { flex: 1 1 100%; margin-left: 0; }
}

/* ---------- regular width: 768 px and up ----------
   Width decides, never the device (HIG Layout; docs/adr/0010). Four thresholds, named once here and nowhere else:
   768   regular width. The tabs leave the bottom for a glass pill near the top that shares its row with the page's
         actions (iPadOS's tab bar); sheets are centred cards over the dimmed page; the top scroll edge appears.
   1024  the sidebar: a floating glass column, inset and concentric with the window, the tabs listed with their names
         (a Mac, an iPad in landscape). Workouts and Nutrition put a day beside their list instead of pushing it.
   1280  the Chat is docked: an edge-to-edge glass pane alongside the content, always there, as a Mac inspector is.
         Up to 1439 the sidebar folds into a rail of the same tabs as icons, each showing its name on hover and focus,
         so the content between the rail and the Chat keeps room for a list beside its day (#221).
   1440  the full sidebar again, exactly as from 1024: the width no longer has to choose.
   The sidebar and the docked Chat are glass (--glass-regular, more opaque than a bar, as Apple sets larger elements);
   what is selected inside is the --glass-lens tint. Nothing but a horizontal row (the seven-day strip) runs under the
   sidebar; the content column starts at its trailing edge. Two widths hold for content at every threshold: a split
   view's detail column is never under --detail-min, a phone's own column (the list beside it gives way first), and the
   Dashboard runs two columns only while each is at least --column-min (#221). */
@media (min-width: 768px) {
  .page { padding-bottom: 48px; }
  .scroll-edge { display: none; }
  html { scroll-padding-top: var(--edge-top); scroll-padding-bottom: 0; }       /* under the bar row and the top edge (#212) */
  /* The top scroll edge: under the floating controls the content blurs and fades as it passes, once it has started to
     pass (body.scrolled). One per window, never a bar background of its own. */
  .scroll-edge-top { display: block; position: fixed; top: 0; left: 0; right: 0; z-index: 4; height: var(--edge-top); pointer-events: none; opacity: 0; transition: opacity .15s;
                     -webkit-backdrop-filter: blur(18px) saturate(140%); backdrop-filter: blur(18px) saturate(140%);
                     -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0, rgba(0, 0, 0, 1) 24%, rgba(0, 0, 0, 0) 100%);
                     mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0, rgba(0, 0, 0, 1) 24%, rgba(0, 0, 0, 0) 100%);
                     background: linear-gradient(to bottom, var(--edge1), var(--edge0)); }
  .scroll-edge-top::after { content: ""; position: absolute; inset: 0; background: var(--bg); opacity: .35; }
  body.scrolled .scroll-edge-top { opacity: 1; }
  #tabs[hidden] ~ .scroll-edge-top { display: none; }
  @media (prefers-reduced-motion: reduce) { .scroll-edge-top { transition: none; } }
  /* Sheets are centred cards over the dimmed page, rounded all round. */
  .sheet { align-items: center; }
  .grabber { display: none; }       /* a centred card is no sheet from the bottom edge: nothing to hint at (#219) */
  .sheet-body { max-width: 560px; max-height: calc(100dvh - 80px); border-radius: var(--radius-sheet); padding-bottom: 22px; }
  /* The Chat as a column. */
  .view.chat { height: 100dvh; max-width: 752px; }
  #composer { position: static; padding-bottom: 24px; }
  #conversation { padding: 24px 16px 6px; }
  /* What the empty Chat set on a phone (#217) back as every width from here has it: no tab bar's room under it. */
  .view.chat.empty { padding-bottom: 0; }
  .view.chat.empty #composer { position: static; }
  .view.chat.empty #conversation { padding-bottom: 6px; }
  #messages { gap: 18px; }
  .banner { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; padding: 12px 16px; border-radius: var(--radius-card); background: var(--cell);
            color: var(--text); text-decoration: none; font-size: 15px; line-height: 20px; }
  .banner .grow { display: flex; flex-direction: column; }
  .banner .chevron { width: 16px; height: 16px; stroke-width: 2.2; color: var(--muted); }
}

/* 768 to 1023: the tabs as a glass pill near the top, sharing the row with the page's actions (iPadOS in portrait). */
@media (min-width: 768px) and (max-width: 1023px) {
  body { padding-top: calc(var(--inset) + var(--topbar) + 12px); }
  body.signed-out { padding-top: 0; }
  .page { max-width: 752px; padding-top: 8px; }
  #tabs { position: fixed; top: var(--inset); left: var(--inset); right: var(--inset); bottom: auto; z-index: 6; display: flex; align-items: center;
          justify-content: center; height: var(--topbar); pointer-events: none; }
  #tabs > * { pointer-events: auto; }
  #tabs .brand { display: none; }                          /* the bar carries no name, as an iPad tab bar does not */
  #tabs .tablist { gap: 10px; }
  #tabs .pill { flex: none; height: var(--topbar); padding: 4px; border-radius: calc(var(--topbar) / 2); }
  #tabs a { flex: none; height: 100%; flex-direction: row; gap: 8px; padding: 0 16px; border-radius: calc(var(--topbar) / 2 - 4px); font-size: 15px; line-height: 20px;
            font-weight: 500; }
  #tabs a .icon { width: 22px; height: 22px; }
  #tabs a.active { color: var(--text); font-weight: 600; }
  #tabs a.active .icon { color: var(--accent); }
  #tabs a.coach { width: var(--topbar); height: var(--topbar); }
  #tabs .badge { top: 6px; left: auto; right: 6px; margin: 0; }
  #tabs a.coach .badge { top: -3px; right: -3px; }
  /* A page's action in its title row (Nutrition's Log food) shares the bar row, trailing, in its glass circle. */
  .title > .round { position: fixed; top: calc(var(--inset) + 6px); right: var(--inset); z-index: 7; }
  .view.chat { height: calc(100dvh - var(--inset) - var(--topbar) - 12px); }
  #conversation { padding-top: 8px; }
  /* A day or a ride is pushed within its tab, its title large; the way back floats in the bar row as a glass capsule. */
  .page.pushed { gap: 22px; padding-top: 8px; }
  .navbar.pushed { margin: 0; padding: 6px 0 10px; height: auto; border: none; justify-content: flex-start; }
  .navbar.pushed h1, .navbar.pushed .bar-title { font-size: 32px; line-height: 36px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px; text-transform: uppercase; }
  .navbar.pushed .back-spacer { display: none; }
  .navbar.pushed .back { position: fixed; top: calc(var(--inset) + 6px); left: var(--inset); z-index: 7; min-width: 0; height: 44px; padding: 0 16px 0 10px;
                         border-radius: 22px; background-color: var(--glass); -webkit-backdrop-filter: blur(24px) saturate(180%); backdrop-filter: blur(24px) saturate(180%);
                         border: 1px solid var(--glass-line); box-shadow: inset 0 1px 0 var(--glass-highlight), var(--glass-shadow); color: var(--text); }
}

/* 1024 and up: the sidebar, and a day beside its list. */
@media (min-width: 1024px) {
  body { padding-left: var(--side); }
  body.signed-out { padding-left: 0; }
  .scroll-edge-top { left: var(--side); }
  #tabs { position: fixed; top: var(--inset); left: var(--inset); bottom: var(--inset); right: auto; width: var(--sidebar); z-index: 6;
          display: flex; flex-direction: column; gap: 18px; padding: 18px 10px; overflow-y: auto; border-radius: calc(var(--radius-window) - var(--inset));
          background-color: var(--glass-regular); -webkit-backdrop-filter: blur(40px) saturate(180%); backdrop-filter: blur(40px) saturate(180%);
          border: 1px solid var(--glass-line); box-shadow: inset 0 1px 0 var(--glass-highlight), var(--glass-shadow); }
  #tabs .brand { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 0 12px; }
  #tabs .brand .wordmark { width: 126px; height: 34px; }
  #tabs .tablist { flex-direction: column; align-items: stretch; gap: 2px; }
  #tabs .pill { flex: none; flex-direction: column; gap: 2px; height: auto; padding: 0; border: none; border-radius: 0; background: none;
                box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
  #tabs a, #tabs a.coach { flex: none; width: auto; height: 44px; flex-direction: row; justify-content: flex-start; gap: 12px; padding: 0 12px;
                           border: none; border-radius: 12px; background: none; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none;
                           color: var(--text); font-size: 15px; line-height: 20px; font-weight: 500; }
  #tabs a .icon { width: 24px; height: 24px; color: var(--muted); }
  #tabs a .label, #tabs a.coach .label { display: block; flex: 1; }
  #tabs a.active, #tabs a.coach.active { background: var(--glass-lens); box-shadow: inset 0 0 0 1.5px var(--text); color: var(--text); font-weight: 600; }
  #tabs a.active .icon { color: var(--accent); }
  #tabs .badge, #tabs a.coach .badge { position: static; margin: 0; }
  .sheet { left: var(--side); }
  .page { max-width: 1040px; }
  #view-settings { max-width: 720px; }
  /* The strip touches the leading edge and runs under the sidebar, the one thing that does. */
  #view-workouts .strip { margin-left: calc(-1 * (var(--side) + 16px)); padding-left: calc(var(--side) + 16px); }
  /* The split: the list column stays put and scrolls on its own; the detail column scrolls with the window. The list is
     440 where there is room and gives way first: the detail's column never gets under a phone's (#221; the 24 px are its
     page's padding, 8 leading and 16 trailing). */
  .split { display: flex; align-items: flex-start; max-width: 1240px; margin: 0 auto; }
  .split > section.page { max-width: none; margin: 0; }
  .split > section.list-pane { flex: 0 1 440px; min-width: 0; position: sticky; top: 0; max-height: 100dvh; overflow-y: auto; }
  .split > section.list-pane > * { flex-shrink: 0; }          /* a scrolling flex column must not squash its rows */
  .split > .detail-pane { flex: 1 0 calc(var(--detail-min) + 24px); min-width: calc(var(--detail-min) + 24px); }
  .split > .detail-pane > .page { max-width: none; padding-left: 8px; }
  .detail-pane .placeholder { display: flex; align-items: center; justify-content: center; min-height: 60dvh; color: var(--muted); font-size: 15px; }
  .list-pane .strip > li { flex-basis: 54px; }
  .list-pane .segmented { max-width: 280px; }
  /* A day in the detail column is a column, not a stack: no way back, the title large, the row that led here held. */
  .navbar.pushed .back, .navbar.pushed .back-spacer { display: none; }
  .navbar.pushed { justify-content: flex-start; margin: 0; padding: 6px 0 10px; height: auto; border: none; }
  .navbar.pushed h1, .navbar.pushed .bar-title { font-size: 32px; line-height: 36px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px; text-transform: uppercase; }
  .day-row.selected { background: var(--fill); }
  .day-row.selected .chev { visibility: hidden; }
  /* The Dashboard fills the width it has: two columns of cards under one title, while each column holds its cards
     (--column-min: under it "Rest HR" and "Training status" wrapped a word a line, #221); narrower, one column. A
     column is at least half the width, so there are never three. */
  #view-dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(max(var(--column-min), calc((100% - 22px) / 2)), 1fr));
                    grid-auto-flow: dense; align-items: start; gap: 22px; }
  #view-dashboard > .title, #view-dashboard > .card.warn, #view-dashboard > .group, #view-dashboard > .first-note { grid-column: 1 / -1; }
  #view-dashboard > .caveat-note { grid-column: 1 / -1; }          /* under This week and the tiles beside it (#220) */
  #view-dashboard .vitals { grid-column: 1 / -1; grid-template-columns: repeat(4, minmax(0, 1fr)); }
  #view-dashboard #training { grid-column: auto; grid-template-columns: repeat(2, minmax(0, 1fr)); }     /* beside This week (#153) */
}

/* 1280 and up: the Chat docked, an edge-to-edge glass pane alongside the content. The Chat tab goes: the pane is the Chat. */
@media (min-width: 1280px) {
  body { padding-right: var(--chat-pane); }
  body.signed-out { padding-right: 0; }
  .scroll-edge-top { right: var(--chat-pane); }
  #view-chat { position: fixed; top: 0; right: 0; bottom: 0; width: var(--chat-pane); z-index: 5; margin: 0; max-width: none;
               height: 100dvh; border-left: 1px solid var(--glass-line); background-color: var(--glass-regular);
               -webkit-backdrop-filter: blur(40px) saturate(180%); backdrop-filter: blur(40px) saturate(180%); }
  #view-chat .navbar { position: static; width: auto; display: flex; height: 52px; overflow: visible; clip-path: none; white-space: normal;
                       border-bottom: 1px solid var(--glass-line); }
  #view-chat .navbar h1 { font-size: 15px; line-height: 20px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; font-stretch: condensed; }
  #conversation { padding: 16px 16px 6px; }
  #messages { gap: 14px; }
  .message.athlete .text { max-width: 88%; }
  #composer { padding: 8px 12px 16px; }
  .compose { background-color: var(--cell); -webkit-backdrop-filter: none; backdrop-filter: none; }      /* no glass on glass */
  #tabs a.coach { display: none; }
  .split { max-width: none; }
  .sheet { right: var(--chat-pane); }
}

/* 1280 to 1439, while the Chat is docked: the sidebar folds into a rail (#221). The sidebar 248, a list 440 and the Chat
   400 left a Day about 120 px at 1280 and the Dashboard two columns of 277; the rail gives that width to the content.
   The same glass and the same tabs, as icons in 44 px squares, the current one marked as in the sidebar. Each keeps its
   name in the page for a screen reader, hidden as .sr hides, and shows it beside the icon on keyboard focus, and on hover
   where there is a pointer to hover; Escape hides it again (app.js, .hush). The C alone heads the rail: the wordmark
   needs 120 px. --sidebar narrows, so --side and everything placed from it (sheets, toasts, the strip) follow. */
@media (min-width: 1280px) and (max-width: 1439px) {
  :root { --sidebar: 68px; }
  #tabs { align-items: center; padding: 18px 12px; overflow: visible; }
  #tabs .brand { justify-content: center; padding: 0; }
  #tabs .brand .wordmark, #brand-who { display: none; }
  #tabs .rail-mark { display: flex; width: 40px; height: 40px; margin: 0; border-radius: 20px; }
  #tabs .rail-mark .mark { width: 32px; height: 32px; }
  #tabs .tablist, #tabs .pill { align-items: center; gap: 6px; }
  #tabs a, #tabs a.coach { width: 44px; height: 44px; justify-content: center; gap: 0; padding: 0; }
  /* The name: out of sight until asked for, then a capsule beside the icon, over the content. Its ::before bridges the
     gap to the icon, so the pointer can move onto the name without it going. */
  #tabs a .label { position: absolute; left: calc(100% + 16px); top: 50%; width: 1px; height: 1px; max-width: none; overflow: hidden;
                   clip-path: inset(50%); white-space: nowrap; transform: translateY(-50%); padding: 6px 12px; border-radius: 16px;
                   background: var(--cell); color: var(--text); border: 1px solid var(--glass-line); box-shadow: var(--glass-shadow);
                   font-size: 15px; line-height: 20px; font-weight: 600; }
  #tabs a .label::before { content: ""; position: absolute; top: -1px; bottom: -1px; right: 100%; width: 17px; }
  #tabs a:not(.hush):focus-visible .label { width: auto; height: auto; overflow: visible; clip-path: none; }
  @media (hover: hover) {
    #tabs a:not(.hush):hover .label { width: auto; height: auto; overflow: visible; clip-path: none; }
  }
  #tabs .badge, #tabs a.coach .badge { position: absolute; top: 0; right: 0; left: auto; margin: 0; }
}

/* ---------- One Ride, in detail ----------
   The screen a completed Ride opens into: its numbers, the route it took, the power it was ridden at, what was
   written against what happened, the efforts, and where the time went. Every colour is a token, so the whole
   screen follows the appearance switch without a second set of rules. */
.ride-head { display: flex; flex-direction: column; gap: 6px; }
.ride-head h1 { margin: 0; font-size: 32px; line-height: 36px; font-weight: 800; letter-spacing: -.02em; }
.ride-head .device { color: var(--muted); font-size: 15px; line-height: 20px; }
.ride-head .outward { align-self: flex-start; display: inline-flex; align-items: center; min-height: 44px; margin: -6px 0; color: var(--accent);
                      font-size: 15px; line-height: 20px; font-weight: 600; text-decoration: none; }     /* 44 px to touch (#223) */
.ride-head .outward::after { content: " \2197"; }                     /* it leaves the app (#165) */
.ride-head .outward:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.ride-tiles { display: flex; gap: 8px; }
.ride-tiles .tile { flex: 1; display: flex; flex-direction: column; gap: 2px; padding: 12px; border-radius: 12px; background: var(--cell); }
.ride-tiles .tile b { font-size: 26px; line-height: 30px; font-weight: 800; letter-spacing: -.02em; }
.ride-tiles .tile b span { font-size: 12px; font-weight: 600; color: var(--muted); }

/* The map: tiles from the service's own origin under an SVG track. The ground shows while they load and wherever
   the square has none, so the track never sits on nothing. */
/* The map and the power chart are 326 px where there is room and shrink with their card where there is not, their height
   following (#223: at 320 px both ran past the screen, which scrolled sideways). app.js places the tiles in shares. */
.ride-map { position: relative; width: 326px; max-width: 100%; aspect-ratio: 326 / 196; border-radius: 10px; overflow: hidden;
            background: var(--fill); }      /* MAP_W × MAP_H in app.js; narrower where there is no room (#223) */
.ride-map img { position: absolute; border: 0; }
.ride-map svg { position: absolute; inset: 0; width: 100%; height: 100%; }
svg.ride-chart { display: block; max-width: 100%; height: auto; }
.ride-map .credit { position: absolute; right: 5px; bottom: 4px; padding: 1px 4px; border-radius: 4px;
                    background: var(--cell); color: var(--muted); font-size: 9px; line-height: 12px; }
.ride-legend { display: flex; flex-wrap: wrap; gap: 12px; color: var(--muted); font-size: 13px; line-height: 18px; }
.ride-legend span { display: inline-flex; align-items: center; gap: 5px; }
.ride-legend i { width: 10px; height: 10px; border-radius: 2px; background: currentColor; }
.ride-legend i.ring { border-radius: 5px; background: none; box-shadow: inset 0 0 0 2px currentColor; }
/* The swatch carries the band's colour (3:1 as a graphic); its word is muted text, 4.5:1, where light's easy and tempo
   tones read 4.28:1 and 3.82:1 as words (#223). */
.ride-legend > span > span { color: var(--muted); }

/* Ridden against written: the top bar is what happened, the one under it what the calendar held. */
.ride-plan { display: flex; flex-direction: column; gap: 6px; }
.ride-plan .head { display: flex; align-items: baseline; justify-content: space-between; font-size: 15px; line-height: 20px; }
.ride-plan .head b { font-weight: 700; }
.ride-plan .head b span { font-weight: 400; color: var(--muted); }
.ride-plan .bars { display: flex; flex-direction: column; gap: 3px; }
.ride-plan .track { height: 8px; border-radius: 4px; background: var(--fill); overflow: hidden; }
.ride-plan .track i { display: block; height: 8px; border-radius: 4px; background: var(--accent); }
.ride-plan .track.written i { background: var(--fill-strong); }

.ride-reps { margin: 0; padding: 0; list-style: none; }
.ride-reps li { display: flex; align-items: baseline; gap: 10px; padding: 9px 0; border-top: 1px solid var(--line); }
.ride-reps .n { width: 26px; color: var(--muted); font-size: 13px; line-height: 18px; font-weight: 600; }
.ride-reps .dur { flex: 1; font-size: 17px; line-height: 22px; font-weight: 700; }
.ride-reps .w { font-size: 17px; line-height: 22px; font-weight: 700; }
.ride-reps .w span { font-size: 12px; font-weight: 600; color: var(--muted); }
.ride-reps .pct { min-width: 56px; white-space: nowrap; text-align: right; color: var(--muted); font-size: 13px; line-height: 18px; }
.ride-reps .pct.alone { color: var(--text); font-size: 17px; line-height: 22px; font-weight: 700; }  /* read in % of FTP (#158) */

.ride-zones { display: flex; height: 14px; gap: 1px; border-radius: 7px; overflow: hidden; }
/* The season on the Training block card (#267): each block its share of the weeks, this week's marked, the past dimmed. */
.season { display: flex; height: 12px; gap: 2px; margin: 12px 0 4px; }
.season i { display: block; min-width: 3px; border-radius: 3px; background: var(--easy); }
.season i.kind-build { background: var(--tempo); }
.season i.kind-peak { background: var(--accent); }
.season i.kind-taper { background: var(--fill-strong); }
.season i.past { opacity: .35; }
.season i.now { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--text); }
.season-ends { display: flex; justify-content: space-between; color: var(--muted); font-size: 13px; line-height: 18px; }
.ride-zones i { display: block; }
.ride-band { display: flex; align-items: center; gap: 10px; font-size: 15px; line-height: 20px; }
.ride-band i { width: 10px; height: 10px; border-radius: 2px; }
.ride-band .name { flex: 1; }
.ride-band b { font-weight: 700; }
.ride-band .share { width: 40px; text-align: right; color: var(--muted); font-size: 13px; line-height: 18px; }
.ride-moved { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 9px 0; border-top: 1px solid var(--line); }
.ride-moved .name { flex: 1; font-size: 15px; line-height: 20px; }
.ride-moved b { font-size: 17px; line-height: 22px; font-weight: 700; }
.ride-moved .note { width: 116px; text-align: right; color: var(--muted); font-size: 13px; line-height: 18px; }

/* The effort bands, named once for every chart on the Ride screen: the profile's bars, the track's strokes, the
   zone bar's segments and the legend's dots all answer the same question in the same colour. */
.band-easy { color: var(--easy); }
.band-tempo { color: var(--tempo); }
.band-hard { color: var(--accent); }
.band-hr { color: var(--muted); }
rect.band-easy, .ride-zones i.band-easy, .ride-band i.band-easy { fill: var(--easy); background: var(--easy); }
rect.band-tempo, .ride-zones i.band-tempo, .ride-band i.band-tempo { fill: var(--tempo); background: var(--tempo); }
rect.band-hard, .ride-zones i.band-hard, .ride-band i.band-hard { fill: var(--accent); background: var(--accent); }
.ride-zones i.dim { opacity: .62; }
.ftp-line { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; }
.hr-line { fill: none; stroke: var(--muted); stroke-width: 1.2; stroke-linejoin: round; opacity: .85; }
/* The track wears a halo of the ground it sits on, or volt on a street map loses its edges. */
.track-halo { fill: none; stroke: var(--bg); stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; opacity: .55; }
polyline.track { fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
polyline.track.band-easy { stroke: var(--easy); }
polyline.track.band-tempo { stroke: var(--tempo); }
polyline.track.band-hard { stroke: var(--accent); stroke-width: 3; }
.track-start { fill: var(--accent); stroke: var(--bg); stroke-width: 2; }
.track-end { fill: none; stroke: var(--easy); stroke-width: 2.5; }
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.ride-tiles .tile .label { font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
#view-ride .tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px; }
#view-ride .fact { padding: 8px 0; }

/* ---------- Nutrition (redesign) ----------
   Today's efforts, Eat today (the day's number, Log food under it, the macros, and where the number comes from folded
   under it), the hour after a ride, the day's Meals and the meal plan's row, over the days before. Cards are solid on
   --cell. The header carries only the title, so the page ends as the other tabs do. */
.n-card { gap: 14px; }
.n-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* Listen (#167): a text button at the end of a card's head, or of the morning report's heading. */
/* The Dictionary (#169): each entry a heading and its words; a label with an entry reads as a link to it. */
.row.dict-entry { display: block; scroll-margin-top: 72px; }
.row.dict-entry h2 { margin: 0 0 4px; font-size: 17px; line-height: 22px; font-weight: 700; }
.row.dict-entry p { margin: 0; color: var(--muted); font-size: 15px; line-height: 21px; }
a.dict-term { position: relative; color: inherit; text-decoration: underline dotted; text-underline-offset: 3px; }
/* A label is small ("NP" was 19 by 15): its link reaches 44 px round it, over the tile's padding, without moving it (#223). */
a.dict-term::after { content: ""; position: absolute; left: 50%; top: 50%; min-width: 100%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
a.dict-term:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* Listen (#210): a speaker and its word in the text colour, a square in the danger tone while it reads. */
.link.listen { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; padding: 0; color: var(--text); font-size: 15px;
               line-height: 20px; font-weight: 600; white-space: nowrap; }
.link.listen .icon { width: 20px; height: 20px; }
.link.listen[aria-pressed="true"] { color: var(--danger); }
h2 > .link.listen { float: right; margin-left: 12px; }
.n-listen { display: flex; justify-content: flex-end; margin-top: 8px; }
.n-label { display: flex; align-items: center; gap: 8px; min-width: 0; font: inherit; color: var(--accent); }      /* a heading (#212): the span sets the size */
.n-label .icon { width: 18px; height: 18px; }
.n-label span { font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.n-right { flex-shrink: 0; font-size: 13px; line-height: 18px; color: var(--muted); }
.n-right.strong { font-weight: 700; color: var(--accent); }
.n-words { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.n-words b { font-weight: 700; }
.n-words .sub { font-size: 13px; line-height: 18px; color: var(--muted); }
.n-fine { font-size: 12px; line-height: 16px; color: var(--muted); }
.n-empty { font-size: 15px; line-height: 20px; color: var(--muted); }
.icon.tiny { width: 14px; height: 14px; stroke-width: 2; }

/* Today's efforts: a Ride or an Effort, and its three tiles. */
.n-effort { display: flex; align-items: center; gap: 12px; }
.n-badge { width: 40px; height: 40px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: 20px;
           background: var(--fill); color: var(--accent); }
.n-badge .icon { width: 22px; height: 22px; }
.n-badge.small { width: 36px; height: 36px; border-radius: 18px; }
.n-badge.small .icon { width: 20px; height: 20px; }
.n-done { display: flex; align-items: center; gap: 4px; flex-shrink: 0; font-size: 12px; line-height: 16px; font-weight: 600; color: var(--good); }
.n-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.n-tile { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 10px; border-radius: 10px; background: var(--fill); }
.n-tile .k { display: flex; align-items: center; gap: 6px; font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em;
             text-transform: uppercase; color: var(--muted); }
.n-tile b { font-size: 24px; line-height: 30px; font-weight: 800; letter-spacing: -.02em; white-space: nowrap; }
.n-tile b small { font-size: 12px; font-weight: 600; letter-spacing: 0; color: var(--muted); }
.n-tile.burned b { color: var(--accent); }
/* The macro tiles of the intake target: a card on --fill each (12 px, a small shape inside a card), the key and name,
   eaten over "/ target g" (a third of a phone is too narrow for both on one line, so every tile stacks them alike), the
   thin meter, what is left and g/kg. */
.n-macro { gap: 6px; padding: 12px 12px 14px; border-radius: 12px; }
.n-macro b { font-size: 24px; line-height: 28px; white-space: normal; }
.n-macro b small { display: block; font-size: 13px; line-height: 18px; white-space: nowrap; }
.n-macro .n-meter { margin-top: 4px; }
.n-tile .left { font-size: 13px; line-height: 18px; font-weight: 700; }
.n-tile .fine { font-size: 12px; line-height: 16px; color: var(--muted); }

/* Stacked bars: each part's share is its flex-grow, set by app.js. */
.n-bars, .n-split { display: flex; flex-direction: column; gap: 6px; }
.n-split .k { font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.n-bar { display: flex; gap: 2px; height: 14px; }
.n-bar i { flex: 0 1 0; min-width: 0; border-radius: 0; }
.n-bar i:first-child { border-radius: 4px 0 0 4px; }
.n-bar i:last-child { border-radius: 0 4px 4px 0; }
.n-bar i:only-child { border-radius: 4px; }
.n-bar .bmr, .swatch.bmr { background: var(--fill-strong); box-shadow: inset 0 0 0 1px var(--control-line); }     /* its quiet fill, 3:1 at its edge (#223) */
.n-bar .life, .swatch.life { background: var(--easy); }
.n-bar .ride, .swatch.ride { background: var(--accent); }
.n-bar .eat { background: var(--text); }
.n-bar .gap, .swatch.deficit { background: none; box-shadow: inset 0 0 0 1.5px var(--muted); }
.n-bar .none { background: none; }
/* Each macro has one colour (#210): its key square, its part of the split and its meter. Protein is the accent; carbs are
   moss and fat the easy tone in dark. In light they take deeper tones of the same (a deep moss, a deep dust), so a meter
   holds 3:1 on its track and its tile. Over is over in every colour. */
:root { --macro-carbs: #5E7A12; --macro-fat: #80605C; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --macro-carbs: var(--moss); --macro-fat: #B5A19D; }
}
:root[data-theme="dark"] { --macro-carbs: var(--moss); --macro-fat: #B5A19D; }
.n-bar .protein, .swatch.protein { background: var(--accent); }
.n-bar .carbs, .swatch.carbs { background: var(--macro-carbs); }
.n-bar .fat, .swatch.fat { background: var(--macro-fat); }
.n-macro.carbs .n-meter i { background: var(--macro-carbs); }
.n-macro.fat .n-meter i { background: var(--macro-fat); }
.n-macro .n-meter.over i { background: var(--danger); }
.n-legend { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; line-height: 16px; color: var(--muted); }

/* The energy rows run edge to edge along the card's foot, as a grouped list inside it. */
.n-rows { display: flex; flex-direction: column; margin: 0 -16px -16px; border-top: 1px solid var(--line); }
.n-row { display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 6px 16px; border-bottom: 1px solid var(--line); }
.n-row:last-child { border-bottom: none; }
.n-row .n-words b { font-size: 15px; line-height: 20px; font-weight: 600; }
.n-row .value { font-weight: 700; white-space: nowrap; }
.n-big small { font-size: 12px; font-weight: 600; color: var(--muted); }
.swatch { width: 10px; height: 10px; flex-shrink: 0; border-radius: 3px; }
.n-tile .k .swatch { width: 8px; height: 8px; border-radius: 2px; }

/* Eat today: the day's number, the meter and what is eaten and left, Log food, the macros, and the disclosure. */
.n-big { font-size: 34px; line-height: 38px; font-weight: 800; letter-spacing: -.02em; }
.n-big small { font-size: 15px; letter-spacing: 0; }
.n-progress { margin-top: -6px; font-size: 13px; line-height: 18px; font-weight: 600; color: var(--muted); }
.n-progress.over { color: var(--danger); }
/* Log food under the day's number is Nutrition's one filled button in the accent (#210), .button.primary: volt with
   espresso on it in dark, espresso with volt on it in light. Its plus takes the button's own colour. A day's page keeps
   its Log food on the fill (.button), since from 1024 px it stands beside this one. */
.n-log { display: flex; align-items: center; justify-content: center; gap: 8px; }
.n-log .icon { width: 20px; height: 20px; stroke-width: 2.2; }
.n-log:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* "How 3136 is worked out": the brand's disclosure (as the recipe's "Which days"), a chevron that turns down when open.
   The rows inside keep their edge-to-edge list, so the body's last child is not the card's foot. */
.n-how { display: flex; flex-direction: column; }
.n-how summary { display: flex; align-items: center; gap: 6px; width: fit-content; min-height: 44px; margin: -8px 0 -8px -4px;
                 padding: 0 8px 0 4px; border-radius: 12px; font-size: 15px; line-height: 20px; font-weight: 600; cursor: pointer;
                 list-style: none; }
.n-how summary::-webkit-details-marker { display: none; }
.n-how summary::marker { content: ""; }
.n-how summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
.n-how .chevron { width: 16px; height: 16px; stroke-width: 2.2; color: var(--accent); transition: transform .2s ease; }
.n-how[open] .chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .n-how .chevron { transition: none; } }
.n-how-body { display: flex; flex-direction: column; gap: 14px; padding-top: 16px; }
.n-how-body .n-rows { margin: 0 -16px; border-bottom: 1px solid var(--line); }
.n-meter { position: relative; height: 10px; border-radius: 5px; background: var(--fill); overflow: hidden; }
.n-meter i { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 5px; background: var(--accent); }
.n-meter.thin { height: 6px; border-radius: 3px; background: var(--fill-strong); }
.n-meter.over i { background: var(--danger); }
.n-callout { padding: 10px 12px; border-radius: 10px; background: var(--fill); font-size: 13px; line-height: 18px; }

/* The hour after a ride: the one card with an accent edge. Suggest meals inside it is on the fill: Log food is the
   screen's one yellow button (#210). */
.n-post { gap: 12px; box-shadow: inset 0 0 0 1.5px var(--accent); }
.n-headline { font-size: 22px; line-height: 26px; font-weight: 800; font-stretch: condensed; text-transform: uppercase; letter-spacing: .2px; }
.n-text { margin-top: -8px; font-size: 15px; line-height: 20px; color: var(--muted); }
.n-suggest { display: flex; align-items: center; justify-content: center; gap: 8px; }
.n-suggest .icon { width: 20px; height: 20px; }
.n-asks { margin-top: -4px; font-size: 12px; line-height: 16px; color: var(--muted); text-align: center; }

/* Daily Fuel: the day's Meals on a rail, eaten filled, planned a ring, the Fuel's on-the-bike line a quiet ring. */
.n-timeline { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.n-step { display: flex; gap: 12px; }
.n-step .time { width: 40px; flex-shrink: 0; padding-top: 1px; font-size: 13px; line-height: 20px; font-weight: 600; color: var(--muted); }
.n-step .rail { width: 12px; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; }
.n-step .rail i { width: 12px; height: 12px; margin-top: 4px; flex-shrink: 0; border-radius: 6px; border: 2px solid var(--accent); background: var(--accent); }
.n-step .rail b { flex: 1; width: 2px; margin-top: 4px; background: var(--fill-strong); }
.n-step:last-child .rail b { background: none; }
.n-step.planned .rail i { background: none; }
.n-step.estimated .rail i { background: none; border-style: dashed; }     /* a meal told to the Coach, not logged yet (#242) */
.n-step.estimated .top .kcal { color: var(--muted); }                      /* not in the day's totals until Log it */
.n-step .body .n-tag.waiting { align-self: flex-start; margin-top: 6px; }
.n-step.guide .rail i { background: none; border-color: var(--fill-strong); }
/* What intervals.icu holds beyond the Meals (#210): the first row, "Other food", eaten but not a Meal here, so a muted
   dot, and its small line says it was logged elsewhere. */
.n-step.elsewhere .rail i { background: var(--muted); border-color: var(--muted); }
.n-step .body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding-bottom: 16px; }
/* A row leads with the Meal's name in bold (the review of 2026-09-28). It breaks between its words, never inside one, so a
   Planned tag beside it cannot clip "Dinner"; when the name, the tag and the kcal cannot share a line (Edit's minus takes
   46 px), the kcal goes under, still at the end. Under it the small line: where the Meal came from, then its macros. */
.n-step .top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 8px; }
.n-step .top .name { flex: 1 1 0; min-width: min-content; font-size: 15px; line-height: 20px; font-weight: 700; }
.n-step .top .kcal { margin-left: auto; font-size: 15px; line-height: 20px; font-weight: 700; white-space: nowrap; }
.n-step .what { font-size: 15px; line-height: 20px; overflow-wrap: anywhere; }
.n-step .sub { font-size: 12px; line-height: 16px; color: var(--muted); overflow-wrap: anywhere; }
.n-step.guide .what, .n-step.guide .top .name { color: var(--muted); }
.n-step .body .button.small { align-self: flex-start; margin-top: 8px; }
.n-tag { flex-shrink: 0; padding: 1px 8px; border-radius: 10px; background: var(--fill); font-size: 11px; line-height: 16px; font-weight: 600;
         letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
/* Taking a Meal off, iOS's two ways. A swipe left slides the row (.n-slide, solid on the card's --cell) off a red Delete
   behind it; touch-action leaves vertical moves to the page's scroll. Edit in the card's header puts a red minus before
   each Meal instead. The red is the light theme's danger in both themes: white on it is 5.6:1, where the dark theme's
   lighter danger would not carry white text. */
#nutrition-fuel { --destroy: #C4271B; }
.n-head .n-edit { min-height: 44px; margin: -12px -4px -12px 0; font-size: 15px; font-weight: 600; }
#nutrition-fuel .n-label { flex: 1; }
.n-swipe { position: relative; flex: 1; min-width: 0; display: flex; overflow: hidden; }
.n-slide { position: relative; z-index: 1; flex: 1; min-width: 0; display: flex; gap: 12px; background: var(--cell);
           transition: transform .25s cubic-bezier(.2, .8, .2, 1); }
/* Out of Edit, a row reaches the card's edges, so it slides off under the card's own edge, not an invisible line 16 in. */
#nutrition-fuel:not(.editing) .n-swipe { margin: 0 -16px; }
#nutrition-fuel:not(.editing) .n-slide { padding: 0 16px; }
.n-slide.can-swipe { touch-action: pan-y; -webkit-user-select: none; user-select: none; }
.n-slide.dragging { transition: none; }
.n-delete { position: absolute; top: 0; right: 16px; bottom: 12px; width: 88px; border: none; border-radius: 14px; background: var(--destroy);
            color: var(--on-red); font-size: 15px; font-weight: 600; }
.n-delete:focus-visible { outline: 2px solid var(--text); outline-offset: -4px; }
.n-minus { position: relative; width: 44px; height: 44px; flex-shrink: 0; margin: -12px 0 0 -10px; display: flex; align-items: center;
           justify-content: center; padding: 0; border: none; border-radius: 22px; background: none; color: var(--on-red); }
.n-minus::before { content: ""; position: absolute; left: 10px; top: 10px; width: 24px; height: 24px; border-radius: 12px; background: var(--destroy); }
.n-minus .icon { position: relative; width: 20px; height: 20px; stroke-width: 2; }
.n-minus:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.n-minus:disabled { opacity: .4; }
.n-minus-gap { width: 44px; flex-shrink: 0; margin-left: -10px; }
@media (prefers-reduced-motion: reduce) { .n-slide { transition: none; } }

/* The days before (#210): the fortnight behind today as bars, oldest first, seven to a row so each day is a 44 px target
   even in the 440 px list of a wide screen. A bar is the day's target with a muted edge, filled from its foot by what was
   eaten (the accent, the danger tone once over); a day not logged is the empty target with a dashed edge. Each day opens
   its page, and the one in the detail is filled. Under the strip one row counts the days not logged and logs the latest. */
.n-days { gap: 12px; }
.n-strip { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px 0; margin: 0 -8px; padding: 0; list-style: none; }
.n-day { display: flex; flex-direction: column; align-items: center; gap: 2px; min-height: 44px; padding: 6px 0 4px; border-radius: 12px;
         color: var(--text); text-decoration: none; }
.n-day-bar { display: block; width: 18px; height: 44px; margin-bottom: 4px; border: 1.5px solid var(--muted); border-radius: 6px; overflow: hidden; }
.n-day-bar svg { display: block; width: 100%; height: 100%; }
.n-day .fill { fill: var(--accent); }
.n-day.over .fill { fill: var(--danger); }
.n-day.over .n-day-bar { border-color: var(--danger); }
.n-day.none .n-day-bar { border: 1.5px dashed var(--muted); }
.n-day .dow { font-size: 11px; line-height: 13px; font-weight: 600; letter-spacing: .4px; text-transform: uppercase; color: var(--muted); }
.n-day .dom { font-size: 15px; line-height: 20px; font-weight: 700; }
.n-day:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.n-day.selected { background: var(--fill); }
.n-days-row { display: flex; align-items: center; gap: 8px; width: auto; min-height: 52px; margin: 0 -16px -16px; padding: 6px 16px;
              border: none; border-top: 1px solid var(--line); background: none; color: var(--text); font: inherit; text-align: left;
              cursor: pointer; }
.n-days-row .grow { font-size: 15px; line-height: 20px; }
.n-days-row .act { font-size: 15px; line-height: 20px; font-weight: 600; white-space: nowrap; }
.n-days-row .chevron { width: 16px; height: 16px; stroke-width: 2.2; color: var(--muted); }
.n-days-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 0 0 var(--radius-card) var(--radius-card); }

/* The meal plan's row: one cell, a way in. */
.n-link { display: flex; align-items: center; gap: 12px; min-height: 64px; padding: 10px 16px; border-radius: var(--radius-card);
          background: var(--cell); color: var(--text); text-decoration: none; }
.n-link .n-words b { font-weight: 600; }
.n-link .chevron { width: 18px; height: 18px; color: var(--muted); }
/* ---------- Log food (redesign) ---------- */
/* Log food opens from the button under the day's number on Nutrition ([data-log-food]), on the tab used last on this
   device: the tab bar only navigates, and the action sits beside the number it changes. */

/* The sheet: full height, 34 at the top, the one head (the grabber, the title, the meal slot's glass chip and Close, #219).
   In dark the sheet is a cell over the dimmed page and what sits on it is a fill; in light it is the crema ground with
   white on it. */
.logfood { position: fixed; inset: 0; z-index: 9; display: flex; align-items: flex-end; justify-content: center; }
.logfood-body { --sheet: var(--bg); --sheet-card: var(--cell); --sheet-step: var(--fill);
                position: relative; width: 100%; max-width: 600px; height: calc(100dvh - env(safe-area-inset-top) - 12px); overflow-y: auto;
                display: flex; flex-direction: column; gap: 16px; padding: 8px 16px 0; border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
                background: var(--sheet); overscroll-behavior: contain; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logfood-body { --sheet: var(--cell); --sheet-card: var(--fill); --sheet-step: var(--fill-strong); }
}
:root[data-theme="dark"] .logfood-body { --sheet: var(--cell); --sheet-card: var(--fill); --sheet-step: var(--fill-strong); }
body.logfood-open { overflow: hidden; }
/* The meal slot's chip, in the sheet's one head under the title (#219), as wide as its words. */
.sheet-head .slot-chip { align-self: flex-start; margin-top: 4px; }
.slot-chip { position: relative; display: inline-flex; align-items: center; gap: 4px; height: 44px; padding: 0 12px 0 14px; border-radius: 22px; color: var(--text); }
.slot-chip select { -webkit-appearance: none; appearance: none; height: 44px; margin: -1px 0; padding: 0; border: none; outline: none; background: transparent;
                    color: inherit; font-size: 15px; font-weight: 600; cursor: pointer; }
.slot-chip .icon { width: 16px; height: 16px; stroke-width: 2; pointer-events: none; }
.slot-chip select.unpicked { color: var(--accent); }          /* a past day: no slot until the athlete picks one */
.slot-chip:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.logfood-modes { flex-shrink: 0; }
.logfood-pane { display: flex; flex-direction: column; gap: 14px; padding-bottom: 24px; }
.logfood-body .list { background: var(--sheet-card); }
.logfood-hint { padding: 0 4px; font-size: 13px; line-height: 18px; color: var(--muted); }
.logfood-label { padding: 0 4px; font-size: 15px; line-height: 20px; font-weight: 600; }
.logfood-label.small { font-size: 13px; line-height: 18px; font-weight: 400; color: var(--muted); }
.logfood-head2 { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 0 4px; }
.logfood-head2 h3 { min-width: 0; font-size: 17px; line-height: 22px; font-weight: 700; }
.logfood-head2 .muted { flex-shrink: 0; }
.logfood-error { padding: 12px 16px; border-radius: var(--radius-card); background: var(--warn-bg); color: var(--warn-text); font-size: 15px; line-height: 20px; }
.logfood .waiting { display: flex; align-items: center; gap: 8px; padding: 4px; font-size: 15px; line-height: 20px; color: var(--muted); }
.logfood .waiting .dot { animation: pulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .logfood .waiting .dot { animation: none; } }

/* Photo: the button that opens the camera, then the photo with Retake on it, then what is on the plate. */
.logfood-shot { height: 190px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; border: none;
                border-radius: var(--radius-card); background: var(--sheet-card); color: var(--muted); font-size: 15px; line-height: 20px; font-weight: 600; }
.logfood-shot .icon.big { width: 36px; height: 36px; stroke-width: 1.6; }
.logfood-shot .asks { margin-top: -4px; font-size: 13px; line-height: 18px; font-weight: 400; }
.logfood-shot:not(:disabled) { color: var(--text); }
.logfood-shot:not(:disabled) .icon.big { color: var(--accent); }
.logfood-shot:not(:disabled) .asks { color: var(--muted); }
.logfood-shot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.logfood-photo { position: relative; height: 190px; border-radius: var(--radius-card); overflow: hidden; background: var(--sheet-card); }
.logfood-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }
.logfood-photo .button.small { position: absolute; right: 10px; bottom: 10px; background: var(--sheet); }
.logfood-item { display: flex; align-items: center; gap: 10px; min-height: 64px; padding: 8px 8px 8px 16px; }
.logfood-item .words { display: flex; flex-direction: column; }
.logfood-item .name { font-size: 15px; line-height: 20px; font-weight: 600; overflow-wrap: anywhere; }
.logfood-item .note, .logfood-item .said { font-size: 12px; line-height: 16px; color: var(--muted); }
.logfood-item .note.guessed { color: var(--watch); }
.stepper { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.stepper .grams { width: 58px; text-align: center; font-size: 15px; line-height: 20px; font-weight: 700; }
/* A .step here is a Log food button (the grams steppers, Close, Log on a recent meal): scoped to the sheet and its toast,
   because a workout's target rows on the Day screen are <p class="step"> too (app.js, .targets .step above). */
.logfood .step, .food-toast .step { position: relative; flex-shrink: 0; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; padding: 0; border: none;
        border-radius: 18px; background: var(--sheet-step); color: var(--text); }
.logfood .step::after, .food-toast .step::after { content: ""; position: absolute; inset: -4px; }                                    /* 44 pt to touch */
.logfood .step .icon, .food-toast .step .icon { width: 16px; height: 16px; stroke-width: 2; }
.logfood .step.big { width: 44px; height: 44px; border-radius: 22px; }
.logfood .step.big::after { content: none; }
.logfood .step.big .icon { width: 18px; height: 18px; }

/* Text and Scan: the words, the number under the barcode, and the grams of each food, each field with its edge (#223). */
.logfood-body textarea { width: 100%; min-height: 90px; padding: 14px 16px; border: none; border-radius: var(--radius-card); background: var(--sheet-card);
                         box-shadow: inset 0 0 0 1px var(--control-line); font-size: 17px; line-height: 22px; resize: none; outline: none; }
.logfood-body textarea:focus-visible, .logfood-code-row input:focus-visible, .grams-field input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.grams-field { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.grams-field input { width: 76px; height: 44px; padding: 0 12px; border: none; border-radius: 22px; background: var(--sheet-step); text-align: right;
                     box-shadow: inset 0 0 0 1px var(--control-line); font-size: 17px; font-weight: 700; outline: none; -moz-appearance: textfield; }
.grams-field input::-webkit-outer-spin-button, .grams-field input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.grams-field .unit { width: 12px; color: var(--muted); font-size: 15px; }
.logfood-missed { display: flex; flex-direction: column; gap: 6px; }
.logfood-estimate { display: flex; flex-direction: column; gap: 6px; }       /* Let the Coach read it, and the ask it uses */
.logfood-code { display: flex; flex-direction: column; gap: 8px; }
.logfood-code-row { display: flex; gap: 8px; }
.logfood-code-row input { flex: 1; min-width: 0; height: 50px; padding: 0 16px; border: none; border-radius: 25px; background: var(--sheet-card);
                          box-shadow: inset 0 0 0 1px var(--control-line); font-size: 17px; letter-spacing: 1px; outline: none; }
.logfood-code-row .button { flex-shrink: 0; background: var(--sheet-step); }
/* The camera stays dark in either mode, so the volt frame reads on it. */
.logfood-camera { position: relative; height: 260px; border-radius: var(--radius-card); overflow: hidden; background: var(--camera); }
.logfood-camera video { display: block; width: 100%; height: 100%; object-fit: cover; }
.logfood-camera .frame { position: absolute; left: 50%; top: 50%; width: 230px; height: 120px; margin: -60px 0 0 -115px; border: 2px solid var(--volt);
                         border-radius: 14px; }
.logfood-camera .frame::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 2px; background: var(--volt); }

/* The totals and Log, kept at the bottom of the sheet while the list scrolls under them. */
.logfood-foot { position: sticky; bottom: 0; display: flex; flex-direction: column; gap: 12px; margin: auto -16px 0;
                padding: 12px 16px calc(env(safe-area-inset-bottom) + 16px); background: var(--sheet); }
.logfood-totals { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.logfood-tile { display: flex; flex-direction: column; min-width: 0; padding: 8px 10px; border-radius: 14px; background: var(--sheet-card); }
.logfood-tile b { font-size: 17px; line-height: 22px; font-weight: 800; white-space: nowrap; }
.logfood-tile span { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }

/* The one line after a log, on glass 12 px above the tab pill; a write that was not confirmed stays until it is closed. */
.food-toast { position: fixed; left: 16px; right: 16px; z-index: 6; bottom: calc(env(safe-area-inset-bottom) + 12px + var(--tabbar) + 12px);
              display: flex; align-items: center; gap: 8px; min-height: 50px; padding: 12px 8px 12px 18px; border-radius: 25px; color: var(--good);
              font-size: 15px; line-height: 20px; font-weight: 600; }
.food-toast.warn { align-items: flex-start; border-radius: var(--radius-card); color: var(--warn-text); }
.food-toast .grow { flex: 1; min-width: 0; }
.food-toast .icon { width: 18px; height: 18px; stroke-width: 2; }
.food-toast .step { background: transparent; color: var(--text); }
/* ---------- meal ideas, recipes and the shopping list (redesign) ----------
   The Coach's meal ideas are a full-height sheet over Nutrition: 34 px at the top, the one head (the grabber, the eyebrow,
   title and asks, and Close, #219), the room left, three idea cards and a floating glass bar for what is ticked. A recipe and the shopping list are pushed
   views with a glass back circle and a glass toolbar that stay at the top while the page scrolls. On the sheet the
   layers step up one: the sheet is the cell, its cards the fill, their chips the strong fill (in light, crema, white,
   fill). Glass only on what floats; the one yellow button is Shopping list in the bar, and the bar shows only while a meal
   is ticked. Each card's Add to Daily Fuel is a secondary button: the chip fill, an accent plus, a text-coloured label. */
.ideas { --sheet-ground: var(--bg); --sheet-card: var(--cell); --sheet-chip: var(--fill); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ideas { --sheet-ground: var(--cell); --sheet-card: var(--fill); --sheet-chip: var(--fill-strong); }
}
:root[data-theme="dark"] .ideas { --sheet-ground: var(--cell); --sheet-card: var(--fill); --sheet-chip: var(--fill-strong); }
body.sheet-open { overflow: hidden; }

.glass.circle { width: 48px; height: 48px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; padding: 0;
                border-radius: 24px; color: var(--text); text-decoration: none; }
.glass.circle .icon { width: 22px; height: 22px; stroke-width: 2; }
.glass.capsule { display: flex; align-items: center; height: 48px; padding: 0 2px; border-radius: 24px; }
.glass.capsule button { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; padding: 0; border: none;
                        background: transparent; color: var(--text); }
.glass.capsule .icon { width: 22px; height: 22px; }
.eyebrow-line { display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em;
                text-transform: uppercase; color: var(--muted); }
.eyebrow-line .icon { width: 16px; height: 16px; color: var(--accent); }
.fine.center { text-align: center; }

/* The sheet */
.ideas-body { height: calc(100dvh - env(safe-area-inset-top) - 14px); max-height: none; gap: 16px;
              padding: 8px 16px calc(env(safe-area-inset-bottom) + 24px); background: var(--sheet-ground); }
.ideas-body.has-bar { padding-bottom: calc(env(safe-area-inset-bottom) + 112px); }     /* the bar's 88 px and 24 clear: it covers nothing */
.ideas-content { display: flex; flex-direction: column; gap: 16px; }
.room { display: flex; flex-direction: column; gap: 8px; }
.room-chips { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.room-chip { display: flex; flex-direction: column; min-width: 0; padding: 6px 10px; border-radius: 8px; background: var(--sheet-card); }
.room-chip b { font-size: 17px; line-height: 22px; font-weight: 800; white-space: nowrap; }
.room-chip span { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.idea { display: flex; flex-direction: column; gap: 12px; padding: 14px 16px 16px; border-radius: var(--radius-card); background: var(--sheet-card);
        box-shadow: inset 0 0 0 1.5px transparent; }
.idea.added { box-shadow: inset 0 0 0 1.5px var(--good); }
.idea-head { display: flex; align-items: flex-start; gap: 10px; }
.idea-head .grow { display: flex; flex-direction: column; gap: 2px; }
.idea h3 { font-size: 17px; line-height: 22px; font-weight: 700; }
.idea .time { display: flex; align-items: center; gap: 5px; font-size: 13px; line-height: 18px; color: var(--muted); }
.idea .time .icon { width: 14px; height: 14px; }
.idea .kcal { font-size: 22px; line-height: 26px; font-weight: 800; white-space: nowrap; }
.idea .kcal span { font-size: 12px; font-weight: 600; color: var(--muted); }
.tick { width: 44px; height: 44px; margin: -10px -6px -10px -10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
        padding: 0; border: none; background: transparent; color: var(--on-accent); }
.ring { width: 26px; height: 26px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: 13px;
        border: 2px solid var(--muted); color: var(--on-accent); }
.ring.on { border-color: var(--accent); background: var(--accent); }
.ring .icon { width: 14px; height: 14px; stroke-width: 3; }
.macro-chips { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.macro { display: flex; align-items: baseline; gap: 4px; padding: 6px 10px; border-radius: 8px; background: var(--sheet-chip); }
.macro span { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em; color: var(--muted); }
.macro b { font-size: 15px; line-height: 20px; font-weight: 700; }
.idea .why { font-size: 15px; line-height: 20px; }
.idea .why.over { color: var(--danger); }
.idea-actions { display: flex; gap: 8px; }
.idea-actions .button { height: 44px; display: flex; align-items: center; justify-content: center; gap: 8px; padding: 0 16px; border-radius: 22px;
                        background: var(--sheet-chip); color: var(--text); font-size: 15px; text-decoration: none; }
.idea-actions .button:first-child { flex: 1; }
.idea-actions .add-button .icon { color: var(--accent); }
.idea-actions .added-button { color: var(--good); }
.idea-actions .icon { width: 18px; height: 18px; stroke-width: 2; }
.ideas-content .again { display: flex; align-items: center; justify-content: center; gap: 8px; background: var(--sheet-card); font-size: 15px; }
.ideas-content .again .icon { width: 18px; height: 18px; }
.ideas-content > .fine, .ideas-content > .ideas-hint { margin-top: -4px; }
.selection-bar { position: absolute; left: 50%; bottom: calc(env(safe-area-inset-bottom) + 28px); z-index: 2; transform: translateX(-50%);
                 width: calc(100% - 32px); max-width: 568px; min-height: 60px; display: flex; align-items: center; gap: 8px;
                 padding: 6px 6px 6px 18px; border-radius: 30px; animation: bar-in .24s cubic-bezier(.2, .8, .2, 1); }
@keyframes bar-in { from { opacity: 0; transform: translate(-50%, 24px); } to { opacity: 1; transform: translate(-50%, 0); } }
@media (prefers-reduced-motion: reduce) { .selection-bar { animation: none; } }
.selection-bar .count { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 15px; line-height: 20px; font-weight: 700;
                        white-space: nowrap; }
.selection-bar .lens, .selection-bar .main { height: 48px; flex-shrink: 0; display: flex; align-items: center; gap: 6px; padding: 0 16px;
                                             border: none; border-radius: 24px; font-size: 15px; white-space: nowrap; text-decoration: none; }
.selection-bar .lens { background: var(--glass-lens); color: var(--text); font-weight: 600; }
.selection-bar .lens:focus-visible, .selection-bar .main:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.selection-bar .main { background: var(--accent); color: var(--on-accent); font-weight: 700; }
.selection-bar .icon { width: 18px; height: 18px; }

/* A recipe and the shopping list: pushed views whose controls float at the top. The page clips what runs past its sides
   (the fade below is as wide as the window, which beside a sidebar and a docked Chat ran 44 px past it at 1280, #223);
   clip, not hidden, so the bar still sticks. */
.page.floating { padding-top: calc(env(safe-area-inset-top) + 12px); overflow-x: clip; }
.float-bar { position: sticky; top: calc(env(safe-area-inset-top) + 12px); z-index: 3; display: flex; align-items: center;
             justify-content: space-between; }
/* The top scroll edge: what scrolls under the floating Back and its buttons fades into the ground first, as the bottom
   edge does under the tab bar, so a header never reads through the glass. Painted behind the buttons, never takes a tap. */
.float-bar::before { content: ""; position: absolute; z-index: -1; pointer-events: none; left: calc(50% - 50vw); width: 100vw;
                     top: calc(-12px - env(safe-area-inset-top)); height: calc(env(safe-area-inset-top) + 12px + 100% + 20px);
                     background: linear-gradient(to top, var(--edge0), var(--edge1) 24px); }
.recipe-head { display: flex; flex-direction: column; gap: 6px; padding-top: 12px; }
.recipe-head h1 { font-size: 32px; line-height: 34px; font-weight: 800; font-stretch: condensed; letter-spacing: .2px; text-transform: uppercase;
                  overflow-wrap: anywhere; }
.recipe-head .lead { margin-top: 0; font-size: 15px; line-height: 20px; }
.recipe-head .sources { font-size: 15px; line-height: 20px; color: var(--muted); }
/* "Which days": the brand's disclosure, not the browser's triangle. A chevron after the label turns down when open. */
.recipe-head .sources summary { display: flex; align-items: center; gap: 6px; width: fit-content; min-height: 44px; margin-left: -4px;
                                padding: 0 8px 0 4px; border-radius: 12px; color: var(--text); font-weight: 600; cursor: pointer;
                                list-style: none; }
.recipe-head .sources summary::-webkit-details-marker { display: none; }
.recipe-head .sources summary::marker { content: ""; }
.recipe-head .sources summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
.recipe-head .sources .chevron { width: 16px; height: 16px; stroke-width: 2.2; color: var(--accent); transition: transform .2s ease; }
.recipe-head .sources[open] .chevron { transform: rotate(90deg); }
.recipe-head .sources p { padding-bottom: 4px; }
@media (prefers-reduced-motion: reduce) { .recipe-head .sources .chevron { transition: none; } }
.card.portions h2 { font-size: 17px; line-height: 22px; font-weight: 700; }
.card.portions .between { align-items: center; }
.card.portions .room-chip { padding: 8px 10px; border-radius: 14px; background: var(--fill); }
.stepper { display: flex; align-items: center; gap: 4px; padding: 3px; border-radius: 22px; background: var(--fill); }
.stepper .step { position: relative; width: 38px; height: 38px; display: flex; align-items: center; justify-content: center; padding: 0;
                 border: none; border-radius: 19px; background: transparent; color: var(--text); }
.stepper .step::after { content: ""; position: absolute; inset: -3px; }                        /* 44 pt to touch */
.stepper .step .icon { width: 16px; height: 16px; stroke-width: 2; }
.stepper .n { width: 28px; text-align: center; font-size: 17px; line-height: 22px; font-weight: 800; }
.row.ingredient { min-height: 52px; font-size: 15px; line-height: 20px; }
.row.ingredient .grow { font-weight: 600; display: flex; flex-direction: column; gap: 2px; padding: 8px 0; }
.row.ingredient .sub { font-size: 13px; line-height: 18px; font-weight: 400; color: var(--muted); }
.row.ingredient b { font-weight: 700; white-space: nowrap; }
.recipe-steps { display: flex; flex-direction: column; gap: 8px; padding: 0; list-style: none; }
.recipe-steps li { display: flex; gap: 12px; padding: 14px 16px; border-radius: var(--radius-card); background: var(--cell); font-size: 15px;
                   line-height: 20px; }
.recipe-steps .num { width: 28px; height: 28px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: 14px;
                     background: var(--fill); color: var(--accent); font-weight: 800; }
.recipe-steps .grow { display: flex; flex-direction: column; gap: 2px; }
.recipe-steps small { font-size: 12px; line-height: 16px; color: var(--muted); }
.recipe-foot { display: flex; flex-direction: column; gap: 10px; }
.recipe-foot .button, #recipe-body > a.button { display: flex; align-items: center; justify-content: center; gap: 8px; }
.recipe-foot .icon { width: 20px; height: 20px; stroke-width: 2; }
.recipe-added { min-height: 50px; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 17px; line-height: 22px;
                font-weight: 600; color: var(--good); }

/* The shopping list */
.basket { margin-top: 6px; font-size: 13px; line-height: 18px; }
.basket-bar { position: relative; height: 6px; border-radius: 3px; background: var(--fill); overflow: hidden; }
.basket-bar i { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 3px; background: var(--accent); }
.shop-item { width: 100%; min-height: 56px; display: flex; align-items: center; gap: 12px; padding: 6px 16px 6px 12px; border: none;
             background: transparent; color: var(--text); text-align: left; }
.shop-item .grow, .home-item .grow { display: flex; flex-direction: column; }
.shop-item .name, .home-item .name { font-size: 15px; line-height: 20px; font-weight: 600; }
.shop-item small, .home-item small { font-size: 12px; line-height: 16px; color: var(--muted); }
.shop-item b { font-size: 15px; line-height: 20px; font-weight: 700; white-space: nowrap; }
.shop-item.done .grow, .shop-item.done b { opacity: .5; }
.shop-item.done .name { text-decoration: line-through; }
.home-item { min-height: 56px; padding: 6px 8px 6px 16px; }
.home-item .round { width: 44px; height: 44px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; padding: 0;
                    border: none; border-radius: 22px; background: var(--fill); color: var(--text); }
.home-item .round .icon { width: 18px; height: 18px; stroke-width: 2; }
.group > .footnote { padding: 0 4px; }
.card.add-item { gap: 8px; }
.card.add-item .row.field { padding: 0; }
.card.add-item input { border-radius: 10px; background: var(--fill); box-shadow: inset 0 0 0 1px var(--control-line); padding: 0 10px; }

/* ---------- Meal plan (redesign) ----------
   A pushed view over Nutrition, its back circle and Select floating on glass at the top: the strip of days (the picked
   one filled in the accent, the kind of day as three bars), the card that says what the plan is built from with the New
   ideas control, the day's target, its meals, and the two actions. Cards are solid on --cell, their tiles --fill; the one
   yellow button is Use this plan. In selection mode a glass bar floats above the tab pill for Recipes and a shopping list. */
.plan .plan-select { height: 48px; min-width: 48px; padding: 0 18px; border-radius: 24px; color: var(--text); font-size: 15px; font-weight: 600; }
.plan .plan-head { padding-top: 4px; }
.plan-body { gap: 20px; }
.plan.selecting { padding-bottom: calc(env(safe-area-inset-bottom) + var(--edge-height) + 84px); }

/* The strip of days */
.plan-days { display: flex; gap: 6px; }
.plan-day { position: relative; flex: 1; min-width: 0; min-height: 44px; display: flex; flex-direction: column; align-items: center; gap: 4px;
            padding: 8px 0 10px; border: none; border-radius: var(--radius-card); background: var(--cell); color: var(--text); }
.plan-day small { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.plan-day b { font-size: 22px; line-height: 26px; font-weight: 800; }
.plan-day .kcal { font-size: 11px; line-height: 14px; font-weight: 600; }
.plan-day .bars { display: flex; align-items: flex-end; gap: 2px; height: 16px; }
.plan-day .bars i { width: 5px; height: 5px; border-radius: 1px; background: var(--easy); }
.plan-day.endurance .bars i { height: 8px; background: var(--tempo); }
.plan-day.endurance .bars i:nth-child(2) { height: 10px; }
.plan-day.key .bars i { height: 8px; background: var(--accent); }
.plan-day.key .bars i:nth-child(2) { height: 16px; }
.plan-day.key .bars i:nth-child(3) { height: 12px; }
.plan-day.on { background: var(--accent); color: var(--on-accent); }
.plan-day.on small { color: var(--on-accent); }
.plan-day.on .bars i { background: var(--on-accent); }
.plan-day:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Built from your log */
.plan-log { gap: 12px; }
.plan-log-head { display: flex; align-items: center; gap: 8px; }
.plan-log-head .icon { width: 18px; height: 18px; }
.plan-log-head .icon.accent { color: var(--accent); }
.plan-log-head h2 { flex: 1; min-width: 0; font-size: 17px; line-height: 22px; font-weight: 700; }
.plan-rebuild { position: relative; height: 32px; display: flex; align-items: center; gap: 6px; padding: 0 12px; border: none; border-radius: 16px;
                background: var(--fill); color: var(--text); font-size: 13px; line-height: 18px; font-weight: 600; }
.plan-rebuild::after { content: ""; position: absolute; inset: -6px 0; }                          /* 44 pt to touch */
.plan-log-head .plan-rebuild .icon { width: 16px; height: 16px; }
.plan-text { font-size: 15px; line-height: 20px; }
/* The row of new ideas wraps as a Settings row does (#223): the control keeps to the row's end, or takes a line. */
.plan-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; }
.plan-row-name { font-size: 15px; line-height: 20px; font-weight: 600; }
.plan-novelty { flex-shrink: 0; margin-left: auto; }
.plan-note { padding: 8px 12px; border-radius: 10px; background: var(--warn-bg); color: var(--warn-text); font-size: 13px; line-height: 18px; }
.plan .waiting { display: flex; align-items: center; gap: 8px; font-size: 15px; line-height: 20px; color: var(--muted); }
.plan .waiting .dot { animation: pulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .plan .waiting .dot { animation: none; } }

/* The day's target */
.plan-target { gap: 12px; }
.plan-eyebrow { display: flex; flex-direction: column; gap: 2px; font-size: 13px; line-height: 18px; color: var(--muted); }
.plan-eyebrow .label { font-size: 12px; line-height: 16px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.plan-target .n-headline { font-size: 28px; line-height: 32px; }
.plan-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.plan-tile { display: flex; flex-direction: column; min-width: 0; padding: 8px 10px; border-radius: 14px; background: var(--fill); }
.plan-tile b { font-size: 17px; line-height: 22px; font-weight: 800; white-space: nowrap; }
.plan-tile span { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }

/* The meals */
.plan-meal { display: flex; align-items: center; gap: 10px; min-height: 84px; padding: 10px 8px 10px 12px; }
.plan-meal .tick { margin: -10px -6px -10px -6px; }
.plan-when { width: 74px; flex-shrink: 0; display: flex; flex-direction: column; padding-left: 4px; }
.plan-when b { font-size: 13px; line-height: 18px; font-weight: 700; }
.plan-when b:empty { display: none; }
.plan-when small { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: 0; text-transform: uppercase; color: var(--muted); }
.plan-what { display: flex; flex-direction: column; gap: 3px; }
.plan-what .name { font-size: 15px; line-height: 20px; font-weight: 600; overflow-wrap: anywhere; }
.plan-what .macros { font-size: 12px; line-height: 16px; color: var(--muted); }
.plan-tagline { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; font-size: 12px; line-height: 16px; }
.plan-tag { flex-shrink: 0; padding: 1px 8px; border-radius: 9px; background: var(--fill); font-weight: 700; }
.plan-tag.new { background: var(--accent); color: var(--on-accent); }
.plan-tagline .reason { color: var(--muted); }
.plan-swap { width: 44px; height: 44px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; padding: 0; border: none;
             border-radius: 22px; background: transparent; color: var(--accent); }
.plan-swap .icon { width: 20px; height: 20px; }
.plan-foot { padding: 0 4px; }

/* The actions */
.plan-actions { display: flex; flex-direction: column; gap: 10px; }
.plan-action { display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 17px; }
.plan-action .icon { width: 20px; height: 20px; stroke-width: 2; }
.plan-done { padding: 10px 12px; border-radius: 10px; background: var(--cell); font-size: 15px; line-height: 20px;
             box-shadow: inset 0 0 0 1.5px var(--good); }

/* The selection bar floats over the page, above the tab pill: 12 below it, 62 of pill, 12 between. */
.plan-bar { position: fixed; bottom: calc(env(safe-area-inset-bottom) + 86px); z-index: 4; }

/* Recipes of what is selected */
.plan-recipe { gap: 12px; }
.plan-recipe h2 { font-size: 22px; line-height: 26px; font-weight: 800; font-stretch: condensed; text-transform: uppercase; letter-spacing: .2px;
                  overflow-wrap: anywhere; }
.plan-parts { display: flex; flex-direction: column; border-radius: 14px; background: var(--fill); overflow: hidden; }
.plan-parts .row.ingredient { min-height: 44px; }
.plan-steps li { background: var(--fill); }

/* ---------- Photo background (redesign) ---------- */
/* A photo of the athlete's own as the dark ground (Settings, Appearance, Background, Photo; ground.js keeps it on this
   device). It is fixed behind everything and never moves the page; the <img> fades in once decoded. Over it a scrim,
   --photo-scrim, black at 60 %: at 55 % the crema of a title keeps only 4.3:1 over a white sky, at 60 % it keeps 5.2:1.
   The muted text that sits on the ground (footnotes, the line under a title) takes --ground-muted, a paler oat that
   keeps 4.6:1 there too (both measured by tests/test_ground_view.py); inside a card it stays oat, on the card's solid --cell. Cards stay solid and glass stays on the
   floating controls, so on a photo nothing but the ground changes. Dark mode only: light mode keeps the crema ground,
   as it does with espresso. Written twice like the dark tokens: the phone's dark, and a forced one. */
:root { --photo-scrim: rgba(0, 0, 0, 0); }
@media (prefers-color-scheme: dark) {
  :root[data-background="photo"]:not([data-theme="light"]) { --photo-scrim: rgba(0, 0, 0, .6); --ground-muted: #EDE6E3; }
  :root[data-background="photo"]:not([data-theme="light"]) .ground-photo { display: block; }
}
:root[data-theme="dark"][data-background="photo"] { --photo-scrim: rgba(0, 0, 0, .6); --ground-muted: #EDE6E3; }
:root[data-theme="dark"][data-background="photo"] .ground-photo { display: block; }
.ground-photo { display: none; position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; background: var(--bg); }
.ground-photo img { display: block; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .25s ease-out; }
.ground-photo.ready img { opacity: 1; }
.ground-photo::after { content: ""; position: absolute; inset: 0; background: var(--photo-scrim); }
@media (prefers-reduced-motion: reduce) { .ground-photo img { transition: none; } }
.footnote, .lead, .title .muted { color: var(--ground-muted, var(--muted)); }
.list .footnote, .card .footnote, .sheet .footnote, .sheet .lead { color: var(--muted); }
/* The sign-in screen is espresso whatever the Background, as it is whatever the mode. */
body.signed-out { --ground-muted: initial; }
body.signed-out #ground-photo { display: none; }

/* The photo's row in Settings: the thumbnail, the line saying where it shows, Change and Remove (44 px each). The file
   input under them is visually hidden; Photo and Change open it. */
.row.ground-row { justify-content: flex-start; min-height: 64px; padding-right: 8px; }
.ground-thumb { width: 44px; height: 44px; flex-shrink: 0; border-radius: 10px; object-fit: cover; background: var(--fill); }
.ground-row .link { min-width: 44px; font-size: 15px; }
.ground-picker { position: relative; }

/* ---------- Settings design fixes ---------- */
/* Nutrition goal and diet: a card of two segmented choices (real radio groups, 44 px segments in a 50 px capsule, so the
   track's 25 and the segment's 22 share a centre), the Note, and what the chosen goal sets as rows of a name and its rule. */
.nutrition-settings .group-head .key { font-size: 15px; line-height: 20px; }
.goal-card .choice-block { display: flex; flex-direction: column; gap: 10px; padding: 16px 16px 14px; }
.goal-card .choice-block + .choice-block { padding-top: 2px; }
.goal-card .choice-block + .choice-block::before { display: none; }         /* Goal and Diet read as one block, as drawn */
.goal-card .choice-name { font-size: 15px; line-height: 20px; font-weight: 700; }
.goal-card .choice-note { font-size: 15px; line-height: 20px; color: var(--muted); }
.goal-card .choice-note:empty { display: none; }
/* The four diets are two rows of two at every width: in one row they were cut to "Omniv… Pescata…" at 320 px (#223). */
.segmented.diets { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.goal-card .rule { display: flex; gap: 12px; padding: 12px 16px; }
.goal-card .rule-name { width: 76px; flex-shrink: 0; font-size: 15px; line-height: 20px; font-weight: 700; }
.goal-card .rule .grow { flex: 1; min-width: 0; }
.goal-card .rule-value { font-size: 15px; line-height: 20px; }
.goal-card .rule .sub { margin-top: 2px; font-size: 13px; line-height: 18px; color: var(--muted); }
.goal-card .rules { display: flex; flex-direction: column; }
.goal-card .rules > * { position: relative; }
.goal-card .rules > * + *::before { content: ""; position: absolute; top: 0; left: 16px; right: 0; height: 1px; background: var(--line); }
/* Training method (ADR 0023): a card whose rows are the six Methods, each short (the review of 2026-09-28): its name, one
   line each of its summary, who it suits and why it is recommended, and its numbers; its evidence, what it does here and
   how the limits reduce it behind "Why this works" (.method-more). A ring on the left marks the one picked; what a Method
   is, which led the page, is the footnote under the card. Under it the chosen Method's dials: a choice as rows (two lines
   of summary, all of it for the one picked), a number as a segmented control, the switches as ticks. */
.methods .methods-layer { padding: 8px 4px 0; font-size: 15px; line-height: 20px; font-weight: 700; color: var(--text); }
.methods .methods-dial { padding: 4px 4px 0; font-size: 15px; line-height: 20px; font-weight: 600; color: var(--text); }
.methods .methods-dial-note { padding: 0 4px; }
.methods .methods-number { padding: 0 4px; }
.method-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 9px; background: var(--fill); font-size: 11px;
              line-height: 16px; font-weight: 700; vertical-align: 2px; color: var(--muted); }
.method-tag.recommended { background: var(--accent); color: var(--on-accent); }      /* Recommended for you (#264) */
.method-why { display: block; font-size: 13px; line-height: 18px; font-weight: 600; color: var(--text); }
.method-suits { display: block; font-size: 15px; line-height: 20px; color: var(--text); }
.method-evidence { display: block; font-size: 13px; line-height: 18px; color: var(--muted); }
.method-reduced { display: block; font-size: 13px; line-height: 18px; font-weight: 600; color: var(--text); }
.methods .methods-switch { padding: 0 4px; }
.methods .methods-ask { display: flex; flex-direction: column; gap: 8px; padding: 12px 16px; border-radius: 14px; background: var(--fill); }
.methods .methods-ask[hidden] { display: none; }
.methods .methods-ask p { font-size: 15px; line-height: 20px; }
.methods .methods-ask-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.method { display: flex; flex-direction: column; gap: 4px; width: 100%; min-height: 44px; padding: 12px 16px 12px 48px; border: none;
          background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.method::after { content: ""; position: absolute; left: 16px; top: 13px; width: 20px; height: 20px; border-radius: 50%;
                 box-shadow: inset 0 0 0 1.5px var(--muted); }
.method.active::after { box-shadow: inset 0 0 0 6px var(--accent); }
.method[aria-disabled="true"] { cursor: default; }
.method[aria-disabled="true"] .method-name, .method[aria-disabled="true"] .method-summary,
.method[aria-disabled="true"] .method-meta, .method[aria-disabled="true"]::after { opacity: .5; }
.method:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.method-name { display: block; font-size: 17px; line-height: 22px; font-weight: 600; }
.method-summary { display: block; font-size: 15px; line-height: 20px; color: var(--muted); }
.method:not(.active) .method-summary { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.method-meta, .method-here, .method-note, .method-blocked { display: block; font-size: 13px; line-height: 18px; }
.method-meta { color: var(--muted); }
.method-note, .method-blocked { color: var(--warn-text); }
/* A Method's card (.method-card, the review of 2026-09-28): the radio row, then its "Why this works", which cannot sit
   inside the radio. Its summary is the brand's disclosure in the size of the card's tags (12/16, the chevron 12 px), so
   it shares the name's line: it stands at the row's top right, 44 px tall, over the room the name keeps for it
   (.method-room: the same words and chevron, unseen and unread, floated so only the name's first line gives way), so the
   name never runs under it in either language and a badge that wraps has the whole line. Its body opens under the row,
   and the row's lines are whole while it is open (.open, app.js). */
.method-list .method { position: relative; gap: 2px; padding-top: 10px; padding-bottom: 10px; }
.method-list .method::after { top: 11px; }                  /* the ring stays on the name's line */
.method-room, details.method-more > summary { gap: 4px; padding: 0 8px 0 4px; font-size: 12px; line-height: 16px; font-weight: 600; }
.method-room { visibility: hidden; float: right; display: flex; align-items: center; height: 22px; margin-left: 8px; }
.method-list .method-tag { white-space: nowrap; }
.method-room .chevron, details.method-more > summary .chevron { width: 12px; height: 12px; }
.method-list .method .method-summary, .method-list .method .method-suits, .method-list .method .method-why {
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
.method-card.open .method .method-summary, .method-card.open .method .method-suits, .method-card.open .method .method-why {
  display: block; overflow: visible; }
details.method-more > summary { position: absolute; top: 0; right: 8px; margin: 0; }
.method-more-body { display: flex; flex-direction: column; gap: 6px; padding: 0 16px 14px 48px; }
.method-more-body p { font-size: 13px; line-height: 18px; }
.method-lead { display: flex; flex-direction: column; gap: 4px; padding: 12px 16px; }
.method-addon { display: flex; align-items: flex-start; gap: 12px; min-height: 44px; padding: 12px 16px; cursor: pointer; }
/* An add-on's tick is a 20 px box drawn in a 44 px checkbox, so it is 44 px to touch where it stood (#223): its edge in
   the muted colour, as a method's ring; ticked, the accent with a check in the colour on it. */
.method-addon input { -webkit-appearance: none; appearance: none; position: relative; flex-shrink: 0; width: 44px; height: 44px;
                      margin: -11px -12px -11px -12px; border: none; border-radius: 12px; background: none; cursor: pointer; }
.method-addon input::before { content: ""; position: absolute; left: 12px; top: 12px; width: 20px; height: 20px; border-radius: 6px;
                              box-shadow: inset 0 0 0 1.5px var(--muted); }
.method-addon input:checked::before { background: var(--accent); box-shadow: none; }
.method-addon input:checked::after { content: ""; position: absolute; left: 19px; top: 15px; width: 6px; height: 11px; border: solid var(--on-accent);
                                     border-width: 0 2px 2px 0; transform: rotate(45deg); }
.method-addon input:disabled { cursor: default; }
.method-addon input:disabled::before, .method-addon input:disabled::after { opacity: .5; }
.method-addon input:focus-visible { outline: none; }
.method-addon input:focus-visible::before { outline: 2px solid var(--accent); outline-offset: 2px; }
.method-addon .grow { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }

/* ---------- regular width, continued: what the later sections float ----------
   The same thresholds as above (768, 1024, 1280); these rules sit here because the rules they override are above. */
@media (min-width: 768px) {
  .food-toast { left: 50%; right: auto; bottom: 24px; width: min(568px, calc(100vw - 32px)); transform: translateX(-50%); }
  .plan-bar { bottom: 24px; }
  .plan.selecting { padding-bottom: 112px; }
  .logfood { align-items: center; }
  .logfood-body { height: auto; max-height: calc(100dvh - 80px); border-radius: var(--radius-sheet); }
  .ideas-body { height: auto; max-height: calc(100dvh - 80px); padding-bottom: 24px; }
  .ideas-body.has-bar { padding-bottom: 112px; }
  .selection-bar { bottom: 52px; }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .float-bar { top: calc(var(--inset) + var(--topbar) + 12px); }      /* under the bar row, not in it */
}
@media (min-width: 1024px) {
  .logfood { left: var(--side); }
  .food-toast { left: calc(var(--side) + 16px); width: 408px; transform: none; }
  .plan-bar { left: calc(50% + var(--side) / 2); }
  .float-bar { justify-content: flex-end; }        /* the detail column has no way back: the list beside it is the way */
  .float-bar > a.circle { display: none; }
}
@media (min-width: 1280px) {
  .logfood { right: var(--chat-pane); }
  .plan-bar { left: calc((100vw - var(--chat-pane) + var(--side)) / 2); }
}

/* ---------- Settings in groups (#170), eight that mirror the tabs since 28 Sep 2026 ----------
   Every section names its group; only the open group's show. Below 1024 px the list is Settings and a group opens pushed,
   with the way back; from 1024 px the list stays beside the open group. A long page opens from a row of its group (#211):
   Training's Method and Weather rules each name their page, and a page shows that section alone, under its title and the
   way back to Training, at every width; the rest of the group names no page (""), and steps aside for it. */
.settings-detail { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
#view-settings.grouped:not([data-open="goal"]) [data-group="goal"],
#view-settings.grouped:not([data-open="training"]) [data-group="training"],
#view-settings.grouped:not([data-open="nutrition"]) [data-group="nutrition"],
#view-settings.grouped:not([data-open="coach"]) [data-group="coach"],
#view-settings.grouped:not([data-open="notify"]) [data-group="notify"],
#view-settings.grouped:not([data-open="data"]) [data-group="data"],
#view-settings.grouped:not([data-open="ai"]) [data-group="ai"],
#view-settings.grouped:not([data-open="app"]) [data-group="app"],
#view-settings.grouped:not([data-open="owner"]) [data-group="owner"],
#view-settings.grouped:not([data-page="methods"]) [data-page="methods"],
#view-settings.grouped:not([data-page="weather"]) [data-page="weather"],
#view-settings.grouped:not([data-page=""]) [data-page=""] { display: none !important; }
/* On its page a section's own heading would say the title again: the bar above says it once. */
#view-settings [data-page]:not([data-page=""]) > h2, #view-settings [data-page]:not([data-page=""]) > .group > h2,
#view-settings [data-page]:not([data-page=""]) > .group-head > h2 { display: none; }
/* The root list: Coaching, Setup and App, a small header over each section's rows, as iOS sets them. */
#settings-groups > h2.settings-section { padding: 0 16px; font-size: 13px; line-height: 18px; font-weight: 600; letter-spacing: .08em;
                                         text-transform: uppercase; color: var(--muted); }
#settings-groups > .list + h2.settings-section { margin-top: 14px; }
/* The realism check on the Target weight (#250): its words, its call at the end, and the two fixes under them when it is
   too fast. Too fast and a refused Target weight read in the warning colour, which never stands alone: the words say it. */
.row.realism { flex-wrap: wrap; padding-top: 8px; padding-bottom: 8px; }
.row.realism .name { display: block; }
.row.realism .sub.bad, .row.realism .value.bad { color: var(--danger); }
.realism-fixes { display: flex; flex-wrap: wrap; gap: 8px; width: 100%; }
@media (max-width: 1023px) {
  #view-settings.grouped:not([data-open=""]) > .title, #view-settings.grouped:not([data-open=""]) > #settings-groups,
  #view-settings.grouped:not([data-open=""]) > .first-note { display: none; }
  /* A section whose heading is its group's name (Goal, Connections): the bar over the group says it once. Beside the list
     from 1024 px the bar is not shown, and the heading is. */
  #view-settings .names-group > h2 { display: none; }
}
#settings-back { height: auto; min-height: 44px; }      /* a long title takes two lines at 390, the German ones most */
#settings-back .bar-title { min-width: 0; text-align: center; }
@media (min-width: 768px) {
  #settings-back .bar-title { text-align: start; }       /* from here a pushed bar leads with its title */
}
@media (min-width: 1024px) {
  /* The open group is a detail like a Day: never under a phone's column, the list of groups giving way first (#221). */
  #view-settings.grouped { display: grid; grid-template-columns: minmax(200px, 280px) minmax(var(--detail-min), 1fr); column-gap: 24px; row-gap: 22px;
                           align-items: start; }
  #view-settings.grouped > .first-note { grid-column: 1 / -1; }
  #view-settings.grouped > .title { grid-column: 1 / -1; }
  #view-settings.grouped > #settings-groups { position: sticky; top: 16px; }
  #view-settings.grouped[data-page=""] #settings-back { display: none !important; }       /* a group: the list beside it is the way */
  #settings-back { flex-direction: column; align-items: flex-start; gap: 10px; padding-top: 0; }
  #settings-back .back { display: flex; position: static; }       /* a page of Training: the way back to it, over its title */
  #settings-groups a[aria-current="page"] { background: var(--fill); box-shadow: inset 0 0 0 1.5px var(--text); }    /* the open group, marked at 3:1 as a tab is (#223) */
  /* At the list's top or bottom the ring takes the list's corners, or the list's rounding cuts the ring's square ones. */
  #settings-groups .list > a[aria-current="page"]:first-child { border-top-left-radius: inherit; border-top-right-radius: inherit; }
  #settings-groups .list > a[aria-current="page"]:last-child { border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; }
}
/* A key of the athlete's own (Q1 and Q2, 28 Sep 2026): its field saves as it changes, and only while the value is theirs
   a small line in the same row says so, with Back to the default: a button in the text colour, underlined as a line that
   does something is, 44 px to touch across the line's height. A value refused is said under its field in the warning
   colour, which never stands alone: the words say it. */
.own-row .own-line, .own-row .own-refused { flex: 1 1 100%; margin: -2px 0 0; font-size: 13px; line-height: 18px; color: var(--muted); }
.own-row .own-line[hidden], .own-row .own-refused[hidden] { display: none; }
.own-row .own-refused { color: var(--danger); }
.own-reset { min-height: 44px; margin: -13px 0; padding: 13px 0; border: none; background: transparent; font: inherit; color: var(--text);
             text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.own-reset:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.own-reset:disabled { color: var(--muted); cursor: default; }
/* Notifications (#255): a Nudge's time under its switch, set in from the row's edge as a part of it; the Nudges and the
   quiet hours spaced as Settings' sections are, their one status line under both. */
#notify-settings, #about { display: flex; flex-direction: column; gap: 26px; }       /* About's parts, spaced the same */
#notify-settings > .footnote { margin-top: -18px; }
.row.nudge-time { padding-left: 32px; padding-top: 0; }
.list > .row.nudge-time::before { display: none; }       /* no hairline between a switch and its own time */
.row.nudge-time .field-above { flex-direction: row; align-items: center; justify-content: space-between; gap: 12px; }
.row.nudge-time .field-above-name { font-size: 15px; line-height: 20px; }
.row.field-form.nudge-time input { flex: 0 1 140px; width: auto; }
/* A row that is a choice alone, its group's heading naming it (Language, Show power as): the segments share its width. */
.row.lone > .segmented { flex: 1 1 100%; margin-left: 0; }
.row.lone > .segmented button { flex: 1 1 0; }
/* Connections: the sources not connected wait behind one row, Connect another service, which opens in place as the
   Invite's rows do (#216): a chevron in the accent at its end that turns down when open, the focus ring inside the card. */
.conn-more > summary { cursor: pointer; list-style: none; }
.conn-more > summary::-webkit-details-marker { display: none; }
.conn-more > summary::marker { content: ""; }
.conn-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 12px; }
.conn-more > summary .chevron { width: 18px; height: 18px; stroke-width: 2.2; transition: transform .2s ease; }
.conn-more[open] > summary .chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .conn-more > summary .chevron { transition: none; } }
.conn-more-rows > * { position: relative; }
.conn-more-rows > *::before { content: ""; position: absolute; top: 0; left: 16px; right: 0; height: 1px; background: var(--line);
                              transform: scaleY(.5); transform-origin: top; }

/* Records (#160): five durations in a row on the Dashboard, the value large, W/kg under it. */
.record-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; }
.record { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 8px 6px; border-radius: 10px; background: var(--fill); text-align: center; }
.record .label { font-size: 11px; line-height: 14px; font-weight: 600; letter-spacing: .06em; color: var(--muted); }
.record .big { font-size: 18px; line-height: 22px; font-weight: 800; white-space: nowrap; }
.record .big small { font-size: 11px; font-weight: 600; color: var(--muted); }
.record .sub { font-size: 11px; line-height: 14px; color: var(--muted); white-space: nowrap; }
#records-body .value { display: flex; flex-direction: column; align-items: flex-end; font-weight: 600; }
#records-body .value .sub { font-weight: 400; }

/* The training block (#159): its line, the reasons under it, and Yes / Not quite / Ask the Coach for a plan, which is a
   fill button with a line under it saying how long a plan takes (#209; never its cost, ADR 0027). */
.card.block { gap: 8px; }
.block .block-line { font-size: 17px; line-height: 22px; font-weight: 600; }
.block > a.line-link { margin: -12px 0; }        /* a Goal line that opens Goal: its 44 px over the lines round it (2026-09-28) */
.block .actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 6px; }
.block .actions .button { flex: 1 1 140px; }
.block .footnote:empty { display: none; }

/* A connected source's sync switches (#168): indented under its row; the confirmation says what stops before it writes. */
.row.conn-switch { padding-left: 52px; font-size: 15px; }
.row.conn-confirm { flex-direction: column; align-items: stretch; gap: 8px; padding-left: 52px; }
.row.conn-confirm p { font-size: 15px; line-height: 20px; }
.row.conn-confirm .actions { display: flex; gap: 10px; }
.row.conn-confirm .actions .button { flex: 1 1 0; min-width: 0; padding: 0 12px; }
.row.conn-confirm .footnote:empty { display: none; }
