/* ============================================================
   Taurifer — editorial
   Light: cream surfaces, near-black ink, burnt-orange accent.
   Dark: the same layout on warm charcoal, reached only by
   swapping the :root tokens (see [data-theme="dark"] below).
   Separation via hairline rules and whitespace — not cards.
   ============================================================ */

@font-face{font-family:"Plex Sans";src:url(fonts/plexsans.woff2) format("woff2");font-weight:100 700;font-display:swap}
@font-face{font-family:"Plex Mono";src:url(fonts/plexmono-400.woff2) format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:"Plex Mono";src:url(fonts/plexmono-500.woff2) format("woff2");font-weight:500;font-display:swap}
@font-face{font-family:"Plex Mono";src:url(fonts/plexmono-600.woff2) format("woff2");font-weight:600;font-display:swap}

:root{
  color-scheme:light;
  /* `color-scheme` is a keyword property, so the few rules that pin a native
     control to one appearance read it from here instead of hard-coding it and
     stranding that control in the light palette. */
  --scheme:light;
  --bg:#F4F2EF;
  --surface:#FFFFFF;
  --ink:#1B1A17;
  --ink-soft:#6E6A63;
  --ink-faint:#716D66;
  --rule:#E4E1DA;
  --rule-strong:#D9D5CD;
  --accent:#E04E14;
  --accent-deep:#B8410E;
  --positive:#2F7D33;
  --cta:#161513;
  --well:#FAF8F5;
  --cta-ink:#FFFFFF;
  /* Ink for copy sitting on a filled --accent-deep ground. It is not --cta-ink:
     in dark the CTA inverts to a light pill while the orange stays orange, so
     the two inks move in opposite directions. */
  --accent-ink:#FFFFFF;
  --danger:#C93A2B;
  /* Channel triplets for the tints, focus rings and washes that need the theme
     colour at partial alpha. Keeping them beside the hex means a theme swap
     carries the translucent surfaces with it. */
  --accent-rgb:224,78,20;
  --positive-rgb:47,125,51;
  --danger-rgb:201,58,43;
  --ink-rgb:27,26,23;
  --knob:#FFFFFF;
  /* Scrims stay dark in both themes: they exist to push the page back. */
  --scrim:rgba(27,26,23,.42);
  --radius:14px;
  /* Settings is one text column: the caption of a group, the label of every row
     in it, and the rule between those rows all begin at this inset. */
  --settings-inset:16px;
  /* The dock floats: it is a capsule of --dock-h parked --dock-gap above the
     safe area, inset --dock-inset from each edge. --nav stays the single
     "space the dock claims at the bottom" token every other floating element
     (rest bar, toast, tour, FAB) already measures itself against. */
  --dock-h:62px;
  --dock-gap:10px;
  --dock-inset:14px;
  --nav:calc(var(--dock-h) + var(--dock-gap));
  --dock-glass:rgba(250,248,245,.8);
  --dock-glass-opaque:rgba(250,248,245,.97);
  --dock-edge:rgba(27,26,23,.10);
  --dock-sheen:rgba(255,255,255,.7);
  --dock-pill:rgba(255,255,255,.82);
  --dock-pill-sheen:rgba(255,255,255,.82);
  --maxw:560px;

  --display:"Plex Sans",system-ui,sans-serif;
  --body:"Plex Sans",system-ui,-apple-system,Segoe UI,sans-serif;
  --mono:"Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --r:var(--radius);
  --shadow:none;
  --check:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
  --bolt:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M13.6 2 4 13.4h6.1L9.4 22 20 10.2h-6.6z'/%3E%3C/svg%3E");
  /* The forward arrow is drawn, not typed. U+2192 is not in the Plex Sans
   * subset we ship, so a "→" in `content` falls through to whatever the
   * platform hands us: SF Pro on iOS draws a proper arrow, while Android
   * lands on a symbol fallback that ignores our weight and sits off-centre
   * against the label. A mask renders identically everywhere. */
  /* A <select> cannot carry a pseudo-element, so its chevron is a background
     image rather than a mask, and the ink has to be baked into the SVG. That
     is why this one glyph exists twice instead of following currentColor. */
  --select-caret:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231B1A17' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  --arrow:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12h15'/%3E%3Cpolyline points='13.2 6.2 19 12 13.2 17.8'/%3E%3C/svg%3E");
}

/* ---- Dark ----
   The whole theme is this block: no rule below it names a colour that is not a
   token, so dark is a palette, not a second stylesheet. app.js resolves the
   Appearance setting (system/light/dark) to a concrete value and writes it to
   <html data-theme>, which is why there is no prefers-color-scheme query here —
   the attribute is always the answer, and an inline snippet in index.html sets
   it before the first paint.

   The paper stays warm rather than neutral grey so the burnt orange, the
   exercise illustrations and the app icon still belong to the same object. Ink
   is off-white for the same reason, and because pure white on near-black
   haloes. The CTA is the one element that inverts outright: a near-black pill
   disappears on this ground, so it becomes a light pill with dark ink, and the
   orange moves up rather than down (--accent-deep is the *lighter* orange here
   because its job is emphasis, and emphasis on dark means more light). */
:root[data-theme="dark"]{
  color-scheme:dark;
  --scheme:dark;
  --bg:#141310;
  --surface:#1E1C18;
  --ink:#F2EFE9;
  --ink-soft:#ADA79D;
  --ink-faint:#99938A;
  --rule:#2E2B26;
  --rule-strong:#4A453D;
  --accent:#F2703B;
  /* Text emphasis stays in the same burnt-orange family as the mark. The
     first dark pass was lighter but too peach beside the red danger action. */
  --accent-deep:#FF8A3D;
  --positive:#63C267;
  /* A quiet parchment rather than a lit white card. Scale and placement make
     the CTA primary; maximum luminance does not need to. */
  --cta:#DED7CC;
  --well:#191713;
  --cta-ink:#161513;
  --accent-ink:#231A14;
  --danger:#FF6670;
  --accent-rgb:242,112,59;
  --positive-rgb:99,194,103;
  --danger-rgb:255,102,112;
  --ink-rgb:242,239,233;
  --knob:#F7F5F1;
  --scrim:rgba(0,0,0,.62);
  /* Dark content showing through glass becomes visual noise around the nav
     labels. Keep the material, but make it read as smoked glass, not a lens. */
  --dock-glass:rgba(30,28,24,.92);
  --dock-glass-opaque:rgba(24,22,19,.97);
  --dock-edge:rgba(255,255,255,.10);
  --dock-sheen:rgba(255,255,255,.04);
  --dock-pill:rgba(255,255,255,.07);
  --dock-pill-sheen:rgba(255,255,255,.05);
  --select-caret:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23F2EFE9' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}

/* ---- Dark-theme hierarchy refinements ----
   A palette swap gets the colours right, but near-black compresses depth:
   controls that were obvious as white fields on cream become adjacent dark
   rectangles. These few rules restore the light theme's hierarchy without
   adding cards or glow. */
:root[data-theme="dark"] .setrow input,
:root[data-theme="dark"] .kg,
:root[data-theme="dark"] .saveset,
:root[data-theme="dark"] .copylast{
  border-color:var(--rule-strong);
}
:root[data-theme="dark"] .stepbtn{
  background:rgba(var(--ink-rgb),.035);
}
:root[data-theme="dark"] .saveset{
  color:var(--ink);
}
/* Small artwork should read as archival plates rather than rounded app tiles.
   The drawing and its sampled paper are otherwise untouched. */
:root[data-theme="dark"] .exthumb--sm{
  border-radius:7px;
  border-color:var(--rule-strong);
}
/* Licensed artwork is painted on its own warm paper, so on charcoal it is a
   plate whatever we do; the only question is whether that plate looks meant.
   Light dissolves the field into the page because the page is the same paper —
   dark cannot, so the field becomes a deliberate archival plate instead of a
   parchment rectangle with a gradient smeared across its top and bottom edge.
   This is the same answer .exthumb--lg already gives on the preview page. */
:root[data-theme="dark"] .exdet-art{
  margin-inline:0;
  border-radius:14px;
  background:var(--exercise-art-bg);
}
:root[data-theme="dark"] .exdet-art__figure{
  padding-block:var(--exercise-art-lead);
}
/* The gate's two brand images are the same case as the artwork above, and the
   first thing a dark install ever sees. Both are drawn on cream: the hero
   export carries its own field, and the mark keeps a pale rim where its ground
   was taken away — on charcoal that field is a hard white rectangle and the rim
   a smudge around the yoke. So dark gives each one the plate it is already
   painted for. Nothing crops, nothing moves: the paper fills the same box the
   art was contained in, and the corner is the only thing added. */
:root[data-theme="dark"] .firstrun__logo,
:root[data-theme="dark"] .firstrun-hero__art{
  background-color:var(--brand-paper);
}
:root[data-theme="dark"] .firstrun-hero__art{border-radius:14px}
:root[data-theme="dark"] .firstrun__logo{border-radius:16px}
:root[data-theme="dark"] nav{
  backdrop-filter:blur(28px) saturate(125%);
  -webkit-backdrop-filter:blur(28px) saturate(125%);
}

*{box-sizing:border-box}
/* Nudging load or reps means tapping the same 44px target several times in a row,
   and a browser reads two of those taps as double-tap-to-zoom: the page jumps
   mid-set and there is no way back one-handed. A pinch, or a second finger
   landing while the first drags a card, does the same. `pan-x pan-y` leaves the
   browser scrolling and takes both zoom gestures away, and it is what stops them
   in iOS Safari, which ignores `user-scalable=no`. Panning policies set further
   in (the focus card's `touch-action:pan-y`) still win, because the browser
   intersects this with theirs. */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%;touch-action:pan-x pan-y}
button,input,select,textarea,label,summary,a,[role=button]{touch-action:manipulation}
body{
  margin:0;
  font-family:var(--body);
  color:var(--ink);
  background:var(--bg);
  padding-bottom:calc(var(--nav) + env(safe-area-inset-bottom));
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  font-variant-numeric:tabular-nums;
}
h1,h2,h3,p{margin:0}
button,input,select,textarea{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}
button,a[href],summary,input:not([type=hidden]),select,textarea,
[role=button],[role=tab],[role=switch],[role=checkbox],
[tabindex]:not([tabindex="-1"]),.term[data-term]{
  min-width:44px;min-height:44px;
}
main{
  width:min(100%,var(--maxw));margin:0 auto;
  padding:calc(12px + env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) 36px max(16px,env(safe-area-inset-left));
}
.view{display:none;animation:rise .35s cubic-bezier(.2,.7,.2,1) both}
.view.active{display:block}
.hidden{display:none!important}
.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}

/* ---- Typography ---- */
.page-title{
  font-size:30px;font-weight:650;letter-spacing:-0.02em;line-height:1.05;color:var(--ink);
}
.page-date{margin-top:4px;font-size:14px;color:var(--ink-soft);font-weight:400}
.section-label{
  font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;
  color:var(--ink-faint);margin:32px 0 12px;
}
.section-label--accent{color:var(--accent-deep)}
.section-label__count{color:var(--ink-faint)}
.eyebrow{font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:var(--ink-faint);margin-bottom:4px}
.eyebrow--link{background:none;border:0;padding:0;cursor:pointer;text-align:left;color:var(--accent-deep)}
.eyebrow--link::after{content:" ›"}
h2{font-size:30px;font-weight:650;letter-spacing:-0.02em;line-height:1.05}
.subhead{
  font-size:11px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;
  color:var(--ink-faint);margin:26px 0 12px;display:flex;align-items:center;gap:12px;
}
.subhead::after{content:"";flex:1;height:1px;background:var(--rule)}
.lede{color:var(--ink-soft);font-size:14px;line-height:1.5;margin-top:4px}

/* ---- Page headers ---- */
.pagehead{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin:4px 0 18px}
.pagehead__text{min-width:0;flex:1}
.pagehead__actions{display:flex;align-items:center;gap:4px;flex:none;margin-top:4px}
.pagehead--center{position:relative;justify-content:center;align-items:center;min-height:44px}
.pagehead--center .pagehead__back{
  position:absolute;left:0;top:50%;transform:translateY(-50%);
}
.pagehead--center .page-title{font-size:17px;font-weight:600;letter-spacing:0;text-align:center}
.pagehead__back,.back-link{
  background:none;border:0;padding:8px 4px;cursor:pointer;color:var(--accent-deep);
  font-size:15px;font-weight:500;min-height:44px;display:inline-flex;align-items:center;gap:2px;
}
.back-link:focus-visible{border-radius:8px}
.icon-btn{
  width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;
  border:0;background:transparent;color:var(--ink-soft);cursor:pointer;border-radius:50%;
}
.icon-btn:hover{color:var(--ink)}
.icon-btn--profile{border:1.5px solid var(--rule);color:var(--ink-faint);background:transparent}
.icon-btn svg,.nav__icon{display:block}
.text-btn{
  background:none;border:0;padding:8px 4px;cursor:pointer;color:var(--ink-soft);
  font-size:15px;font-weight:500;min-height:44px;
}
.text-btn--accent{color:var(--accent-deep)}
.link-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 0;border-bottom:1px solid var(--rule);cursor:pointer;background:none;border-left:0;border-right:0;border-top:0;width:100%;text-align:left;color:inherit;font:inherit}
.link-row__meta{color:var(--ink-soft);font-size:13px}
.link-row__chev{color:var(--ink-faint);flex:none}

/* ---- Segmented progress ---- */
.segbar{display:flex;gap:6px;margin:8px 0}

/* ---- Primary CTA ---- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  border:1px solid var(--rule);border-radius:var(--radius);background:var(--surface);color:var(--ink);
  padding:13px 16px;font-weight:600;cursor:pointer;font-size:15px;
  transition:transform .12s ease,filter .2s ease,border-color .2s ease,background .2s ease;
}
.btn:active{transform:translateY(1px) scale(.995)}
.btn:disabled{opacity:.4;cursor:default}
.btn--ghost{background:transparent;padding:8px 13px;font-size:13px;border-radius:999px}
.btn--steel{background:var(--surface);border-color:var(--rule-strong)}
.btn--cta{
  position:relative;width:100%;height:54px;border:0;border-radius:var(--radius);
  background:var(--cta);color:var(--cta-ink);font-size:17px;font-weight:600;
  justify-content:center;padding:0 48px;
}
.btn--cta::after{
  content:"";position:absolute;right:18px;top:50%;transform:translateY(-50%);
  width:22px;height:22px;
  /* Brand accent stays here deliberately: on the near-black CTA the brighter
   * orange measures ~4.6:1 while --accent-deep drops to ~3.3:1 (C1). */
  color:var(--accent);background:currentColor;
  -webkit-mask:var(--arrow) no-repeat center/contain;mask:var(--arrow) no-repeat center/contain;
}
.btn--noarrow{padding:0 20px}
.btn--noarrow::after{content:none;display:none}
.btn--danger{background:transparent;color:var(--danger);border-color:transparent}
.btn--save{margin-top:6px;flex-direction:column;gap:2px;height:auto;min-height:54px;padding:12px 48px}
.btn--save.is-stamped{animation:stamp .5s ease}
.btn__meta{font-size:11px;font-weight:500;letter-spacing:.04em;opacity:.75}
.btnrow{display:flex;gap:10px;flex-wrap:wrap}
.btnrow .btn{flex:1 1 160px}
.cta-sub{
  display:block;width:100%;text-align:center;margin:12px 0 0;padding:8px;
  background:none;border:0;cursor:pointer;color:var(--ink);font-size:15px;font-weight:500;
}
.cta-sub--accent{color:var(--accent-deep);text-decoration:underline;text-underline-offset:3px}

/* ---- Fields ---- */
.field{display:block;margin:14px 0 0;color:var(--ink-soft);font-size:12px;font-weight:600;
  text-transform:uppercase;letter-spacing:.08em}
input:not(:where([type=checkbox],[type=radio])),select,textarea{
  width:100%;margin-top:7px;background:var(--surface);border:1px solid var(--rule);
  color:var(--ink);border-radius:12px;padding:13px 12px;font-family:var(--body);
  font-size:16px;letter-spacing:normal;text-transform:none;transition:border-color .15s ease,box-shadow .15s ease;
}
/* Boxes and radios keep a full 44px hit area but paint a 22px control inside it:
   the native rendering blown up to 44px is a slab, and shrinking the element to
   the size of the artwork would put the tap target below the thumb-sized floor
   every other control here holds. `.visually-hidden` proxies (the inputs behind
   the switches) are left on the native rendering — they are clipped anyway, and
   the positioning this needs would knock them out of their clip. */
input[type=checkbox],input[type=radio]{
  width:44px;height:44px;min-width:44px;min-height:44px;margin:0;padding:0;accent-color:var(--accent);
  background:none;border:0;border-radius:0;box-shadow:none;
}
input[type=checkbox]:not(.visually-hidden),input[type=radio]:not(.visually-hidden){
  appearance:none;-webkit-appearance:none;position:relative;flex:none;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
input[type=checkbox]:not(.visually-hidden)::before,input[type=radio]:not(.visually-hidden)::before{
  content:"";position:absolute;top:50%;left:50%;width:22px;height:22px;
  transform:translate(-50%,-50%);border-radius:7px;
  border:1.5px solid var(--rule-strong);background:var(--surface);
  transition:background-color .18s ease,border-color .18s ease,transform .12s ease;
}
input[type=radio]:not(.visually-hidden)::before{border-radius:50%}
input[type=checkbox]:not(.visually-hidden)::after{
  content:"";position:absolute;top:50%;left:50%;width:14px;height:14px;
  transform:translate(-50%,-50%) scale(.2);background:var(--cta-ink);
  -webkit-mask:var(--check) no-repeat center/contain;mask:var(--check) no-repeat center/contain;
  opacity:0;transition:opacity .12s ease,transform .2s cubic-bezier(.2,1.3,.4,1);
}
input[type=radio]:not(.visually-hidden)::after{
  content:"";position:absolute;top:50%;left:50%;width:10px;height:10px;border-radius:50%;
  transform:translate(-50%,-50%) scale(.2);background:var(--cta-ink);
  opacity:0;transition:opacity .12s ease,transform .2s cubic-bezier(.2,1.3,.4,1);
}
input[type=checkbox]:not(.visually-hidden):checked::before,
input[type=radio]:not(.visually-hidden):checked::before{
  background:var(--accent-deep);border-color:var(--accent-deep);
}
input[type=checkbox]:not(.visually-hidden):checked::after,
input[type=radio]:not(.visually-hidden):checked::after{
  opacity:1;transform:translate(-50%,-50%) scale(1);
}
input[type=checkbox]:not(.visually-hidden):active::before,
input[type=radio]:not(.visually-hidden):active::before{transform:translate(-50%,-50%) scale(.9)}
/* The reminder types are disabled until notifications are actually granted, so
   the boxes have to look locked rather than simply on. */
input[type=checkbox]:not(.visually-hidden):disabled,
input[type=radio]:not(.visually-hidden):disabled{cursor:default}
input[type=checkbox]:not(.visually-hidden):disabled::before,
input[type=radio]:not(.visually-hidden):disabled::before,
input[type=checkbox]:not(.visually-hidden):disabled:checked::after,
input[type=radio]:not(.visually-hidden):disabled:checked::after{opacity:.32}
input[type=checkbox]:not(.visually-hidden):disabled:active::before,
input[type=radio]:not(.visually-hidden):disabled:active::before{transform:translate(-50%,-50%)}
input[type=checkbox]:not(.visually-hidden):focus-visible,
input[type=radio]:not(.visually-hidden):focus-visible{outline:none}
input[type=checkbox]:not(.visually-hidden):focus-visible::before,
input[type=radio]:not(.visually-hidden):focus-visible::before{
  box-shadow:0 0 0 3px rgba(var(--accent-rgb),.28);
}
@media (prefers-reduced-motion:reduce){
  input[type=checkbox]:not(.visually-hidden)::before,input[type=radio]:not(.visually-hidden)::before,
  input[type=checkbox]:not(.visually-hidden)::after,input[type=radio]:not(.visually-hidden)::after{transition:none}
}
input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
input[type=number]{-moz-appearance:textfield;appearance:textfield}
textarea{resize:vertical;min-height:80px}
select{appearance:none;-webkit-appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--ink-soft) 50%),linear-gradient(135deg,var(--ink-soft) 50%,transparent 50%);
  background-position:calc(100% - 18px) 19px,calc(100% - 13px) 19px;background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:34px}
input:not(:where([type=checkbox],[type=radio])):focus,select:focus,textarea:focus{
  outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(var(--accent-rgb),.12)}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
select:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---- Toggle (iOS-style) ----
   The switch is a 51x31 track with a 27px knob, but the button that carries it
   stays 44px tall so the tap target matches every other control. Track and knob
   are painted as centred pseudo-elements: styling the button box itself is what
   turned the switch into a 51x44 slab with the knob stranded at its top edge.
   `is-on` is the visual truth (the notifications switch shows it optimistically
   while the permission prompt is up, before `aria-pressed` commits). */
.toggle{
  position:relative;width:51px;height:44px;min-width:51px;flex:none;
  background:none;border:0;border-radius:999px;padding:0;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.toggle::before{
  content:"";position:absolute;top:50%;left:0;width:51px;height:31px;margin-top:-15.5px;
  border-radius:999px;background:var(--rule-strong);
  box-shadow:inset 0 0 0 1px rgba(27,26,23,.05);
  transition:background-color .28s cubic-bezier(.32,.72,0,1);
}
.toggle::after{
  content:"";position:absolute;top:50%;left:2px;width:27px;height:27px;margin-top:-13.5px;
  border-radius:999px;background:var(--knob);
  box-shadow:0 3px 8px rgba(27,26,23,.15),0 3px 1px rgba(27,26,23,.06),0 0 0 .5px rgba(27,26,23,.04);
  transition:transform .28s cubic-bezier(.32,.72,0,1),width .28s cubic-bezier(.32,.72,0,1);
}
.toggle[aria-checked="true"]::before,.toggle[aria-pressed="true"]::before,.toggle.is-on::before{
  background:var(--accent-deep);box-shadow:none;
}
.toggle[aria-checked="true"]::after,.toggle[aria-pressed="true"]::after,.toggle.is-on::after{transform:translateX(20px)}
/* Held down, the knob stretches towards the side it is about to travel to. */
.toggle:active::after{width:31px}
.toggle[aria-checked="true"]:active::after,.toggle[aria-pressed="true"]:active::after,.toggle.is-on:active::after{
  transform:translateX(16px);
}
.toggle[aria-busy="true"]{cursor:progress}
.toggle[aria-busy="true"]::before,.toggle[aria-busy="true"]::after{opacity:.55}
.toggle:focus-visible{outline:none}
.toggle:focus-visible::before{box-shadow:0 0 0 3px rgba(var(--accent-rgb),.35)}
@media (prefers-reduced-motion:reduce){
  .toggle::before,.toggle::after{transition:none}
  .toggle:active::after{width:27px}
  .toggle[aria-checked="true"]:active::after,.toggle[aria-pressed="true"]:active::after,.toggle.is-on:active::after{
    transform:translateX(20px);
  }
}

/* ---- In-page tabs ---- */
.tabs{
  display:flex;gap:0;overflow-x:auto;-webkit-overflow-scrolling:touch;
  border-bottom:1px solid var(--rule);margin:0 0 18px;scrollbar-width:none;
}
.tabs::-webkit-scrollbar{display:none}
.tabs button,.seg.seg--stats button,.seg.seg--window button,#prFilterSeg button{
  flex:none;border:0;background:transparent;color:var(--ink-soft);cursor:pointer;
  padding:12px 14px;font-size:14px;font-weight:500;letter-spacing:0;text-transform:none;
  font-family:var(--body);border-bottom:2px solid transparent;margin-bottom:-1px;border-radius:0;
  white-space:nowrap;
}
.tabs button.active,.seg.seg--stats button.active,.seg.seg--window button.active,#prFilterSeg button.active{
  color:var(--ink);border-bottom-color:var(--accent);background:transparent;box-shadow:none;
}

/* Day tabs / legacy seg */
.seg{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:16px}
.seg:not(.seg--stats):not(.seg--window) button{
  flex:1;min-width:0;border:1px solid var(--rule);background:var(--surface);color:var(--ink-soft);
  cursor:pointer;border-radius:10px;padding:11px 8px;font-weight:600;font-size:13px;
}
.seg:not(.seg--stats):not(.seg--window) button.active{
  color:var(--ink);border-color:var(--accent);background:var(--surface);
}

/* ---- Stat band (unified: value above caption, partial hairlines) ---- */
.statband,.statrow,.metrics{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);margin:12px 0 0;
  background:transparent;
}
.statband--4,.statrow--4,.metrics{grid-template-columns:repeat(4,minmax(0,1fr))}
.statband__cell,.statrow__cell,.metric{
  position:relative;padding:14px 8px;text-align:center;min-width:0;
  border:0;border-radius:0;background:transparent;overflow:visible;
}
.statband__cell:not(:last-child)::after,
.statrow__cell:not(:last-child)::after,
.metric:not(:last-child)::after{
  content:"";position:absolute;right:0;top:20%;bottom:20%;width:1px;background:var(--rule);
}
.statband__n,.statrow__val,.metric__val{
  font-size:22px;font-weight:650;line-height:1.1;color:var(--ink);
  font-variant-numeric:tabular-nums;letter-spacing:-0.01em;
}
.statrow--4 .statrow__val,.metrics .metric__val{font-size:18px}
.statband__n.is-attn,.statrow__cell.is-attn .statrow__val{color:var(--accent-deep)}
.statband__n.is-attn::after,.statrow__val .statrow__dot{
  content:"";display:inline-block;width:6px;height:6px;border-radius:50%;
  background:var(--accent);margin-left:5px;vertical-align:middle;
}
.statrow__dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--accent);margin-left:4px;vertical-align:middle}
.statband__cap,.statrow__cap,.metric__label{
  display:block;margin-top:4px;font-size:12px;color:var(--ink-soft);font-weight:400;
  text-transform:none;letter-spacing:0;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.statrow--4 .statrow__cap,.metrics .metric__label{font-size:11px}
.metric__val small{font-weight:500;font-size:12px;color:var(--ink-soft);margin-left:2px}
.metric--hot::after{display:none!important;content:none!important}

/* ---- Recommendation block ---- */
.recblock,.exdet__rec,.exercise .rec-wrap{
  border-left:3px solid var(--accent);padding:10px 12px 10px 14px;margin:14px 0;
  background:transparent;
}
.recblock__lab,.exdet__rec .chip{
  display:block;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--accent-deep);margin-bottom:4px;background:none;border:0;padding:0;
}
.recblock__lab::before,.chip::before{display:none!important;content:none!important}
.recblock__head{font-size:18px;font-weight:640;color:var(--ink);margin:0 0 2px}
.recblock__body,.rec{font-size:14px;line-height:1.45;color:var(--ink-soft);margin:0}
.recblock__cap{font-size:13px;color:var(--ink-faint);margin:4px 0 0}
.recblock__link{float:right;color:var(--accent-deep);font-size:14px;background:none;border:0;cursor:pointer;padding:0}

/* ---- Radio cards (onboarding, block review) ---- */
.radio-card{
  display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:14px 16px;cursor:pointer;color:var(--ink);font:inherit;
  transition:border-color .15s ease;
}
.radio-card.is-selected{border-color:var(--accent);border-width:1.5px;padding:13.5px 15.5px}
.radio-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.radio-card__title{font-size:16px;font-weight:600;line-height:1.25;color:var(--ink)}
.radio-card__cap{font-size:13px;line-height:1.35;color:var(--ink-soft)}
.radio-card__mark{
  flex:none;width:22px;height:22px;border-radius:50%;border:1.5px solid var(--rule-strong);
  background:transparent;
}
.radio-card.is-selected .radio-card__mark{
  border-color:var(--accent);background:var(--accent);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='white' d='M4.5 8.5L2 6l.7-.7 1.8 1.8 4-4 .7.7z'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:center;background-size:12px;
}
.tag-rec{
  display:inline-block;margin-left:6px;font-size:10px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--accent-deep);vertical-align:middle;
}
#blockStrategies{display:flex;flex-direction:column;gap:10px;margin:0 0 16px}

/* ---- Cards (rare — settings groups) ---- */
.card{
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:0;margin-bottom:18px;overflow:hidden;box-shadow:none;
}
.card > .field,.card > .lede,.card > .btn,.card > .btnrow,.card > details,.card > fieldset,
.card > .notifytypes,.card > label.btn{margin-left:14px;margin-right:14px}
.card > .lede:first-child,.card > .cardtitle{margin-top:14px}
.card > .btn,.card > .btnrow,.card > details,.card > label.btn{margin-bottom:14px}
.cardtitle{
  color:var(--ink-faint);font-size:11px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;margin:14px 14px 4px;padding:0;
}
.card--danger{border-color:rgba(var(--danger-rgb),.25)}
.card--flat{background:transparent;border:0;border-radius:0;padding:0}
/* Settings is a list of rows, not a stack of cards. A box drawn around every
   group spends a border, a radius and a fill to say what the caption above it
   and the space below it already say — and it said it in one theme only, since
   dark dropped the boxes years ago and read the better for it. Unboxed, the
   space before a caption *is* the boundary, so it has to be wide enough to be
   read as one: 34px above a caption against 8px below it. */
.settings-group{margin-bottom:34px}
.settings-group > .lede{font-size:12px;text-transform:none;letter-spacing:0;font-weight:400;padding:0 0 12px;margin:0;color:var(--ink-soft)}
.settings-group__label{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-faint);margin:0 0 8px var(--settings-inset);
}
.settings-row{
  position:relative;
  display:flex;align-items:center;gap:12px;min-height:56px;padding:12px var(--settings-inset);
  border:0;background:transparent;
  width:100%;text-align:left;appearance:none;cursor:pointer;
}
/* The rule goes above each row rather than below it, and starts where the row's
   label does. A line run edge to edge reads as a page separator; one held to
   the text column reads as the rows belonging to each other. Drawing it above
   also keeps a row joined to whatever it opens or explains: the caption under a
   toggle, and the panel under a disclosure, are the tail of that row, so the
   next rule falls after them where the next row begins. */
.settings-row::before{
  content:"";position:absolute;left:var(--settings-inset);right:0;top:0;height:1px;background:var(--rule);
}
div.settings-row{cursor:default}
.settings-row:first-child::before{display:none}
.settings-row__label{flex:1;min-width:0;font-size:16px;font-weight:500;color:var(--ink)}
.settings-row__cap{display:block;font-size:12px;color:var(--ink-soft);font-weight:400;margin-top:2px}
.settings-row__value{flex:none;margin-left:auto;display:inline-flex;align-items:center;gap:8px;color:var(--ink-soft);font-size:15px}
.settings-row__chev,.settings-row .chevron{color:var(--ink-faint);flex:none}
.settings-row--danger .settings-row__label,.settings-row.is-danger .settings-row__label{color:var(--danger)}
/* The one row that is still a box: a destructive action is not part of the
   list above it, and the outline is what says so. */
.settings-row.is-danger,#reset.settings-row{
  margin-top:24px;border:1px solid var(--rule);border-radius:var(--radius);
  min-height:56px;
}
.settings-row.is-danger::before,#reset.settings-row::before{display:none}
.settings-row select,.select-flat{
  appearance:none;-webkit-appearance:none;border:0;background-color:transparent;
  margin:0;padding:0 18px 0 0;width:auto;min-width:0;text-align:right;color:var(--ink);
  font-size:16px;border-radius:0;box-shadow:none;
  background-image:var(--select-caret);
  background-position:right center;background-size:14px 14px;background-repeat:no-repeat;
}
.settings-row select:focus,.select-flat:focus{box-shadow:none;border:0}
.settings-row select:focus-visible,.select-flat:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px}
.settings-profile{
  display:flex;align-items:center;gap:14px;padding:8px 0 18px;
}
.settings-profile__mark{
  width:52px;height:52px;border-radius:50%;background:var(--cta);color:var(--cta-ink);
  display:flex;align-items:center;justify-content:center;font-weight:700;font-size:14px;flex:none;
}
.settings-profile__name{font-size:17px;font-weight:600}
.settings-profile__cap{font-size:13px;color:var(--ink-soft);margin-top:2px}

/* ---- Today dashboard ---- */
.today-program{padding:4px 0 16px;border-bottom:1px solid var(--rule)}
.today-program__name{font-size:16px;font-weight:600}
.today-program__week{font-size:14px;color:var(--ink-soft);margin-top:2px}
.today-session{padding:4px 0 8px}
.today-session__name{font-size:28px;font-weight:650;letter-spacing:-0.02em;margin:4px 0 6px}
.today-session__muscles{font-size:15px;color:var(--ink-soft)}
.today-session__meta{font-size:14px;color:var(--ink-soft);margin-top:4px}
.today-ready{display:flex;align-items:center;gap:8px;margin:12px 0 0;font-size:14px;color:var(--ink);cursor:pointer;background:none;border:0;padding:0;text-align:left;min-height:44px}
.today-ready__dot{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:none}
.today-session + .btn--cta,#startWorkout{margin-top:32px}
/* The alternative to the day Today leads with, so it sits under the CTA and
   reads as the quieter of the two. */
.today-otherday{width:100%;min-height:48px;margin-top:10px}
.weekstrip{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-top:12px;text-align:center}
.weekstrip__day{display:flex;flex-direction:column;align-items:center;gap:8px;font-size:12px;color:var(--ink-soft);font-weight:500}
.weekstrip__mark{width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink)}
.weekstrip__dot{width:6px;height:6px;border-radius:50%;background:var(--rule-strong)}
.weekstrip__dot.is-today{width:8px;height:8px;background:var(--accent)}
.today-next{margin-top:4px}
.today-last{display:flex;align-items:center;gap:8px;margin:22px 0 8px;font-size:13px;color:var(--ink-faint)}

/* ---- Active workout ---- */
.wo-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px}
.wo-head__center{text-align:center;min-width:0;flex:1}
.wo-head__title{font-size:17px;font-weight:600}
.wo-head__sub{font-size:13px;color:var(--ink-soft);margin-top:2px}
.wo-head__end{display:flex;align-items:center;gap:6px;flex:none;position:relative;justify-self:end}
/* Rest lives in the workout chrome, never over the card's controls: a square
   with the stopwatch while idle, a counting pill once it is running. */
.wo-rest{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  width:44px;height:44px;padding:0;flex:none;cursor:pointer;
  border:1px solid var(--rule-strong);border-radius:12px;
  background:var(--surface);color:var(--ink);
  font-family:var(--mono);font-size:15px;line-height:1;
  transition:border-radius .2s ease,width .2s ease,color .2s ease,border-color .2s ease;
}
.wo-rest__dot{display:none;width:8px;height:8px;border-radius:50%;background:var(--accent);flex:none}
.wo-rest__time{display:none;font-variant-numeric:tabular-nums}
.wo-rest.is-running{width:auto;min-width:88px;padding:0 14px;border-radius:999px}
.wo-rest.is-running .wo-rest__dot,.wo-rest.is-running .wo-rest__time{display:block}
.wo-rest.is-running .wo-rest__icon{display:none}
.wo-rest.is-done{color:var(--accent-deep);border-color:var(--accent)}
@media (prefers-reduced-motion:reduce){.wo-rest{transition:none}}
/* The floating bar is the List-mode surface; Focus reads it from the header. */
body.is-focus-wo .restbar{display:none!important}

.wo-progress{margin:2px 0 12px}
.wo-progress__top{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px}
.wo-progress__lab{font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft)}
.focusnav{
  flex:none;width:44px;height:44px;padding:0;border:0;background:none;cursor:pointer;
  color:var(--ink);font-size:24px;line-height:1;border-radius:50%;
}
.focusnav:disabled{color:var(--ink-faint);cursor:default}
.segbar--ex{gap:8px;margin:0}
body.is-workout #logWorkspace{padding-bottom:72px}
body.is-workout #logForm{padding-bottom:24px}
/* Focus mode ends at the deck: the log-set button and the set list that the
   padding in List mode clears both live inside the card. */
body.is-workout.is-focus-wo #logWorkspace,
body.is-workout.is-focus-wo #logForm{padding-bottom:0}
/* Focus fills the screen by layout, not by arithmetic: every box between the
   viewport and the deck hands its height down, so the card cannot be left short
   by a measurement taken while the viewport was something else. */
/* The card is the session: Focus gives it the whole screen and puts the way
   back in the header, where the mockups put it. */
body.is-focus-wo nav{display:none}
body.is-focus-wo{padding-bottom:0}
body.is-workout.is-focus-wo main{
  display:flex;flex-direction:column;
  height:calc(100dvh - env(safe-area-inset-bottom));
  padding-bottom:10px;
}
body.is-workout.is-focus-wo #log.view.active,
body.is-workout.is-focus-wo #workoutShell,
body.is-workout.is-focus-wo #logForm,
body.is-workout.is-focus-wo #workout{display:flex;flex-direction:column;flex:1;min-height:0}
body.is-workout.is-focus-wo .deck{flex:1;min-height:0}
body.is-workout.is-focus-wo #dayTabs,
body.is-workout.is-focus-wo #logForm > .field,
body.is-workout.is-focus-wo #logForm > .btn--save{display:none!important}

/* ---- Focus deck ----
   A paged track: the live card at rest position, an inert copy of each
   neighbour parked just off either side. Dragging moves all three as one. */
body.is-focus-wo{overflow-x:hidden}
.deck{position:relative;margin:4px 0 0;overflow-x:clip;overflow-clip-margin:24px}
/* Each card sits in its own slot; the track carries all three as one. */
.deck__track{position:absolute;inset:0;will-change:transform}
.deck__track.is-settling{transition:transform .21s cubic-bezier(.2,.7,.2,1)}
.deck__slot{position:absolute;top:0;bottom:0;width:100%}
.deck__slot--prev,.deck__slot--next{visibility:hidden;pointer-events:none}
.deck__slot--prev{right:calc(100% + 14px)}
.deck__slot--next{left:calc(100% + 14px)}
.deck.is-swiping .deck__slot,
.deck__track.is-settling .deck__slot{visibility:visible}
@media (prefers-reduced-motion:reduce){
  .deck__track.is-settling{transition:none}
}

/* ---- Focus card ---- */
article.exercise--focus{
  position:absolute;inset:0;margin:0;
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--surface);border:1px solid var(--rule);border-radius:18px;
  padding:0;box-shadow:0 1px 2px rgba(0,0,0,.04),0 10px 30px rgba(0,0,0,.10);
  touch-action:pan-y;
  -webkit-user-select:none;user-select:none;-webkit-touch-callout:none;
}
article.exercise--focus input,article.exercise--focus textarea{-webkit-user-select:text;user-select:text}
article.exercise--focus.is-dragging{box-shadow:0 18px 44px rgba(0,0,0,.20)}
article.exercise--focus.is-peek{box-shadow:0 1px 2px rgba(0,0,0,.04),0 10px 30px rgba(0,0,0,.08)}
#workout.is-focus > .exercise{display:none}

.fcard__head{flex:none;padding:18px 20px 0}
.focus-ex__eyebrow{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
.focus-ex__muscle{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.focus-ex__setof{flex:none;font-size:12px;font-weight:600;color:var(--ink-soft);text-transform:uppercase;letter-spacing:.06em;white-space:nowrap}
.focus-ex__setof b{color:var(--accent-deep);font-weight:700}
.focus-ex__title{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-top:5px}
.focus-ex__titletext{min-width:0;flex:1}
.focus-ex__name{font-size:27px;font-weight:650;letter-spacing:-0.02em;margin:0;line-height:1.14}
.focus-ex__target{font-size:13px;color:var(--ink-soft);margin:6px 0 0}
.focus-ex__alvo{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin-right:6px}
.focus-ex__tools{display:flex;align-items:center;gap:8px;flex:none;margin-top:2px}
/* One shape for every card tool. */
.focus-ex__tools .focus-tool{
  position:relative;flex:none;width:44px;height:44px;min-height:44px;padding:0;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--rule-strong);border-radius:12px;background:var(--surface);color:var(--ink);
  cursor:pointer;font-size:inherit;text-transform:none;letter-spacing:0;
}
.focus-ex__tools .focus-tool:active{background:var(--bg)}
.focus-ex__tools .focus-tool.has-note::after{
  content:"";position:absolute;top:7px;right:7px;width:6px;height:6px;border-radius:50%;background:var(--accent);
}

/* ---- Ledger: the only scrolling region of the card ---- */
.fcard__ledger{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  touch-action:none;
  padding:0 20px 6px;margin-top:18px;
  scrollbar-width:thin;scrollbar-color:var(--rule-strong) transparent;
}
.fcard__ledger.is-scrollable{touch-action:pan-y}
.fcard__ledger::-webkit-scrollbar{width:4px}
.fcard__ledger::-webkit-scrollbar-thumb{background:var(--rule-strong);border-radius:4px}
.fcard__ledger::-webkit-scrollbar-track{background:transparent}
.ledger__top{position:sticky;top:0;z-index:1;background:var(--surface)}
.ledger__lab{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin:0 0 10px}
.ledger__count{font-size:15px;color:var(--ink-soft);margin:0 0 10px}
.ledger__ticks{display:flex;flex-wrap:wrap;gap:7px;margin:0 0 16px}
.ledger__tick{
  width:24px;height:24px;border-radius:50%;border:1px solid var(--rule-strong);
  display:inline-block;position:relative;
}
.ledger__tick::after{
  content:"";position:absolute;inset:0;background:var(--ink-faint);
  -webkit-mask:var(--check) no-repeat center/12px;mask:var(--check) no-repeat center/12px;
}
.ledger__head,.ledger__row{
  display:grid;grid-template-columns:2.4rem minmax(0,1.5fr) minmax(0,1fr) minmax(0,1.2fr) 1.75rem;gap:12px;
  align-items:center;font-variant-numeric:tabular-nums;width:100%;
}
.ledger__head{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);
  padding:0 0 10px;border-bottom:1px solid var(--rule-strong);
}
.ledger__head span,.ledger__row > span{min-width:0}
.ledger__n{padding-left:2px;font-weight:600;color:var(--ink-soft)}
.ledger__load{white-space:nowrap}
.ledger__row{
  position:relative;width:100%;min-height:52px;padding:6px 0;margin:0;
  border:0;border-bottom:1px solid var(--rule);border-radius:0;
  background:none;color:var(--ink);font:inherit;font-size:16px;font-weight:450;
  text-align:left;cursor:pointer;
}
.ledger__row .ledger__n{color:var(--ink)}
.ledger__check{
  width:20px;height:20px;justify-self:end;background:var(--ink);
  -webkit-mask:var(--check) no-repeat center/contain;mask:var(--check) no-repeat center/contain;
}
.ledger__row.is-past,.ledger__row.is-empty{cursor:default}
.ledger__row.is-past .ledger__check,.ledger__row.is-empty .ledger__check{display:none}
.ledger__row.is-empty{grid-template-columns:1fr auto 24px;color:var(--ink-soft)}
.fcard__ledger:has(.ledger__row.is-past) .ledger__head,
.fcard__ledger:has(.ledger__row.is-past) .ledger__row.is-past{
  grid-template-columns:2.4rem minmax(0,1.6fr) minmax(0,1fr) minmax(0,1.3fr);
}
.ledger__empty{color:var(--ink-soft)}
.ledger__dash{color:var(--rule-strong)}
/* A logged effort reads as its word, which needs more room than a RIR digit. */
.ledger__eff{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The set being re-opened is marked in place: it stays where it was logged. */
.ledger__row.is-editing{background:rgba(var(--accent-rgb),.07)}
.ledger__row.is-editing::before{
  content:"";position:absolute;left:-20px;top:0;bottom:0;width:3px;background:var(--accent);
}
.ledger__row.is-editing .ledger__check{background:var(--accent)}
.ledger__more{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;min-height:52px;padding:8px 0;margin:0;
  border:0;border-bottom:1px solid var(--rule);border-radius:0;
  background:none;color:var(--ink-soft);font:inherit;font-size:16px;text-align:left;cursor:pointer;
}
.ledger__more .icon-mask{transition:transform .2s ease}
.ledger__more[aria-expanded="true"] .icon-mask{transform:rotate(180deg)}
@media (prefers-reduced-motion:reduce){.ledger__more .icon-mask{transition:none}}
/* Every set that is not in the well still needs a field to hold its value. */
.focus-inputs{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}

/* ---- Well: attached, and the same height whatever the ledger is doing ---- */
.focus-well{
  flex:none;padding:14px 20px 16px;background:var(--well);border-top:1px solid var(--rule);
}
.focus-cue{display:flex;align-items:center;flex-wrap:wrap;gap:0 6px;font-size:14px;line-height:1.35;margin:0}
.focus-cue__bolt{
  flex:none;width:19px;height:19px;background:var(--accent);
  -webkit-mask:var(--bolt) no-repeat center/contain;mask:var(--bolt) no-repeat center/contain;
}
.focus-cue__lab{color:var(--accent-deep);font-size:13px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}
.focus-cue__sep{color:var(--ink-faint)}
.focus-cue__text{color:var(--ink-soft);min-width:0}
.focus-well__cancel{
  display:block;width:100%;min-height:44px;margin:8px 0 0;padding:8px;
  border:0;background:none;cursor:pointer;color:var(--ink-soft);font-size:16px;text-align:center;
}
.focus-well .btn--cta{margin-top:12px}
.focus-well .btn--cta.saveset{
  border:0;background:var(--cta);color:var(--cta-ink);
  text-transform:none;letter-spacing:normal;font-size:17px;font-weight:600;
  min-height:54px;min-width:0;padding:0 20px;border-radius:var(--radius);
}
.focus-well .btn--cta.saveset:hover{color:var(--cta-ink);border-color:transparent}
/* Exercise and workout completion are the same shape, one step apart. */
.focus-well.is-done{padding-top:18px;padding-bottom:24px}
.focus-done{display:flex;align-items:center;gap:14px}
.focus-done__mark{
  flex:none;width:46px;height:46px;border-radius:50%;border:2px solid var(--accent);position:relative;
}
.focus-done__mark::after{
  content:"";position:absolute;inset:0;background:var(--accent);
  -webkit-mask:var(--check) no-repeat center/22px;mask:var(--check) no-repeat center/22px;
}
.focus-done__text{min-width:0}
.focus-done__title{font-size:18px;font-weight:650;letter-spacing:-.01em}
.focus-done__sub{font-size:14px;color:var(--ink-soft);margin-top:3px}

/* ---- A set lands ----
   Logging a set re-renders the whole card, so the motion is written into the
   markup of that one render (`is-fresh`) rather than driven from a handler:
   the card the lifter sees next is the card that plays it, once.
   Everything here moves transform, opacity and colour only. The ledger sizes
   and scrolls itself to the newest row straight after the render, and that
   measurement has to read the settled layout, not a frame of the animation. */
.ledger__row.is-fresh{
  animation:setland-row .34s cubic-bezier(.2,.7,.2,1) both,
            setland-wash .8s cubic-bezier(.3,0,.6,1) backwards;
}
@keyframes setland-row{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes setland-wash{
  0%,35%{background-color:rgba(var(--accent-rgb),.13)}
  100%{background-color:rgba(var(--accent-rgb),0)}
}
/* The tick is the receipt for the set: it snaps in accent, settles to ink. */
.ledger__row.is-fresh .ledger__check{animation:setland-check .46s .12s cubic-bezier(.2,1.3,.4,1) both}
@keyframes setland-check{
  0%{opacity:0;transform:scale(.3);background-color:var(--accent)}
  55%{opacity:1;transform:scale(1.16);background-color:var(--accent)}
  100%{opacity:1;transform:scale(1);background-color:var(--ink)}
}
/* Past the fold the ticks are the ledger, so the newest one lands the same way. */
.ledger__tick.is-fresh{animation:setland-tick .44s .1s cubic-bezier(.2,1.3,.4,1) both}
@keyframes setland-tick{
  0%{opacity:0;transform:scale(.4)}
  55%{opacity:1;transform:scale(1.14)}
  100%{opacity:1;transform:scale(1)}
}
/* The counter moved on, so it ticks over rather than swapping in place. */
.focus-ex__setof.is-fresh b{display:inline-block;animation:setland-count .38s cubic-bezier(.2,.9,.3,1) both}
@keyframes setland-count{
  0%{opacity:0;transform:translateY(-7px)}
  60%{opacity:1}
  100%{opacity:1;transform:none}
}
/* The well re-arms for the next set: the cue first, its numbers just behind. */
.focus-well.is-fresh .focus-cue{animation:setland-arm .3s .04s cubic-bezier(.2,.7,.2,1) both}
.focus-well.is-fresh .curset{animation:setland-arm .3s .1s cubic-bezier(.2,.7,.2,1) both}
@keyframes setland-arm{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}
/* Finishing the exercise is the larger beat: the mark draws in under a ring. */
.focus-well.is-fresh.is-done .focus-done__mark{animation:setland-mark .44s cubic-bezier(.2,1.25,.4,1) both}
.focus-well.is-fresh.is-done .focus-done__mark::before{
  content:"";position:absolute;inset:-3px;border-radius:50%;border:2px solid var(--accent);
  animation:setland-ring .66s .08s cubic-bezier(.2,.7,.2,1) both;
}
.focus-well.is-fresh.is-done .focus-done__text{animation:setland-arm .34s .1s cubic-bezier(.2,.7,.2,1) both}
@keyframes setland-mark{
  0%{opacity:0;transform:scale(.55)}
  60%{opacity:1;transform:scale(1.08)}
  100%{opacity:1;transform:scale(1)}
}
@keyframes setland-ring{from{opacity:.6;transform:scale(1)}to{opacity:0;transform:scale(1.55)}}

/* Rest bar — one floating clock, parked just above the tab bar in every mode */
.restbar{
  position:fixed;right:max(14px,env(safe-area-inset-right));
  bottom:calc(var(--nav) + 16px + env(safe-area-inset-bottom));z-index:28;
  display:inline-flex;align-items:center;gap:6px;
  border:1px solid var(--rule);background:var(--surface);color:var(--ink);
  border-radius:12px;padding:8px 12px;cursor:pointer;
  font-family:var(--mono);font-size:13px;line-height:1;min-height:44px;
  box-shadow:0 4px 16px rgba(0,0,0,.08);
}
.restbar.hidden{display:none!important}
.restbar__dot{width:7px;height:7px;border-radius:50%;background:var(--accent)}
.restbar.is-done{border-color:var(--accent);color:var(--accent-deep)}
.restbar__time{min-width:2.5em;text-align:center}

/* Overtime — the clock has passed zero and is counting up in the negative.
   A slow breathing ring plus a blinking dot reads as "still waiting", which no
   part of the countdown does. Placed after both rest blocks so it wins. */
@keyframes restover-ring{
  0%,100%{box-shadow:0 0 0 0 rgba(var(--danger-rgb),0)}
  50%{box-shadow:0 0 0 5px rgba(var(--danger-rgb),.15)}
}
@keyframes restover-dot{
  0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.25;transform:scale(.72)}
}
.wo-rest.is-over,.restbar.is-over{
  color:var(--danger);border-color:var(--danger);
  animation:restover-ring 1.5s ease-in-out infinite;
}
.wo-rest.is-over .wo-rest__dot,.restbar.is-over .restbar__dot{
  background:var(--danger);animation:restover-dot 1.5s ease-in-out infinite;
}
.restbar.is-over .restbar__time{min-width:3.4em}
/* Held in the timer sheet: the clock is frozen, so the live dot goes quiet. */
.wo-rest.is-paused .wo-rest__dot,.restbar.is-paused .restbar__dot{background:var(--ink-faint)}
@media (prefers-reduced-motion:reduce){
  /* Colour alone carries overtime; the dot holds at half strength so it still
     differs from the steady countdown dot. */
  .wo-rest.is-over,.restbar.is-over,
  .wo-rest.is-over .wo-rest__dot,.restbar.is-over .restbar__dot{animation:none}
  .wo-rest.is-over .wo-rest__dot,.restbar.is-over .restbar__dot{opacity:.5}
}

/* Hidden legacy gauge (tests may still poke it; UI uses Today readiness) */
.gauge{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}

/* ---- Exercise cards (log) ---- */
.exercise{
  position:relative;background:transparent;border:0;border-bottom:1px solid var(--rule);
  border-radius:0;padding:18px 0;margin-bottom:0;overflow:visible;box-shadow:none;
  --heat:var(--ink-faint);
}
.exercise::before{display:none}
.exercise.is-add,.exercise.is-add2{--heat:var(--accent)}
.exercise.is-hold{--heat:var(--accent)}
.exercise.is-reduce{--heat:var(--ink-soft)}
.exercise.is-new{--heat:var(--ink-faint)}

.ex__top{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.ex__head{flex:1;min-width:0}
.ex__name,.ex__nameh{font-size:22px;font-weight:640;line-height:1.15;letter-spacing:-0.01em}
.ex__tag{
  display:inline-block;font-size:11px;letter-spacing:.08em;color:var(--ink-faint);
  border:0;padding:0;text-transform:uppercase;vertical-align:middle;margin-right:10px;margin-bottom:4px;
  font-weight:600;
}
.ex__meta{font-size:13px;color:var(--ink-soft);margin-top:6px;line-height:1.5}
.nowrap{white-space:nowrap}
.subst{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 8px;font-size:12px}
/* Which slot a swap stands in gets the whole line rather than trailing the
   picker, so a long seed name wraps as a sentence instead of a column. */
.subst__from{flex:1 0 100%;margin:0;font-size:12px;line-height:1.35;color:var(--ink-faint)}
.subst__pick{flex:1;min-width:0;margin:0;padding:6px 9px;font:inherit;font-size:16px;color:var(--ink);background:var(--surface);border:1px solid var(--rule);border-radius:8px;text-align:left;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Effort picker — the Easy / Hard / Max face of RIR. One control in two shapes:
   a compact line under a set row, a stacked band under the current set. Three
   words never fit a column beside the numbers, so it is never asked to. */
.effort{display:flex;background:var(--surface);border:1px solid var(--rule);border-radius:11px;overflow:hidden}
.effort__btn{flex:1 1 0;min-width:0;margin:0;padding:0 4px;min-height:46px;border:0;border-radius:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  background:transparent;color:var(--ink-soft);cursor:pointer;
  font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;line-height:1.1;
  transition:background .15s ease,color .15s ease}
.effort__btn+.effort__btn{border-left:1px solid var(--rule)}
.effort__word{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.effort__btn:not(.active):hover{color:var(--ink);background:var(--bg)}
.effort__btn:focus-visible{outline:2px solid var(--accent);outline-offset:-3px}
.effort__btn.active{color:var(--accent-ink);background:var(--accent-deep)}
/* Until the lifter picks, the highlighted word is Taurifer's guess from the
   last session: it reads as a tint, so a confirmed set never looks the same. */
.effort--suggested .effort__btn.active{color:var(--accent-deep);background:rgba(var(--accent-rgb),.09)}
.rirmode{border:0;padding:0;margin:0 0 14px}
.rirmode legend{font-size:11px;color:var(--ink-soft);margin-bottom:6px;text-transform:uppercase;letter-spacing:.07em;font-weight:600}
.rirmode__opts{display:flex;flex-wrap:wrap;align-items:center;gap:10px 16px}
.rirmode__opt{display:inline-flex;align-items:center;gap:12px;margin:0;font-size:15px;font-weight:500;letter-spacing:normal;text-transform:none;color:var(--ink);cursor:pointer;white-space:nowrap;-webkit-tap-highlight-color:transparent}
.rirmode__opt input{width:auto;flex:none;margin:0 -11px}

.heat{display:none} /* heat strip removed; recommendation block carries the signal */
.chip{
  display:inline-flex;align-items:center;gap:6px;font-weight:600;
  text-transform:uppercase;letter-spacing:.08em;font-size:11px;color:var(--accent-deep);white-space:nowrap;
}
.rec{font-size:14px;line-height:1.45;color:var(--ink-soft);margin:0 0 12px}
.rec b{color:var(--ink);font-weight:600}
.prev{font-size:12px;color:var(--ink-soft);margin:0 0 3px;display:flex;flex-wrap:wrap;align-items:center;gap:4px 10px}
.prev span{color:var(--ink-faint)}
.delta-prev{font-size:12px;color:var(--ink-faint);margin:0 0 10px}
.rec__block{font-size:12px;line-height:1.4;color:var(--ink-faint);margin:0 0 12px;font-style:italic}
.insession{font-size:12px;line-height:1.4;color:var(--ink);margin:0 0 10px;padding:6px 9px;
  border-left:2px solid var(--accent);border-radius:0 6px 6px 0;background:rgba(var(--accent-rgb),.04)}

.sets__head,.setrow{display:grid;grid-template-columns:44px minmax(0,2.6fr) minmax(0,.95fr) minmax(0,.75fr) auto;gap:6px;align-items:center}
/* Effort mode drops the RIR column; the picker spans a second line, and the
   header's caption for it spans the same one so the two stay in step. */
.sets__head.has-effort,.setrow.has-effort{grid-template-columns:44px minmax(0,2.6fr) minmax(0,.95fr) auto}
.setrow.has-effort .effort,.sets__head .sets__head-eff{grid-column:1/-1}
.sets__head .sets__head-eff{text-align:left;padding-top:6px}
.setrow.has-effort{row-gap:7px;padding-bottom:11px;border-bottom:1px solid var(--rule)}
.setrow.has-effort+.setrow.has-effort{margin-top:11px}
.sets__head{margin-bottom:4px;padding-top:8px;border-top:1px solid var(--rule)}
.setrow>*{min-width:0}
.setrow>.setrow__n{min-width:44px;width:44px}
.sets__head span{font-size:11px;color:var(--ink-faint);letter-spacing:.08em;text-align:center;text-transform:uppercase;font-weight:600;white-space:nowrap}
.sets__head span:first-child{text-align:left}
.setrow{margin-top:8px}
.setrow__n{font-weight:600;color:var(--ink-soft);font-size:15px;text-align:center;
  background:none;border:0;padding:0;cursor:pointer;line-height:1;min-width:44px;min-height:44px;width:44px;justify-self:start}
.setrow.is-done .saveset{color:var(--cta-ink);border-color:transparent;background:var(--cta)}
/* One exercise, one worded action. Three stacked SAVEs shouted the same verb
   down the column and made a log read like a form, so only the set actually
   being worked keeps the word: the sets waiting behind it hold the same button
   at the same size, drawn as the check they are about to become. */
.setrow:not(.is-next):not(.is-done) .saveset{border-color:transparent;background:transparent}
.setrow:not(.is-next):not(.is-done) .saveset .saveset__label{visibility:hidden}
.setrow:not(.is-next):not(.is-done) .saveset::after{
  content:"";position:absolute;inset:0;background:var(--ink-faint);
  -webkit-mask:var(--check) no-repeat center/15px;mask:var(--check) no-repeat center/15px;
}
.setrow:not(.is-next):not(.is-done) .saveset:hover::after{background:var(--ink)}
.setrow.is-next .saveset{color:var(--ink);border-color:var(--ink-soft)}
.setrow.is-next .setrow__n{color:var(--accent-deep)}
.setrow.is-next .setrow__n::after{content:"";display:block;margin:3px auto 0;width:4px;height:4px;border-radius:50%;background:var(--accent)}
/* The save column is content-sized, so the committed ✓ overlays the label
   rather than replacing it — a narrower button would drag the row's inputs
   out of line with the rows above it and with the header. */
.saveset,.sets__head .sets__head-save{border:1px solid transparent;
  text-transform:uppercase;letter-spacing:.05em;font-size:11.5px;font-weight:700;
  padding:0 8px;min-width:46px}
.saveset{position:relative;flex:none;border-color:var(--rule);background:var(--surface);color:var(--ink-soft);
  border-radius:10px;cursor:pointer;min-height:46px;line-height:1}
.saveset:hover{color:var(--ink);border-color:var(--ink-soft)}
.setrow.is-done .saveset__label{visibility:hidden}
/* Drawn, not typed, for the same reason as --arrow: U+2713 is outside the
 * Plex Sans subset, so a "✓" here fell back to a platform symbol font and
 * came out at a different weight on Android than on iOS. */
.setrow.is-done .saveset::after{content:"";position:absolute;inset:0;
  background:currentColor;
  -webkit-mask:var(--check) no-repeat center/15px;mask:var(--check) no-repeat center/15px}
/* Header mirror of the save button: hidden, but it holds the column open so
   the KG / REPS / RIR labels stay centred over their inputs. */
.sets__head .sets__head-save{visibility:hidden;height:0;border-block-width:0}

.setrow input{margin-top:0;text-align:center;font-weight:500;font-size:16px;padding:12px 4px;
  background:var(--surface);border-color:var(--rule);font-variant-numeric:tabular-nums;min-height:46px}
.setrow.is-suggested input{color:var(--ink-soft)}

.kg{display:flex;align-items:stretch;min-width:0;background:var(--surface);border:1px solid var(--rule);
  border-radius:12px;overflow:hidden}
.kg:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px rgba(var(--accent-rgb),.12)}
.kg input{width:auto;flex:1;min-width:0;margin-top:0;padding-left:0;padding-right:0;
  border:0;background:transparent;border-radius:0;min-height:44px}
.kg input:focus{box-shadow:none;border-color:transparent}
/* The −/+ glyph is not text to be selected: the second tap of a burst would
   otherwise raise a selection and iOS's magnifier over the control. */
.stepbtn{flex:none;width:44px;min-width:44px;min-height:44px;border:0;background:var(--bg);color:var(--ink-soft);
  cursor:pointer;font-size:20px;line-height:1;padding:0;
  -webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
.stepbtn:hover{color:var(--ink)}
.copylast{flex:none;margin-left:auto;border:1px solid var(--rule);background:var(--surface);color:var(--ink-soft);
  text-transform:uppercase;letter-spacing:.06em;font-size:11px;font-weight:700;
  border-radius:8px;padding:8px 11px;cursor:pointer}
.copylast:hover{color:var(--accent-deep);border-color:var(--ink-soft)}

/* Current-set cells — bare numbers over a hairline, with the −/+ underneath.
   The figure size is a variable because the effort word has to know it: that
   column reads as a word, but it holds the same slot as the numbers beside it. */
.curset{--curset-val:34px;margin:10px 0 0}
.curset__grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}
.curset__cell{min-width:0;padding:8px 2px 0;text-align:center;position:relative}
.curset__cell:not(:last-child)::after{
  content:"";position:absolute;right:0;top:12%;bottom:8%;width:1px;background:var(--rule);
}
.curset__cell-lab{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin-bottom:8px;
  display:flex;align-items:baseline;justify-content:center;gap:5px;white-space:nowrap}
.curset__cell-lab.is-accent{color:var(--accent-deep)}
.curset__val,.curset__cell input.curset__val{
  width:100%;max-width:100%;box-sizing:border-box;margin:0;padding:0;border:0;background:transparent;border-radius:0;
  font-size:var(--curset-val);font-weight:650;line-height:1.06;color:var(--ink);text-align:center;
  font-variant-numeric:tabular-nums;height:44px;min-height:44px;box-shadow:none;
}
.curset__val::placeholder{color:var(--rule-strong);opacity:1}
.curset__cell input.curset__val:focus{outline:none;box-shadow:none;border-color:transparent}
.curset__cell input.curset__val:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
/* An effort is a word: a size that keeps three of them on one line, in the same
   slot height as the figures, so this column's hairline sits on their line. */
.curset__val--word{
  font-size:26px;font-weight:650;letter-spacing:-.01em;
  display:flex;align-items:center;justify-content:center;
  height:44px;min-height:44px;
}
.curset__val--word:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}
/* The word is also the question: tap it and its explainer pops off the top. */
.curset__val--word[data-effspin]{
  cursor:pointer;transition:color .18s ease,transform .18s cubic-bezier(.2,1.2,.4,1);
}
.curset__val--word[data-effspin]:active{transform:scale(.94)}
.curset__val--word[data-effspin].is-open{color:var(--accent)}
.unit-hint{
  font-size:10px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--ink-faint);
}
.curset__cell-lab .unit-hint{font-size:10px;color:var(--ink-faint)}
.curset__cell-lab.is-accent .unit-hint{color:var(--ink-soft)}
.sets__head .unit-hint,.ledger__head .unit-hint{margin-left:4px;font-size:10px;letter-spacing:0;text-transform:none;font-weight:500}
.curset__underline{display:block;height:2px;margin:8px auto 0;width:56%;background:var(--rule-strong);border-radius:1px}
.curset__cell.is-active .curset__underline,.curset__cell.is-load .curset__underline{background:var(--accent)}
.curset__steps{display:flex;justify-content:space-evenly;align-items:center;margin-top:2px}
.curset__steps .stepbtn{
  width:44px;height:44px;min-width:44px;min-height:44px;border-radius:8px;border:0;background:transparent;
  color:var(--ink);font-size:22px;font-weight:400;line-height:1;padding:0;
}
.curset__steps .stepbtn:hover{color:var(--ink);background:transparent}

/* ---- Effort explainer ----
   The reps-left shorthand is an answer to a question, not a permanent caption,
   so it floats: the anchor is a zero-height strip the width of the effort
   column, parked over it — clear of the column label, which the lifter still
   needs to read. The pill and its arrow are centred on the word and scale out
   of it, so it grows from the value it explains rather than fading in over it.
   Dark, like the toast: the pill crosses the well's edge onto the ledger. */
.effortpop{
  position:absolute;left:0;right:0;bottom:calc(100% + 4px);height:0;z-index:9;
  opacity:0;pointer-events:none;
  transform:translateY(10px) scale(.7);transform-origin:50% 100%;
}
.effortpop__hint{
  position:absolute;bottom:8px;left:50%;z-index:2;transform:translateX(-50%);
  width:max-content;max-width:min(190px,54vw);box-sizing:border-box;
  padding:7px 12px;border-radius:10px;
  background:var(--cta);color:var(--cta-ink);
  font-size:13px;font-weight:600;line-height:1.15;text-align:center;
  box-shadow:0 10px 22px rgba(0,0,0,.18),0 2px 5px rgba(0,0,0,.08);
}
/* A rotated square: the half above the pill's edge hides behind it. */
.effortpop__arrow{
  position:absolute;bottom:3px;left:50%;z-index:1;width:10px;height:10px;margin-left:-5px;
  background:var(--cta);border-bottom-right-radius:2px;transform:rotate(45deg);
}
.effortpop.is-open{opacity:1;pointer-events:auto;transform:none;
  animation:effortpop-in .3s cubic-bezier(.2,1.3,.34,1) both}
.effortpop.is-closing{opacity:0;pointer-events:none;
  animation:effortpop-out .17s cubic-bezier(.45,0,.75,.35) both}
@keyframes effortpop-in{
  0%{opacity:0;transform:translateY(12px) scale(.62);filter:blur(3px)}
  45%{opacity:1;filter:blur(0)}
  68%{transform:translateY(-3px) scale(1.035)}
  100%{opacity:1;transform:translateY(0) scale(1);filter:blur(0)}
}
@keyframes effortpop-out{
  0%{opacity:1;transform:translateY(0) scale(1)}
  100%{opacity:0;transform:translateY(7px) scale(.88)}
}
/* The arrow lands a beat behind the pill, out of a dot. */
.effortpop.is-open .effortpop__arrow{animation:effortpop-arrow .3s .05s cubic-bezier(.2,1.4,.4,1) both}
@keyframes effortpop-arrow{
  from{opacity:0;transform:rotate(45deg) scale(.2)}
  to{opacity:1;transform:rotate(45deg) scale(1)}
}
/* Stepping the effort under an open pill swaps its reading in place. */
.effortpop.is-bump .effortpop__hint{animation:effortpop-swap .38s cubic-bezier(.2,1.3,.4,1) both}
@keyframes effortpop-swap{
  0%{opacity:0;transform:translateX(-50%) translateY(-6px) scale(.94)}
  55%{opacity:1;transform:translateX(-50%) translateY(0) scale(1.06)}
  100%{opacity:1;transform:translateX(-50%) scale(1)}
}
@media (prefers-reduced-motion:reduce){
  .effortpop.is-open,.effortpop.is-closing,
  .effortpop.is-open .effortpop__arrow,.effortpop.is-bump .effortpop__hint{animation:none}
  .curset__val--word[data-effspin]{transition:none}
}

/* ---- Exercise note sheet ---- */
body.is-sheet-open{overflow:hidden}
.sheet-scrim{
  position:fixed;inset:0;z-index:40;background:var(--scrim);
  opacity:0;transition:opacity .22s ease;
}
.sheet-scrim.is-open{opacity:1}
.sheet{
  position:fixed;left:0;right:0;bottom:var(--kb,0px);z-index:41;
  display:flex;flex-direction:column;
  /* Size against the band the keyboard leaves visible (--vvh tracks
     visualViewport.height), not 100dvh: dvh ignores the software keyboard, so
     the sheet grew taller than the screen and pushed its own header out of
     sight above the top edge. */
  height:min(74dvh,660px);max-height:calc(var(--vvh,100dvh) - 56px);
  background:var(--surface);border-radius:20px 20px 0 0;
  box-shadow:0 -8px 40px rgba(0,0,0,.18);
  padding-bottom:env(safe-area-inset-bottom);
  transform:translateY(101%);transition:transform .26s cubic-bezier(.2,.7,.2,1);
}
.sheet.is-open{transform:none}
/* Under the thumb the sheet is placed frame by frame, so it cannot also be
   easing towards a target; the transition comes back on release and carries the
   sheet the rest of the way, down or back to rest. */
.sheet.is-dragging{transition:none;will-change:transform}
.sheet-scrim.is-dragging{transition:none}
@media (prefers-reduced-motion:reduce){
  .sheet,.sheet-scrim{transition:none}
}
.sheet__grab{display:block;flex:none;width:38px;height:5px;border-radius:3px;background:var(--rule-strong);margin:8px auto 0}
/* The handle and the head are the drag rail: the browser must not read a push
   there as a scroll, or it takes the gesture and the sheet never moves. Below
   them the sheet's own scrollers keep their native panning. */
.sheet__grab,.sheet__head{touch-action:none}
.sheet__head{
  flex:none;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:8px;
  padding:4px 10px 12px;border-bottom:1px solid var(--rule);
}
.sheet__act{
  justify-self:start;min-height:44px;padding:10px 8px;border:0;background:none;cursor:pointer;
  color:var(--ink-soft);font-size:16px;
}
.sheet__act--accent{justify-self:end;color:var(--accent-deep);font-weight:650}
.sheet__titles{text-align:center;min-width:0}
.sheet__title{font-size:17px;font-weight:650}
.sheet__sub{font-size:14px;color:var(--ink-soft);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sheet__body{flex:1;min-height:0;display:flex;padding:16px 16px 0}
.sheet__text{
  flex:1;width:100%;margin:0;padding:0;border:0;background:transparent;border-radius:0;resize:none;
  font-size:17px;line-height:1.5;letter-spacing:normal;text-transform:none;color:var(--ink);
}
.sheet__text:focus{outline:none;box-shadow:none;border-color:transparent}
/* The plain-text program preview: the export exactly as it will be copied, so
   line breaks are preserved and long exercise names wrap instead of clipping. */
.sheet__pre{
  flex:1;min-width:0;margin:0;padding-bottom:8px;overflow:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  white-space:pre-wrap;overflow-wrap:anywhere;font-family:inherit;
  font-size:15px;line-height:1.55;letter-spacing:normal;text-transform:none;color:var(--ink);
  -webkit-user-select:text;user-select:text;
}
.sheet__foot{flex:none;padding:12px 16px 16px;font-size:13px;color:var(--ink-faint)}
.sheet__foot--actions{display:flex;justify-content:flex-end;gap:8px;border-top:1px solid var(--rule)}
/* A head with nothing on the left still needs its first grid cell, or the title
   slides out of the middle. */
.sheet__spacer{display:block;min-width:44px}
.sheet__act--icon{justify-self:end;display:inline-flex;align-items:center;justify-content:center;color:var(--ink-soft)}

/* The in-workout swap control. It was a <select> of the template's alternates;
   it is a button onto the full library now, and it reads back what is actually
   being performed. */
.subst__pick.is-swapped{border-style:solid;border-color:var(--accent-deep);color:var(--accent-deep)}

/* ---- Program editor: library affordances ---- */
.pex__swap{flex:none;color:var(--ink-soft)}
/* A slot with no library link is the one worth pointing at: it came from an
   import or predates the picker, and one tap adopts a real movement. */
.pex__swap.is-unlinked{color:var(--accent-deep)}
.pex__alts{display:flex;align-items:baseline;gap:8px;margin:10px 0 0}
.pex__altlab{flex:none;font-size:12px;font-weight:600;color:var(--ink-soft)}
.pex__altpick{
  flex:1;min-width:0;padding:7px 10px;border:1px dashed var(--rule-strong);border-radius:10px;
  background:none;font:inherit;font-size:13px;color:var(--ink);text-align:left;cursor:pointer;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* A linked slot states what it follows and offers the way out, rather than
   letting a muscle edit quietly disagree with the id the row carries. */
.pex__linked{margin:10px 0 0;font-size:12px;color:var(--ink-faint);line-height:1.5}
.pex__detach{
  background:none;border:0;padding:0;font:inherit;font-size:12px;font-weight:600;
  color:var(--accent-deep);text-decoration:underline;text-underline-offset:2px;cursor:pointer;
}
.pex__mus input[readonly]{color:var(--ink-soft);background:var(--well)}

/* ---- Library flow ---- */
/* Tabs are a hairline-underlined row, not a segmented pill: the quick sheet and
   the full page use the same control so moving between them reads continuous. */
.picktabs{display:flex;gap:0;border-bottom:1px solid var(--rule)}
.picktab{
  flex:1;min-height:44px;padding:10px 6px;border:0;border-bottom:2px solid transparent;
  background:none;font:inherit;font-size:14px;font-weight:600;color:var(--ink-soft);cursor:pointer;
}
.picktab.is-active{color:var(--accent-deep);border-bottom-color:var(--accent-deep)}
.picktab:focus-visible{outline:2px solid var(--accent-deep);outline-offset:-2px}

.libwrap{padding:0 16px calc(96px + env(safe-area-inset-bottom))}
.libstep{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin:12px 0 18px}
.libstep__lab{grid-column:1/-1;margin:0;font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-faint)}
.libstep__bar{height:3px;border-radius:2px;background:var(--rule-strong)}
.libstep__bar.is-done{background:var(--accent-deep)}
#libSearch{margin:0 0 12px}
.pick__filters--row{margin:10px -16px 0;padding:0 16px 2px}
.pick__list--page{max-height:none;overflow:visible;margin:12px -16px 0;padding:0 16px}

.librow{display:flex;align-items:center;gap:12px;padding:12px 0;border-bottom:1px solid var(--rule)}
.librow__preview{flex:none;padding:0;border:0;background:none;cursor:pointer;border-radius:10px}
.librow__preview:focus-visible{outline:2px solid var(--accent-deep);outline-offset:2px}
.librow__text{flex:1;min-width:0}
.librow__name{margin:0;font-size:15px;font-weight:600;color:var(--ink)}
.librow__meta{margin:2px 0 0;font-size:12px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.librow__edit{
  margin-top:4px;padding:0;border:0;background:none;font:inherit;font-size:12px;font-weight:600;
  color:var(--accent-deep);text-decoration:underline;text-underline-offset:2px;cursor:pointer;
}
.librow__check{
  flex:none;width:44px;height:44px;border:0;background:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
.librow__check::before{
  content:"";width:24px;height:24px;border:2px solid var(--rule-strong);border-radius:50%;
}
.librow__check.is-on::before{
  border-color:var(--accent);background:var(--accent) no-repeat center/13px 13px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.6 4.6L19 7.2'/%3E%3C/svg%3E");
}
.librow__check:focus-visible{outline:2px solid var(--accent-deep);outline-offset:-6px;border-radius:50%}

/* The action bar is the one place the count and the commit live, pinned so a
   long list never hides how many are selected. */
.libbar{
  position:fixed;left:0;right:0;bottom:0;z-index:30;
  display:flex;flex-direction:column;gap:6px;
  padding:10px 16px calc(12px + env(safe-area-inset-bottom));
  background:var(--bg);border-top:1px solid var(--rule);
}
.libbar.is-hidden{display:none}
.libbar__count{margin:0;font-size:13px;color:var(--ink-soft)}
.libbar .btn{margin:0}

.libcount{margin:0 0 14px;font-size:13px;color:var(--ink-soft)}
.libcfg{display:flex;align-items:flex-start;gap:12px;padding:14px 0;border-bottom:1px solid var(--rule)}
.libcfg__body{flex:1;min-width:0}
.libcfg__name{margin:0 0 8px;font-size:15px;font-weight:600}
.libcfg__fields{display:flex;gap:8px}
.libcfg__field{flex:1;display:flex;flex-direction:column;gap:4px;font-size:11px;font-weight:600;color:var(--ink-soft)}
.libcfg__field input{
  width:100%;min-height:44px;padding:8px;border:1px solid var(--rule-strong);border-radius:10px;
  background:var(--surface);font:inherit;font-family:var(--mono);font-size:16px;text-align:center;color:var(--ink);
}
.libcfg__drop{
  flex:none;width:44px;height:44px;display:flex;align-items:center;justify-content:center;
  border:0;background:none;color:var(--ink-soft);cursor:pointer;
}

/* ---- Exercise preview ---- */
.preview{padding:0 16px calc(28px + var(--nav) + env(safe-area-inset-bottom))}
.preview__eyebrow{margin:16px 0 2px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint)}
.preview__title{margin:0 0 4px;font-size:30px;font-weight:650;letter-spacing:-.02em}
.preview__sub{margin:0 0 16px;font-size:14px;color:var(--ink-soft)}
.preview__head{margin:22px 0 8px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint)}
.preview__rows{margin:0}
.preview__row{display:flex;justify-content:space-between;gap:16px;padding:12px 0;border-bottom:1px solid var(--rule)}
.preview__row dt{font-size:14px;color:var(--ink-soft)}
.preview__row dd{margin:0;font-size:14px;font-weight:600;text-align:right}
.preview__notes{margin:0;font-size:14px;color:var(--ink-soft);line-height:1.5}
.preview .btn--cta{margin-top:26px}

/* ---- Import review ---- */
/* A full view rather than a sheet: reviewing a twelve-exercise split is not a
   glance, and nothing is written until Import is pressed. */
.impreview{padding:0 16px calc(28px + var(--nav) + env(safe-area-inset-bottom))}
.impreview__file{margin:14px 0 0;font-size:13px;color:var(--ink-faint)}
.impreview__head{margin:6px 0 4px;font-size:26px;font-weight:650;letter-spacing:-.01em}
.impcounts{display:flex;gap:0;margin:16px 0 6px;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.impcount{
  flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:12px 4px;font-size:12px;color:var(--ink-soft);text-align:center;
}
.impcount+.impcount{border-left:1px solid var(--rule)}
.impcount b{font-size:20px;font-weight:650;color:var(--accent-deep);font-family:var(--mono)}
.improw{
  display:grid;grid-template-columns:1fr auto auto 1.1fr;align-items:center;gap:8px;
  padding:12px 0;border-bottom:1px solid var(--rule);
}
.improw__from{margin:0;font-size:14px;color:var(--ink)}
.improw__arrow{color:var(--ink-faint);font-size:13px}
.improw__to{min-width:0}
.improw__name{margin:0;font-size:14px;font-weight:600;color:var(--ink)}
.impbadge{display:inline-block;margin-top:4px;font-size:11px;font-weight:600}
.impbadge.is-done{color:var(--ink-faint)}
.impbadge.is-open{color:var(--accent-deep)}
/* The actions belong to the row above them and wrap under it on a narrow
   screen rather than squeezing the two names into nothing. */
.improw__acts{grid-column:1/-1;display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.improw__btn{
  min-height:36px;padding:7px 12px;border:1px solid var(--rule-strong);border-radius:999px;
  background:var(--surface);font:inherit;font-size:13px;color:var(--ink);cursor:pointer;
}
/* A settled row carries one quiet chip instead of three alternatives, so the
   screen you read before replacing a program is the list, not the controls. */
.improw.is-folded{padding:10px 0}
.improw__btn--change{border-color:var(--rule);color:var(--ink-soft)}
.improw.is-open{background:transparent}
.improw.is-open .improw__btn:first-child{border-color:var(--accent-deep);color:var(--accent-deep)}
.impreview__safe{margin:20px 0 12px;text-align:center;font-size:13px;color:var(--ink-faint)}
#importCommit[disabled]{opacity:.5;cursor:not-allowed}

/* ---- Exercise picker sheet ---- */
/* Search and filters sit outside .sheet__body so the list is the only thing
   that scrolls: a lifter refining a search should never have to scroll back up
   to reach the box they are typing in. */
.pick__tools{flex:none;padding:10px 16px 0;display:flex;flex-direction:column;gap:10px}
.pick__search{
  width:100%;padding:11px 14px;border:1px solid var(--rule-strong);border-radius:12px;
  background:var(--well);color:var(--ink);font:inherit;font-size:16px;
}
.pick__search:focus{outline:none;border-color:var(--accent-deep)}
.pick__search::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
/* The filter row scrolls sideways rather than wrapping to three lines and
   eating the list it is meant to narrow. */
.pick__filters{
  display:flex;gap:8px;overflow-x:auto;scrollbar-width:none;
  margin:0 -16px;padding:0 16px 2px;
}
.pick__filters::-webkit-scrollbar{display:none}
.pchip{
  flex:none;padding:7px 13px;border:1px solid var(--rule-strong);border-radius:999px;
  background:var(--surface);color:var(--ink-soft);font:inherit;font-size:13px;font-weight:600;
  cursor:pointer;white-space:nowrap;
}
.pchip.is-active{background:var(--cta);border-color:var(--cta);color:var(--cta-ink)}
/* Artwork tiles. The empty variant is the same box with nothing in it — no
   glyph, no initials, no shimmer — so illustrated and plain rows line up and a
   missing illustration never looks like a failure. */
.exthumb{
  flex:none;display:block;border-radius:10px;background:var(--well);
  border:1px solid var(--rule);object-fit:cover;
}
.exthumb--sm{width:52px;height:52px}
.exthumb--md{width:72px;height:72px}
.exthumb--lg{width:100%;height:auto;aspect-ratio:1;border-radius:14px}
.exthumb--empty{background:var(--well)}

/* Browsing a library is not a confirmation prompt: the picker takes the whole
   visible viewport instead of the default 74dvh band, so search, filters and
   footer can all stay put and the list still has rows to show. --vvh is the
   band the software keyboard leaves visible, so a full-height picker with the
   keyboard up is still a picker rather than a header with two rows under it.
   Safe-area padding at the top because a sheet this tall reaches the status
   bar once the app is installed and running standalone. */
.sheet--pick{
  height:var(--vvh,100dvh);max-height:var(--vvh,100dvh);
  padding-top:env(safe-area-inset-top);
}
.pick__body{padding-top:10px;flex-direction:column;overflow:hidden}
.pick__list{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;margin:0 -16px;padding:0 16px}
.pick__section{
  position:sticky;top:0;z-index:1;background:var(--surface);
  padding:10px 0 6px;font-size:12px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink-faint);
}
.pick__empty{padding:28px 4px;text-align:center;color:var(--ink-soft);font-size:14px}
/* Opaque, or the row the list is scrolled to shows through behind the button. */
.sheet--pick .sheet__foot--actions{background:var(--surface)}
.pickrow{
  display:flex;align-items:center;gap:10px;width:100%;
  padding:11px 2px;border:0;border-bottom:1px solid var(--rule);
  background:none;font:inherit;text-align:left;cursor:pointer;
}
.pickrow:last-child{border-bottom:0}
.pickrow__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.pickrow__name{font-size:15px;font-weight:600;color:var(--ink)}
.pickrow__meta{font-size:12px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pickrow__eq{
  flex:none;padding:3px 8px;border-radius:999px;background:var(--well);
  border:1px solid var(--rule);font-size:11px;font-weight:600;color:var(--ink-faint);
}
/* The tick only takes space once a row can be selected, so single-pick lists
   are not padded for a control they never show. */
.pickrow__tick{flex:none;width:0;transition:width .12s ease}
.pickrow[role="checkbox"] .pickrow__tick{
  width:20px;height:20px;border:2px solid var(--rule-strong);border-radius:6px;
}
/* Crossed gradients read as an X, not a tick — draw the checkmark. */
.pickrow[role="checkbox"].is-selected .pickrow__tick{
  background-color:var(--cta);border-color:var(--cta);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.6 4.6L19 7.2'/%3E%3C/svg%3E");
  background-size:13px 13px;background-position:center;background-repeat:no-repeat;
}

/* ---- Custom exercise sheet ---- */
.custom__form{width:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;padding-bottom:16px}
.custom__lab{margin:16px 0 8px;font-size:12px;font-weight:600;color:var(--ink-soft)}
/* Tagging muscles means reading all nineteen, not swiping a rail to find
   Calves. The form scrolls vertically, so these wrap instead. */
.custom__form .pick__filters{flex-wrap:wrap;overflow:visible;margin:0;padding:0}
.custom__delete{margin-top:22px;width:100%;color:var(--danger)}
.custom__note{margin-top:16px;font-size:13px;color:var(--ink-faint)}

/* ---- Choose-another-day sheet ---- */
/* One row per day of the split, so the sheet is as tall as the program is long
   and only scrolls when a long split outgrows the screen. Arming a day is not
   starting it — switching days can discard a session in progress — so the rows
   read as a set of choices and the CTA below them is what commits one. */
.sheet--daypick{height:auto}
.sheet__head--stack{grid-template-columns:1fr;padding:4px 16px 14px}
.sheet__head--stack .sheet__titles{text-align:left}
.sheet__head--stack .sheet__title{font-size:20px;letter-spacing:-.01em}
/* The head's one-line sub is a caption for a title; this one is a sentence. */
.sheet__head--stack .sheet__sub{white-space:normal;overflow:visible;margin-top:4px;line-height:1.4}
.daypick__body{flex-direction:column;overflow:hidden;padding:0 16px}
.daypick__list{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;margin:0 -16px;padding:0 16px}
.daypick__row{
  display:flex;align-items:center;gap:14px;width:100%;min-height:64px;padding:12px 12px 12px 9px;
  background:none;border:0;border-bottom:1px solid var(--rule);border-left:3px solid transparent;
  cursor:pointer;text-align:left;font:inherit;color:inherit;
}
.daypick__row:last-child{border-bottom:0}
.daypick__row.is-selected{border-left-color:var(--accent);background:rgba(var(--accent-rgb),.05);border-radius:0 10px 10px 0}
.daypick__n{flex:none;min-width:18px;text-align:center;font-size:15px;font-weight:650;color:var(--ink-faint);font-variant-numeric:tabular-nums}
.daypick__row.is-selected .daypick__n{color:var(--accent-deep)}
.daypick__main{min-width:0;flex:1}
/* The chip rides the title's line only, so the line under it keeps the full
   width and never has to drop the count to make room. */
.daypick__line{display:flex;align-items:center;gap:8px}
.daypick__title{flex:1;min-width:0;font-size:16px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.daypick__sub{display:block;margin-top:2px;font-size:13px;color:var(--ink-soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The day Today is already on, marked wherever it sits in the split. */
.daypick__chip{
  flex:none;font-size:11px;font-weight:650;text-transform:uppercase;letter-spacing:.06em;
  color:var(--accent-deep);background:rgba(var(--accent-rgb),.08);border-radius:999px;padding:4px 9px;
  /* Sits inside the title's own line box, so the chipped row is no taller than
     the rows around it. */
  line-height:1.1;
}
.sheet__foot--stack{display:flex;flex-direction:column;gap:2px;border-top:1px solid var(--rule)}
.sheet__foot--stack .text-link{min-height:44px}

/* ---- Rest timer sheet ---- */
/* Tapping a running clock used to end the rest, which is the one thing a lifter
   mid-set never means. The clock opens this instead: hold it, nudge it, restart
   it at another length, or end it deliberately. */
.sheet--rest{height:auto}
.restsheet{flex-direction:column;align-items:center;gap:20px;padding:20px 16px calc(20px + env(safe-area-inset-bottom))}
.restdial{position:relative;width:min(56vw,210px);flex:none;aspect-ratio:1}
.restdial__svg{display:block;width:100%;height:100%;transform:rotate(-90deg)}
.restdial__track{fill:none;stroke:var(--rule);stroke-width:7}
.restdial__arc{
  fill:none;stroke:var(--accent);stroke-width:7;stroke-linecap:round;
  transition:stroke-dashoffset .25s linear,stroke .2s ease;
}
.restdial__clock{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-weight:600;font-size:clamp(32px,10vw,42px);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
}
/* Held: the ring goes quiet so a frozen clock never reads as a running one. */
.sheet--rest.is-paused .restdial__arc{stroke:var(--rule-strong)}
.sheet--rest.is-paused .restdial__clock{color:var(--ink-soft)}
/* Past the bell, the same red the chip and the bar use for overtime. */
.sheet--rest.is-over .restdial__arc{stroke:var(--danger)}
.sheet--rest.is-over .restdial__clock{color:var(--danger)}
.restsheet__presets{display:flex;flex-wrap:wrap;justify-content:center;gap:6px}
.restpreset{
  min-width:0;padding:0 12px;border-radius:999px;cursor:pointer;
  border:1px solid var(--rule-strong);background:var(--surface);color:var(--ink-soft);
  font-family:var(--mono);font-size:14px;line-height:1;
}
.restpreset.is-active{border-color:var(--accent);color:var(--accent-deep);background:var(--well)}
.restsheet__controls{display:flex;align-items:center;justify-content:center;gap:4px;width:100%}
.restctl{
  display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  flex:1 1 0;min-width:0;padding:6px 2px;border:0;background:none;cursor:pointer;
  color:var(--ink-soft);font-size:12px;line-height:1;
}
.restctl:disabled{opacity:.35;cursor:default}
.restctl__label{white-space:nowrap}
.restctl--primary{
  flex:none;width:68px;height:68px;border-radius:50%;
  background:var(--accent);color:var(--cta-ink);
}
.restctl--primary .icon-mask{width:26px;height:26px}
/* Idle or held, the button offers to start; running, it offers to hold. */
.restctl--primary .restctl__pause{display:none}
.sheet--rest:not(.is-idle):not(.is-paused) .restctl--primary .restctl__play{display:none}
.sheet--rest:not(.is-idle):not(.is-paused) .restctl--primary .restctl__pause{display:inline-block}
/* Short screens: the dial gives up the room the presets and controls need. */
@media (max-height:640px){
  .restsheet{gap:14px;padding-top:14px}
  .restdial{width:min(44vw,152px)}
}
@media (prefers-reduced-motion:reduce){
  .restdial__arc{transition:none}
}

/* Narrow phones: tighter gutters and a set column the ledger can still line up. */
@media (max-width:360px){
  .fcard__head,.fcard__ledger{padding-left:16px;padding-right:16px}
  .focus-well{padding-left:16px;padding-right:16px}
  .ledger__head,.ledger__row{grid-template-columns:2.1rem minmax(0,1.5fr) minmax(0,0.9fr) minmax(0,1.15fr) 1.5rem;gap:8px}
  .fcard__ledger:has(.ledger__row.is-past) .ledger__head,
  .fcard__ledger:has(.ledger__row.is-past) .ledger__row.is-past{
    grid-template-columns:2.1rem minmax(0,1.6fr) minmax(0,1fr) minmax(0,1.25fr);
  }
  .ledger__row.is-editing::before{left:-16px}
}
/* Short screens trade padding and type scale, never a control: the ledger may
   shrink to a row or two, but the well keeps its inputs and its action. */
@media (max-height:740px){
  body.is-workout.is-focus-wo main{padding-bottom:8px}
  .wo-head{margin-bottom:10px}
  .wo-progress{margin:0 0 8px}
  .wo-progress__top{margin-bottom:8px}
  .fcard__head{padding-top:15px}
  .fcard__ledger{margin-top:12px}
  .exercise--focus .focus-ex__name{font-size:24px}
  .focus-well{padding:12px 14px 14px}
  .focus-well.is-done{padding-bottom:21px}
  .curset{--curset-val:30px;margin-top:8px}
  .curset__cell-lab{margin-bottom:6px}
  .curset__val--word{font-size:24px}
}
@media (max-height:660px){
  body.is-workout.is-focus-wo main{padding-bottom:6px}
  .wo-head{margin-bottom:6px}
  .wo-progress__top{margin-bottom:6px}
  .fcard__head{padding-top:12px}
  .fcard__ledger{margin-top:8px}
  .focus-ex__muscle{display:none}
  .exercise--focus .focus-ex__name{font-size:21px}
  .focus-ex__target{margin-top:4px}
  .focus-well{padding:10px 14px 12px}
  .focus-well.is-done{padding-bottom:18px}
  .focus-cue{font-size:13px}
  .curset{--curset-val:26px;margin-top:4px}
  .curset__cell{padding-top:2px}
  .curset__cell-lab{margin-bottom:4px}
  .curset__val--word{font-size:21px}
  .curset__underline{margin-top:5px}
  .focus-well .btn--cta,.focus-well .btn--cta.saveset{height:48px;min-height:48px;font-size:16px;margin-top:8px}
  .focus-well__cancel{margin-top:2px;padding:4px}
  .ledger__row,.ledger__more{min-height:46px}
  .ledger__ticks{display:none}
  .focus-done__mark{width:40px;height:40px}
  .focus-done__mark::after{-webkit-mask-size:19px;mask-size:19px}
  .focus-done__title{font-size:16px}
}
@media (max-height:600px){
  .exercise--focus .focus-ex__name{font-size:19px;line-height:1.2}
  /* Two lines of title at most; the rest of the card needs the room more. */
  .exercise--focus .focus-ex__name,
  .exercise--focus .focus-ex__name .ex__namebtn{
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  }
  .focus-ex__target{font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .focus-cue{flex-wrap:nowrap;font-size:12.5px}
  .focus-cue__text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .curset{--curset-val:24px}
  .curset__val--word{font-size:18px}
  .effortpop__hint{padding:6px 10px;font-size:12px}
  .curset__underline{margin-top:4px}
  .ledger__head{padding-bottom:8px}
  .ledger__row,.ledger__more{min-height:44px}
  .focus-well .btn--cta,.focus-well .btn--cta.saveset{height:46px;min-height:46px;margin-top:6px}
}


/* ---- Metrics (aliased to unified stat-band above) ---- */
.trend{font-size:13px;color:var(--ink-soft);margin:0;display:grid;gap:3px}
.trend__fig{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px;margin:0}
.trend b{color:var(--ink);font-weight:600}
.trend__delta{font-weight:600;white-space:nowrap}
.trend .up{color:var(--positive)}.trend .down{color:var(--danger)}
.prledger{margin:0;overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:0;border-radius:0;background:transparent}
.prledger table{font-size:12px;margin:0}
.pr-kind{display:inline-block;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:4px}
.pr-kind--load{color:var(--accent-deep);background:rgba(var(--accent-rgb),.08)}
.pr-kind--reps{color:var(--ink);background:rgba(var(--ink-rgb),.05)}
.pr-kind--e1rm{color:var(--accent-deep);background:rgba(var(--accent-rgb),.08)}
.prtimeline{display:flex;flex-direction:column;gap:0;margin-top:4px}
.prtl__row{display:grid;grid-template-columns:auto 1fr auto auto auto;gap:6px 10px;align-items:center;
  padding:14px 0;border-radius:0;background:transparent;border:0;border-bottom:1px solid var(--rule);font-size:13px;line-height:1.35}
.prtl__date{font-size:12px;color:var(--ink-soft);white-space:nowrap}
.prtl__ex{font-weight:500;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.prtl__set{font-size:13px;color:var(--ink);white-space:nowrap;font-variant-numeric:tabular-nums}
.prtl__delta{font-size:13px;font-weight:600;color:var(--accent-deep);white-space:nowrap}
@media(max-width:520px){
  .prtl__row{grid-template-columns:1fr 1fr;gap:4px 8px}
  .prtl__date{grid-column:1}.prtl__ex{grid-column:2;text-align:right}
  .pr-kind{grid-column:1}.prtl__set{grid-column:2;text-align:right}
  .prtl__delta{grid-column:1/-1;text-align:right}}
canvas{width:100%;display:block;margin-top:6px}

/* ---- Tables ---- */
.table{background:transparent;border:0;border-radius:0;overflow:auto;-webkit-overflow-scrolling:touch;margin-bottom:8px}
table{width:100%;border-collapse:separate;border-spacing:0;min-width:520px}
th,td{padding:11px 8px;text-align:left;font-size:13px;white-space:nowrap;border-bottom:1px solid var(--rule)}
th{font-size:11px;color:var(--ink-faint);background:transparent;text-transform:uppercase;letter-spacing:.06em;font-weight:600}
td{font-size:13px;color:var(--ink);font-variant-numeric:tabular-nums}
.empty{padding:22px 16px;color:var(--ink-soft);font-size:14px;line-height:1.5;text-align:center;
  background:transparent;border:1px dashed var(--rule);border-radius:var(--radius)}
.table .empty{background:none;border:0;border-radius:0}

.emptystate{background:transparent;border:0;border-radius:0;padding:20px 0;margin-bottom:14px;text-align:center}
.emptystate__title{font-weight:650;font-size:18px;margin-bottom:6px}
.emptystate__body{color:var(--ink-soft);font-size:14px;line-height:1.5;margin-bottom:14px}
.emptystate .btn{width:100%}

/* ---- History / sessions ---- */
.sessions{display:flex;flex-direction:column;gap:0;margin-bottom:8px}
.session{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;
  background:transparent;border:0;border-bottom:1px solid var(--rule);border-radius:0;padding:16px 0}
.session__day{font-weight:640;font-size:20px;letter-spacing:-0.01em}
.session__eyebrow{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin-bottom:4px}
.session__sub{font-size:13px;color:var(--ink-soft);margin-top:4px;line-height:1.4}
.session__delta{font-size:13px;color:var(--ink-soft);margin-top:4px}
.session__stat{color:var(--ink);font-weight:600}
.session__muscles{color:var(--ink-soft);font-size:14px;margin-top:2px}
.session__del{flex:none;border:0;background:transparent;color:var(--danger);
  font-size:13px;font-weight:500;border-radius:10px;padding:9px 8px;cursor:pointer}
.session__edit{flex:none;border:0;background:transparent;color:var(--accent-deep);
  font-size:13px;font-weight:500;padding:9px 8px;cursor:pointer}
.session__btns{flex:none;display:flex;gap:4px;align-items:center}
.session__info{min-width:0;flex:1}
.cal{margin-bottom:18px}
.cal__nav{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:6px}
.cal__title{font-size:15px;font-weight:600}
.cal__summary{text-align:center;font-size:13px;color:var(--ink-soft);margin-bottom:12px}
.cal__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px 0;text-align:center}
.cal__dow{font-size:11px;color:var(--ink-faint);font-weight:500;padding:4px 0}
.cal__cell{padding:6px 0 10px;font-size:14px;color:var(--ink);position:relative;min-height:42px}
.cal__cell.is-out{color:var(--ink-faint)}
.cal__mark{display:block;margin:4px auto 0;width:10px;height:10px;line-height:10px;font-size:10px}
.cal__mark.is-check{color:var(--ink)}
.cal__mark.is-pr,.cal__mark.is-today{width:6px;height:6px;border-radius:50%;background:var(--accent);margin-top:6px}
.hist-search{margin-bottom:12px;display:flex;align-items:center;gap:8px}
.hist-search input{flex:1;min-width:0}
.hist-month{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin:18px 0 4px}

/* ---- Volume ---- */
.volume{display:flex;flex-direction:column;gap:12px}
.vrow{display:grid;grid-template-columns:1fr auto auto;gap:6px 12px;align-items:center}
.vrow__name{grid-row:1;grid-column:1;font-size:15px;color:var(--ink);font-weight:500}
.vrow__num{grid-row:1;grid-column:2;font-size:13px;color:var(--ink-soft);text-align:right;font-variant-numeric:tabular-nums}
.vrow__num b{color:var(--ink);font-weight:600}
.vrow__status{grid-row:1;grid-column:3;font-size:13px;color:var(--accent-deep);text-align:right;min-width:4rem}
.vrow__status.is-on{color:var(--positive)}
.vrow__bar{grid-row:2;grid-column:1/-1;display:block;height:4px;border-radius:2px;background:var(--rule);overflow:hidden}
.vrow__fill{display:block;height:100%;border-radius:2px;background:var(--accent)}
.vrow__fill.is-high,.vrow__fill.is-on{background:var(--positive)}
.vrow__head{display:contents}

/* ---- Program ---- */
.pmeta{background:transparent;border:0;border-radius:0;padding:0;margin-bottom:14px;box-shadow:none;display:flex;flex-direction:column;gap:10px}
.pmeta__row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:12px}
.pmeta__name{flex:1;min-width:12rem;margin-top:0;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);font-weight:600}
.pmeta__name input{margin-top:5px;font-weight:600;font-size:16px;text-transform:none;letter-spacing:normal;padding:10px 11px;background:var(--surface)}
.pmeta__started{flex:none;margin-top:0;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);font-weight:600}
/* 16px, like every other field: below that iOS zooms the page in on focus and
   leaves it there. */
.pmeta__started input{margin-top:5px;font-size:16px;padding:9px 11px;background:var(--surface);color-scheme:var(--scheme)}
.pmeta__chips{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin-left:auto}
.pmeta__chip{font-size:12px;color:var(--ink-soft);background:var(--surface);border:1px solid var(--rule);border-radius:999px;padding:6px 10px;white-space:nowrap}
.pmeta__chip--status{color:var(--ink);border-color:var(--rule-strong);font-weight:600}
.program-overview__name{font-size:26px;font-weight:650;letter-spacing:-0.02em}
.program-overview__meta{font-size:14px;color:var(--ink-soft);margin-top:4px}
.program-overview__started{font-size:13px;color:var(--ink-soft);margin-top:6px}
.program-day{border-bottom:1px solid var(--rule)}
.program-day__row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;width:100%;
  padding:14px 0;background:none;border:0;cursor:pointer;text-align:left;font:inherit;color:inherit}
.program-day__name{font-size:17px;font-weight:600}
.program-day__muscles{font-size:13px;color:var(--ink-soft);margin-top:2px}
.program-day__meta{font-size:13px;color:var(--ink-soft);flex:none;display:flex;align-items:center;gap:8px}
.program-day__exs{padding:0 0 12px}
.program-day__ex{display:flex;justify-content:space-between;gap:10px;padding:10px 0;border-top:1px solid var(--rule);font-size:15px}
.program-day__ex-sets{color:var(--ink-soft);flex:none}
.program-day__link{color:var(--accent-deep);font-size:14px;background:none;border:0;cursor:pointer;padding:8px 0;display:block}
#programEditor.hidden,#programEditChrome.hidden{display:none!important}
.peditor{display:flex;flex-direction:column;gap:14px}
.pday{background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);padding:14px;box-shadow:none}
.pday__head{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.pday__name{flex:1;margin-top:0;font-weight:700;font-size:16px;letter-spacing:0;
  text-transform:none;padding:9px 11px;background:var(--bg);border-color:var(--rule)}
.pday__count{flex:none;font-size:11px;letter-spacing:.04em;color:var(--ink-faint);text-transform:uppercase;white-space:nowrap}
.pday__empty{color:var(--ink-faint);font-size:13px;font-style:italic;margin:2px 0 10px}
.pexlist{display:flex;flex-direction:column;gap:11px;margin-bottom:12px}
.pday__add{width:100%;font-size:13px;padding:10px}
.peditor__addday{width:100%;margin-top:14px;font-size:14px}
.pex{background:var(--bg);border:1px solid var(--rule);border-radius:12px;padding:12px}
/* Four 44px controls plus a name will not fit 390px, and library names are
   longer than the hand-typed ones were ("Incline chest press machine"), so the
   name takes its own row and the controls sit under it. */
.pex__head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}
.pex__name{flex:1 1 100%;margin-top:0;font-weight:600;font-size:16px;padding:10px 11px}
.pex__move{flex:none;display:flex;gap:5px}
.pex__nums{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:11px}
.pex__num,.pex__mus{display:block;text-transform:uppercase;letter-spacing:.07em;font-size:10.5px;font-weight:600;color:var(--ink-soft)}
.pex__num input{margin-top:5px;text-align:center;font-weight:500;padding:9px 4px;background:var(--surface)}
.pex__mus{margin-top:11px}
.pex__mus input{margin-top:5px;padding:10px 11px;font-size:16px;background:var(--surface)}
.iconbtn{flex:none;width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--rule);background:var(--surface);color:var(--ink-soft);border-radius:9px;cursor:pointer;font-size:12px;line-height:1}
.iconbtn:hover:not(:disabled){color:var(--ink);border-color:var(--ink-soft)}
.iconbtn:disabled{opacity:.3;cursor:default}
.iconbtn--del:hover:not(:disabled){color:var(--danger);border-color:rgba(var(--danger-rgb),.4);background:rgba(var(--danger-rgb),.06)}
.advanced{margin-top:24px;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.advanced[open]{padding-bottom:16px}
.advanced summary{cursor:pointer;list-style:none;min-height:52px;padding:14px 0;font-weight:500;font-size:15px;color:var(--ink);
  display:flex;align-items:center;justify-content:space-between;gap:8px;text-transform:none;letter-spacing:0}
.advanced summary::-webkit-details-marker{display:none}
.advanced summary::after{
  content:"";flex:none;width:16px;height:16px;background:var(--ink-faint);
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  -webkit-mask-size:contain;mask-size:contain;transition:transform .2s ease;
}
.advanced[open] summary::after{transform:rotate(180deg)}
.advanced textarea{font-family:var(--mono);font-size:16px}
.file{display:flex;cursor:pointer;margin-top:10px;text-transform:none}
.file input{display:none}
.program__endblock{margin:14px 0}
.program-footer{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-top:1px solid var(--rule);margin-top:8px}
.program-footer__link{color:var(--accent-deep);background:none;border:0;cursor:pointer;font-size:15px;padding:8px 0}

/* ---- Bottom nav — floating glass dock ----
   A capsule that hovers over the page rather than capping it, so the content
   stays visible (blurred) underneath: the edge highlight and the drop shadow
   are what read as glass, and the blur is what keeps the labels legible over
   whatever scrolls behind. Content still stops at the dock's top edge —
   body's padding-bottom reserves --nav — so nothing important sits under it. */
nav{
  position:fixed;z-index:25;
  left:50%;transform:translateX(-50%);
  bottom:calc(var(--dock-gap) + env(safe-area-inset-bottom));
  width:min(
    calc(100% - env(safe-area-inset-left) - env(safe-area-inset-right) - 2*var(--dock-inset)),
    var(--maxw)
  );
  height:var(--dock-h);
  display:grid;grid-template-columns:repeat(4,1fr);
  padding:6px;gap:2px;
  border:1px solid var(--dock-edge);border-radius:calc(var(--dock-h)/2);
  background:var(--dock-glass);
  backdrop-filter:blur(30px) saturate(180%);-webkit-backdrop-filter:blur(30px) saturate(180%);
  box-shadow:
    inset 0 1px 0 var(--dock-sheen),
    inset 0 -1px 0 rgba(27,26,23,.05),
    0 10px 30px rgba(27,26,23,.13),
    0 2px 8px rgba(27,26,23,.07);
}
/* Without backdrop-filter the panel is a plain translucent sheet and the page
   shows through sharp, so pay for legibility with opacity instead. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  nav{--dock-glass:var(--dock-glass-opaque)}
}
nav button{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  background:transparent;border:0;color:var(--ink-soft);cursor:pointer;
  border-radius:calc(var(--dock-h)/2 - 6px);
  font-weight:500;letter-spacing:0;font-size:11px;text-transform:none;
  transition:color .2s ease,transform .18s ease;min-height:44px;
}
nav button:active{transform:scale(.93)}
nav button:focus-visible{outline-offset:-3px}
/* The selected tab sits in a lighter lens. This one is painted on the button
   itself and is the fallback: where :has can name the live tab, the block below
   swaps it for a single pill that slides between the four. */
nav button.active{
  color:var(--accent-deep);
  background:var(--dock-pill);
  box-shadow:inset 0 1px 0 var(--dock-pill-sheen),0 1px 3px rgba(27,26,23,.08);
}
@supports selector(:has(*)){
  nav button.active{background:transparent;box-shadow:none}
  nav::before{
    content:"";position:absolute;z-index:-1;
    top:6px;bottom:6px;left:6px;
    /* One of four columns of the padding box, gaps discounted. */
    width:calc((100% - 12px - 6px)/4);
    /* A step is one column plus one gap, and 100% here is the pill's own width. */
    transform:translateX(calc(var(--dock-i,0) * (100% + 2px)));
    border-radius:calc(var(--dock-h)/2 - 6px);
    background:var(--dock-pill);
    box-shadow:inset 0 1px 0 var(--dock-pill-sheen),0 1px 3px rgba(27,26,23,.09);
    transition:transform .38s cubic-bezier(.32,.72,0,1);
  }
  nav:has(button:nth-child(1).active){--dock-i:0}
  nav:has(button:nth-child(2).active){--dock-i:1}
  nav:has(button:nth-child(3).active){--dock-i:2}
  nav:has(button:nth-child(4).active){--dock-i:3}
}
.nav__icon{width:22px;height:22px;background:currentColor;
  -webkit-mask:no-repeat center/contain;mask:no-repeat center/contain}
nav button.active .nav__icon{background:var(--accent)}
.nav__icon[data-i=log]{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 10.5L12 4l8 6.5V20a1 1 0 0 1-1 1h-4.5v-6h-5v6H5a1 1 0 0 1-1-1V10.5z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 10.5L12 4l8 6.5V20a1 1 0 0 1-1 1h-4.5v-6h-5v6H5a1 1 0 0 1-1-1V10.5z'/%3E%3C/svg%3E")}
.nav__icon[data-i=stats]{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M5 20V10M12 20V4M19 20v-7'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M5 20V10M12 20V4M19 20v-7'/%3E%3C/svg%3E")}
.nav__icon[data-i=history]{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12a9 9 0 1 0 3-6.7'/%3E%3Cpath d='M3 4v5h5'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 12a9 9 0 1 0 3-6.7'/%3E%3Cpath d='M3 4v5h5'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E")}
.nav__icon[data-i=program]{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M8 4h10a2 2 0 0 1 2 2v14H8a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z'/%3E%3Cpath d='M9 9h8M9 13h8M9 17h5'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M8 4h10a2 2 0 0 1 2 2v14H8a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z'/%3E%3Cpath d='M9 9h8M9 13h8M9 17h5'/%3E%3C/svg%3E")}
body.is-settings nav,body.is-exercise nav,body.is-onboarding nav,
body.is-library nav,body.is-preview nav,body.is-import nav{display:none}
body.is-settings,body.is-exercise,body.is-onboarding,
body.is-library,body.is-preview,body.is-import{padding-bottom:env(safe-area-inset-bottom)}
/* The dock floats, so the page keeps scrolling past it: rows pass through the
   inset gutters beside the capsule and the strip beneath it, and what shows
   there is the top half of a checkmark or the bottom of a word. A capsule only
   earns its float if what goes under it is composed, so the paper takes the
   last stretch of the page back. Below the dock and the rest bar, above
   everything that scrolls, and gone wherever the dock itself is. */
body::after{
  content:"";position:fixed;z-index:24;pointer-events:none;
  left:0;right:0;bottom:0;
  height:calc(var(--nav) + env(safe-area-inset-bottom) + 26px);
  /* The first stop is a length, not a percentage: it has to land exactly on the
     dock's top edge whatever the safe area is, so the gutters beside the capsule
     are paper rather than half a word, and the softening happens above it. */
  background:linear-gradient(to top,
    var(--bg) calc(var(--nav) + env(safe-area-inset-bottom)),transparent);
}
body.is-firstrun::after,body.is-focus-wo::after,
body.is-settings::after,body.is-exercise::after,body.is-onboarding::after,
body.is-library::after,body.is-preview::after,body.is-import::after{content:none}

/* ---- Toast ---- */
.toast{
  position:fixed;left:50%;transform:translateX(-50%) translateY(0);
  bottom:calc(var(--nav) + 22px + env(safe-area-inset-bottom));z-index:50;
  width:min(92%,420px);background:var(--cta);border:0;color:var(--cta-ink);border-radius:12px;
  padding:13px 16px;font-size:14px;box-shadow:0 8px 24px rgba(0,0,0,.18);
  animation:toastin .3s cubic-bezier(.2,.7,.2,1)
}

.ex__topend{flex:none;display:flex;align-items:center;gap:8px}
.ex__caret{flex:none;width:34px;height:34px;border:1px solid var(--rule);background:var(--surface);color:var(--ink-soft);
  border-radius:9px;cursor:pointer;font-size:12px;line-height:1}
.ex__rest,.ex__skip{flex:none;border:1px solid var(--rule);background:var(--surface);color:var(--ink-soft);
  border-radius:9px;cursor:pointer;font-size:13px;line-height:1}
.ex__rest{width:34px;height:34px;padding:0;display:inline-flex;align-items:center;justify-content:center}
.ex__skip{padding:0 10px;height:34px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;font-size:11px}
/* Skipping is the rarest thing a lifter does to an exercise, and it was wearing
   the same plate as the timer, the caret and the commit button. It keeps the
   word — the control toggles to Restore, which no glyph says — and gives up the
   plate, so the header carries two tools and one aside. A skipped card is the
   exception: Restore is then the only action left on it, so it keeps the plate
   that makes it reachable. */
.exercise:not(.is-skipped) .ex__topend .ex__skip{border-color:transparent;background:transparent}
.exercise:not(.is-skipped) .ex__topend .ex__skip:hover{color:var(--ink)}
.exercise.is-skipped .heat,.exercise.is-skipped .rec,.exercise.is-skipped .setup,
.exercise.is-skipped .subst,.exercise.is-skipped .prev,.exercise.is-skipped .delta-prev,.exercise.is-skipped .sets__head,.exercise.is-skipped .setrow,
.exercise.is-skipped .rec__block,.exercise.is-skipped .insession,.exercise.is-skipped .rec-wrap,.exercise.is-skipped .curset{display:none}
.ex__state{display:inline-block;margin-left:.35em;color:var(--ink-soft);font-weight:400;font-size:.82em}
.skipbar{display:flex;align-items:center;justify-content:space-between;gap:10px;
  background:var(--surface);border:1px solid var(--rule);border-radius:10px;
  padding:8px 12px;margin-bottom:12px;font-size:13px;color:var(--ink-soft)}
.skipbar__show{border:1px solid var(--rule);background:var(--bg);color:var(--ink);
  border-radius:8px;padding:4px 10px;cursor:pointer;font-weight:600;font-size:11px}
.exercise.is-collapsed .ex__caret{transform:rotate(-90deg)}
.exercise.is-collapsed .rec,.exercise.is-collapsed .setup,.exercise.is-collapsed .prev,.exercise.is-collapsed .delta-prev,
.exercise.is-collapsed .rec__block,.exercise.is-collapsed .insession,.exercise.is-collapsed .rec-wrap,.exercise.is-collapsed .curset,
.exercise.is-collapsed .sets__head,.exercise.is-collapsed .setrow,.exercise.is-collapsed .heat{display:none}

.setup{font-size:12px;color:var(--ink-soft);margin:0 0 10px;line-height:1.4;border-left:2px solid var(--rule);padding-left:9px}
.setup span{color:var(--ink-faint);text-transform:uppercase;letter-spacing:.1em;font-size:10px;margin-right:7px}

.blockprompt{position:relative;background:transparent;border:0;border-left:3px solid var(--accent);
  border-radius:0;padding:12px 40px 12px 14px;margin-bottom:14px;font-size:14px;color:var(--ink);line-height:1.45}
.blockprompt b{font-weight:600;color:var(--accent-deep);margin-right:2px;text-transform:none;letter-spacing:0;font-family:inherit}
.blockprompt__act{margin-left:6px;border:0;background:none;color:var(--accent-deep);
  border-radius:0;padding:5px 0;cursor:pointer;font-weight:600;font-size:14px;text-decoration:underline}
.blockprompt__dismiss{
  position:absolute;top:2px;right:0;width:40px;height:40px;margin:0;padding:0;
  border:0;background:transparent;color:var(--ink-soft);cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
}
.blockprompt__dismiss:hover{color:var(--ink)}
.fatigue{background:transparent;border:0;border-left:3px solid var(--ink-soft);
  border-radius:0;padding:12px 0 12px 14px;margin-bottom:14px;font-size:14px;color:var(--ink);line-height:1.45}
.fatigue b{font-weight:600;color:var(--ink);margin-right:2px;text-transform:none;letter-spacing:0;font-family:inherit}
.fatigue__trim{margin-left:6px;border:0;background:none;color:var(--accent-deep);
  padding:5px 0;cursor:pointer;font-weight:600;font-size:14px;text-decoration:underline}

.term{display:inline-flex;align-items:center;justify-content:center;border:0;background:transparent;color:inherit;padding:10px 8px;margin:-10px -8px;
  min-width:44px;min-height:44px;box-sizing:border-box;vertical-align:middle;
  text-decoration:underline dotted;text-decoration-color:var(--ink-faint);cursor:pointer;font:inherit;line-height:1.4}
.term:hover{text-decoration-color:var(--accent);color:var(--accent-deep)}
/* Form controls drop inherited casing and tracking (HTML's UA sheet), so a term
   used as a column heading has to be told to look like one. */
.sets__head .term,.curset__cell-lab .term{text-transform:uppercase;letter-spacing:.08em}
.glossary{
  position:absolute;z-index:60;max-width:280px;
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:14px 16px 12px;box-shadow:0 8px 24px rgba(0,0,0,.1);
}
.glossary__close{position:absolute;top:0;right:0;border:0;background:transparent;color:var(--ink-soft);cursor:pointer;font-size:14px;line-height:1;width:44px;height:44px;min-width:44px;min-height:44px;padding:0}
.glossary__term{font-weight:700;font-size:13px;margin:0 24px 6px 0;text-transform:uppercase;letter-spacing:.04em}
.glossary__body{margin:0;font-size:13px;line-height:1.45;color:var(--ink-soft)}

/* Block review — full-screen sheet */
.blockreview{
  position:fixed;inset:0;z-index:70;max-width:none;margin:0;
  background:var(--bg);border:0;border-radius:0;
  padding:calc(12px + env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left));
  overflow:auto;
}
.blockreview__title{font-size:17px;font-weight:600;margin:0;text-transform:none;letter-spacing:0;color:var(--ink);text-align:center}
.blockreview__body{margin:0 0 14px;font-size:14px;line-height:1.5;color:var(--ink-soft)}
.blockreview__body p{margin:0 0 8px}
.blockreview__body b{font-weight:600;text-transform:none;letter-spacing:0;font-size:inherit;color:var(--ink);font-family:inherit}
.blockreview__rec{margin-top:10px!important;font-weight:600;color:var(--ink)}
.blockreview__why{margin:0!important;font-size:13px;color:var(--ink-faint)}
.blockprogress{margin:0 0 16px;padding:12px 0;border:0;border-radius:0;background:transparent}
.blockprogress__title{font-weight:600;font-size:12px;margin:0 0 8px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-faint)}
.review__summary{margin:0;font-size:14px;line-height:1.55;color:var(--ink)}

/* ============================================================
   Session summary — the screen a finished session earns
   Full-bleed over the app: the work is done, so nothing else competes for the
   page. One column, the evidence in the order it matters — records, how the
   lifts moved, what the muscles took, where the week now stands.
   Motion is one beat. `is-played` is set a frame after the panel is up, so the
   strike is seen rather than missed, and every block settles in behind it in
   reading order. The animations only exist under `is-played`, so a panel that
   never gets there is a fully legible page rather than a blank one.
   ============================================================ */
.sumsheet{
  /* The bottom band is named because the pinned action has to reach through it:
     it sticks to the true bottom edge and pays this padding out of its own box,
     so the report scrolls under a button rather than past a gap beside one. */
  --sum-pad-b:calc(30px + env(safe-area-inset-bottom));
  position:fixed;inset:0;z-index:75;overflow-y:auto;overscroll-behavior:contain;
  background:var(--bg);
  padding:calc(30px + env(safe-area-inset-top)) max(16px,env(safe-area-inset-right))
    var(--sum-pad-b) max(16px,env(safe-area-inset-left));
}
.sumsheet.hidden{display:none!important}
.sumsheet__body{width:min(100%,var(--maxw));margin:0 auto}

/* The strike: the same mark a finished exercise draws, at the size the end of
   a whole session deserves, under a ring that opens once and goes. */
.sum-crest{display:flex;justify-content:center}
.sum-crest__mark{
  position:relative;width:64px;height:64px;border-radius:50%;border:2px solid var(--accent);
}
.sum-crest__mark::after{
  content:"";position:absolute;inset:0;background:var(--accent);
  -webkit-mask:var(--check) no-repeat center/30px;mask:var(--check) no-repeat center/30px;
}
.sumsheet.is-played .sum-crest__mark{animation:sum-strike .5s cubic-bezier(.2,1.25,.4,1) both}
.sumsheet.is-played .sum-crest__mark::before{
  content:"";position:absolute;inset:-6px;border-radius:50%;border:2px solid var(--accent);
  animation:sum-ring .8s .1s cubic-bezier(.2,.7,.2,1) both;
}
@keyframes sum-strike{
  0%{opacity:0;transform:scale(.5)}
  55%{opacity:1;transform:scale(1.12)}
  100%{opacity:1;transform:scale(1)}
}
@keyframes sum-ring{from{opacity:.55;transform:scale(1)}to{opacity:0;transform:scale(1.7)}}
/* Each block carries its own index, so the stagger follows the reading order
   however many blocks this particular session earned. */
.sumsheet.is-played .sumsheet__body>*:not(.sum-crest){
  animation:sum-settle .4s cubic-bezier(.2,.7,.2,1) both;
  animation-delay:calc(var(--i,0) * 55ms);
}
@keyframes sum-settle{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

.sum-eyebrow{
  margin-top:16px;text-align:center;font-size:11px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--accent-deep);
}
/* The heading takes focus so a screen reader is read the session before the
   controls, not because it is one. A ring here would draw a box around a title
   nobody can act on, so it stays off — every actual control keeps its own. */
.sum-hero{margin-top:6px;text-align:center;font-size:34px;font-weight:650;letter-spacing:-.02em;line-height:1.05}
.sum-hero:focus,.sum-hero:focus-visible{outline:none}
.sum-sub{margin-top:6px;text-align:center;font-size:14px;color:var(--ink-soft)}
.sum-stats{margin-top:22px}

/* A record reads as one line: what kind, on what, by how much, at what. */
.sum-prs{list-style:none;margin:0;padding:0}
.sum-pr{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:6px 11px;
  padding:12px 0;border-bottom:1px solid var(--rule);
}
.sum-pr:first-child{border-top:1px solid var(--rule)}
/* A block whose records are all one kind drops the badge, so the row is two
   columns and the load-by-reps figure keeps the right edge. */
.sum-prs--onekind .sum-pr{grid-template-columns:minmax(0,1fr) auto}
.sum-pr__badge{
  font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;
  color:var(--cta-ink);background:var(--accent-deep);border-radius:999px;padding:4px 8px;white-space:nowrap;
}
.sum-pr__text{min-width:0}
/* The lift is the point of the line, so a long name wraps rather than being cut
   off — an ellipsis on "Chest-supported machine row" hides which lift it was. */
.sum-pr__name{display:block;font-size:15px;font-weight:600;color:var(--ink);line-height:1.25}
.sum-pr__over{display:block;margin-top:2px;font-size:12px;color:var(--accent-deep)}
.sum-pr__val{font-family:var(--mono);font-size:13px;color:var(--ink-soft);white-space:nowrap;align-self:center}
.sum-more{margin-top:10px;font-size:13px;color:var(--ink-faint)}

.sum-chips{display:flex;flex-wrap:wrap;gap:8px}
.sum-chip{
  font-size:13px;color:var(--ink-soft);background:var(--surface);
  border:1px solid var(--rule);border-radius:999px;padding:7px 12px;
}
.sum-chip.is-up{color:var(--positive);border-color:rgba(var(--positive-rgb),.35)}
.sum-chip.is-new{color:var(--accent-deep);border-color:rgba(var(--accent-rgb),.35)}

.sum-baseline{
  margin-top:24px;padding-left:14px;border-left:2px solid var(--accent);
  font-size:15px;line-height:1.55;color:var(--ink-soft);
}
.sum-week{font-size:15px;color:var(--ink)}
.sum-segbar{margin-top:10px}
.sum-next{
  display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-top:26px;padding:14px 0;
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
}
.sum-next__lab{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint)}
.sum-next__day{font-size:17px;font-weight:600}
.sum-next__meta{margin-left:auto;font-size:13px;color:var(--ink-soft)}
.sum-secondary{margin-top:26px}
/* The way out is never below the fold. A session ends with a lifter holding a
   phone and breathing hard, and on a session with records, four muscles and a
   next day the only exit used to be a scroll away — so the action stays put and
   the report moves under it. Only the action: pinning the secondary link with it
   would park a two-storey block over the muscle rows it is meant to sit beside. */
.sum-actions{
  position:sticky;bottom:calc(-1 * var(--sum-pad-b));z-index:1;
  margin-top:14px;padding:12px 0 var(--sum-pad-b);background:var(--bg);
}
.sum-actions::before{
  content:"";position:absolute;left:0;right:0;bottom:100%;height:20px;
  background:linear-gradient(to top,var(--bg),transparent);pointer-events:none;
}

@media (prefers-reduced-motion:reduce){
  /* The panel still arrives as a full screen; it just stops moving to do it. */
  .sumsheet.is-played .sum-crest__mark,
  .sumsheet.is-played .sumsheet__body>*:not(.sum-crest){animation:none}
  .sumsheet.is-played .sum-crest__mark::before{content:none}
}

.importchoice{
  position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:70;width:min(92%,360px);margin:0;
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:20px 18px 16px;box-shadow:0 16px 40px rgba(0,0,0,.12);
}
.importchoice__title{font-weight:650;font-size:18px;margin:0 0 8px;text-transform:none;letter-spacing:0}
.importchoice__body{margin:0 0 14px;font-size:14px;line-height:1.45;color:var(--ink-soft);white-space:pre-line}

.storage-recovery{
  position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:80;width:min(92%,380px);margin:0;
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:20px 18px 16px;box-shadow:0 16px 40px rgba(0,0,0,.12);
  max-height:90vh;overflow:auto;color:var(--ink);
}
.storage-recovery::backdrop{background:var(--scrim)}
.storage-recovery__copy{margin:0 0 12px;padding:10px 12px;background:var(--well);border-radius:var(--radius);font-size:14px;line-height:1.4}
.storage-recovery__copy-label{font-weight:650;margin:0 0 4px}
.settings-warn{margin:8px 0 0;font-size:13px;line-height:1.4;color:var(--accent-deep)}
.settings-warn.hidden,[hidden].settings-warn{display:none}

.thisweek{margin-bottom:14px;padding:0;background:transparent;border:0}
.thisweek__title{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin-bottom:8px}
.thisweek__rows{display:flex;flex-direction:column;gap:4px;font-size:14px;line-height:1.35;color:var(--ink)}
.thisweek__status{margin-top:6px;font-size:13px;color:var(--ink-soft)}
.thisweek__status b{color:var(--ink);font-weight:600}

.attention{display:flex;flex-direction:column;gap:0;margin-bottom:14px}
.attn__grp{display:flex;flex-direction:column;gap:0;padding:0;border-bottom:0}
.attn__lead{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);width:100%;margin:16px 0 4px}
.attn__why{width:100%;margin:0;font-size:13px;line-height:1.35;color:var(--ink-soft)}
.attn__chip{
  display:flex;align-items:center;gap:10px;width:100%;min-height:56px;padding:12px 0;
  border:0;border-bottom:1px solid var(--rule);background:transparent;color:var(--ink);
  border-radius:0;font-size:15px;cursor:pointer;font-weight:500;text-align:left;font:inherit;
}
.attn__chip:hover{color:var(--ink)}
.attn__chip .attn__dot{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:none}
.attn__chip .listrow__main{flex:1;min-width:0}
.attn__chip .listrow__title{font-size:16px;font-weight:600}
/* The reason is the only thing on the row a lifter has not already read in the
   title, so it gets the width the destination label used to take and a second
   line to land in. Ten rows that all stopped at "…weekly volume t…" read as a
   rendering fault rather than as ten lifts sharing one cause. */
.attn__chip .listrow__sub{
  font-size:13px;color:var(--ink-soft);margin-top:2px;line-height:1.35;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
}
.attn--add{display:none} /* ready list owns add signals */
.attn--add .attn__lead{color:var(--accent-deep)}
/* This week reads as a headline: verdict, then the arithmetic, then the session bar.
   Rules live in .statrow under it, so this block stays flat. */
.ov-week-status{font-size:clamp(26px,8vw,30px);font-weight:650;letter-spacing:-0.02em;line-height:1.08;color:var(--ink);margin:0}

/* A grouped-list row: 44px tall, full-bleed to the card edge so the press tint
   and the hairline read as part of the list above it. The box's own 44px hit
   area is pulled in by 11px on each side so the 22px artwork lines up with the
   panel's 14px gutter and sits 14px from its label. */
.field--check{
  position:relative;display:flex;align-items:center;gap:14px;min-height:44px;
  font-size:15px;font-weight:500;letter-spacing:normal;text-transform:none;color:var(--ink);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.field--check input{width:auto;flex:none;margin:0 -11px}
.field--check + .field--check::before{
  content:"";position:absolute;top:0;left:50px;right:0;border-top:1px solid var(--rule);
}
.field--check:active{background:var(--well)}
.field--check:has(input:disabled){color:var(--ink-faint);cursor:default}
.field--check:has(input:disabled):active{background:none}

.modeswitch{display:flex;gap:6px;margin-bottom:12px}
.modeswitch button{flex:1;border:1px solid var(--rule);background:var(--surface);color:var(--ink-soft);cursor:pointer;border-radius:10px;padding:9px 8px;font-weight:600;font-size:13px}
.modeswitch button.active{color:var(--ink);border-color:var(--accent)}
/* Onboarding */
.onboarding{position:fixed;inset:0;z-index:50;overflow:auto;background:var(--bg);
  padding:calc(12px + env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) calc(88px + env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left))}
.onboarding.active{display:block}
.onb{max-width:var(--maxw);margin:0 auto}
.onb__head{margin-bottom:18px}
.onb__step{margin:6px 0 0;font-size:11px;color:var(--ink-faint);letter-spacing:.08em;text-transform:uppercase;font-weight:600}
.onb__body{margin-bottom:18px}
.onb__lede{margin:0 0 16px;font-size:15px;line-height:1.45;color:var(--ink-soft)}
.onb__opts{display:flex;flex-direction:column;gap:10px}
.onb__opts.onb__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:8px}
.onb__changes{margin:18px 0 8px}
.onb__changes-lab{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-faint);margin:0 0 6px;
}
.onb__review{display:flex;flex-direction:column;gap:12px}
.onb__day{background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);padding:12px 14px}
.onb__dayname{font-weight:700;font-size:13px;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}
.onb__ex{font-size:13px;color:var(--ink-soft);padding:4px 0;border-top:1px solid var(--rule)}
.onb__ex:first-of-type{border-top:0;padding-top:0}
.onb__ex b{color:var(--ink);font-weight:600}
.onb__actions{display:flex;flex-direction:column;gap:8px;margin-top:4px}
.onb__nav{display:flex;gap:10px;position:sticky;bottom:0;padding:12px 0;background:linear-gradient(transparent,var(--bg) 30%)}
.onb__nav .btn{flex:1}
.onb__nav .btn--steel{flex:.4}
.onb__hint{margin:16px 0;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint)}
.onb__hint-body{font-size:13px;color:var(--ink-soft);margin:0 0 16px;text-transform:none;letter-spacing:0;font-weight:400}
.onb__import{text-align:center;font-size:14px;color:var(--ink-soft);margin:14px 0 8px;padding:0;background:none;border:0}
.onb__import button{background:none;border:0;color:var(--ink);text-decoration:underline;text-underline-offset:3px;cursor:pointer;font:inherit;padding:0}

/* Tour */
.tour{position:fixed;left:0;right:0;bottom:calc(var(--nav) + 10px + env(safe-area-inset-bottom));z-index:75;
  padding:0 12px;display:flex;justify-content:center;pointer-events:none}
.tour.hidden{display:none}
.tour__card{pointer-events:auto;width:min(100%,var(--maxw));
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:14px 18px;box-shadow:0 8px 24px rgba(0,0,0,.1);animation:toastin .3s cubic-bezier(.2,.7,.2,1)}
.tour__head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:4px}
.tour__eyebrow{color:var(--accent-deep);font-size:11px;letter-spacing:.08em;text-transform:uppercase;margin:0;font-weight:600}
.tour__skip{border:0;background:transparent;color:var(--ink-soft);cursor:pointer;font-size:12px;padding:4px 2px}
.tour__title{font-weight:650;font-size:22px;line-height:1.15;margin:0 0 8px;letter-spacing:-0.01em}
.tour__body{margin:0 0 12px;font-size:14px;line-height:1.5;color:var(--ink-soft)}
.tour__body b{color:var(--ink);font-weight:600}
.tour__extra:empty{display:none}
.tour__extra{margin:0 0 12px}
.tour__extra .btn{width:100%;justify-content:center}
.tour__dots{display:flex;flex-wrap:wrap;gap:6px;justify-content:center;margin-bottom:12px}
.tour__dot{width:7px;height:7px;border-radius:50%;background:var(--rule)}
.tour__dot.is-on{background:var(--accent)}
.tour__nav{display:flex;gap:10px}
.tour__nav .btn{flex:1}

/* Icon and copy share the first row; the action takes a row of its own, the way
   the first-run install card sets it. A CTA is a 100%-width control that does
   not shrink, so beside the copy it would take the whole banner and squeeze the
   text to one word per line. */
.installbanner{position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(var(--nav) + 16px + env(safe-area-inset-bottom));z-index:64;
  width:min(92%,420px);display:grid;grid-template-columns:auto 1fr;gap:10px 12px;align-items:center;
  background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);
  padding:13px 14px;box-shadow:0 8px 24px rgba(0,0,0,.1);
  animation:toastin .3s cubic-bezier(.2,.7,.2,1)}
.installbanner.hidden{display:none}
/* An open sheet makes the page behind it inert, so the banner is already
   unreachable there; drawn over the sheet it only hides the sheet's own rows.
   It comes back with the page, still dismissed or not as the lifter left it. */
body.is-sheet-open .installbanner{display:none}
.installbanner__close{position:absolute;top:6px;right:8px;border:0;background:transparent;color:var(--ink-soft);cursor:pointer;font-size:13px;line-height:1;padding:4px}
.installbanner__icon{width:34px;height:34px;border-radius:9px;background:#161513 url("icons/icon-192.png") center/cover no-repeat}
/* The corner the ✕ is drawn in belongs to the copy row alone, so the action
   below it still sits square in the card. */
.installbanner__text{min-width:0;padding-right:26px}
.installbanner__title{font-weight:650;font-size:14px;margin:0 0 2px}
.installbanner__body{margin:0;font-size:12.5px;line-height:1.4;color:var(--ink-soft)}
.installbanner__body b{color:var(--ink);font-weight:600}
.installbanner__action{grid-column:1/-1;height:44px;font-size:15px}
.ios-share{display:inline-block;width:15px;height:15px;vertical-align:-3px;margin:0 1px;
  fill:none;stroke:var(--accent);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

.stats-seg{display:none}
.stats-seg.active{display:block}
.seg--stats{margin-bottom:14px;background:transparent;border:0;border-radius:0;padding:0;gap:0;border-bottom:1px solid var(--rule);
  display:flex;flex-wrap:nowrap;justify-content:space-between;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.seg--stats::-webkit-scrollbar{display:none}
/* All five sections have to sit on one phone width — trim the tab padding rather than
   leave "Review" hanging off the edge. The end tabs flush with the page gutters, and
   anything narrower than the row still scrolls. */
.seg.seg--stats button{padding:12px 9px;font-size:13px}
.seg.seg--stats button:first-child{padding-left:0}
.seg.seg--stats button:last-child{padding-right:0}
@media (max-width:360px){.seg.seg--stats button{padding:12px 6px;font-size:12px}}
/* The smallest phone is the one width where six columns still ask for more room
   than the card has, so the ledger tightens before it falls back to scrolling. */
@media (max-width:360px){
  .chartcard{padding:14px}
  .chartcard .prledger th,.chartcard .prledger td{font-size:11px}
  .chartcard .prledger th + th,.chartcard .prledger td + td{padding-left:6px}
}
.seg--window{margin:8px 0 12px}

.session--edit{display:block}
.edhead{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin-bottom:12px}
.edate{display:flex;flex-direction:column;align-items:flex-end;color:var(--ink-soft);font-size:11px;gap:0;text-transform:uppercase;letter-spacing:.08em}
.edate input{width:auto;margin-top:5px;padding:9px 11px;font-size:16px}
.edrow{display:grid;grid-template-columns:1fr 4.2rem 3.4rem 3.4rem auto;gap:8px;align-items:center;margin-top:8px}
.edrow--head span{text-transform:uppercase;letter-spacing:.08em;font-size:10.5px;color:var(--ink-faint);text-align:center;font-weight:600}
.edrow--head span:first-child{text-align:left}
.edrow__name{font-size:13px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.edrow__name small{color:var(--ink-faint)}
.edrow__in{margin-top:0;text-align:center;font-size:16px;padding:9px 4px;background:var(--surface)}
.edrow__rm{
  justify-self:end;display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;flex:none;padding:0;cursor:pointer;
  border:1px solid var(--rule-strong);border-radius:10px;background:var(--surface);
  color:var(--ink-soft);font:inherit;font-size:19px;line-height:1;
}
.edrow__rm.is-undo{color:var(--accent-deep);border-color:var(--accent);font-size:16px}
.edrow__rm-glyph{display:block;pointer-events:none}
.edrow.is-removed .edrow__name{text-decoration:line-through;text-decoration-color:var(--ink-faint)}
.edrow.is-removed .edrow__in{visibility:hidden}
input[aria-invalid="true"],[aria-invalid="true"]{outline:2px solid var(--accent-deep);outline-offset:1px}
.edbtns{display:flex;gap:10px;margin-top:14px}
.edbtns .btn{flex:1;padding:12px}
/* Throwing the session away sits after the edits and under a rule, so the eye
   reaches Save first and never lands on it while aiming for something else. */
.edrisk{display:flex;justify-content:center;margin-top:14px;padding-top:12px;border-top:1px solid var(--rule)}
.edrisk .session__del{padding:10px 12px}

.datepick{display:flex;flex-direction:column;align-items:flex-end;color:var(--ink-soft);font-size:11px;gap:0;text-transform:uppercase;letter-spacing:.06em;font-weight:600}
.datepick input{width:auto;margin-top:5px;padding:9px 11px;font-size:16px}
.sectionhead{margin:6px 0 16px}
.sectionhead__row{display:flex;align-items:flex-end;justify-content:space-between;gap:14px}

@keyframes rise{from{opacity:0}to{opacity:1}}
@keyframes woEnter{from{opacity:0;transform:translateY(16px) scale(.98)}to{opacity:1;transform:none}}
@keyframes woLeave{from{opacity:0;transform:translateY(-12px)}to{opacity:1;transform:none}}
.wo-anim-enter{animation:woEnter .32s cubic-bezier(.2,.7,.2,1) both}
.wo-anim-leave{animation:woLeave .28s cubic-bezier(.2,.7,.2,1) both}
@media (prefers-reduced-motion:reduce){
  .wo-anim-enter,.wo-anim-leave{animation:none}
}
@keyframes toastin{from{opacity:0;transform:translateX(-50%) translateY(10px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
@keyframes stamp{0%{transform:scale(1)}30%{transform:scale(.97)}60%{transform:scale(1.01)}100%{transform:scale(1)}}

.pday__caret{transition:transform .2s ease}
.pday.is-collapsed .pday__caret{transform:rotate(-90deg)}
.pday.is-collapsed .pexlist,.pday.is-collapsed .pday__add{display:none}
.pday.is-collapsed .pday__head{margin-bottom:0}

.ex__namebtn{display:flex;align-items:baseline;gap:6px;width:100%;margin:0;padding:0;border:0;background:none;color:var(--ink);text-align:left;cursor:pointer}
.ex__namebtn::after{content:"›";flex:none;color:var(--ink-faint);font-size:.8em}
.focus-ex__name .ex__namebtn::after{content:none;display:none}
/* The name opens the exercise page: give it a tap target without
   moving the baseline the title sits on. */
.focus-ex__name .ex__namebtn{font:inherit;color:inherit;display:block;padding:7px 0;margin:-7px 0;min-height:44px}
.ex__namebtn:hover{color:var(--accent-deep)}

.exnote{margin-top:10px}
.exnote__toggle{display:flex;align-items:baseline;gap:8px;width:100%;margin:0;padding:7px 0 0;border:0;border-top:1px solid var(--rule);
  background:none;cursor:pointer;text-align:left;font-size:12px;color:var(--ink-faint)}
.exnote__lab{flex:none;text-transform:uppercase;letter-spacing:.1em;font-size:10px}
.exnote__preview{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-style:italic}
.exnote.has-note .exnote__preview{color:var(--ink-soft);font-style:normal}
.exnote.has-note .exnote__lab{color:var(--accent-deep)}
.exnote.is-open .exnote__preview{display:none}
.exnote__input{margin-top:8px;min-height:62px;font-size:16px;line-height:1.45;
  background:var(--surface);border:1px solid var(--rule);border-radius:10px;padding:9px 11px;resize:vertical}
.exnote__input.hidden{display:none}
.exercise.is-collapsed .exnote,.exercise.is-skipped .exnote{display:none}

.exview__back{margin:0;padding:8px 4px;border:0;background:transparent;color:var(--accent-deep);
  cursor:pointer;font-weight:500;text-transform:none;letter-spacing:0;font-size:15px;min-height:44px}
.exdet__name{font-weight:650;font-size:28px;line-height:1.1;letter-spacing:-0.02em;margin-bottom:4px}
.exdet__meta{margin-bottom:14px}
.exdet__rec{border-left:3px solid var(--accent);background:transparent;border-top:0;border-right:0;border-bottom:0;
  border-radius:0;padding:10px 0 10px 14px;margin-bottom:14px}
.exdet__rec .rec{margin:8px 0 0}
/* The illustration is an opaque square on its own warm ground. Dropping it
   straight onto the page would read as a pasted tile, so the field carries that
   warmth full-bleed and dissolves into the page above and below it. The fade is
   a colour bridge between two near-neutral backgrounds and is the one gradient
   the design allows; it authorises none elsewhere. See
   docs/design/exercise-detail-illustration.md. */
.exdet-art{
  /* Overridden per movement from the sampled paper colour of its own artwork;
     the shipped set spans #E3D4BE to #F4EDE1, so one constant would draw a
     visible square around most of the library. This fallback only shows if a
     movement ever carries art without a sampled colour. */
  --exercise-art-bg:#ECE0CF;
  --exercise-art-lead:18px;
  --exercise-art-trail:30px;
  margin:2px calc(-1 * max(16px,env(safe-area-inset-right))) 12px calc(-1 * max(16px,env(safe-area-inset-left)));
  /* The stops are lengths, not percentages, because they have to land on the
     empty bands above and below the picture. A percentage stop moves with the
     field's height, so on a wide shell the fade climbs back over the artwork
     and re-draws the very edge it exists to dissolve. */
  background:linear-gradient(to bottom,
    var(--bg) 0,var(--exercise-art-bg) var(--exercise-art-lead),
    var(--exercise-art-bg) calc(100% - var(--exercise-art-trail)),var(--bg) 100%);
}
.exdet-art__figure{
  display:flex;justify-content:center;
  padding:var(--exercise-art-lead) 0 var(--exercise-art-trail);
}
.exdet-art__img{display:block;width:min(80%,450px);height:auto}
/* Four columns of rules around a single stat cluster fight the orange
   recommendation rail for the eye. On this page whitespace does the grouping,
   so the shared band drops its frame here and only here. */
.exdet__stats{border-top:0;border-bottom:0;margin-top:4px}
.exdet__stats .statrow__cell::after{content:none}
.exsessions{display:flex;flex-direction:column;gap:0}
.exsess{background:transparent;border:0;border-bottom:1px solid var(--rule);border-radius:0;padding:14px 0}
.exsess__head{display:flex;align-items:center;flex-wrap:wrap;gap:6px 10px;font-size:13px;color:var(--ink-soft)}
.exsess__date{color:var(--ink);font-weight:600}
.exsess__delta{margin-left:auto;font-weight:600;font-size:12px;color:var(--accent-deep)}
.exsess__sets{display:flex;flex-wrap:wrap;gap:5px 12px;margin-top:8px;font-size:15px;color:var(--ink);font-variant-numeric:tabular-nums}
.exsess__set small{color:var(--ink-soft);font-size:.82em;margin-left:1px}
.exsess__set.is-warmup{color:var(--ink-faint)}
.exsess__note{font-size:13px;color:var(--ink-soft);margin:10px 0 0;line-height:1.45;border-left:2px solid var(--rule);padding-left:9px;white-space:pre-wrap}
.exsess__note span{display:block;color:var(--ink-faint);text-transform:uppercase;letter-spacing:.1em;font-size:10px;margin-bottom:3px}

.announce-host{position:static;pointer-events:none}
.announce-host .toast{pointer-events:none}
.sessionbanner{position:relative;margin:0 0 .75rem;padding:0;border:0;border-bottom:1px solid var(--rule);border-radius:0;background:transparent;cursor:default}
.sessionbanner.hidden{display:none}
.sessionbanner.is-missed{border-color:var(--accent)}
.sessionbanner__act{display:block;width:100%;margin:0;padding:.75rem 2.75rem .75rem 0;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer;min-height:44px}
.sessionbanner__close{position:absolute;top:0;right:0;width:44px;height:44px;margin:0;padding:0;border:0;background:transparent;color:inherit;font:inherit;line-height:1;cursor:pointer;opacity:.7}
.sessionbanner__title{font-weight:700;margin:0 0 .25rem}
.sessionbanner__body{margin:0;font-size:.9rem;color:var(--ink-soft)}
.notifytypes{display:grid;gap:0;margin:2px 0 4px}
#unfinishedBanner{margin:0 0 .75rem;padding:.75rem 0;border:0;border-bottom:1px solid var(--rule);border-radius:0}
#unfinishedBanner.hidden{display:none}

.ready-list__row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 0;border-bottom:1px solid var(--rule);width:100%;background:none;border-left:0;border-right:0;border-top:0;cursor:pointer;text-align:left;font:inherit;color:inherit}
.ready-list__name{font-size:16px;font-weight:600}
.ready-list__why{font-size:13px;color:var(--ink-soft);margin-top:2px}
.ready-list__delta{color:var(--accent-deep);font-weight:600;font-size:15px;flex:none}

.sectionhead-period{font-size:14px;color:var(--ink-soft);background:none;border:0;cursor:pointer;padding:8px 0;min-height:44px}

@media (prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important}
}
@media (min-width:680px){
  .metrics,.statrow--4,.statband--4{grid-template-columns:repeat(4,minmax(0,1fr))}
  .page-title,h2{font-size:32px}
}
@media (max-width:360px){
  /* A 288px row cannot hold the set action, a three-part load control, reps,
     RIR and Save in one line at 44px each. Keep the outer fields explicit and
     stack the load input over its two steppers inside the flexible column. */
  .sets__head,.setrow{grid-template-columns:44px minmax(0,1fr) 44px 44px 44px;gap:4px}
  .sets__head.has-effort,.setrow.has-effort{grid-template-columns:44px minmax(0,1fr) 44px 44px;row-gap:6px}
  .setrow{align-items:start}
  .setrow .kg{display:grid;grid-template-columns:repeat(2,minmax(44px,1fr));align-items:stretch}
  .setrow .kg input{grid-column:1/-1;grid-row:1;width:100%;min-width:44px;border-bottom:1px solid var(--rule)}
  .setrow .kg input:focus{border-bottom-color:var(--rule)}
  .setrow .kg .stepbtn{width:100%;min-width:44px}
  .setrow .kg .stepbtn[data-dir="-1"]{grid-column:1;grid-row:2}
  .setrow .kg .stepbtn[data-dir="1"]{grid-column:2;grid-row:2;border-left:1px solid var(--rule)}
  .effort__btn{font-size:11px;letter-spacing:.02em}
  .vrow{grid-template-columns:1fr auto}
  .setrow input{font-size:16px;padding:12px 2px}
  .stepbtn{width:44px;min-width:44px;font-size:17px}
  .saveset,.sets__head .sets__head-save{min-width:44px;padding:0 4px}
  .edrow{grid-template-columns:1fr 3.4rem 3rem 3rem auto;gap:6px}
  .curset__val{font-size:26px}
}

/* ---- Shell class aliases (HTML / renderToday) ---- */
.pagehead__titles{min-width:0;flex:1}
.settings-btn{
  width:44px;height:44px;border-radius:50%;border:1.5px solid var(--rule-strong);
  background:transparent;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--ink-faint);padding:0;
}
.settings-btn:hover{color:var(--ink-soft);border-color:var(--rule-strong)}
.settings-btn .icon-mask{width:20px;height:20px}
.icon-mask{
  display:inline-block;width:22px;height:22px;background:currentColor;flex:none;
  -webkit-mask:no-repeat center/contain;mask:no-repeat center/contain;
}
.icon-mask--sm{width:16px;height:16px}
.icon-mask--minus{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E")}
.icon-mask--plus{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E")}
.icon-mask--play{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M8 5.4a1 1 0 0 1 1.53-.85l9 6.6a1 1 0 0 1 0 1.7l-9 6.6A1 1 0 0 1 8 18.6z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M8 5.4a1 1 0 0 1 1.53-.85l9 6.6a1 1 0 0 1 0 1.7l-9 6.6A1 1 0 0 1 8 18.6z'/%3E%3C/svg%3E")}
.icon-mask--pause{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Crect x='7' y='5' width='3.6' height='14' rx='1.2'/%3E%3Crect x='13.4' y='5' width='3.6' height='14' rx='1.2'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Crect x='7' y='5' width='3.6' height='14' rx='1.2'/%3E%3Crect x='13.4' y='5' width='3.6' height='14' rx='1.2'/%3E%3C/svg%3E")}
.icon-mask--stop{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Crect x='6.5' y='6.5' width='11' height='11' rx='2'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Crect x='6.5' y='6.5' width='11' height='11' rx='2'/%3E%3C/svg%3E")}
.icon-mask--reset{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12a8 8 0 1 1-2.34-5.66'/%3E%3Cpolyline points='20 3.5 20 8.5 15 8.5'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12a8 8 0 1 1-2.34-5.66'/%3E%3Cpolyline points='20 3.5 20 8.5 15 8.5'/%3E%3C/svg%3E")}
.icon-mask--chev-down{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E")}
.icon-mask--overflow{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Ccircle cx='5' cy='12' r='1.6'/%3E%3Ccircle cx='12' cy='12' r='1.6'/%3E%3Ccircle cx='19' cy='12' r='1.6'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Ccircle cx='5' cy='12' r='1.6'/%3E%3Ccircle cx='12' cy='12' r='1.6'/%3E%3Ccircle cx='19' cy='12' r='1.6'/%3E%3C/svg%3E")}
.icon-mask--note{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 3.5h8.5l4.5 4.5v12.5h-13z'/%3E%3Cpath d='M14 3.5V8h4.5'/%3E%3Cpath d='M8.5 12.5h7M8.5 16h4.5'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 3.5h8.5l4.5 4.5v12.5h-13z'/%3E%3Cpath d='M14 3.5V8h4.5'/%3E%3Cpath d='M8.5 12.5h7M8.5 16h4.5'/%3E%3C/svg%3E")}
.icon-mask--timer{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='13.5' r='7.5'/%3E%3Cpath d='M9.5 2.5h5'/%3E%3Cpath d='M12 2.5V6'/%3E%3Cpath d='M18.6 7.4l1.6-1.6'/%3E%3Cpath d='M12 13.5V9.8'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='13.5' r='7.5'/%3E%3Cpath d='M9.5 2.5h5'/%3E%3Cpath d='M12 2.5V6'/%3E%3Cpath d='M18.6 7.4l1.6-1.6'/%3E%3Cpath d='M12 13.5V9.8'/%3E%3C/svg%3E")}
.icon-mask--skip{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 5 14 12 6 19'/%3E%3Cpath d='M18 5v14'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 5 14 12 6 19'/%3E%3Cpath d='M18 5v14'/%3E%3C/svg%3E")}
.icon-mask--close{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E")}
.icon-mask--chev-up{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 15 12 9 18 15'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 15 12 9 18 15'/%3E%3C/svg%3E")}
.icon-mask--gear{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.93 5.63L9.98 2.51A9.7 9.7 0 0 1 14.02 2.51L14.07 5.63A6.7 6.7 0 0 1 16.48 7.02L19.21 5.51A9.7 9.7 0 0 1 21.23 9L18.55 10.61A6.7 6.7 0 0 1 18.55 13.39L21.23 15A9.7 9.7 0 0 1 19.21 18.49L16.48 16.98A6.7 6.7 0 0 1 14.07 18.37L14.02 21.49A9.7 9.7 0 0 1 9.98 21.49L9.93 18.37A6.7 6.7 0 0 1 7.52 16.98L4.79 18.49A9.7 9.7 0 0 1 2.77 15L5.45 13.39A6.7 6.7 0 0 1 5.45 10.61L2.77 9A9.7 9.7 0 0 1 4.79 5.51L7.52 7.02Z'/%3E%3Ccircle cx='12' cy='12' r='3.2'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.93 5.63L9.98 2.51A9.7 9.7 0 0 1 14.02 2.51L14.07 5.63A6.7 6.7 0 0 1 16.48 7.02L19.21 5.51A9.7 9.7 0 0 1 21.23 9L18.55 10.61A6.7 6.7 0 0 1 18.55 13.39L21.23 15A9.7 9.7 0 0 1 19.21 18.49L16.48 16.98A6.7 6.7 0 0 1 14.07 18.37L14.02 21.49A9.7 9.7 0 0 1 9.98 21.49L9.93 18.37A6.7 6.7 0 0 1 7.52 16.98L4.79 18.49A9.7 9.7 0 0 1 2.77 15L5.45 13.39A6.7 6.7 0 0 1 5.45 10.61L2.77 9A9.7 9.7 0 0 1 4.79 5.51L7.52 7.02Z'/%3E%3Ccircle cx='12' cy='12' r='3.2'/%3E%3C/svg%3E")}
.icon-mask--search{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='10.75' cy='10.75' r='6.25'/%3E%3Cpath d='M15.4 15.4L20 20'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round'%3E%3Ccircle cx='10.75' cy='10.75' r='6.25'/%3E%3Cpath d='M15.4 15.4L20 20'/%3E%3C/svg%3E")}
.icon-mask--sheet{-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4.5' width='17' height='15' rx='2'/%3E%3Cpath d='M3.5 9.25h17M3.5 14.25h17M11.5 9.25v10.25'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.5' y='4.5' width='17' height='15' rx='2'/%3E%3Cpath d='M3.5 9.25h17M3.5 14.25h17M11.5 9.25v10.25'/%3E%3C/svg%3E")}
.icon-btn{color:var(--ink)}
.icon-btn--ghost{border:0;background:transparent;color:var(--ink)}
.icon-btn--note{width:44px;height:44px;color:var(--ink-soft);flex:none}
.today-prog{padding:8px 0 16px;border-bottom:1px solid var(--rule)}
.today-prog__name{font-size:16px;font-weight:600}
.today-prog__week{font-size:14px;color:var(--ink-soft);margin-top:4px}
.today-prog .segbar{margin-top:12px}
.today-prog__done{margin-top:10px;text-align:right;font-size:13px;color:var(--ink-soft)}

/* Today, once a session is saved: a recap stands where the start CTA would be. */
.today-done{padding:4px 0 8px}
.today-done__name{display:flex;align-items:center;gap:10px}
.today-done__check{
  width:22px;height:22px;flex:none;background:var(--accent);
  -webkit-mask:var(--check) no-repeat center/contain;mask:var(--check) no-repeat center/contain;
}
.today-done__pr{margin:10px 0 0;font-size:15px;color:var(--ink)}
.today-done__note{margin:10px 0 0;font-size:14px;color:var(--ink-soft)}
#reviewTodaySession{width:100%;min-height:52px;margin-top:28px;font-size:16px}

/* Today's exercise preview — hairline rows, first few visible, rest disclosed. */
.today-exlist{margin-top:10px;border-top:1px solid var(--rule)}
.today-ex{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  min-height:44px;padding:12px 0;background:none;border:0;border-bottom:1px solid var(--rule);
  cursor:pointer;text-align:left;font:inherit;color:inherit;
}
.today-ex__name{min-width:0;flex:1;font-size:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.today-ex__value{flex:none;display:inline-flex;align-items:center;gap:10px;font-size:16px;color:var(--ink)}
.today-exmore{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  min-height:44px;padding:12px 0;background:none;border:0;
  cursor:pointer;text-align:left;font:inherit;font-size:15px;color:var(--ink-soft);
}
/* The list ends on its own padding, so the CTA sits closer than it does without one. */
.today-session:has(.today-exlist) + .btn--cta,
.today-session:has(.today-exlist) + #startWorkout{margin-top:4px}
.segbar__seg{flex:1;height:4px;border-radius:2px;background:var(--rule-strong)}
.segbar__seg.is-done,.segbar__seg.is-current{background:var(--accent)}
/* Exercise progress: done is near-black, the current one orange, the rest gray. */
.segbar--ex .segbar__seg{height:5px;border-radius:3px}
.segbar--ex .segbar__seg.is-done{background:var(--ink)}
.segbar--ex .segbar__seg.is-current{background:var(--accent)}
.text-link{background:none;border:0;color:var(--accent-deep);cursor:pointer;font:inherit;padding:8px 0;font-size:15px}
.text-link--center{display:block;width:100%;text-align:center;color:var(--ink);margin-top:10px}
.link-accent{background:none;border:0;color:var(--accent-deep);cursor:pointer;font:inherit;padding:8px 4px;font-size:15px}
.week-letters{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-top:12px;text-align:center}
.week-letters__d{font-size:12px;color:var(--ink-soft);font-weight:500}
.week-letters__m{margin-top:8px;min-height:18px;display:flex;align-items:center;justify-content:center}
.week-letters__check{font-size:12px;color:var(--ink)}
.week-letters__dot{width:6px;height:6px;border-radius:50%;background:var(--rule-strong);display:inline-block}
.week-letters__dot.is-today{width:8px;height:8px;background:var(--accent)}
.ov-week-line{font-size:16px;color:var(--ink-soft);margin-top:6px}
/* One segment per planned session; filled ones are the sessions already logged. The
   line above says the same thing in words, so the bar is decoration to assistive tech. */
.ov-week-bar{display:flex;gap:8px;margin-top:14px}
.ov-week-bar__seg{flex:1 1 0;min-width:0;max-width:56px;height:4px;border-radius:2px;background:var(--rule-strong)}
.ov-week-bar__seg.is-done{background:var(--ink)}
.listrow{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:56px;padding:12px 0;background:none;border:0;border-bottom:1px solid var(--rule);cursor:pointer;text-align:left;font:inherit;color:inherit}
.listrow--static{cursor:default}
.range-static{font-size:14px;color:var(--ink)}
.tour-rest-hint{margin:0;font-size:12px;color:var(--ink-soft);max-width:11rem;text-align:right}
.listrow__main{min-width:0;flex:1}
.listrow__title{font-size:16px;font-weight:600}
.listrow__sub{font-size:13px;color:var(--ink-soft);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.listrow__meta{flex:none;display:inline-flex;align-items:center;gap:8px;color:var(--ink-soft);font-size:13px;white-space:nowrap}
.listrow__value{flex:none;display:inline-flex;align-items:center;gap:8px}
.chevron{
  width:16px;height:16px;flex:none;color:var(--ink-faint);background:currentColor;
  -webkit-mask:no-repeat center/contain;mask:no-repeat center/contain;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 6 15 12 9 18'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 6 15 12 9 18'/%3E%3C/svg%3E");
}
.chevron.is-down{
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
.chevron.is-up{
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 15 12 9 18 15'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 15 12 9 18 15'/%3E%3C/svg%3E");
}
.link-row-cta{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:44px;
  padding:10px 0;background:none;border:0;border-bottom:1px solid var(--rule);cursor:pointer;
  text-align:left;font:inherit;color:var(--ink);font-size:15px;font-weight:500;
}
.link-row-cta .chevron{color:var(--ink-faint)}
.range-quiet{background:none;border:0;padding:0;cursor:pointer;color:var(--ink);font:inherit;font-size:14px;display:inline-flex;align-items:center;gap:4px;min-height:44px}
.range-quiet .caret{width:12px;height:12px;background:currentColor;
  -webkit-mask:no-repeat center/contain;mask:no-repeat center/contain;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
.strength-pick select,.select-flat{
  appearance:none;-webkit-appearance:none;border:0;background-color:transparent;margin:0;padding:8px 22px 8px 0;
  width:auto;max-width:100%;text-align:left;color:var(--ink);font-size:16px;font-weight:600;border-radius:0;box-shadow:none;
  background-image:var(--select-caret);
  background-position:right center;background-size:14px 14px;background-repeat:no-repeat;
}
.strength-pick select:focus,.select-flat:focus{box-shadow:none}
.strength-pick select:focus-visible,.select-flat:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px}
.today-footer{display:flex;align-items:center;gap:8px;margin:22px 0 8px;font-size:13px;color:var(--ink-faint)}
.today-footer__icon{opacity:.7}
.today-upnext,.today-week{margin:0}
.icon-btn--ghost{border:0;background:transparent}
.wo-overflow{position:absolute;right:0;top:100%;z-index:15;min-width:220px;background:var(--surface);border:1px solid var(--rule);border-radius:var(--radius);padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.1)}
.wo-overflow__item{display:block;width:100%;margin-top:10px;min-height:44px;padding:10px 12px;border:1px solid var(--rule);border-radius:10px;background:var(--surface);color:var(--ink);font-family:inherit;font-size:14px;font-weight:600;text-align:left;cursor:pointer}
.wo-overflow__item.hidden{display:none}
.is-hidden{display:none!important}
.card--plain{background:transparent;border:0;padding:0;margin:0;overflow:visible}
.section-label--row{display:flex;align-items:center;justify-content:space-between}
.settings-head{display:flex;align-items:center;justify-content:center;position:relative;min-height:44px;margin-bottom:18px}
.settings-head .back-link{position:absolute;left:0}
.settings-head__title{font-size:17px;font-weight:600}
.settings-identity{display:flex;align-items:center;gap:14px;padding:8px 0 18px}
.settings-identity__mark{width:52px;height:52px;border-radius:14px;overflow:hidden;background:#efe5df;box-shadow:0 1px 2px rgba(23,23,25,.12),0 6px 18px rgba(23,23,25,.08);flex:none}
.settings-identity__mark img{display:block;width:100%;height:100%;object-fit:cover}
.settings-identity__name{font-size:17px;font-weight:600}
.settings-identity__cap{font-size:13px;color:var(--ink-soft);margin-top:2px}
/* A panel is the tail of the row that opened it: it shares that row's text
   column, and the rule that closes it is the next row's. */
.settings-panel{display:none;padding:4px var(--settings-inset) 14px;background:transparent}
.settings-panel.is-open{display:block}
.settings-group > .field,.settings-group > .lede,.settings-group > .rirmode{margin-left:var(--settings-inset);margin-right:var(--settings-inset)}
.settings-group .notifytypes .field--check{margin-left:calc(var(--settings-inset) * -1);margin-right:calc(var(--settings-inset) * -1);padding:2px var(--settings-inset)}
.settings-group > .lede{padding-bottom:10px}
.blockreview__head{display:flex;align-items:center;justify-content:center;position:relative;margin-bottom:18px;min-height:44px}
.blockreview__head .back-link{position:absolute;left:0}
.blockreview__head-title{font-size:17px;font-weight:600;margin:0}
.onb__cancel{color:var(--accent-deep)}
.onb__title{font-size:17px;font-weight:600;text-align:center}
.exview-head{display:flex;align-items:center;justify-content:center;position:relative;min-height:44px;margin-bottom:14px}
/* The rule named .exview__back, which nothing in the markup uses — every header
   built on .exview-head has been centring its back link inline instead of
   pinning it left. The buttons all carry .back-link. */
.exview-head .back-link,.exview-head .exview__back{position:absolute;left:0;display:inline-flex;align-items:center;min-height:44px}
.exview-head__title{margin:0;font-size:17px;font-weight:600}
.exview-head__right{position:absolute;right:0}
/* One lift, four facts: which lift it is, how its top set has moved, the records
   behind that, and the curve. They shared a card and nothing else — the picker
   and its caption were inset 14px while the figures, the ledger and the canvas
   ran to the card's own border, and the seven gaps between them were each
   chosen on its own. One column at one inset, one rhythm, and a hairline only
   where the subject changes. */
.chartcard{padding:16px}
.chartcard > .field,.chartcard > .lede{margin:0}
.chartcard .strength-pick select{padding:0 22px 0 0;font-size:18px}
.chartcard .trend{margin-top:8px}
/* The records are what the two figures are drawn from, and the curve is those
   records over time, so each earns a rule rather than a guessed gap. */
.chartcard .prledger{margin-top:16px;padding-top:15px;border-top:1px solid var(--rule)}
.chartcard__cap{margin-top:16px;padding-top:15px;border-top:1px solid var(--rule)}
/* This ledger is a block inside a card, not a wide table on a page of its own,
   so it drops the shared 520px floor: seven narrow columns fit the card's
   column, and the last of them stops being cut off mid-figure. */
.chartcard .prledger table{min-width:0}
.chartcard .prledger th,.chartcard .prledger td{padding:9px 0;font-size:12px}
.chartcard .prledger th + th,.chartcard .prledger td + td{padding-left:8px}
.chartcard .prledger tr > :last-child{text-align:right}
/* The last row's own rule and the chart caption's would otherwise sit 15px
   apart with nothing between them. One seam. */
.chartcard .prledger tbody tr:last-child > *{border-bottom:0}
.chart-wrap{margin:4px 0 0}
.strength-pick{margin-bottom:0}
.is-danger{color:var(--danger)!important}

/* Calendar + progress stat row overrides kept for class aliases — unified band rules above win for separators */
.cal-head{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:6px}
.cal-head__title{font-size:15px;font-weight:600}
.cal-summary{text-align:center;font-size:13px;color:var(--ink-soft);margin-bottom:12px}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px 0;text-align:center}
.cal-grid__dow{font-size:11px;color:var(--ink-faint);font-weight:500;padding:4px 0}
.cal-grid__day{padding:6px 0 10px;font-size:14px;color:var(--ink);min-height:44px;position:relative}
.cal-grid__day.is-out{color:var(--ink-faint)}
.cal-grid__mark{display:block;margin:4px auto 0;font-size:10px;line-height:10px}
.cal-grid__mark.is-check{color:var(--ink)}
.cal-grid__mark.is-pr,.cal-grid__mark.is-today{width:6px;height:6px;border-radius:50%;background:var(--accent);margin-top:6px}
.hist-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;min-height:56px;padding:14px 0;border-bottom:1px solid var(--rule);cursor:default}
.session__open{display:flex;align-items:center;justify-content:space-between;gap:12px;flex:1;min-width:0;background:none;border:0;padding:0;cursor:pointer;text-align:left;font:inherit;color:inherit}
.hist-eyebrow{font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);margin-bottom:4px}
.hist-row__title{font-size:20px;font-weight:640}
.ready-row__delta{color:var(--accent-deep);font-weight:600;flex:none;white-space:nowrap;display:inline-flex;align-items:center;gap:6px}
.coach-dest{font-size:13px;font-weight:600;color:var(--ink-soft);flex:none;white-space:nowrap}
/* Program overview day accordion (mock 05) */
.prog-day{border-bottom:1px solid var(--rule)}
.prog-day__head{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  min-height:56px;padding:14px 0;background:none;border:0;cursor:pointer;text-align:left;font:inherit;color:inherit;
}
.prog-day__title{font-size:16px;font-weight:600;color:var(--ink)}
.prog-day__muscles{font-size:13px;color:var(--ink-soft);margin-top:2px}
.prog-day__right{flex:none;display:flex;align-items:center;gap:6px;font-size:13px;color:var(--ink-soft)}
.prog-day__body{padding:0 0 12px}
.prog-ex{
  display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;
  padding:10px 0;background:none;border:0;border-top:1px solid var(--rule);cursor:pointer;
  text-align:left;font:inherit;color:var(--ink);font-size:15px;
}
.prog-ex__sets{flex:none;color:var(--ink-soft);font-variant-numeric:tabular-nums}
.prog-overview__name{font-size:28px;font-weight:650;letter-spacing:-0.02em;margin:4px 0 4px}
.prog-overview__meta{font-size:14px;color:var(--ink-soft);margin-bottom:12px}
.prog-overview__week{font-size:14px;font-weight:600;margin-top:8px}
.prog-overview__started{font-size:13px;color:var(--ink-soft);margin:8px 0 4px}

/* ---- First-run setup: install, then choose a program ----
   The screen is one layout; only its install section changes, and it is written
   by app.js from what the browser actually offers (a captured
   beforeinstallprompt, Safari's manual Home Screen flow, or nothing) rather
   than from any width breakpoint. The orange ground is --accent-deep, not
   --accent: white body copy measures ~5.5:1 on it and ~4.0:1 on the brighter
   accent, which is under AA. */
.firstrun{
  /* Above the app shell and its docked chrome, below the sheet layer (40/41):
     the iOS instructions are a sheet over this screen, not under it. */
  position:fixed;inset:0;z-index:35;background:var(--bg);
  overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  padding:calc(env(safe-area-inset-top) + clamp(20px,4.5vw,34px)) 0 calc(env(safe-area-inset-bottom) + 30px);
}
.firstrun:focus{outline:none}
.firstrun.hidden{display:none}
/* The gate covers the app rather than replacing it: `main` still holds the
   import review the gate can open, so the shell is left in place and only the
   chrome that would show through is taken away. */
body.is-firstrun{overflow:hidden}
/* The shell keeps its layout — the import review the gate opens is inside it,
   and the gate steps aside rather than tearing it down — but nothing behind an
   aria-modal screen may be reachable by tab or by a screen reader, and
   `visibility:hidden` takes both away without removing the box. */
body.is-firstrun main{visibility:hidden}
body.is-firstrun nav,body.is-firstrun #installBtn,body.is-firstrun #restBar{display:none}
.firstrun__inner{
  --firstrun-pad:clamp(18px,5vw,34px);
  max-width:900px;margin:0 auto;padding:0 var(--firstrun-pad);
}
/* The paper both brand images were painted on. It is a property of the files,
   not of the theme, which is why it is one constant rather than a palette
   entry: light lets the page be it, dark hands it back as a plate. */
.firstrun{--brand-paper:#F4F2EF}
.firstrun__brand{
  display:flex;align-items:center;justify-content:center;
  gap:clamp(17px,4.5vw,22px);margin-bottom:clamp(28px,7vw,44px);
}
/* The mark stands on the paper here: no plate, no rounded corner of ours, and
   `contain` centres the whole yoke rather than cropping it. The art it points
   at is the ground-free rendering (assets/brand/mark.png), so there is no
   square to hide in the first place — on cream. Dark hands the paper back as a
   plate (see the dark block near the top of this file), because the ground is
   what the rendering's pale edge was drawn against. */
.firstrun__logo{
  display:block;flex:none;width:clamp(62px,16vw,76px);height:clamp(62px,16vw,76px);object-fit:contain;
}
/* Tracking adds a letter-space after the last R that nothing fills, so the row
   would sit half of it left of true centre without taking that space back. */
.firstrun__wordmark{
  margin-right:-.26em;font-size:clamp(21px,5.3vw,26px);font-weight:650;
  letter-spacing:.26em;text-transform:uppercase;color:var(--ink);
}
/* The ethos hero (ADR 0006): ink type and the complete illustration on the
   page's own paper — no plate. Text and art always occupy separate grid areas.
   On a phone the picture becomes a strong, centred beat between the sentence
   and poem; once the column is genuinely wide enough, it moves beside the copy.
   This lets the art grow without asking a fixed line of the poem to wrap around
   it, putting graphite behind type, or bleeding any part of the export off the
   screen. `container-type` sizes the compact composition from the usable hero
   column rather than from the viewport outside its safe padding. */
.firstrun-hero{
  position:relative;isolation:isolate;container-type:inline-size;display:grid;
  grid-template-areas:"title" "art" "body";
  /* This is a legible supporting passage, not a second headline. Its compact
     scale leaves enough of the first screen to introduce the setup controls. */
  --poem:clamp(11px,3.39cqw,13px);
  --poem-line:calc(1.42 * var(--poem));
}
.firstrun-hero__title{
  grid-area:title;position:relative;z-index:1;margin:0 auto;max-width:14ch;
  font-size:clamp(32px,10cqw,46px);font-weight:700;letter-spacing:-0.025em;line-height:1.04;color:var(--ink);
  text-align:center;
}
/* Decorative, so it is painted rather than marked up: nothing to announce, and
   a missing export leaves paper behind the copy instead of a broken image,
   which is what the empty-tile doctrine asks for. The file is drawn on the
   app's own paper, so on cream it needs no field of its own to dissolve into;
   dark paints that paper under it instead. The box uses the export's exact
   1072/998 ratio and `contain`; neither axis can crop, in either theme. */
.firstrun-hero__art{
  grid-area:art;z-index:0;aspect-ratio:1072/998;width:min(78cqw,250px);max-width:100%;
  margin:clamp(13px,3.7cqw,19px) auto clamp(15px,4cqw,22px);
  background:no-repeat center/contain url("assets/brand/milo-hero.webp");
  pointer-events:none;
}
.firstrun-hero__body{
  grid-area:body;position:relative;z-index:1;width:min(100%,40ch);margin:0;justify-self:center;
  /* Set in the body face, not the mono one. The passage is the app's own voice
     introducing itself, and mono made it read as sample output beside the
     headline it belongs to. Line breaks are copy here (ADR 0006), so the
     measure below is checked against the face rather than assumed. */
  white-space:pre-line;font-size:var(--poem);
  line-height:var(--poem-line);color:var(--ink);
}
.firstrun__lede{
  width:min(100%,var(--maxw));margin:clamp(30px,6vw,44px) 0 0;
  font-size:16px;line-height:1.45;color:var(--ink-soft);
}
.firstrun__section{width:min(100%,var(--maxw));margin:40px 0 0}
.firstrun__eyebrow{
  display:flex;align-items:center;gap:14px;margin:0;
  font-size:11px;font-weight:650;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-deep);
}
.firstrun__eyebrow::after{content:"";flex:1;height:1px;background:var(--accent)}
.firstrun__rows{margin-top:4px}
.firstrun__continue{
  display:flex;align-items:center;justify-content:center;gap:12px;
  width:min(100%,var(--maxw));margin:36px 0 0;padding:12px;min-height:44px;
  background:none;border:0;cursor:pointer;font:inherit;font-size:15px;font-weight:500;color:var(--ink);
}

@media (min-width:760px){
  .firstrun-hero{
    --poem:clamp(13px,1.7cqw,15px);
    grid-template-columns:minmax(0,1fr) minmax(340px,44%);
    grid-template-areas:"title art" "body art";
    column-gap:clamp(32px,5cqw,48px);align-items:start;
  }
  .firstrun-hero__title{align-self:end;font-size:clamp(44px,6.2cqw,54px);margin:0;text-align:left}
  .firstrun-hero__art{width:100%;margin:0;align-self:center}
  .firstrun-hero__body{margin-top:clamp(24px,3cqw,30px);justify-self:start}
}

.firstrun-row{
  display:flex;align-items:center;gap:18px;width:100%;min-height:78px;padding:18px 0;
  background:none;border:0;border-bottom:1px solid var(--rule);
  cursor:pointer;text-align:left;font:inherit;color:var(--ink);
}
.firstrun-row:last-child{border-bottom:0}
.firstrun-row__icon{
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:46px;height:46px;border:1.5px solid var(--ink);
}
.firstrun-row__icon::before{
  content:"";width:22px;height:22px;background:var(--ink);
  -webkit-mask:no-repeat center/contain;mask:no-repeat center/contain;
}
.firstrun-row__icon--plus::before{
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
}
.firstrun-row__icon--import::before{
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4v10M7.5 10l4.5 4.5L16.5 10M5 19h14'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4v10M7.5 10l4.5 4.5L16.5 10M5 19h14'/%3E%3C/svg%3E");
}
.firstrun-row__text{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.firstrun-row__title,.firstrun-row__cap{overflow-wrap:anywhere}
.firstrun-row__title{font-size:19px;font-weight:650;letter-spacing:-0.01em;color:var(--ink)}
.firstrun-row__cap{font-size:14px;line-height:1.35;color:var(--ink-soft)}
.firstrun-row__cap:empty{display:none}
.firstrun-row__go{flex:none;width:20px;height:20px;color:var(--ink)}
.firstrun-row__go::before{
  content:"";display:block;width:100%;height:100%;background:currentColor;
  -webkit-mask:var(--arrow) no-repeat center/contain;mask:var(--arrow) no-repeat center/contain;
}
.firstrun-row__icon--start::before{
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 6.8v10.4L18 12z'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 6.8v10.4L18 12z'/%3E%3C/svg%3E");
}
.firstrun-row__busy{
  position:absolute;right:0;top:50%;margin-top:-9px;
  width:18px;height:18px;border:2px solid var(--rule);
  border-top-color:var(--accent);border-radius:50%;
}
#firstRunSharedStart{position:relative}
.firstrun-row:disabled,.firstrun-row[aria-busy="true"]{opacity:.55;cursor:progress}
.firstrun-row[aria-busy="true"] .firstrun-row__go,
.firstrun-row:has(.firstrun-row__busy:not(.hidden)) .firstrun-row__go{visibility:hidden}
.firstrun__error{
  width:100%;margin:12px 0 0;font-size:14px;line-height:1.4;color:var(--danger);
  overflow-wrap:anywhere;
}
.firstrun__error:empty{display:none}

/* The install card. It bleeds to the edge of the setup column, which is the
   screen on a phone, and it is the only orange field in the app. */
.installcard{
  display:grid;grid-template-columns:auto 1fr;gap:20px 22px;align-items:start;
  margin:20px 0 0;
  padding:28px var(--firstrun-pad) 24px;background:var(--accent-deep);color:var(--accent-ink);
}
.installcard__icon{flex:none;display:block;width:56px;height:56px;color:var(--accent-ink)}
.installcard__text{min-width:0}
.installcard__title{margin:0;font-size:24px;font-weight:650;letter-spacing:-0.01em;color:var(--accent-ink)}
.installcard__body{margin:8px 0 0;font-size:15px;line-height:1.4;color:var(--accent-ink)}
.installcard__action{grid-column:1/-1;border-radius:0}

/* The iOS instruction sheet. Safari exposes no install event, so the only thing
   Taurifer can do is point at Safari's own control: the bar below is a drawing
   of it, never a live control. */
.sheet--install{height:auto;background:var(--bg)}
.sheet--setup{height:auto;max-height:min(74dvh,660px)}
.sheet--setup .sheet__title{overflow-wrap:anywhere}
.sheet--setup .sheet__foot--actions{flex-wrap:wrap}
.sheet--setup .sheet__foot--actions .btn{flex:1 1 140px}
.share-setup{
  flex-direction:column;gap:14px;overflow:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;padding-bottom:8px;
}
.share-setup__link{
  margin:0;min-width:0;padding:12px;background:var(--well);border:1px solid var(--rule);border-radius:12px;
  overflow:auto;overflow-wrap:anywhere;word-break:break-all;max-height:12em;
  font-family:var(--mono);font-size:13px;line-height:1.45;color:var(--ink);
  -webkit-user-select:text;user-select:text;
}
.share-setup__link:empty{display:none}
.share-setup__status{margin:0;font-size:14px;line-height:1.4;color:var(--ink-soft);overflow-wrap:anywhere}
.share-setup__status:empty{display:none}
.installsheet{
  display:flex;flex-direction:column;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;padding:14px 22px 20px;
}
.installsheet__head{display:flex;align-items:center;gap:18px}
.installsheet__mark{
  flex:none;width:62px;height:62px;background:var(--well) url("icons/icon-192.png") center/cover no-repeat;
}
.installsheet__titles{min-width:0}
.installsheet__title{margin:0;font-size:26px;font-weight:650;letter-spacing:-0.02em;color:var(--ink)}
.installsheet__sub{margin:4px 0 0;font-size:15px;line-height:1.35;color:var(--ink-soft)}
.installsheet__done{margin-top:22px}
.installsheet__foot{margin:14px 0 0;text-align:center;font-size:13px;color:var(--ink-faint)}

/* "Why this weight?" — the arithmetic behind one recommendation, opened on tap.
   Rows are a hairline-separated stack so each fact reads as its own line: last
   session, what it showed, the rule that fired, the load, the reps. Tokens only,
   so the dark appearance is a variable swap and not a second palette (ADR 0009). */
.sheet--why{height:auto;max-height:calc(var(--vvh,100dvh) - 56px)}
.whysheet{
  display:flex;flex-direction:column;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;padding:12px 22px 20px;
}
.whysheet__head{display:flex;align-items:center;gap:12px}
.whysheet__title{margin:0;flex:1;font-size:22px;font-weight:650;letter-spacing:-0.02em;color:var(--ink)}
.whysheet__close{
  flex:none;width:34px;height:34px;border:0;border-radius:50%;background:var(--well);
  color:var(--ink-soft);font-size:15px;line-height:1;cursor:pointer;
}
.whysheet__target{
  margin:10px 0 0;padding-left:14px;border-left:3px solid var(--accent);
  font-size:18px;font-weight:640;color:var(--ink);
}
.whysheet__body{margin-top:6px}
.whysheet__row{padding:12px 0;border-bottom:1px solid var(--rule)}
.whysheet__row:last-child{border-bottom:0;padding-bottom:0}
.whysheet__row p{margin:0;font-size:14px;line-height:1.5;color:var(--ink-soft)}
.whysheet__lab{
  display:block;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--accent-deep);margin-bottom:4px;
}
/* The affordance itself: a quiet text link under the recommendation, never a
   button that competes with logging a set. */
.recblock__why,.focus-ex__why{font-size:13px;padding:6px 0 0}
.focus-ex__why{display:block}

/* One row, the way iOS draws it now: back on the left, the address pill in the
   middle, and the ••• menu on the right of the pill — the control step 01 asks
   for. Anything Safari keeps behind that menu (share, tabs, bookmarks) is not
   drawn, because it is not what the lifter has to find. */
.safaribar{
  display:flex;align-items:center;gap:8px;
  /* The top padding is the room the TAP label sits in, above the ••• circle. */
  margin-top:22px;padding:28px 12px 12px;border:1px solid var(--rule);border-radius:14px;background:var(--surface);
}
.safaribar__url{
  display:flex;flex:1;min-width:0;align-items:center;gap:8px;padding:10px 14px;border-radius:999px;background:var(--rule);
}
.safaribar__page,.safaribar__reload,.safaribar__tool{
  flex:none;background:var(--ink-soft);
  -webkit-mask:no-repeat center/contain;mask:no-repeat center/contain;
}
.safaribar__page{width:15px;height:15px;background:var(--ink);
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='13' rx='2'/%3E%3Cpath d='M6.5 8h11'/%3E%3Cpath d='M6.5 12h7'/%3E%3Cpath d='M8 20h8'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='13' rx='2'/%3E%3Cpath d='M6.5 8h11'/%3E%3Cpath d='M6.5 12h7'/%3E%3Cpath d='M8 20h8'/%3E%3C/svg%3E");
}
.safaribar__host{
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:14px;color:var(--ink);
}
.safaribar__reload{width:15px;height:15px;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12a8 8 0 1 1-2.3-5.6'/%3E%3Cpath d='M20 4v5h-5'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12a8 8 0 1 1-2.3-5.6'/%3E%3Cpath d='M20 4v5h-5'/%3E%3C/svg%3E");
}
.safaribar__side{flex:none;display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:50%;background:var(--rule)}
.safaribar__tool{width:20px;height:20px}
.safaribar__tool--back{background:var(--ink-soft);-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 5l-7 7 7 7'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 5l-7 7 7 7'/%3E%3C/svg%3E")}
.safaribar__target{position:relative}
.safaribar__target::before{
  content:"";position:absolute;inset:0;border:2px solid var(--accent);border-radius:50%;
}
.safaribar__tool--more{
  width:auto;height:auto;background:none;-webkit-mask:none;mask:none;
  font-size:13px;letter-spacing:.06em;color:var(--ink);line-height:1;
}
.safaribar__tap{
  position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);
  font-size:10px;font-weight:650;letter-spacing:.12em;text-transform:uppercase;color:var(--accent-deep);
}

.installsteps{list-style:none;margin:24px 0 0;padding:0;border-top:1px solid var(--rule)}
.installsteps__step{display:flex;align-items:baseline;gap:20px;padding:16px 2px;border-bottom:1px solid var(--rule)}
.installsteps__n{flex:none;font-family:var(--mono);font-size:13px;font-weight:600;color:var(--accent-deep)}
.installsteps__body{flex:1;min-width:0;font-size:16px;line-height:1.4;color:var(--ink-soft)}
.installsteps__body b{color:var(--ink);font-weight:650}
/* A short screen still has to reach Done without a scroll inside the sheet. */
@media (max-height:700px){
  .installsheet{padding:10px 18px 16px}
  .installsheet__mark{width:48px;height:48px}
  .installsheet__title{font-size:22px}
  .installsheet__sub{font-size:14px}
  .safaribar{margin-top:16px;padding:24px 10px 10px}
  .installsteps{margin-top:16px}
  .installsteps__step{padding:10px 2px}
  .installsteps__body{font-size:15px}
  .installsheet__done{margin-top:16px}
}
