/* reckoner.fit: Coach's Log
   Vanilla CSS, zero build step. Auto light/dark via prefers-color-scheme,
   overridable with [data-theme="light|dark"] on <html>. */

:root {
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-voice: Georgia, "Times New Roman", serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  --radius: 8px;
  --radius-card: 12px;
  --radius-pill: 999px;
  --maxw: 720px;

  /* Brand palette, from brand/BRAND.md. These do not flip with the theme: they
     are the identity, not the surface. The landing page is built out of them
     directly; the app pages use the --surface/--text tokens below, which do
     flip. Oxide has two values because plain oxide goes muddy on graphite. */
  --chalk: #d9cfbc;
  --iron: #33291f;
  --oxide: #a33f22;
  --oxide-lift: #c95f36;
  --bone: #f2f0e9;
  --graphite: #17150f;

  /* light (default) */
  --surface-0: #f6f5f1;   /* page */
  --surface-1: #efeee8;   /* inset tile */
  --surface-2: #ffffff;   /* raised card */
  --border: rgba(0, 0, 0, 0.09);
  --border-strong: rgba(0, 0, 0, 0.16);
  /* The top bar's own edge, and the only line in the file that is neither
     `--border` nor drawn as a border. See `.topbar` for why it is a background
     layer, and why this one value is a flat colour rather than black at a low
     alpha like every other line here. It is a warm light grey pitched between
     the page below it and the white masthead above, which is the whole brief:
     a hairline that separates the two without ruling them apart. */
  --topbar-edge: #e8e5de;

  --text-primary: #23221e;
  --text-secondary: #605e57;
  --text-muted: #928f86;

  --accent: #185fa5;
  --accent-bg: #e6f1fb;
  --success: #0f6e56;
  --success-bg: #e1f5ee;
  --warning: #854f0b;
  --warning-bg: #faeeda;

  /* The personal record badge's upper tier (FM-30). Oxide, because the brand
     mark's top plate is the reckoned increment and a lift that has not moved in
     a month moving is exactly that. It is the semantic pair the brand colour
     could not be on its own: `--oxide` at full strength is unreadable on a tint
     of itself, so the text value is the darker oxide on light and the lifted one
     on graphite, which is the split the two brand values already exist for. */
  --record: #a33f22;
  --record-bg: #f8e9e2;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --surface-0: #17150f;
    --surface-1: #201d16;
    --surface-2: #2a271f;
    --border: rgba(255, 255, 255, 0.10);
    --border-strong: rgba(255, 255, 255, 0.18);
    --topbar-edge: rgba(255, 255, 255, 0.10);

    --text-primary: #f2f0e9;
    --text-secondary: #b6b2a7;
    --text-muted: #7f7c72;

    --accent: #85b7eb;
    --accent-bg: rgba(23, 95, 165, 0.22);
    --success: #5dcaa5;
    --success-bg: rgba(15, 110, 86, 0.22);
    --warning: #ef9f27;
    --warning-bg: rgba(133, 79, 11, 0.24);
    --record: #d97a4e;
    --record-bg: rgba(163, 63, 34, 0.18);
  }
}

:root[data-theme="dark"] {
  --surface-0: #17150f;
  --surface-1: #201d16;
  --surface-2: #2a271f;
  --border: rgba(255, 255, 255, 0.10);
  --border-strong: rgba(255, 255, 255, 0.18);
  --topbar-edge: rgba(255, 255, 255, 0.10);
  --text-primary: #f2f0e9;
  --text-secondary: #b6b2a7;
  --text-muted: #7f7c72;
  --accent: #85b7eb;
  --accent-bg: rgba(23, 95, 165, 0.22);
  --success: #5dcaa5;
  --success-bg: rgba(15, 110, 86, 0.22);
  --warning: #ef9f27;
  --warning-bg: rgba(133, 79, 11, 0.24);
  --record: #d97a4e;
  --record-bg: rgba(163, 63, 34, 0.18);
}

* { box-sizing: border-box; }

/* Pin the text size against iOS Safari's auto-inflation. Left to itself, Safari
   boosts small text inside a full-width block on a narrow screen, so an 11px
   caption in the feed rendered bigger than the 14px rows above it. 100% means
   "draw it at the size the CSS asked for", which is what every size here wants. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface-0);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* The page is a column with the footer at the bottom of it, so a short page
   (an empty history, a rest day) doesn't leave the footer floating halfway up. */
body { min-height: 100vh; display: flex; flex-direction: column; }
.wrap { max-width: var(--maxw); margin: 0 auto; padding: 20px 20px 64px;
        width: 100%; box-sizing: border-box; flex: 1; }

/* top bar */
/* Full width, with the row inside it held to the reading column: the hairline
   and the raised tint run edge to edge, the content lines up with the page.

   It stays put and the page runs underneath it. The brand is the way back to
   Today and the chip is the way to the account, and on a phone both used to be
   a scroll away from anywhere below the fold. surface-2 is opaque, so the page
   passing behind it does not show through. The z-index clears the page and the
   cookie notice at 30, and stays under the modal scrim at 60, which has to be
   able to dim this too. */
/* The edge under it is a 1px background layer, not a border, and that is not
   fussiness. A border here came back reported as a near black rule on a light
   screen at an alpha where it should have been invisible, twice, at two
   different strengths. A border is the one line an engine will re draw for you:
   pinning the bar gives it a compositing layer of its own, a sub-pixel width
   invites the renderer to round it, and a raised contrast setting is entitled
   to darken it on the reader's behalf. A background layer is none of those
   things. It is exactly one pixel of exactly the colour named, everywhere. */
.topbar {
  position: sticky; top: 0; z-index: 40;
  background:
    linear-gradient(var(--topbar-edge), var(--topbar-edge)) left bottom / 100% 1px no-repeat,
    var(--surface-2);
}
/* A hash landing or a scrollIntoView would otherwise put its target under the
   bar. This is the whole site's allowance for it, set on the scrolling element
   so every anchor gets it without having to remember. */
html { scroll-padding-top: 72px; }
.topbar-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 12px 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  box-sizing: border-box;
}
.brand {
  display: flex; align-items: baseline; gap: 2px;
  text-decoration: none; color: inherit; background: none; border: 0; padding: 0;
  font-family: inherit; cursor: pointer;
}
/* The mark is a circle, so it can't sit on the text baseline like the letters
   do. Centre it against them instead. */
.brand-mark { width: 22px; height: 22px; align-self: center; margin-right: 7px; }
.brand b { font-size: 18px; font-weight: 500; letter-spacing: -0.01em; color: var(--text-primary); }
.brand span { font-size: 12px; color: var(--text-muted); }
.meta { font-size: 13px; color: var(--text-secondary); white-space: nowrap; }
.theme-toggle {
  background: none; border: 0.5px solid var(--border-strong); color: var(--text-secondary);
  border-radius: var(--radius); padding: 5px 9px; cursor: pointer; font-size: 13px;
}
.theme-toggle:hover { background: var(--surface-1); }

/* the profile chip, and the menu behind it */
.profile-wrap { position: relative; }
.profile-chip {
  display: flex; align-items: center; gap: 9px;
  background: none; border: 0.5px solid var(--border-strong); border-radius: var(--radius-pill);
  padding: 4px 10px 4px 4px; cursor: pointer; font-family: inherit;
}
.profile-chip:hover, .profile-chip[aria-expanded="true"] { background: var(--surface-1); }
.profile-name {
  font-size: 13px; color: var(--text-primary); max-width: 150px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.profile-chip .ti-chevron-down { font-size: 14px; color: var(--text-muted); }

.avatar {
  border-radius: 50%; flex: none; object-fit: cover;
  display: flex; align-items: center; justify-content: center;
}
.avatar-initial { background: var(--iron); color: var(--bone); font-weight: 500; }

/* The one place the design allows a shadow: this floats over the page rather
   than being part of its structure. */
.menu {
  position: absolute; top: calc(100% + 8px); right: 0; width: 296px; z-index: 30;
  background: var(--surface-2); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-card); overflow: hidden;
  box-shadow: 0 8px 28px rgba(15, 13, 9, 0.16);
}
.menu-identity {
  display: flex; gap: 12px; align-items: center; padding: 16px;
  border-bottom: 0.5px solid var(--border);
}
.menu-face { position: relative; flex: none; }
.menu-camera {
  position: absolute; right: -2px; bottom: -2px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--accent); color: var(--on-accent, #fff);
  border: 2px solid var(--surface-2); cursor: pointer; font-size: 10px;
  display: flex; align-items: center; justify-content: center; padding: 0;
}
.menu-who { min-width: 0; flex: 1; }
.menu-name-row { display: flex; align-items: center; gap: 6px; }
.menu-name {
  font-size: 15px; font-weight: 500; color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu-name-unset { color: var(--text-muted); font-weight: 400; }
.menu-name-input {
  flex: 1; min-width: 0; font-size: 14px; padding: 5px 8px;
  background: var(--surface-1);
}
.menu-email {
  font-size: 12px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Why a name was turned down, in the menu and in the one-time question. It
   wraps, because the reason is a sentence rather than a word, and it takes no
   room at all until there is one to print. */
.name-err {
  font-size: 12px; color: var(--warning); margin-top: 6px; line-height: 1.4;
}
.name-ask-input {
  width: 100%; box-sizing: border-box; font-size: 15px; padding: 9px 11px;
  background: var(--surface-1);
}
.name-ask-hint {
  font-size: 12px; color: var(--text-muted); margin: 10px 0 0; line-height: 1.5;
}
.icon-btn {
  background: none; border: 0; color: var(--text-muted); cursor: pointer;
  padding: 2px; font-size: 13px; flex: none; font-family: inherit;
}
.icon-btn:hover { color: var(--text-secondary); }
.icon-btn-go {
  background: var(--accent); color: var(--on-accent, #fff);
  border-radius: var(--radius); padding: 6px 8px; font-size: 12px;
}
.menu-theme {
  display: flex; align-items: center; gap: 10px; padding: 12px 16px;
  border-bottom: 0.5px solid var(--border);
}
.menu-theme > .ti { font-size: 17px; color: var(--text-secondary); width: 18px; }
.menu-theme-label { flex: 1; font-size: 14px; color: var(--text-primary); }
.menu-item {
  width: 100%; display: flex; align-items: center; gap: 10px; padding: 11px 16px;
  background: none; border: 0; cursor: pointer; font-family: inherit;
  text-align: left; text-decoration: none; box-sizing: border-box;
}
.menu-item:hover { background: var(--surface-1); }
.menu-item.active { background: var(--surface-1); }
.menu-item > .ti { font-size: 17px; color: var(--text-secondary); width: 18px; }
.menu-item-label { flex: 1; font-size: 14px; color: var(--text-primary); }
.menu-item.active .menu-item-label { font-weight: 500; }
.menu-item-hint { font-size: 12px; color: var(--text-muted); }
/* Setup's waiting count. Same accent-on-tint as the pending badge the Setup
   page itself draws, so the chip in the menu and the list on the page read as
   one thing. */
.menu-count {
  font-size: 12px; padding: 2px 8px; border-radius: var(--radius-pill);
  background: var(--accent-bg); color: var(--accent);
}
/* The six section links when the bar is too narrow to hold them: the same nav,
   folded into the menu under the identity row. */
.menu-sections {
  display: flex; flex-direction: column; padding: 6px;
  border-bottom: 0.5px solid var(--border);
}
.menu-sections a {
  padding: 10px 10px; border-radius: var(--radius); font-size: 15px;
  color: var(--text-secondary); text-decoration: none;
}
.menu-sections a:hover { background: var(--surface-1); }
.menu-sections a.active { background: var(--surface-1); color: var(--text-primary); font-weight: 500; }
.menu-sep { border-top: 0.5px solid var(--border); }

/* overlays: the avatar cropper, and anything else that floats over the page */
.modal {
  position: fixed; inset: 0; z-index: 60; background: rgba(15, 13, 9, 0.45);
  display: flex; align-items: center; justify-content: center; padding: 20px;
  overflow: auto;
}
.modal-card {
  background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 22px 24px; position: relative;
  max-width: 460px; width: 100%; box-sizing: border-box;
}
.modal-card h2 { font-size: 20px; font-weight: 500; margin: 0 0 3px; }
.modal-sub { font-size: 13px; color: var(--text-secondary); margin: 0 0 18px; }
.modal-x {
  position: absolute; top: 16px; right: 16px; background: none; border: 0;
  color: var(--text-secondary); cursor: pointer; font-size: 20px; line-height: 1; padding: 4px;
}
.modal-actions { display: flex; gap: 10px; margin-top: 18px; justify-content: flex-end; }
.crop-frame {
  position: relative; width: 264px; height: 264px; margin: 0 auto 18px;
  border-radius: 50%; overflow: hidden; cursor: grab;
  touch-action: none; user-select: none; background: var(--surface-1);
}
.crop-photo {
  position: absolute; left: 50%; top: 50%; max-width: none; pointer-events: none;
}
.crop-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.9); pointer-events: none;
}
.crop-zoom { display: flex; align-items: center; gap: 12px; }
.crop-zoom input[type="range"] { flex: 1; accent-color: var(--accent); }
.crop-zoom-sm { font-size: 15px; color: var(--text-muted); }
.crop-zoom-lg { font-size: 21px; color: var(--text-muted); }
.crop-err { font-size: 13px; color: var(--warning); min-height: 20px; margin-top: 8px; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* the page's own greeting, above the stat row */
.page-title {
  font-size: 24px; font-weight: 500; letter-spacing: -0.01em;
  margin: 2px 0 20px; color: var(--text-primary);
}

/* today card */
.card {
  background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 18px 20px;
}
.today-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.today-marks { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.eyebrow { font-size: 12px; color: var(--text-muted); letter-spacing: 0.03em; text-transform: uppercase; }
.day-name { font-size: 18px; font-weight: 500; }
.pill {
  font-size: 12px; padding: 5px 12px; border-radius: var(--radius);
  background: var(--accent-bg); color: var(--accent);
}

.lift {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center;
  padding: 10px 0; border-top: 0.5px solid var(--border);
}
.lift:last-child { border-bottom: 0.5px solid var(--border); }
.lift-name { font-size: 15px; }
.lift-scheme { font-size: 12px; color: var(--text-muted); margin-left: 4px; }
.lift-load { font-size: 14px; font-weight: 500; }
.lift-rpe {
  font-size: 11px; color: var(--text-secondary); background: var(--surface-1);
  padding: 3px 8px; border-radius: var(--radius); min-width: 54px; text-align: center;
}
.tag-rotate { font-size: 11px; color: var(--accent); margin-left: 6px; }

/* A movement's cue: how to perform it. Only a handful of entries carry one, the
   ones whose safety is in the execution rather than in the selection, so this
   line is rare and is not styled to compete with the prescription. Secondary
   rather than muted: the athlete it is written for has no other option. */
.ex-cue { font-size: 12px; color: var(--text-secondary); line-height: 1.4; }
.lift .ex-cue { margin-top: 3px; }
.log-ex .ex-cue { margin: -2px 0 10px; }

/* The warm-up ramp: the lighter sets up to the working weight. Muted and below
   the scheme, because it is preparation rather than the prescription itself, and
   the flame icon marks it apart from the cue above it. */
.ex-ramp { font-size: 12px; color: var(--text-muted); line-height: 1.4; margin-top: 3px; }
.ex-ramp .ti { font-size: 12px; opacity: 0.8; }

/* changelog */
.section-label { font-size: 12px; color: var(--text-muted); letter-spacing: 0.03em;
  text-transform: uppercase; margin: 26px 0 12px; }
.log-item { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 12px; }
.log-icon { font-size: 16px; margin-top: 1px; flex: none; }
.log-icon.up { color: var(--success); }
.log-icon.down { color: var(--warning); }
.log-icon.rot { color: var(--accent); }
.log-icon.hold { color: var(--oxide-lift); }
.log-icon.scale { color: var(--accent); }
.log-text { font-size: 14px; line-height: 1.45; color: var(--text-secondary); }
.log-text b { font-weight: 500; color: var(--text-primary); }
.log-text .quote { font-family: var(--font-voice); font-style: italic; }
.log-text a { color: var(--accent); text-decoration: none; }
/* When the engine decided it. One pass wrote every line of a week's log, so
   they all carry the same stamp rather than a plausible time each. A record
   advanced before the server recorded one has no line here at all. */
.log-when { font-size: 11px; font-family: var(--font-mono); color: var(--text-muted); margin-top: 3px; }
.log-empty { color: var(--text-secondary); font-size: 15px; }

/* The feed groups by event: a logged session or a weigh-in, its rows together,
   then one time beneath them. `.log-body` holds the two text lines; `.feed-when`
   is the group's single stamp, indented to sit under the text, not the icon. */
.feed-group { margin-bottom: 22px; }
.feed-group .log-item { margin-bottom: 9px; }
.log-body { min-width: 0; }
.log-sub { font-size: 13px; line-height: 1.4; color: var(--text-muted); margin-top: 2px; }
/* The group's single stamp: the session name and its time. In the page's own
   sans, not the mono the per-row stamps use, because this line carries a name in
   prose and mono made it read as a different, louder system than the rows above.
   Small and muted, indented to sit under the text column, not the icon. */
.feed-when { font-size: 12px; color: var(--text-muted); padding-left: 28px; margin-top: 6px; }
.log-more {
  margin-top: 4px; display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  padding: 8px 14px; cursor: pointer; font-family: inherit; font-size: 13px;
  color: var(--text-primary);
}
.log-more:hover { background: var(--surface-1); }

/* stat row */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 20px; }
.stat { background: var(--surface-1); border: 0.5px solid var(--border);
        border-radius: var(--radius); padding: 12px 14px; }
.stat-label { font-size: 12px; color: var(--text-secondary); }
.stat-value { font-size: 22px; font-weight: 500; margin-top: 2px; }
.stat-value.good { color: var(--success); }
.stat-value small { font-size: 13px; color: var(--text-muted); font-weight: 400; }

/* the weight tile: the one stat the athlete writes rather than reads */
.wt-head { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.wt-edit {
  background: none; border: 0; padding: 0; cursor: pointer; font-family: inherit;
  font-size: 12px; color: var(--accent); display: inline-flex; align-items: center; gap: 3px;
}
.wt-edit i { font-size: 13px; }
.wt-value { font-size: 20px; font-weight: 500; margin-top: 2px; }
.wt-value small { font-size: 12px; color: var(--text-muted); font-weight: 400; margin-left: 3px; }
/* Only there when the athlete has offered it. Nothing asks for body fat and
   nothing in the program depends on it, so its absence is not a gap. */
.wt-fat { font-size: 12px; color: var(--text-muted); margin-left: 6px; font-weight: 400; }
.wt-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.wt-row input {
  width: 56px; font-family: var(--font-mono); font-size: 18px; font-weight: 500;
  color: var(--text-primary); background: var(--surface-2);
  border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  padding: 2px 6px; outline: none;
}
/* The stepper arrows go. This tile is a quarter of the reading column wide, and
   two of them would take a third of it to offer a lifter the chance to walk
   their bodyweight up a tenth of a kilo at a time. A stone box with the arrows
   in it clipped "14" to "1", which is a different weight. */
.wt-row input { appearance: textfield; -moz-appearance: textfield; }
.wt-row input::-webkit-outer-spin-button,
.wt-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.wt-row.wt-st { gap: 4px; }
.wt-row.wt-st input { width: 42px; font-size: 16px; }
.wt-row.wt-fat-row input { font-size: 14px; font-weight: 400; padding: 3px 6px; }
.wt-row.wt-actions { justify-content: flex-end; margin-top: 8px; }
.wt-unit { font-size: 13px; color: var(--text-muted); }
.wt-row.wt-fat-row .wt-unit { font-size: 12px; }
.wt-save {
  margin-left: auto; background: var(--accent); color: var(--on-accent, #fff); border: 0;
  border-radius: var(--radius); padding: 4px 7px; cursor: pointer;
  font-size: 12px; font-family: inherit; line-height: 1.2;
}
.wt-save:hover { filter: brightness(1.06); }
.wt-err { font-size: 12px; color: var(--warning); margin-top: 6px; }

.banner {
  font-size: 13px; color: var(--warning); background: var(--warning-bg);
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 16px;
}

/* nav */
/* The section nav, in the masthead beside the lockup. It takes the space
   between the lockup and the avatar and centres itself in it. Links never wrap:
   below the breakpoint js/chrome.js lifts them out of the bar and into the
   account menu rather than letting them run onto a second line. */
.topnav {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 4px;
}
.topnav a {
  font-size: 14px; color: var(--text-secondary); text-decoration: none;
  padding: 6px 10px; border-radius: var(--radius); white-space: nowrap;
}
.topnav a:hover, .topnav a:focus-visible { background: var(--surface-1); color: var(--text-primary); }
.topnav a.active { color: var(--text-primary); background: var(--surface-1); font-weight: 500; }

/* athlete controls */
.controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 16px;
  background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 14px 18px; margin-bottom: 20px;
}
.controls label { font-size: 13px; color: var(--text-secondary); display: flex; align-items: center; gap: 8px; }
/* min-width:0 is load-bearing. A flex item defaults to min-width:auto, which
   for the History exercise <select> is the width of its longest option, a full
   "Seated Dumbbell Shoulder Press (1A)" line. Without this the field refuses to
   shrink below that and pushes the whole page wider than a phone. With it, the
   select shrinks and the browser truncates the option instead. */
.controls .field { display: flex; align-items: center; gap: 8px; flex: 1 1 220px; min-width: 0; }
.controls .field select { min-width: 0; max-width: 100%; }
.controls input[type="range"] { flex: 1; accent-color: var(--accent); }
.controls .bw-val { font-size: 15px; font-weight: 500; min-width: 62px; }
.seg { display: inline-flex; border: 0.5px solid var(--border-strong); border-radius: var(--radius); overflow: hidden; }
.seg button {
  background: none; border: 0; padding: 6px 14px; font-size: 13px; cursor: pointer;
  color: var(--text-secondary); font-family: inherit;
}
.seg button.on { background: var(--accent); color: #fff; }

/* where you stand: the basis card, then one tier bar per lift */

/* An inset card sits on --surface-1 rather than the raised --surface-2, so it
   reads as part of the page rather than as another thing stacked on it. Depth
   here is surface tint and a hairline, never a shadow. */
.card.inset { background: var(--surface-1); }

/* The standards ladder before sex has been answered. Calm, not an error: the
   card takes the accent hairline, the bars are dimmed, and each one carries a
   "general" badge, so a screenshot of the bars alone says what they are without
   the card being in frame. */
.stand-basis { margin-bottom: 28px; }
.stand-basis.partial { border-color: var(--accent); }
.stand-basis.personal { border-color: var(--success); }
.stand-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px 16px; flex-wrap: wrap;
}
.stand-bw { display: flex; align-items: baseline; gap: 12px; }
.stand-bw-label { font-size: 14px; color: var(--text-secondary); }
.stand-bw-val { font-size: 22px; font-weight: 500; color: var(--text-primary); }
.stand-say { margin-top: 14px; padding-top: 14px; border-top: 0.5px solid var(--border); }
.stand-say-title { font-size: 14px; font-weight: 500; color: var(--text-primary); }
.stand-say p { font-size: 13px; color: var(--text-secondary); line-height: 1.55; margin: 4px 0 0; }
.stand-skip { display: block; font-size: 12px; color: var(--text-muted); margin-top: 6px; }

.tier-lift.provisional { opacity: 0.78; }
.tier-general {
  font-size: 10px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted); background: var(--surface-1); padding: 2px 8px;
  border-radius: var(--radius); margin-left: 8px; white-space: nowrap;
}

.tier-lift { margin-bottom: 26px; }
.tier-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.tier-name { font-size: 18px; font-weight: 500; color: var(--text-primary); min-width: 0; }
.tier-name small { color: var(--text-muted); font-size: 14px; font-weight: 400; margin-left: 8px; }
.tier-pill {
  flex: none; font-size: 13px; font-weight: 500;
  padding: 5px 12px; border-radius: var(--radius-pill);
}
.tier-track {
  position: relative; height: 44px; border-radius: var(--radius);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; overflow: hidden;
}
/* Inset top and bottom rather than overhanging, because the track clips its own
   corners and an overhanging marker loses its ends to that clip. Its colour is
   set per lift by js/progress.js, from the tier it lands in. */
.tier-marker {
  position: absolute; top: 5px; bottom: 5px; width: 3px; border-radius: 2px;
  transform: translateX(-50%);
}
.tier-scale { display: grid; grid-template-columns: repeat(6, 1fr); margin-top: 6px; }
.tier-scale span { font-size: 12px; color: var(--text-muted); text-align: center; }
.tier-scale span.on { color: var(--text-primary); font-weight: 500; }
.tier-foot { font-size: 14px; color: var(--text-secondary); margin-top: 10px; line-height: 1.5; }
.tier-foot b { color: var(--text-primary); font-weight: 500; }

/* Six labels across a phone is 56 pixels each and "Intermediate" does not fit
   at 12px. It is a legible 10 here and the bar it labels is unchanged.

   The working set drops to its own line for the same reason. Kept beside the
   name it wraps mid-phrase, and the tier pill then sits against a half-empty
   first line with the rest of the set orphaned under it. */
@media (max-width: 600px) {
  .tier-scale span { font-size: 10px; letter-spacing: -0.01em; }
  .tier-head { align-items: flex-start; }
  .tier-name { font-size: 16px; }
  .tier-name small { display: block; font-size: 13px; margin: 2px 0 0; }
  .tier-general { margin-left: 0; margin-top: 4px; display: inline-block; }
}

.disclaimer { font-size: 12px; color: var(--text-muted); margin-top: 8px; line-height: 1.5; }
/* The Epley footnote closes the page, so it gets the hairline that says so. */
.stand-foot {
  font-size: 13px; line-height: 1.55; margin-top: 8px;
  padding-top: 18px; border-top: 0.5px solid var(--border);
}

/* ---- Fuel ---------------------------------------------------------------- */
/* The target itself is not styled here at all beyond the big number: it is the
   shared .card / .stats / .stat tiles, because it is the same reading of the
   same athlete the other pages give. */

/* the body the target is derived from. An inset card, because these are
   read only here: they are answered on the profile page. */
.fuel-body {
  background: var(--surface-1); display: flex; flex-wrap: wrap;
  align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 18px; margin-bottom: 20px;
}
.fuel-facts { display: flex; flex-wrap: wrap; gap: 22px; }
.fuel-fact-label { font-size: 12px; color: var(--text-muted); }
.fuel-fact-value { font-size: 16px; font-weight: 500; color: var(--text-primary); }

/* the day's calories, the one number on the page somebody reads from across a
   kitchen. The macros under it are the shared stat tiles. */
.fuel-kcal {
  font-size: 36px; font-weight: 500; letter-spacing: -0.01em;
  line-height: 1.2; margin: 6px 0 16px;
}
.fuel-kcal small { font-size: 18px; font-weight: 400; color: var(--text-muted); }
.stat-note { color: var(--text-muted); }

/* nutrition: the 'why' behind the target */
.why-list { margin: 8px 0 20px; padding-left: 18px; }
.why-list li { font-size: 13px; color: var(--text-secondary); line-height: 1.55; margin-bottom: 6px; }

/* nutrition: the template day */
.seg-sm button { padding: 4px 11px; font-size: 12px; }
/* the section's own heading row: what it is, and the one action that rebuilds it */
.fuel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.fuel-head .section-label { margin-bottom: 10px; }
.link-action {
  background: none; border: 0; padding: 4px 2px; cursor: pointer; font: inherit;
  font-size: 13px; font-weight: 500; color: var(--accent);
  display: inline-flex; align-items: center; gap: 6px;
}
.link-action i { font-size: 15px; }
.meal-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 14px; }
.chip {
  background: none; border: 0.5px solid var(--border-strong); color: var(--text-secondary);
  border-radius: var(--radius); padding: 5px 11px; font: inherit; font-size: 12px; cursor: pointer;
}
.chip:hover { background: var(--surface-1); }
.chip.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.meals { display: flex; flex-direction: column; gap: 12px; margin-bottom: 14px; }
.meal {
  background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 12px 16px;
}
/* A pinned meal is tinted as well as outlined. The border alone was the only
   thing separating "I eat this every day" from "here is a suggestion", and at a
   hairline it is not enough to catch at a glance down a column of four. */
.meal.is-pinned { border-color: var(--accent); background: var(--surface-1); }
.meal-top { display: flex; align-items: flex-start; gap: 12px; }
.meal-slot { flex: 1; min-width: 0; font-size: 11px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-muted); }
/* the dish holds a row of its own, so its name never competes with the macros
   for the width of a phone */
.dish-row { margin: 2px 0 0; }
.meal-side { flex: none; text-align: right; }
.meal-kcal { font-size: 14px; font-weight: 500; color: var(--text-primary); }
.meal-macros { font-size: 12px; font-family: var(--font-mono); color: var(--text-muted); margin-top: 2px; }
/* the ingredients are the detail under the headline, so they fold away */
.ing-toggle {
  display: flex; align-items: center; gap: 5px; background: none; border: 0;
  padding: 10px 0 0; cursor: pointer; font: inherit; font-size: 13px; color: var(--accent);
}
.meal-items { margin-top: 6px; }

/* the dish a meal holds: the headline of the card */
.dish-title { font-size: 17px; font-weight: 500; }
.dish-swap { position: relative; display: inline-flex; max-width: 100%; }
.dish-swap::after {
  content: "\2304"; position: absolute; right: 4px; top: 50%; transform: translateY(-65%);
  pointer-events: none; color: var(--text-muted); font-size: 13px;
}
.dish-select {
  width: 100%; appearance: none; -webkit-appearance: none; background: transparent;
  border: 0; border-bottom: 1px dashed var(--border-strong); color: var(--text-primary);
  font: inherit; font-size: 17px; font-weight: 500; padding: 1px 22px 3px 0;
  cursor: pointer; border-radius: 0; text-overflow: ellipsis;
}
.dish-select:hover { color: var(--accent); border-bottom-color: var(--accent); }
.dish-select:focus-visible { outline: none; border-bottom-color: var(--accent); }
.dish-select option { color: var(--text-primary); background: var(--surface-2); font-weight: 400; }
.pin {
  flex: none; width: 30px; height: 30px; display: inline-flex;
  align-items: center; justify-content: center; font-size: 15px;
  background: none; border: 0.5px solid var(--border-strong); color: var(--text-muted);
  border-radius: var(--radius); line-height: 1; cursor: pointer;
}
.pin:hover { background: var(--surface-1); color: var(--text-secondary); }
.pin.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.food-row { display: flex; align-items: baseline; gap: 10px; padding: 6px 0; border-top: 0.5px solid var(--border); font-size: 13px; }
.food-row:first-of-type { border-top: 0; }
.food-name { flex: 1; }
.food-qty { color: var(--text-secondary); min-width: 60px; text-align: right; white-space: nowrap; }
.food-macros { color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; min-width: 108px; text-align: right; }
/* your own dishes + the builder */
.own-row { display: flex; align-items: center; gap: 8px; padding: 7px 0; border-top: 0.5px solid var(--border); font-size: 13px; }
.own-row:first-of-type { border-top: 0; }
.own-name { flex: 1; }
.own-meals { color: var(--text-muted); font-size: 11px; }
.builder {
  background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 14px 16px; margin-top: 10px;
}
.bld-name {
  width: 100%; background: transparent; border: 0; border-bottom: 1px dashed var(--border-strong);
  color: var(--text-primary); font: inherit; font-size: 15px; font-weight: 500; padding: 2px 0 5px;
}
.bld-name:focus-visible { outline: none; border-bottom-color: var(--accent); }
.bld-label { font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); margin: 14px 0 6px; }
.bld-hint { text-transform: none; letter-spacing: 0; font-size: 11px; }
.bld-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.bld-row { display: flex; align-items: center; gap: 6px; padding: 4px 0; flex-wrap: wrap; }
.bld-food {
  flex: 1 1 150px; background: var(--surface-1); border: 0.5px solid var(--border-strong);
  color: var(--text-primary); font: inherit; font-size: 13px; border-radius: var(--radius); padding: 5px 7px;
}
.bld-amt {
  width: 68px; background: var(--surface-1); border: 0.5px solid var(--border-strong);
  color: var(--text-primary); font: inherit; font-size: 13px; border-radius: var(--radius); padding: 5px 7px;
}
.bld-unit { font-size: 12px; color: var(--text-muted); min-width: 44px; }
.bld-remove {
  background: none; border: 0; color: var(--text-muted); font-size: 17px;
  line-height: 1; cursor: pointer; padding: 0 4px;
}
.bld-remove:hover { color: var(--warning); }
.bld-actions { display: flex; align-items: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.bld-error { font-size: 12px; color: var(--warning); }

.day-total {
  font-size: 13px; color: var(--text-secondary); background: var(--surface-1);
  border-radius: var(--radius); padding: 11px 15px; margin-bottom: 18px; line-height: 1.5;
}
.day-total b { color: var(--text-primary); font-weight: 500; }
/* what the day was solved against, so a plate that lands 8 g light is legible
   as close rather than as wrong */
.day-total-aim { color: var(--text-muted); }
/* ---- end Fuel ------------------------------------------------------------ */

/* login */
.login-card {
  max-width: 380px; margin: 8vh auto 0; background: var(--surface-2);
  border: 0.5px solid var(--border); border-radius: var(--radius-card); padding: 28px 26px;
}
.login-card h1 { font-size: 20px; font-weight: 500; margin: 0 0 4px; }
.login-card .sub { font-size: 14px; color: var(--text-secondary); margin: 0 0 20px; }
.login-card label { font-size: 13px; color: var(--text-secondary); display: block; margin-bottom: 6px; }
.login-card input { width: 100%; margin-bottom: 14px; }
.login-card .btn-primary { width: 100%; }
.login-card .msg { font-size: 13px; margin-top: 12px; min-height: 18px; }
.login-card .msg.err { color: var(--warning); }
.login-card .link { background: none; border: 0; color: var(--accent); font-size: 13px;
  cursor: pointer; padding: 0; margin-top: 14px; font-family: inherit; }
/* The acceptance line. It sits inside the card, under the button that creates
   the account, because terms nobody was shown at the moment they agreed are
   worth a good deal less than terms they were. */
.login-card .accept { font-size: 12px; color: var(--text-muted); line-height: 1.5; margin: 14px 0 0; }
.login-card .accept a { color: var(--text-secondary); text-decoration: underline; }
.login-card .accept a:hover { color: var(--accent); }

.code-input { letter-spacing: 0.4em; text-align: center; font-size: 20px; font-family: var(--font-mono); }

/* account chip in masthead */
.acct { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.acct-email { color: var(--text-secondary); max-width: 180px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.acct-logout { background: none; border: 0.5px solid var(--border-strong); color: var(--text-secondary);
  border-radius: var(--radius); padding: 5px 9px; cursor: pointer; font-size: 13px; font-family: inherit; }
.acct-logout:hover { background: var(--surface-1); }

/* form controls (shared) */
select, input[type="number"], input[type="text"], input[type="email"], textarea {
  font-family: inherit; font-size: 15px; color: var(--text-primary);
  background: var(--surface-2); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); padding: 8px 10px;
}
select:focus, input:focus, textarea:focus {
  outline: none; box-shadow: 0 0 0 2px var(--accent-bg); border-color: var(--accent);
}
.btn-primary {
  font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;
  background: var(--accent); color: #fff; border: 0; border-radius: var(--radius);
  padding: 10px 18px;
}
.btn-primary:hover { filter: brightness(1.06); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }

/* ---- Log + The week ------------------------------------------------------
   Everything from here to the "devices" heading belongs to `log.html` and
   `review.html` and to nothing else, so a rebase that lands another page's
   block beside it keeps both. */

/* session logger */

/* An inset card, not a raised one. The day picker is the frame the session is
   entered inside, and the exercise cards are the session, so the picker sits
   below them in the surface stack rather than beside them. */
.controls.inset { background: var(--surface-1); }

/* Cards, spaced by the container. A margin on the card leaves the last one
   pushing an extra gap into the note copy below it. */
#exercises { display: flex; flex-direction: column; gap: 16px; }
.log-ex { background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 16px 18px; }
.log-ex.skipped { opacity: 0.45; }
.log-ex-head { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px; }
.log-ex-name { font-size: 17px; font-weight: 500; }
/* The prescription breaks as one piece or not at all. On a phone the name and
   the scheme share a column with the Done control, and a plain wrap splits
   "4 × 8 @ 16 kg" after the "@", leaving a lone "kg" on the next line. */
.log-ex-target { font-size: 13px; color: var(--text-muted); margin-left: 8px;
  white-space: nowrap; display: inline-block; }
.log-row { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }

/* One box per set, and they wrap. Five sets of a 58px box is wider than a
   phone, and a row that scrolls sideways loses the last set off the edge where
   nobody looks for it. */
.set-inputs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.set-inputs label { font-size: 13px; color: var(--text-secondary); margin-right: 2px; }
.set-inputs input {
  width: 58px; text-align: center; padding: 7px 4px;
  font-family: var(--font-mono); font-size: 16px;
}
/* The load lifted, one box for the exercise, with its unit beside it. */
.load-input { display: flex; align-items: center; gap: 8px; }
.load-input label { font-size: 13px; color: var(--text-secondary); margin-right: 2px; }
.load-input input {
  width: 72px; text-align: center; padding: 7px 4px;
  font-family: var(--font-mono); font-size: 16px;
}
.load-unit { font-size: 13px; color: var(--text-secondary); }

.rpe-wrap { display: flex; align-items: center; gap: 10px; }
.rpe-label { font-size: 13px; color: var(--text-secondary); }
.rpe-pick { display: inline-flex; border: 0.5px solid var(--border-strong); border-radius: var(--radius); overflow: hidden; }
.rpe-pick button { background: none; border: 0; padding: 6px 11px; font-size: 13px; cursor: pointer;
  color: var(--text-secondary); font-family: inherit; transition: background 0.12s ease, color 0.12s ease; }
.rpe-pick button:hover { background: var(--surface-1); }
.rpe-pick button.on { background: var(--accent); color: #fff; }
.done-toggle { font-size: 14px; color: var(--text-secondary); display: flex; align-items: center;
  gap: 7px; flex: none; cursor: pointer; }
.done-toggle input { width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; }
.note-box { width: 100%; min-height: 72px; resize: vertical; }
.note-help { font-size: 13px; color: var(--text-muted); line-height: 1.55; margin: 20px 0 10px; }
#submit-wrap { margin-top: 18px; }

.log-success { background: var(--success-bg); color: var(--success);
  border-radius: var(--radius-card); padding: 16px 18px; }
.log-success-head { font-size: 15px; font-weight: 500; }
.log-success-head i { vertical-align: -1px; margin-right: 5px; }
.log-success-body { font-size: 14px; line-height: 1.5; margin-top: 4px; }
.log-success-links { font-size: 14px; margin-top: 10px; }
.log-success a { color: var(--accent); text-decoration: none; }

/* The personal record celebration (FM-30). A banner in the tier's tint above
   the ordinary "session logged" note, never a modal: a modal would make the
   athlete dismiss the moment, which is the one thing the watch spec is careful
   never to require, and there is no reason the web should ask for more. */
.log-record {
  border-radius: var(--radius-card); padding: 16px 18px; margin-bottom: 12px;
}
.log-record-head { font-size: 17px; font-weight: 600; }
.log-record-head i { vertical-align: -2px; margin-right: 6px; }
.log-record-body { font-size: 15px; line-height: 1.5; margin-top: 2px; }
.log-record-note { font-size: 14px; line-height: 1.5; margin-top: 8px; opacity: 0.85; }

/* The two tiers, shared by the banner, the History line and the chart mark.
   Ordinary is the semantic green, which already means "the target was met"
   everywhere else here. The stall break takes oxide, one step above it. */
.record-ordinary { background: var(--success-bg); color: var(--success); }
.record-stall { background: var(--record-bg); color: var(--record); }
/* An SVG mark has no fill to tint, so the tier arrives as a stroke instead. */
circle.record-ordinary { background: none; stroke: var(--success); }
circle.record-stall { background: none; stroke: var(--record); }

/* the week */
.rev-summary { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 16px; background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 18px 20px; margin-bottom: 16px; }
.rev-summary .headline { font-size: 18px; font-weight: 500; }
.rev-summary .sub { font-size: 13px; color: var(--text-muted); margin-top: 3px; }
/* The settled state. The button keeps its shape and loses its colour, so the
   card reads as the same card with the decision taken rather than as a
   different card that appeared. */
.rev-summary.done { border-color: var(--success); }
.rev-summary.done .btn-primary:disabled {
  background: var(--success-bg); color: var(--success); opacity: 1; cursor: default;
}
.rev-when { font-size: 11px; font-family: var(--font-mono); color: var(--text-muted); margin-top: 6px; }
.rev-warn { font-size: 13px; line-height: 1.5; color: var(--warning); background: var(--warning-bg);
  border-radius: var(--radius); padding: 11px 14px; margin-bottom: 16px; }
.rev-warn i { vertical-align: -1px; margin-right: 6px; }
.rev-notice { font-size: 13px; line-height: 1.5; color: var(--accent); background: var(--accent-bg);
  border-radius: var(--radius); padding: 11px 14px; margin-bottom: 16px; }
.rev-notice i { vertical-align: -1px; margin-right: 6px; }
.rev-day { background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 16px 18px; margin-bottom: 16px; }
.rev-day.held { border-color: var(--border-strong); }
.rev-day-head { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 8px; }
.rev-day-name { font-size: 17px; font-weight: 500; }
.rev-day-why { font-size: 13px; color: var(--text-muted); line-height: 1.5; margin-bottom: 6px; }
.rev-badge { font-size: 12px; padding: 4px 11px; border-radius: var(--radius); flex: none;
  white-space: nowrap; }
.rev-badge i { vertical-align: -1px; margin-right: 3px; }
.rev-badge.held { background: var(--warning-bg); color: var(--warning); }
.rev-badge.logged { background: var(--success-bg); color: var(--success); }
.rev-badge.unlogged { background: var(--surface-1); color: var(--text-muted); }
.rev-badge.accent { background: var(--accent-bg); color: var(--accent); }
.rev-ex { display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center;
  padding: 10px 0; border-top: 0.5px solid var(--border); }
.rev-ex-main { font-size: 15px; }
.rev-ex-name { color: var(--text-primary); }
/* Nowrap for the same reason the log page's target line has it: a before and
   after pair split across a line break reads as two different numbers. */
.rev-ex-diff { color: var(--text-muted); font-size: 12px; margin-left: 8px;
  white-space: nowrap; display: inline-block; }
.rev-ex-note { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.rev-foot { font-size: 13px; color: var(--text-muted); line-height: 1.55;
  border-top: 0.5px solid var(--border); padding-top: 18px; margin: 24px 0 0; }
.rev-foot a { color: var(--accent); text-decoration: none; }

/* the verdict badge: what the engine would do to this lift next week */
.chg { font-size: 12px; padding: 4px 11px; border-radius: var(--radius); white-space: nowrap; }
.chg i { vertical-align: -1px; margin-right: 3px; font-size: 12px; }
.chg.increase { background: var(--success-bg); color: var(--success); }
.chg.reps { background: var(--accent-bg); color: var(--accent); }
.chg.deload { background: var(--warning-bg); color: var(--warning); }
.chg.hold { background: var(--surface-1); color: var(--text-muted); }

/* ======================================================================== */
/* The week: the reckoning cards and the plan builder on one page.          */
/* Values track the design handoff (12/13/15/18/24 sizes, weight <= 500,    */
/* mono for numbers, 0.5px hairlines, insets on --surface-1).               */
/* ======================================================================== */

/* An outlined button, the quiet partner to .btn-primary. Add-day, Cancel and
   the suggest control all use it. */
.btn-secondary { font-family: inherit; font-size: 14px; font-weight: 500;
  cursor: pointer; background: var(--surface-2); color: var(--text-secondary);
  border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  padding: 9px 14px; display: inline-flex; align-items: center; gap: 6px;
  transition: background .12s ease, color .12s ease; }
.btn-secondary:hover { background: var(--surface-1); color: var(--text-primary); }
.btn-secondary:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-secondary i { font-size: 15px; }

.wk-head { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; margin-bottom: 20px; }
.wk-eyebrow { font-size: 12px; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--text-muted); }
.wk-week { font-size: 24px; font-weight: 500; letter-spacing: -0.01em; margin-top: 4px; }
.wk-head-r { font-size: 13px; color: var(--text-secondary); }

.wk-advance { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 16px; background: var(--surface-2);
  border: 0.5px solid var(--border); border-radius: var(--radius-card);
  padding: 16px 18px; margin-bottom: 16px; }
.wk-advance-title { font-size: 18px; font-weight: 500; }
.wk-advance-sub { font-size: 13px; color: var(--text-muted); margin-top: 3px; }

/* FM-42: the week's read. One quiet card: what the week trains, an honest
   wins-and-losses line, and a single reckoned fix. */
.wk-read { display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 14px 18px; margin-bottom: 16px; }
.wk-read-eyebrow { font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-muted); }
.wk-read-trains { font-size: 15px; font-weight: 500; color: var(--text-primary); }
.wk-read-line { font-size: 13px; line-height: 1.5; color: var(--text-secondary); }
.wk-read-fix { margin-top: 6px; padding: 6px 14px; font-size: 13px; }
.wk-read-fix i { vertical-align: -1px; margin-right: 2px; }

/* "When life gets in the way": the week-level controls card. */
.wk-life { background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); margin-bottom: 16px; }
.wk-life-eyebrow { padding: 14px 18px 10px; font-size: 12px; font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); }
.wk-life-row { border-top: 0.5px solid var(--border); }
.wk-life-head { width: 100%; display: grid; grid-template-columns: 22px 1fr auto 16px;
  gap: 12px; align-items: center; text-align: left; padding: 13px 18px; border: 0;
  background: none; cursor: pointer; transition: background 0.12s ease; font-family: inherit; }
.wk-life-head:hover { background: var(--surface-1); }
.wk-life-icon { font-size: 17px; color: var(--text-muted); }
.wk-life-icon.on { color: var(--accent); }
.wk-life-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wk-life-title { font-size: 15px; font-weight: 500; color: var(--text-primary); }
.wk-life-sub { font-size: 13px; color: var(--text-muted); }
.wk-life-chev { font-size: 15px; color: var(--text-muted); }
.wk-life-panel { padding: 0 18px 16px; }
.wk-life-panel .wk-inset { display: flex; flex-direction: column; gap: 12px; padding: 14px; }
.wk-life-explain { font-size: 13px; line-height: 1.5; color: var(--text-secondary);
  max-width: 70ch; }
.wk-life-dur { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.wk-life-dur-label { font-size: 13px; color: var(--text-muted); }
.wk-life-action { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.wk-life-foot { font-size: 13px; color: var(--text-muted); }
.wk-hotel-dates { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.wk-hotel-date { padding: 7px 10px; font-family: var(--font-mono); font-size: 13px;
  color: var(--text-primary); background: var(--surface-2);
  border: 0.5px solid var(--border-strong); border-radius: var(--radius); }
.wk-hotel-kit { display: flex; flex-direction: column; gap: 8px;
  border-top: 0.5px solid var(--border); padding-top: 12px; }
.wk-hotel-kit-sub { font-size: 13px; color: var(--text-secondary); }
.wk-hotel-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chg.hotel { background: var(--accent-bg); color: var(--accent); }

.wk-days { display: flex; flex-direction: column; gap: 14px; }
.wk-card { margin-bottom: 0; padding: 16px 18px 6px; }
.wk-card-tools { display: flex; align-items: center; gap: 6px; flex: none; }
.wk-icon-btn { width: 32px; height: 32px; display: inline-flex; align-items: center;
  justify-content: center; border: 0; background: none; border-radius: var(--radius);
  color: var(--text-secondary); cursor: pointer; transition: background .12s ease, color .12s ease; }
.wk-icon-btn i { font-size: 16px; }
.wk-icon-btn:hover { background: var(--surface-1); color: var(--text-primary); }
.wk-icon-btn.wk-icon-danger:hover { color: var(--warning); }

.wk-lift { display: grid; grid-template-columns: 1fr auto; gap: 12px;
  align-items: start; padding: 11px 0; border-top: 0.5px solid var(--border); }
.wk-lift-main { min-width: 0; }
.wk-lift-top { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }
.wk-lift-name { font-size: 15px; font-weight: 500; }
.wk-lift-detail { font-size: 13px; color: var(--text-muted); }
.wk-lift-note { font-size: 13px; color: var(--text-muted); margin-top: 3px; line-height: 1.45; }
.wk-rest-name { color: var(--text-secondary); }
.wk-rest-line { border-top: 0.5px solid var(--border); padding: 14px 0;
  font-family: var(--font-voice); font-style: italic; font-size: 15px;
  color: var(--text-secondary); }

.wk-add-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* ---- the day editor ---- */
.wk-editor { padding: 20px; display: flex; flex-direction: column; gap: 18px; }
.wk-ed-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.wk-ed-identity { display: grid; grid-template-columns: auto 1fr; gap: 10px; align-items: center; }
.wk-ed-dow, .wk-ed-name { font-family: inherit; font-size: 15px; color: var(--text-primary);
  background: var(--surface-1); border: 0.5px solid var(--border); border-radius: var(--radius);
  padding: 9px 12px; box-sizing: border-box; }
.wk-ed-name { width: 100%; }
.wk-ed-dow:focus, .wk-ed-name:focus, .wk-num:focus, .wk-search-input:focus,
.wk-suggest-name:focus, .wk-suggest-note:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-bg); }

.wk-inset { background: var(--surface-1); border-radius: var(--radius); padding: 16px; }
.wk-inset-line { font-size: 15px; color: var(--text-primary); }
.wk-voice { font-family: var(--font-voice); font-style: italic; font-size: 15px;
  color: var(--text-secondary); line-height: 1.45; }
.wk-rest-inset { display: flex; flex-direction: column; gap: 6px; padding: 18px 16px; }

.wk-empty { display: flex; flex-direction: column; gap: 12px; }
.wk-empty-sub { font-size: 13px; color: var(--text-secondary); max-width: 70ch; margin-top: 4px; }
.wk-starters { display: flex; flex-wrap: wrap; gap: 6px; }
.wk-starter { border-style: dashed; }

.wk-ed-training { display: flex; flex-direction: column; gap: 10px; }
.wk-lift-grid { display: grid; grid-template-columns: 1fr 52px 52px 64px 60px 56px 32px;
  gap: 8px; align-items: center; }
.wk-lift-head { padding: 0 0 2px 2px; }
.wk-lh { font-size: 12px; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--text-muted); text-align: center; }
.wk-lh-rpe { display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  padding: 0; border: 0; background: none; cursor: pointer; font-size: 12px;
  letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-muted); font-family: inherit; }
.wk-lh-rpe.on { color: var(--accent); }
.wk-lh-rpe i, .wk-lh-rpe i { font-size: 13px; }
.wk-lift-id { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.wk-lift-rowname { font-size: 15px; font-weight: 500; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.wk-lift-group { font-size: 12px; color: var(--text-muted); }
.wk-num { width: 100%; box-sizing: border-box; text-align: center;
  font-family: var(--font-mono); font-size: 14px; padding: 9px 4px;
  color: var(--text-primary); background: var(--surface-1);
  border: 0.5px solid var(--border); border-radius: var(--radius); }

.wk-caption { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 24px; font-size: 13px; color: var(--text-muted); }
.wk-caption-l { max-width: 70ch; line-height: 1.45; }
.wk-caption-link { flex: none; border: 0; background: none; padding: 0;
  font-size: 13px; color: var(--accent); cursor: pointer; white-space: nowrap; font-family: inherit; }

/* ---- RPE panel ---- */
.wk-rpe { display: flex; flex-direction: column; gap: 14px; }
.wk-rpe-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.wk-rpe-title { font-size: 18px; font-weight: 500; letter-spacing: -0.01em; }
.wk-rpe-sub { font-size: 13px; color: var(--text-secondary); max-width: 70ch;
  margin-top: 4px; line-height: 1.45; }
.wk-rpe-rows { display: flex; flex-direction: column; gap: 2px; }
.wk-rpe-row { display: grid; grid-template-columns: 30px 96px 1fr auto; gap: 12px;
  align-items: center; padding: 9px 10px; border-radius: var(--radius);
  border: 0.5px solid transparent; }
.wk-rpe-row.target { background: var(--accent-bg); border-color: var(--accent); }
.wk-rpe-val { font-family: var(--font-mono); font-size: 15px; color: var(--text-secondary); }
.wk-rpe-row.target .wk-rpe-val { color: var(--accent); }
.wk-rpe-meter { display: flex; gap: 3px; }
.wk-rpe-block { width: 12px; height: 6px; border-radius: 2px; background: var(--border-strong); }
.wk-rpe-block.on { background: var(--text-muted); }
.wk-rpe-block.on-accent { background: var(--accent); }
.wk-rpe-feel { font-size: 13px; color: var(--text-secondary); line-height: 1.4; }
.wk-rpe-row.target .wk-rpe-feel { color: var(--text-primary); }
.wk-rpe-tag { font-size: 12px; letter-spacing: 0.03em; text-transform: uppercase; color: var(--accent); }
.wk-rpe-foot { display: flex; gap: 10px; align-items: flex-start;
  border-top: 0.5px solid var(--border); padding-top: 12px; }
.wk-rpe-foot i { font-size: 15px; color: var(--text-muted); flex: none; margin-top: 2px; }

/* ---- exercise picker ---- */
.wk-search { position: relative; display: flex; gap: 8px; align-items: center; margin-top: 2px; }
.wk-search-field { position: relative; flex: 1; }
.wk-search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  font-size: 15px; color: var(--text-muted); pointer-events: none; }
/* The descendant selector is load-bearing: a bare .wk-search-input (0,1,0) loses
   to the base input[type="text"] rule (0,1,1), which would force padding back to
   10px and slide the placeholder under the search glyph. Two classes (0,2,0) win,
   so the left padding clears the icon. */
.wk-search-field .wk-search-input { width: 100%; box-sizing: border-box;
  padding: 11px 12px 11px 38px;
  font-size: 15px; color: var(--text-primary); background: var(--surface-1);
  border: 0.5px solid var(--border); border-radius: var(--radius); font-family: inherit; }
.wk-matches { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 5;
  background: var(--surface-2); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); overflow: hidden; }
.wk-match { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px; cursor: pointer; transition: background .12s ease; }
.wk-match + .wk-match { border-top: 0.5px solid var(--border); }
.wk-match:hover, .wk-match.on { background: var(--surface-1); }
.wk-match-name { font-size: 15px; }
.wk-match-group { font-size: 12px; letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-muted); }
.wk-browse-btn { padding: 11px 14px; font-size: 13px; font-weight: 500; border-radius: var(--radius);
  border: 0.5px solid var(--border-strong); background: var(--surface-2); color: var(--text-secondary);
  cursor: pointer; white-space: nowrap; font-family: inherit; transition: background .12s ease; }
.wk-browse-btn:hover, .wk-browse-btn.on { background: var(--surface-1); }
.wk-browse { display: flex; flex-direction: column; gap: 10px; }
.wk-browse-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.wk-browse-list { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.wk-browse-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border-radius: var(--radius); cursor: pointer; font-size: 15px; color: var(--text-primary);
  transition: background .12s ease; }
.wk-browse-item:hover { background: var(--surface-2); }
.wk-browse-item i { font-size: 14px; color: var(--text-muted); }

/* ---- suggest an exercise (FM-15) ---- */
.wk-suggest-row { display: flex; align-items: center; gap: 8px; padding-top: 2px;
  font-size: 13px; color: var(--text-muted); }
.wk-suggest { display: flex; flex-direction: column; gap: 10px; }
.wk-suggest-name, .wk-suggest-note { width: 100%; box-sizing: border-box; padding: 10px 12px;
  font-size: 15px; color: var(--text-primary); background: var(--surface-2);
  border: 0.5px solid var(--border); border-radius: var(--radius); font-family: inherit; }
.wk-suggest-note { resize: vertical; }
.wk-suggest-garmin { display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  font-size: 13px; color: var(--text-secondary); }
.wk-suggest-chips { display: flex; gap: 6px; }
.wk-suggest-send { margin-left: auto; padding: 6px 14px; font-size: 13px; }
.wk-suggest-msg { font-size: 13px; color: var(--text-secondary); display: flex; align-items: center; gap: 6px; }
.wk-suggest-msg.ok { color: var(--success); }

/* ---- editor footer + delete confirm ---- */
.wk-ed-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  border-top: 0.5px solid var(--border); padding-top: 14px; }
.wk-ed-foot-note { font-size: 13px; color: var(--text-muted); }
.wk-ed-foot-btns { display: flex; gap: 8px; }
.wk-ed-err { font-size: 13px; color: var(--warning); }
.wk-confirm { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: 8px; margin-top: 10px; padding-top: 12px; border-top: 0.5px solid var(--border); }
.wk-confirm-text { font-size: 13px; color: var(--text-secondary); margin-right: auto; }
.wk-confirm .btn-secondary, .wk-confirm .btn-danger { padding: 6px 14px; font-size: 13px; }

/* FM-40 plateau swap: the row button and the inline confirm bar the card draws
   under the day while a replacement is reckoned and confirmed. */
.wk-lift-side { display: flex; align-items: center; gap: 6px; }
.wk-swap-btn.wk-icon-btn { color: var(--accent); }
.wk-swap-btn.wk-icon-btn:hover { background: var(--accent-bg); color: var(--accent); }
.wk-swap { display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 10px; padding: 12px; border-radius: var(--radius);
  background: var(--accent-bg); }
.wk-swap i { color: var(--accent); font-size: 16px; }
.wk-swap-text { font-size: 13px; color: var(--text-secondary); }
.wk-swap-main { min-width: 0; margin-right: auto; }
.wk-swap-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; }
.wk-swap-line i { align-self: center; }
.wk-swap-from { font-size: 14px; color: var(--text-muted); text-decoration: line-through; }
.wk-swap-to { font-size: 15px; font-weight: 600; color: var(--text-primary); }
.wk-swap-group { font-size: 12px; color: var(--text-muted); }
.wk-swap-why { font-size: 13px; color: var(--text-secondary); margin-top: 3px; line-height: 1.45; }
.wk-swap-btns { display: flex; gap: 8px; }
.wk-swap-btns .btn-secondary, .wk-swap-btns .btn-primary { padding: 6px 14px; font-size: 13px; }
.wk-swap-spin { animation: wk-spin 0.9s linear infinite; }
@keyframes wk-spin { to { transform: rotate(360deg); } }

@media (max-width: 560px) {
  .wk-lift-grid { grid-template-columns: 1fr 44px 44px 56px 52px 48px 28px; gap: 6px; }
  .wk-caption { flex-direction: column; gap: 8px; }
  .wk-browse-list { grid-template-columns: 1fr; }
}

/* devices: pair a watch + manage linked ones */
.pair-help { font-size: 14px; color: var(--text-secondary); margin: 0 0 14px; }
.pair-help b { font-weight: 500; color: var(--text-primary); }
/* Every other prose link on the site takes --accent, and this one was missed,
   so it fell through to the browser's own blue and then to the browser's own
   visited purple. On the light theme that is merely wrong. On the dark theme
   the visited purple sits on a near black card and cannot be read at all, which
   is the state most readers see, because anyone who has been to the store page
   once has visited it. The underline stays: --accent is a blue on the dark
   theme, and colour alone is not what tells you a word in a sentence is a link. */
.pair-help a { color: var(--accent); }
.pair-form { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* The store badge, in the row with the code box and Link watch. Sized off that
   button rather than off the page: 140px of width puts the badge at 47.8px
   against the button's 38px, close enough to belong to the row and not so small
   that Garmin's own small print inside the artwork stops being legible.
   Width only, so the badge keeps its 278:95 proportion (see STORE.md: the file
   is Garmin's and scaling is the only thing their guidelines allow). */
.pair-ciq { display: inline-block; line-height: 0; border-radius: 6px; }
.pair-ciq img { display: block; width: 140px; height: auto; }
.pair-ciq:hover { filter: brightness(1.18); }
.pair-input { width: 190px; text-transform: uppercase; padding: 9px 10px; }
.pair-input::placeholder { color: var(--text-muted); letter-spacing: 0.2em; }
.pair-msg { font-size: 14px; line-height: 1.5; margin-top: 12px; min-height: 20px; }
.pair-msg.ok { color: var(--success); }
.pair-msg.err { color: var(--warning); }
.pair-msg i { vertical-align: -1px; margin-right: 2px; }

.dev-loading, .dev-empty { font-size: 15px; color: var(--text-secondary); }
.dev { background: var(--surface-2); border: 0.5px solid var(--border);
  border-radius: var(--radius-card); padding: 14px 16px; margin-bottom: 12px;
  display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center; }
.dev.confirming { border-color: var(--border-strong); }
.dev-label { font-size: 15px; font-weight: 500; display: flex; align-items: center; gap: 8px; }
.dev-label i { color: var(--text-muted); font-size: 17px; }
.dev-meta { font-size: 13px; color: var(--text-secondary); margin-top: 3px;
  display: flex; flex-wrap: wrap; gap: 4px 10px; }
/* separator trails the first item, so a wrap doesn't orphan a "·" at the start
   of the next line */
.dev-meta span:not(:last-child)::after { content: " ·"; color: var(--text-muted); }
.dev-unused { color: var(--text-muted); }
.dev-model { font-size: 12px; color: var(--text-muted); font-weight: 400; }
.dev-actions { display: flex; gap: 8px; }
.dev-rename, .dev-remove { background: none; border: 0.5px solid var(--border-strong);
  color: var(--text-secondary); border-radius: var(--radius); padding: 6px 12px;
  cursor: pointer; font-size: 13px; font-family: inherit; }
.dev-rename:hover { background: var(--surface-1); color: var(--text-primary); }
.dev-remove:hover { background: var(--warning-bg); color: var(--warning); border-color: var(--warning); }
.dev-rename-form { grid-column: 1 / -1; margin-top: 10px; padding-top: 12px;
  border-top: 0.5px solid var(--border); }
.dev-rename-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dev-rename-controls input { flex: 1 1 200px; font-size: 14px; padding: 6px 10px; }
.dev-save { font-size: 13px; padding: 6px 14px; }
.dev-rename-hint { font-size: 12px; color: var(--text-muted); margin-top: 8px; }
.dev-confirm { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 10px; margin-top: 10px; padding-top: 12px;
  border-top: 0.5px solid var(--border); }
.dev-confirm-text { font-size: 13px; color: var(--text-secondary); flex: 1 1 240px; }
.dev-confirm-text b { color: var(--text-primary); font-weight: 500; }
.dev-confirm-actions { display: flex; gap: 8px; }
.dev-cancel { background: none; border: 0.5px solid var(--border-strong); color: var(--text-secondary);
  border-radius: var(--radius); padding: 6px 12px; cursor: pointer; font-size: 13px; font-family: inherit; }
.dev-cancel:hover { background: var(--surface-1); }
.btn-danger { font-family: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  background: var(--warning); color: var(--surface-2); border: 0; border-radius: var(--radius);
  padding: 6px 14px; }
.btn-danger:hover { filter: brightness(1.06); }
.btn-danger:disabled { opacity: 0.5; cursor: not-allowed; }

/* account: export links, and the delete-everything confirmation */
.acct-card { margin-bottom: 12px; }
.acct-card p { font-size: 14px; color: var(--text-secondary); line-height: 1.55; margin: 10px 0 0; }
.acct-card ul { font-size: 14px; color: var(--text-secondary); line-height: 1.55;
  margin: 8px 0 0; padding-left: 20px; }
.acct-card li { margin-bottom: 5px; }
.acct-card b { color: var(--text-primary); font-weight: 500; }
.acct-card a { color: var(--accent); text-decoration: none; }
.acct-card a:hover { text-decoration: underline; }
.acct-card .btn-danger { margin-top: 16px; }
.del-form { margin-top: 16px; padding-top: 14px; border-top: 0.5px solid var(--border); }
.del-form label { display: block; font-size: 13px; color: var(--text-secondary); margin-bottom: 8px; }
.del-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.del-controls input { flex: 1 1 220px; font-size: 14px; padding: 8px 10px; }
.del-msg { font-size: 14px; line-height: 1.5; margin-top: 12px; min-height: 20px; }
.del-msg.err { color: var(--warning); }

/* account: the sign-in email change, same shape as the delete confirm above
   but neutral rather than dangerous, so it gets its own class rather than
   reusing .del-*. */
.acct-form { margin-top: 16px; padding-top: 14px; border-top: 0.5px solid var(--border); }
.acct-form label { display: block; font-size: 13px; color: var(--text-secondary); margin-bottom: 8px; }
.acct-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.acct-controls input { flex: 1 1 220px; font-size: 14px; padding: 8px 10px; }
.acct-controls .code-input { flex: 0 1 140px; }
.acct-msg { font-size: 14px; line-height: 1.5; margin-top: 12px; min-height: 20px; }
.acct-msg.ok { color: var(--success); }
.acct-msg.err { color: var(--warning); }

/* the one-file export */
.export-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 16px; }
.export-actions .ti { font-size: 15px; vertical-align: -2px; margin-right: 4px; }
.export-msg { font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
.export-msg.good { color: var(--success); }
.export-msg.err { color: var(--warning); }

/* settings: the line that sends you to Profile for the training numbers */
.settings-lede { margin: 0 0 20px; }
.settings-lede a { color: var(--accent); text-decoration: none; }
.settings-lede a:hover { text-decoration: underline; }

/* history: the bodyweight card, then one card per lane */

/* Card headers on this page all share a shape: what it is on the left, what it
   currently reads and where it is going on the right, both wrapping onto their
   own rows on a phone rather than squeezing. */
.hist-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px 12px; flex-wrap: wrap; margin-bottom: 10px;
}
.hist-keys { display: flex; align-items: center; gap: 8px 16px; flex-wrap: wrap; }
.hist-marks { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.hist-title { font-size: 17px; font-weight: 500; color: var(--text-primary); }
.hist-slot {
  font-size: 12px; font-family: var(--font-mono); color: var(--oxide-lift); margin-left: 8px;
}
.hist-latest { font-size: 13px; color: var(--text-muted); }
.hist-trend { font-size: 13px; font-weight: 500; color: var(--text-secondary); }
.hist-trend.good { color: var(--success); }
.hist-trend i { font-size: 14px; vertical-align: -2px; margin-right: 3px; }

.hist-key { display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--text-secondary); }
.hist-key .key-line { width: 14px; height: 3px; border-radius: 2px; background: var(--accent); }
.hist-key .key-dash { width: 14px; height: 0; border-top: 2px dashed var(--oxide-lift); }

/* the block strip: one pip per week of the block, the last one the planned
   lighter week. Weeks already behind are filled, the current one is ringed, and
   the lighter week is outlined rather than coloured, because it is a week the
   athlete has not reached yet on every view but one. */
/* Stacked under the bodyweight card, so it needs the gap the session cards get
   between them. Flush, the two read as one card with a seam through it. */
#block { margin-top: 10px; }
.block-dots { display: flex; align-items: center; gap: 6px; }
.block-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--surface-1); border: 1px solid var(--border);
}
.block-dot.done { background: var(--accent); border-color: var(--accent); }
.block-dot.now { background: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg); }
.block-dot.ease { background: transparent; border-color: var(--oxide-lift); }
.block-dot.ease.done, .block-dot.ease.now {
  background: var(--oxide-lift); border-color: var(--oxide-lift);
}
.block-dot.ease.now { box-shadow: 0 0 0 3px var(--accent-bg); }

.hist-note { font-size: 12px; color: var(--text-muted); margin-top: 6px; line-height: 1.5; }
.hist-note i { vertical-align: -2px; margin-right: 4px; }
.hist-note a { color: var(--accent); text-decoration: none; }
.hist-note a:hover { text-decoration: underline; }
.hist-empty { font-size: 14px; color: var(--text-secondary); line-height: 1.55; margin: 0; }
.hist-empty a { color: var(--accent); text-decoration: none; }
.hist-explain {
  font-size: 13px; color: var(--text-secondary); line-height: 1.55;
  margin-top: 8px; padding-top: 8px; border-top: 0.5px solid var(--border);
}

/* The record receipt on a lane card (FM-30). The tint is dropped here and only
   the colour kept: the banner on /log is a moment and earns a filled block, this
   is a fact being read at leisure among a dozen other charts. */
.hist-record {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 13px; line-height: 1.5; margin-top: 8px; background: none;
}
.hist-record i { vertical-align: -2px; }
.hist-record-when { color: var(--text-muted); }
/* The same line on a ledger row, where it takes the whole width under the
   exercise so the reps and the load keep the row they already had. */
.sess-ex-record {
  flex-basis: 100%; display: flex; align-items: baseline; gap: 6px;
  font-size: 12px; line-height: 1.5; background: none;
}

.bw-chart, .ex-chart { display: block; overflow: visible; }
.bw-chart text, .ex-chart text { font-family: var(--font-sans); }
.chart-hit { cursor: pointer; }
.chart-tip-layer { pointer-events: none; }
/* The one place a shadow is allowed: depth on this site is surface tint and a
   hairline, and a floating overlay is the exception. */
.chart-tip {
  box-sizing: border-box; background: var(--surface-2); font-family: var(--font-sans);
  border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  padding: 8px 10px; box-shadow: 0 6px 18px rgba(15, 13, 9, 0.18);
}
.chart-tip-head-row { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.chart-tip-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.chart-tip-head {
  font-size: 12px; font-weight: 500; color: var(--text-primary);
  white-space: nowrap; margin-bottom: 3px;
}
.chart-tip-head-row .chart-tip-head { margin-bottom: 0; }
.chart-tip-body { font-size: 12px; color: var(--text-secondary); line-height: 1.5; }
.chart-tip-body b { color: var(--text-primary); font-weight: 500; }
.chart-tip-body b.fat { color: var(--oxide-lift); }

/* body composition, under the chart and only when a body fat is tracked */
.bodycomp { margin-top: 16px; padding-top: 16px; border-top: 0.5px solid var(--border); }
.stats-3 { grid-template-columns: repeat(3, 1fr); }
.bf-eyebrow { margin-bottom: 8px; }
/* The marker sits in `.bf-wrap`, NOT in `.bf-track`. The track clips its own
   rounded corners, and anything inside it that rises above the bar (the marker
   head, and every pixel of its tooltip) is cut off at that edge. */
.bf-wrap { position: relative; }
.bf-track {
  display: flex; height: 26px; border-radius: var(--radius); overflow: hidden;
  border: 0.5px solid var(--border);
}
.bf-hit { position: absolute; top: -4px; bottom: -4px; width: 16px;
  transform: translateX(-50%); cursor: pointer; }
.bf-marker {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 3px;
  border-radius: 2px; background: var(--text-primary); transform: translateX(-50%);
}
.bf-tip {
  display: none; position: absolute; bottom: calc(100% + 6px); left: 50%;
  transform: translateX(-50%); white-space: nowrap; z-index: 2;
  background: var(--surface-2); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(15, 13, 9, 0.18);
  font-size: 12px; font-weight: 500; color: var(--text-primary);
}
.bf-hit:hover .bf-tip, .bf-hit:focus-within .bf-tip { display: block; }
.bf-scale { display: flex; justify-content: space-between; margin-top: 6px; }
.bf-scale span { font-size: 11px; color: var(--text-muted); }
.bf-scale span.on { color: var(--text-primary); font-weight: 500; }
.bf-explain { font-size: 13px; color: var(--text-muted); line-height: 1.55; margin: 12px 0 0; }

/* the row between the two halves: what the dots mean, and what the line tracks */
.hist-controls {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 28px 0 16px;
}
.rpe-legend { display: flex; align-items: center; gap: 10px 14px; flex-wrap: wrap; }
.rpe-lede { font-size: 13px; color: var(--text-secondary); }
.rpe-key { display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--text-secondary); }
.rpe-key .dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
#ex-cards { display: flex; flex-direction: column; gap: 10px; }

/* One heading per training day, and the day's charts under it. The heading is a
   row rather than a card, so it reads as a divider in the page and not as
   another thing to look at beside the charts it opens. */
.day-group { display: flex; flex-direction: column; gap: 12px; }
.day-toggle {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  padding: 11px 4px; background: none; border: 0;
  border-bottom: 0.5px solid var(--border); border-radius: var(--radius);
  font: inherit; color: var(--text-primary); text-align: left; cursor: pointer;
  transition: background 0.12s ease;
}
.day-toggle:hover { background: var(--surface-1); }
.day-toggle > .ti {
  font-size: 16px; color: var(--text-muted); flex: none; align-self: center;
}
.day-toggle-name { font-size: 15px; font-weight: 500; }
.day-toggle-meta { font-size: 12.5px; color: var(--text-muted); margin-left: auto; }
.day-cards { display: flex; flex-direction: column; gap: 16px; }

/* the session ledger: one card per session, newest first, each with its own
   edit and delete controls */
.hist-section-title {
  font-size: 15px; font-weight: 500; color: var(--text-secondary);
  margin: 28px 0 12px; letter-spacing: 0.01em;
}
#sessions { display: flex; flex-direction: column; gap: 10px; }
/* A displaced posting is behind the current one for its day. Muted, not hidden:
   it is still a real session an athlete can edit or delete. */
.sess-card.superseded { opacity: 0.72; }
.sess-when {
  font-size: 12px; color: var(--text-muted); margin-left: 8px;
  font-family: var(--font-mono);
}
.sess-chip {
  font-size: 11px; font-weight: 500; color: var(--text-secondary);
  background: var(--surface-1); border: 0.5px solid var(--border);
  border-radius: 999px; padding: 2px 9px; white-space: nowrap;
}
.sess-chip.warn { color: var(--oxide-lift); border-color: var(--oxide-lift); }

.sess-exercises { display: flex; flex-direction: column; gap: 2px; }
.sess-ex {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px 16px; flex-wrap: wrap; padding: 6px 0;
  border-bottom: 0.5px solid var(--border);
}
.sess-ex:last-child { border-bottom: 0; }
.sess-ex-name { font-size: 14px; color: var(--text-primary); }
.sess-ex-detail {
  font-size: 13px; color: var(--text-secondary); font-family: var(--font-mono);
}
/* A skipped exercise is struck through, the same signal the logger draws. */
.sess-ex.skipped .sess-ex-name { color: var(--text-muted); text-decoration: line-through; }
.sess-ex-edit { flex-direction: column; align-items: stretch; }
.sess-ex-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}

.sess-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.sess-actions button {
  font: inherit; font-size: 13px; cursor: pointer;
  background: var(--surface-1); color: var(--text-secondary);
  border: 0.5px solid var(--border); border-radius: var(--radius);
  padding: 7px 14px; display: inline-flex; align-items: center; gap: 6px;
  transition: background 0.12s ease;
}
.sess-actions button:hover { background: var(--surface-2); }
.sess-actions button i { font-size: 15px; }
.sess-save { color: var(--text-primary); border-color: var(--border-strong); }
.sess-del-confirm { color: var(--danger); border-color: var(--danger); }

.sess-confirm { margin-top: 12px; }
.sess-confirm-copy {
  font-size: 13px; color: var(--text-secondary); line-height: 1.5; margin: 0 0 10px;
}
.sess-status { font-size: 13px; margin-top: 8px; min-height: 0; }
.sess-status:empty { margin-top: 0; }
.sess-working { color: var(--text-muted); }
.sess-error { color: var(--danger); }

/* site footer: the legal/meta row under every page */
/* Matches the top bar: full-width rule, contents on the reading column. */
.site-footer { border-top: 0.5px solid var(--border); }
.footer-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 24px 20px;
  display: flex; flex-wrap: wrap; gap: 10px 20px;
  align-items: center; justify-content: space-between;
  box-sizing: border-box; font-size: 13px; color: var(--text-muted);
}
.footer-inner .brand b { font-size: 16px; }
/* The one place .fit is drawn in oxide rather than muted: the footer lockup is
   the sign-off, and the mark's own colour belongs on it. */
.footer-inner .brand span { color: var(--oxide-lift); }
.footer-links { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.site-footer a { color: var(--text-secondary); text-decoration: none; }
.site-footer a:hover { color: var(--accent); }
/* the login page is one centred card, so centre its footer under it too */
.login-card ~ .site-footer { justify-content: center; }

/* The consent bar js/cookie-notice.js appends to <body>. Its class is
   `rk-consent`, not `cookie-notice`, on purpose: content blockers hide the
   latter by name. See the note in that file before renaming these. */
/* Fixed to the bottom, and sitting under both overlays on purpose. The auth
   scrim is z-index 40 on the landing page and the modal is 60, so 30 keeps the
   notice behind the dim rather than floating over a dialog the athlete is
   typing into. It covers no control at rest, because no page here has anything
   fixed to the bottom of the viewport. */
.rk-consent {
  position: fixed; z-index: 30;
  left: 12px; right: 12px; bottom: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  max-width: var(--maxw); margin: 0 auto;
  box-sizing: border-box;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 10px 16px; padding: 14px 16px;
  background: var(--surface-2);
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-card);
  box-shadow: 0 8px 28px rgba(15, 13, 9, 0.16);
  font-size: 13px; color: var(--text-secondary);
}
.rk-consent-text { margin: 0; flex: 1 1 260px; line-height: 1.5; }
.rk-consent a { color: var(--accent); text-decoration: underline; }
.rk-consent-btn {
  flex: 0 0 auto; cursor: pointer;
  font: inherit; font-weight: 500; color: var(--text-primary);
  background: var(--surface-1);
  border: 0.5px solid var(--border-strong);
  border-radius: var(--radius-pill);
  padding: 7px 18px;
}
.rk-consent-btn:hover { border-color: var(--accent); color: var(--accent); }
.rk-consent-btn:focus-visible {
  outline: none; box-shadow: 0 0 0 2px var(--accent-bg); border-color: var(--accent);
}
/* Accept and Reject sit together. Accept is filled so it reads as the primary
   action, but Reject is a full button beside it, not a downplayed link, so the
   two choices carry equal weight. */
.rk-consent-actions { display: flex; flex-wrap: wrap; gap: 8px; flex: 0 0 auto; }
.rk-consent-btn-primary {
  background: var(--accent); color: var(--on-accent, #fff); border-color: var(--accent);
}
.rk-consent-btn-primary:hover {
  filter: brightness(1.06); color: var(--on-accent, #fff); border-color: var(--accent);
}
/* On a phone the two buttons share their own full-width row as equal thumb
   targets, rather than words wedged beside four lines of prose. */
@media (max-width: 520px) {
  .rk-consent { gap: 12px; }
  .rk-consent-actions { flex: 1 1 100%; }
  .rk-consent-btn { flex: 1 1 0; padding: 10px 18px; }
}

/* A binary on/off slider, for the weekly-email setting on /settings. Built on a
   real checkbox so it stays keyboard reachable and reads to a screen reader as a
   switch; the box itself is taken out of the layout and the track and knob are
   drawn from its :checked state. The copy and the switch share a row and wrap to
   two on a narrow screen. */
.rk-setting {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px 20px; flex-wrap: wrap;
}
.rk-setting-copy { flex: 1 1 260px; }
.rk-setting-copy p { margin: 6px 0 0; }
.rk-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; flex: 0 0 auto; }
.rk-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.rk-switch-track {
  position: relative; width: 46px; height: 28px; border-radius: 999px;
  background: var(--surface-1); border: 0.5px solid var(--border-strong);
  transition: background .15s ease, border-color .15s ease; flex: 0 0 auto;
}
.rk-switch-track::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px;
  border-radius: 50%; background: var(--text-secondary);
  transition: transform .15s ease, background .15s ease;
}
.rk-switch input:checked + .rk-switch-track { background: var(--accent); border-color: var(--accent); }
.rk-switch input:checked + .rk-switch-track::after { transform: translateX(18px); background: #fff; }
.rk-switch input:focus-visible + .rk-switch-track {
  box-shadow: 0 0 0 2px var(--accent-bg); border-color: var(--accent);
}
.rk-switch input:disabled + .rk-switch-track { opacity: .5; cursor: default; }
.rk-switch-state { font-size: 13px; color: var(--text-secondary); min-width: 22px; }

/* The dismissable "your email was updated" banner js/account-page.js inserts at
   the top of /settings after a confirmed change. A green left edge marks it as a
   success rather than a warning; the green reads on both themes, so it is not
   remapped in dark. */
.rk-flash {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 20px; padding: 12px 14px;
  background: var(--surface-2); color: var(--text-primary);
  border: 0.5px solid var(--border-strong); border-left: 3px solid #2f8f5f;
  border-radius: var(--radius-card); font-size: 14px;
}
.rk-flash-text { flex: 1 1 auto; }
.rk-flash-x {
  flex: 0 0 auto; cursor: pointer; font: inherit; font-size: 20px; line-height: 1;
  color: var(--text-muted); background: none; border: 0; padding: 0 4px;
}
.rk-flash-x:hover { color: var(--text-primary); }
.rk-flash-x:focus-visible {
  outline: none; box-shadow: 0 0 0 2px var(--accent-bg); border-radius: 4px;
}

/* pull to refresh: the spinner js/pull-refresh.js appends to <body> */
/* The page itself comes down with the finger, so what the spinner sits in is
   the strip that opens above the top bar. z-index 20 keeps it under the bar at
   40, which is what hides it at rest and what closes over it as the page rides
   back up. The seat's transform is written per frame by the script; only the
   resting state is here. */
.pull-refresh {
  position: fixed; z-index: 20; top: 0; left: 0; right: 0;
  display: flex; justify-content: center; pointer-events: none;
}
.pull-refresh-seat {
  /* Half the spinner's height, so translating the seat by half the open strip
     centres the spinner in it. */
  margin-top: -13px;
  opacity: 0;
}
.pull-refresh-spinner { display: block; width: 26px; height: 26px; }
.pull-refresh-spinner line {
  stroke: var(--text-muted); stroke-width: 2.4; stroke-linecap: round;
  opacity: 0;
}
/* Settling back after a pull that was let go short of the threshold, and the
   ride down into place on one that was not. Absent while the finger is down,
   so the page tracks it exactly. Kept in step with SETTLE_MS in the script. */
.pull-refresh-settling .pull-refresh-seat {
  transition: transform 0.22s ease-out, opacity 0.22s ease-out;
}
/* Twelve steps rather than a smooth turn, because the spokes are twelve and a
   continuous rotation between them reads as a wobble rather than a chase. */
.pull-refresh-spinning .pull-refresh-spinner {
  animation: pull-spin 0.9s steps(12, end) infinite;
  transform-origin: 50% 50%;
}
@keyframes pull-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .pull-refresh-spinning .pull-refresh-spinner { animation: none; }
}

/* The three policy pages: privacy, cookies and terms. Long-form legal prose,
   no API, and public, so they render for a signed-out reader (a Connect IQ
   reviewer among them). They are dark by default like the landing page and
   carry no theme switch, that being a signed-in feature, so the palette here is
   the fixed brand set (bone, chalk, oxide-lift) rather than the surface tokens
   that flip with the system scheme. */
.policy { font-size: 16px; color: var(--bone); padding: 44px 28px 72px; }
.policy h1 {
  font-size: clamp(32px, 4.4vw, 46px); line-height: 1.06; font-weight: 500;
  letter-spacing: -0.02em; color: var(--bone); margin: 0 0 10px;
}
.policy .updated {
  font-size: 13px; font-family: var(--font-mono);
  color: rgba(242, 240, 233, 0.45); margin: 0 0 26px;
}
.policy .lede {
  font-size: 17px; line-height: 1.6; color: rgba(242, 240, 233, 0.76);
  margin: 0 0 28px; text-wrap: pretty;
}

/* Section labels are eyebrows, not headings sized down: 11px oxide caps over
   each block, the 48px above them setting the rhythm between sections. */
.policy h2 {
  font-size: 11px; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--oxide-lift); margin: 48px 0 14px;
}
.policy h3 { font-size: 16px; font-weight: 500; color: var(--chalk); margin: 26px 0 8px; }
.policy p {
  margin: 0 0 16px; font-size: 16px; line-height: 1.62;
  color: rgba(242, 240, 233, 0.72); text-wrap: pretty;
}
.policy ul { margin: 0 0 16px; padding-left: 20px; }
.policy li {
  margin-bottom: 10px; font-size: 15px; line-height: 1.55;
  color: rgba(242, 240, 233, 0.72);
}
.policy b, .policy strong { font-weight: 500; color: var(--bone); }
/* The rare italic aside is set in the voice face, as the design asks. */
.policy em { font-family: var(--font-voice); font-style: italic; color: rgba(242, 240, 233, 0.82); }
.policy a {
  color: var(--oxide-lift); text-decoration: none;
  border-bottom: 0.5px solid rgba(201, 95, 54, 0.45);
}
.policy a:hover { color: var(--chalk); border-bottom-color: rgba(217, 207, 188, 0.6); }
.policy code {
  font-family: var(--font-mono); font-size: 0.88em; color: var(--chalk);
  background: rgba(242, 240, 233, 0.08); border-radius: 4px; padding: 1px 5px;
  word-break: break-word;
}

/* The short version card, and any inline card in the prose (terms has two).
   Hairline border, a faint tint, no shadow. */
.policy .card {
  border: 0.5px solid rgba(242, 240, 233, 0.14); border-radius: var(--radius-card);
  background: rgba(242, 240, 233, 0.045); padding: 24px 26px; margin: 0 0 28px;
}
.policy .card p:last-child, .policy .card ul:last-child { margin-bottom: 0; }
/* Only the short version card, the one right after the intro, carries the wide
   gap before the body sections and a chalk title. */
.policy .lede + .card { margin-bottom: 52px; }
.policy .lede + .card > p:first-child { margin-bottom: 14px; }
.policy .lede + .card > p:first-child b { color: var(--chalk); font-size: 16px; font-weight: 500; }
.policy .note { font-size: 14px; line-height: 1.55; color: rgba(242, 240, 233, 0.5); }
/* The one control on the set, the cookie page's "change your choices" button, is
   a chalk fill here rather than the app's accent, scoped so the shared button
   elsewhere is untouched. It keeps .btn-primary's brightness hover. */
.policy .btn-primary { background: var(--chalk); color: var(--iron); }
/* The button sits on its own row with the current choice beside it, rather than
   inline in a sentence where it wraps and squishes. */
.policy .cookie-choice-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 16px; }
.policy .cookie-choice { font-size: 13px; font-family: var(--font-mono); color: rgba(242, 240, 233, 0.5); }

/* Tables read as one bordered card: header row tinted, a hairline between rows.
   Prose cells wrap, so nothing pushes the reading column into a sideways
   scroll; below 520px each row stacks and labels itself (further down). */
.table-wrap {
  border: 0.5px solid rgba(242, 240, 233, 0.14); border-radius: var(--radius-card);
  overflow: hidden; margin: 4px 0 0;
}
.policy table { border-collapse: collapse; width: 100%; font-size: 14px; }
.policy th {
  text-align: left; vertical-align: top; padding: 11px 18px;
  background: rgba(242, 240, 233, 0.05); color: rgba(242, 240, 233, 0.5);
  font-weight: 500; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
}
.policy td {
  text-align: left; vertical-align: top; padding: 16px 18px;
  border-top: 0.5px solid rgba(242, 240, 233, 0.1);
  font-size: 14px; line-height: 1.5; color: rgba(242, 240, 233, 0.7);
}
.policy td:first-child { color: var(--chalk); font-weight: 500; }
/* A first cell holding a list rather than a single name still wraps. */
.policy td.cell-wrap { white-space: normal; }

/* Four tiles need more room than three did. Two rows of two beats one row of
   four squeezed, and beats a single column, which is four screenfuls of
   scrolling before the session the page exists to show. */
@media (max-width: 640px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  /* Three tiles stay three. Two and an orphan reads as one of them having been
     added later, and these three are one reading of the same body. */
  .stats-3 { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 400px) {
  /* "13 st 6 lb" at 22px does not fit a third of a phone. */
  .stats-3 .stat-value { font-size: 17px; }
}

/* Chart labels are set in viewBox units, so they shrink with the chart. A 640
   unit chart drawn 320 pixels wide halves every number on its axes, and 6px is
   not a number anybody reads in a gym. These put the labels back to roughly the
   size they are on a desktop, at the two widths where it stops being true. */
@media (max-width: 640px) {
  .bw-chart text, .ex-chart text { font-size: 16px; }
}
@media (max-width: 430px) {
  .bw-chart text, .ex-chart text { font-size: 21px; }
}

@media (max-width: 520px) {
  .wrap { padding: 16px 16px 56px; }
  /* The week line used to be hidden here, because in the masthead it was
     fighting the brand lockup for a phone's width. In the session card it can
     have a row of its own under the day's name, which is enough for a phone to
     read it like everything else. */
  .today-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .today-marks { width: 100%; justify-content: flex-start; }
  /* two action buttons leave too little room for the device name beside them,
     so give the name the full width and drop the buttons onto their own row */
  .dev { grid-template-columns: 1fr; }
  .dev-actions { justify-content: flex-end; }

  /* three prose columns don't fit a phone: stack each row into a block and let
     the cells label themselves, rather than making the reader scroll sideways */
  .policy table { min-width: 0; }
  .policy thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .policy tr { display: block; padding: 12px 0; border-top: 0.5px solid var(--border); }
  .policy tr:first-child { border-top: 0; }
  .policy td { display: block; border-top: 0; padding: 0 0 4px; }
  .policy td:last-child { padding-bottom: 0; }
  .policy td:first-child { white-space: normal; }
  .policy td[data-label]::before { content: attr(data-label) ": "; color: var(--text-muted); }
}

/* ---- onboarding + profile ------------------------------------------------ */
/* Shared, because they are two views of one thing: the profile page is direct
   access to the answers, onboarding is a guided walk through the same ones. */

.ob-card { margin-top: 8px; }
.ob-title { font-size: 22px; font-weight: 500; margin: 4px 0 6px; }
.ob-prompt { color: var(--text-secondary); font-size: 14px; margin: 0 0 18px; }
.ob-hint { font-size: 12.5px; color: var(--text-muted); line-height: 1.55; margin: 10px 0 0; }
.ob-sub { font-size: 13px; color: var(--text-secondary); margin: 16px 0 8px; }
.ob-val { font-variant-numeric: tabular-nums; color: var(--text-primary); min-width: 2ch; }
.ob-unit { font-size: 13px; color: var(--text-muted); }

.ob-progress { margin-bottom: 14px; }
.ob-dots { display: flex; gap: 5px; }
.ob-dot { flex: 1; height: 3px; border-radius: 2px; background: var(--border-strong); }
.ob-dot.done { background: var(--success); }
.ob-dot.on { background: var(--accent); }

/* The two phases of gate 1, drawn as two labelled runs of dots rather than one
   undifferentiated row. Setup and the five questions have to read as different
   things, because the promise the product makes is five questions and the only
   thing keeping that honest is that setup is visibly not one of them. */
.phase-rail { display: flex; align-items: flex-end; gap: 12px; }
.phase-grp { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.phase-grp.questions { flex: 1; }
.phase-lab {
  font-size: 10px; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--text-muted); font-weight: 500; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.phase-grp.active .phase-lab { color: var(--text-primary); }
.phase-dots { display: flex; gap: 5px; }
.phase-grp.setup .phase-dots { width: 62px; }
.phase-dot { flex: 1; height: 3px; border-radius: 2px; background: var(--border-strong); }
.phase-dot.done { background: var(--success); }
.phase-dot.on { background: var(--accent); }
.phase-sep { width: 0.5px; align-self: stretch; background: var(--border-strong); margin: 2px 2px 1px; }

/* The region seed, said out loud. A card that is already lit with nobody
   explaining why reads as an answer somebody gave on your behalf. */
.seed-note {
  font-size: 12.5px; color: var(--text-secondary); background: var(--accent-bg);
  border-radius: var(--radius); padding: 9px 12px; margin: 12px 0 0; line-height: 1.5;
}
.seed-note b { color: var(--text-primary); font-weight: 500; }

/* About you: the one screen that asks for personal data, so it says why. */
.why-ask { font-size: 13px; color: var(--text-secondary); line-height: 1.55; margin: 0 0 16px; }
.why-ask b { color: var(--text-primary); font-weight: 500; }
.opt-tag { font-size: 11px; color: var(--text-muted); margin-left: 8px; font-weight: 400; }

/* Day, month and year as three native selects. A native date input makes
   somebody scroll a calendar back to their birth year; a year dropdown is one
   tap. They wrap on a narrow screen rather than shrinking below a tappable
   width. */
.ob-dob { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.ob-dob select { font-size: 15px; min-height: 44px; }
.ob-dob-day { width: 5.5em; }
.ob-dob-month { width: 9em; }
.ob-dob-year { width: 6.5em; }

/* The under-13 refusal. Nothing is destroyed here, so nothing on it looks like
   a dead end: the date stays editable, the eligibility date is named, and
   deleting is a link to the page that already asks for an email first. */
.refuse-note {
  font-size: 14px; color: var(--warning); background: var(--warning-bg);
  border-radius: var(--radius); padding: 12px 15px; line-height: 1.55; margin: 0 0 18px;
}
.refuse-eligible { font-size: 14px; color: var(--text-secondary); line-height: 1.55; margin: 16px 0 0; }
.refuse-eligible b { color: var(--text-primary); font-weight: 500; }
.refuse-cleared { font-size: 13px; color: var(--success); margin: 12px 0 0; display: none; }
.refuse-actions { display: flex; align-items: center; gap: 10px; margin-top: 22px; flex-wrap: wrap; }
.refuse-actions a.btn-danger { text-decoration: none; display: inline-block; line-height: 1.6; }

.ob-seg { flex-wrap: wrap; }
.ob-checks { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.ob-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.ob-row label { font-size: 13px; color: var(--text-secondary); }
.ob-row input[type="number"], .ob-row input[type="text"] {
  background: var(--surface-1); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); padding: 7px 10px; font: inherit; font-size: 14px;
  color: var(--text-primary); width: 8em;
}
.ob-row input[type="range"] { flex: 1; min-width: 140px; }

.ob-cards { display: flex; flex-direction: column; gap: 8px; }
.ob-choice {
  text-align: left; background: var(--surface-1); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); padding: 12px 14px; font: inherit; cursor: pointer;
  display: flex; flex-direction: column; gap: 3px; color: var(--text-primary);
}
.ob-choice span { font-size: 12.5px; color: var(--text-secondary); }
.ob-choice.on { border-color: var(--accent); background: var(--accent-bg); }

.ob-ack { display: flex; align-items: flex-start; gap: 9px; margin-top: 16px; }
.ob-ack label { font-size: 13px; color: var(--text-secondary); line-height: 1.5; }

.ob-anchor { padding: 12px 0; border-bottom: 0.5px solid var(--border); }
.ob-anchor:last-child { border-bottom: 0; }
.ob-anchor-name { font-size: 14px; margin-bottom: 7px; }
.ob-anchor-inputs { display: flex; align-items: center; gap: 7px; margin-top: 8px; }
.ob-anchor-inputs input {
  background: var(--surface-1); border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); padding: 6px 9px; font: inherit; font-size: 13px;
  color: var(--text-primary); width: 7.5em;
}
.ob-x { color: var(--text-muted); }

.ob-actions { display: flex; align-items: center; gap: 8px; margin-top: 22px; }
.ob-actions .btn-primary { margin-left: auto; }
.ob-error { color: var(--warning); font-size: 13px; margin-top: 12px; display: none; }
.ob-note {
  font-size: 13px; color: var(--text-secondary); line-height: 1.6;
  background: var(--surface-1); border-radius: var(--radius);
  padding: 10px 13px; margin: 10px 0 0;
}
.ob-days { list-style: none; padding: 0; margin: 0 0 8px; }
.ob-days li {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 8px 0; border-bottom: 0.5px solid var(--border); font-size: 14px;
}
.ob-days li span { color: var(--text-muted); font-size: 12.5px; }
.ob-confirm { list-style: none; padding: 0; margin: 0; }
.ob-confirm li {
  display: flex; align-items: baseline; gap: 10px;
  padding: 9px 0; border-bottom: 0.5px solid var(--border); font-size: 14px;
}
.ob-confirm-label { color: var(--text-muted); font-size: 12.5px; min-width: 8.5em; }
.ob-edit { margin-left: auto; font-size: 12.5px; color: var(--accent); }
.banner.ob-good { color: var(--success); background: var(--success-bg); }

/* ---- profile ------------------------------------------------------------- */
/* Rows of label, value and a pencil, in cards under eyebrow headings. Nothing
   here is shared with another page, so this whole block can be read as one
   thing and moved as one thing. */

/* Prose inside a profile card. `.pf-lede` is a sentence the athlete is meant to
   read; `.pf-hint` is the quieter line under a heading explaining what the card
   is for. Both sit above the rows, so both clear them. */
.pf-lede { font-size: 15px; line-height: 1.55; margin: 2px 0 16px; }
/* The watch count is written by the script and stays empty when the account
   endpoint did not answer. An empty paragraph still takes its margin, so it
   would leave a gap where a sentence nobody could write should have been. */
.pf-lede:empty { display: none; }
.pf-hint { font-size: 13px; color: var(--text-muted); line-height: 1.5;
           margin: 0 0 4px; max-width: 560px; }
.pf-empty { font-size: 14px; color: var(--text-secondary); margin: 4px 0; }

/* Who the account belongs to. The pills wrap under the name on a phone rather
   than squeezing the name into two characters and an ellipsis.

   It carries its own top margin because it is the one card on the page with no
   eyebrow above it, and `.section-label` is what spaces every other one. */
.pf-hero { margin-top: 26px; }
.pf-hero-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.pf-who { min-width: 0; flex: 1 1 12em; }
.pf-name { font-size: 20px; font-weight: 500; overflow-wrap: anywhere; }
.pf-email { font-size: 13px; color: var(--text-muted); overflow-wrap: anywhere; }
.pf-facts { display: flex; gap: 8px; flex-wrap: wrap; }
.pf-hero-note { margin: 14px 0 0; }

/* Pill tones. `.pill` on its own is the accent one the plan cards use; these
   four are what a profile row says about a value it does not have. Needed is a
   warning because a program cannot be built without it. Optional is quiet on
   purpose: nothing ever asked for body fat, so it must not read as a question
   the athlete failed to answer. */
.pill-neutral { background: var(--surface-1); color: var(--text-secondary); }
.pill-warning { background: var(--warning-bg); color: var(--warning); }
.pill-invite { background: var(--accent-bg); color: var(--accent); }
.pill-quiet { background: var(--surface-1); color: var(--text-muted); }

/* A run of rows inside a card, hairline separated, with no rule above the first
   or below the last: the card's own edge is already doing that job. */
.pf-rows > * { border-top: 0.5px solid var(--border); }
.pf-rows > *:first-child { border-top: 0; }

.pf-row {
  display: grid; grid-template-columns: 170px 1fr auto; gap: 4px 12px;
  align-items: start; padding: 12px 0;
  /* A hash landing stops this row under the sticky top bar, or hard against
     the viewport edge on the pages without one. Slightly more than the site's
     scroll-padding, so the row reads as part of its card rather than as the
     first thing under a rule. */
  scroll-margin-top: 80px;
}
.pf-label { font-size: 13px; color: var(--text-secondary); padding-top: 2px; }
.pf-value { min-width: 0; }
.pf-said { font-size: 15px; font-weight: 500; line-height: 1.35; overflow-wrap: anywhere; }
.pf-meta { display: flex; flex-wrap: wrap; gap: 6px 8px; margin-top: 5px; align-items: baseline; }

/* Equipment: a gym's whole inventory, so the label takes its own line and the
   list gets the full width under it. The pencil stays, unlike the prototype's,
   because this row is editable and is the target of the deep link the returning
   flow's confirm screen sends people to. */
.pf-row-wide { grid-template-columns: 1fr auto; }
/* Placed rather than auto-flowed: the value takes both columns, and left to
   itself the pencil drops onto a third row and centres under the list. */
.pf-row-wide .pf-label { grid-column: 1; grid-row: 1; }
.pf-row-wide .pf-pencil { grid-column: 2; grid-row: 1; }
.pf-row-wide .pf-value { grid-column: 1 / -1; grid-row: 2; }
.pf-row-wide .pf-said { font-weight: 400; font-size: 14px; line-height: 1.5; }

.pf-pencil {
  background: none; border: 0; color: var(--text-muted); cursor: pointer;
  padding: 2px 4px; font-size: 15px; line-height: 1; border-radius: var(--radius);
}
.pf-pencil:hover { color: var(--accent); background: var(--surface-1); }
.pf-row-open .pf-pencil { color: var(--accent); }

.pf-src { font-size: 11px; padding: 2px 8px; border-radius: var(--radius); }
.pf-src-athlete { background: var(--success-bg); color: var(--success); }
.pf-src-watch { background: var(--accent-bg); color: var(--accent); }
.pf-src-engine { background: var(--surface-1); color: var(--text-secondary); }
.pf-at { font-size: 11px; color: var(--text-muted); }
.pf-badge {
  font-size: 11px; margin-left: 8px; padding: 2px 8px; border-radius: var(--radius);
  background: var(--warning-bg); color: var(--warning); text-transform: none; letter-spacing: 0;
}

/* The editor, opened in place under the row it belongs to. */
.pf-edit {
  grid-column: 1 / -1; margin-top: 12px; padding: 14px;
  background: var(--surface-1); border-radius: var(--radius);
}
.pf-ask { font-size: 13.5px; color: var(--text-secondary); margin: 0 0 12px; line-height: 1.5; }
.pf-edit-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }
.pf-edit-err { color: var(--warning); font-size: 13px; margin-top: 8px; }

/* Waiting to be applied: each row is a link down to the setting behind it. */
.pf-pend {
  display: flex; align-items: center; gap: 10px; padding: 11px 0;
  text-decoration: none; color: inherit;
}
.pf-pend:hover .pf-pend-text b { text-decoration: underline; }
.pf-pend .ti-clock { font-size: 17px; color: var(--oxide-lift); flex: none; }
.pf-pend .ti-arrow-up-right { font-size: 15px; color: var(--text-muted); flex: none; }
.pf-pend-text { font-size: 14px; color: var(--text-secondary); flex: 1; line-height: 1.45; }
.pf-pend-text b { color: var(--accent); font-weight: 500; }

/* The link across to the one page that manages watches. */
.pf-across { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.pf-across i { font-size: 14px; }

/* The change log: when, what, and what it became. */
.pf-rev {
  display: flex; flex-wrap: wrap; gap: 2px 12px; align-items: baseline;
  padding: 9px 0; font-size: 14px; color: var(--text-secondary);
}
.pf-rev-when {
  color: var(--text-muted); font-size: 11.5px; font-family: var(--font-mono);
  min-width: 11em;
}
.pf-rev-what { flex: 1 1 14em; min-width: 0; overflow-wrap: anywhere; }
.pf-rev-what b { color: var(--text-primary); font-weight: 500; }
.pf-rev-old { color: var(--text-muted); }
.pf-rev-new { color: var(--text-primary); }
.pf-rev-src { color: var(--text-muted); font-size: 11.5px; }

/* One column once the label column stops earning its width. */
@media (max-width: 560px) {
  .pf-row { grid-template-columns: 1fr auto; }
  .pf-row .pf-label { grid-column: 1; }
  .pf-row .pf-value { grid-column: 1 / -1; }
  .pf-row .pf-pencil { grid-row: 1; grid-column: 2; }
  .pf-rev-when { min-width: 100%; }
}

.pf-dialog {
  border: 0.5px solid var(--border-strong); border-radius: var(--radius-card);
  background: var(--surface-2); color: var(--text-primary);
  padding: 22px; max-width: 420px; font-family: var(--font-sans);
}
.pf-dialog::backdrop { background: rgba(0, 0, 0, 0.45); }
.pf-dialog h2 { font-size: 17px; font-weight: 500; margin: 0 0 10px; }
.pf-dialog p { font-size: 13.5px; line-height: 1.6; color: var(--text-secondary); margin: 0 0 10px; }
.pf-cost { color: var(--warning) !important; background: var(--warning-bg); padding: 10px 12px; border-radius: var(--radius); }
.pf-dialog-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 18px; }

/* admin: the Users / Waitlist / Stats console (js/admin.js). Built from the same
   surface, border and text tokens as the rest of the app, so it reads as one
   page in both themes. */
.admin-toolbar { margin: 4px 0 12px; }
.admin-search {
  width: 100%; max-width: 320px; font-family: inherit; font-size: 14px;
  padding: 8px 12px; border: 0.5px solid var(--border-strong);
  border-radius: var(--radius); background: var(--surface-1); color: var(--text-primary);
}
.admin-table { border-collapse: collapse; width: 100%; font-size: 13.5px; }
/* The table is wider than a phone. Let the card scroll it sideways rather than
   squeezing every column to nothing. */
#user-table, #access-list { overflow-x: auto; }
.admin-th {
  text-align: left; vertical-align: bottom; padding: 8px 12px;
  color: var(--text-muted); font-weight: 500; font-size: 10px;
  letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap;
  border-bottom: 0.5px solid var(--border);
}
.admin-th-sort { cursor: pointer; user-select: none; }
.admin-th-sort:hover { color: var(--text-secondary); }
.admin-th-sort.on { color: var(--accent); }
.admin-table td {
  padding: 10px 12px; border-top: 0.5px solid var(--border);
  color: var(--text-secondary); vertical-align: middle; white-space: nowrap;
}
.u-email { color: var(--accent); text-decoration: none; }
.u-email:hover { text-decoration: underline; }
.u-name { color: var(--text-primary); }
.u-dim { color: var(--text-muted); }
.u-num { text-align: center; }
.u-suspended td { opacity: 0.62; }
/* ...but not the actions cell. Fading it dims the ⋯ toggle, and because an
   opacity below 1 also tints (and becomes the containing block for) a fixed
   descendant, it would wash out the whole overflow menu with the row. The dim
   is meant for the row's data, so the control and its menu stay full strength. */
.u-suspended td.u-actions { opacity: 1; }
.u-note { color: var(--text-muted); font-size: 12px; }
.u-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* The row's actions live behind a ⋯ toggle. The panel floats over the table, so
   it carries the one shadow the design allows a floating layer (see .menu). */
.u-menu { position: relative; display: inline-block; }
.u-menu-toggle {
  font-family: inherit; font-size: 16px; line-height: 1; cursor: pointer;
  color: var(--text-secondary); background: none;
  border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  padding: 4px 11px;
}
.u-menu-toggle:hover { background: var(--surface-1); }
/* Positioned fixed, not absolute, and placed by openMenu() from the toggle's
   viewport rect. The table it lives in is a horizontal scroll box
   (#user-table), and a box that scrolls one axis clips the other, so an
   absolute panel was trapped inside it and only showed once you scrolled the
   box down. Fixed lifts the panel out of that clip; a scroll or resize closes
   it so it can never sit stale over a moved row. */
.u-menu-panel {
  display: none; position: fixed; z-index: 20;
  min-width: 12em; padding: 4px; background: var(--surface-2);
  border: 0.5px solid var(--border-strong); border-radius: var(--radius-card);
  box-shadow: 0 8px 28px rgba(15, 13, 9, 0.16);
}
.u-menu.open .u-menu-panel { display: block; }
.u-menu-item {
  display: block; width: 100%; text-align: left; font-family: inherit;
  font-size: 13px; cursor: pointer; color: var(--text-primary); background: none;
  border: none; border-radius: var(--radius); padding: 7px 10px; white-space: nowrap;
}
.u-menu-item:hover { background: var(--surface-1); }
.u-menu-item-danger { color: var(--warning); }
.u-menu-item-danger:hover { background: var(--warning-bg); }
.admin-btn {
  font-family: inherit; font-size: 12.5px; cursor: pointer; color: var(--text-secondary);
  background: none; border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  padding: 5px 10px; white-space: nowrap;
}
.admin-btn:hover { background: var(--surface-1); }
.admin-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.admin-btn-danger { color: var(--warning); border-color: var(--warning); }
.admin-btn-danger:hover { background: var(--warning-bg); }
.admin-confirm-input {
  font-family: inherit; font-size: 13px; padding: 5px 10px; min-width: 15em;
  border: 0.5px solid var(--warning); border-radius: var(--radius);
  background: var(--surface-1); color: var(--text-primary);
}
.admin-empty { color: var(--text-muted); font-size: 14px; padding: 14px 2px; }
.admin-msg { font-size: 13px; margin-top: 12px; min-height: 1em; }
.admin-msg.ok { color: var(--success); }
.admin-msg.err { color: var(--warning); }
.admin-invite { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 4px 0 14px; }
.admin-invite input[type="email"] {
  font-family: inherit; font-size: 14px; padding: 8px 12px; min-width: 16em;
  border: 0.5px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface-1); color: var(--text-primary);
}
.admin-check { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-secondary); }
.admin-stats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; }
.admin-stat {
  background: var(--surface-1); border-radius: var(--radius); padding: 14px 12px; text-align: center;
}
.admin-stat-n { font-size: 22px; font-weight: 500; color: var(--text-primary); }
.admin-stat-l { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 4px; }
@media (max-width: 640px) {
  .admin-stats { grid-template-columns: repeat(2, 1fr); }
}
