/* v4.6.129: Progress is a navigation group, not a new analytics screen. */
.progress-switch{--progress-slide:0%;display:flex;position:relative;isolation:isolate;background:none;border-bottom:1px solid var(--line);padding:0;margin:6px 18px 0}
.progress-switch[hidden]{display:none}
.progress-switch:before{content:'';position:absolute;left:0;bottom:-1px;width:50%;height:2px;background:var(--accent);transform:translateX(var(--progress-slide,0%));transition:transform var(--dur-move) var(--settle);z-index:-1}
.progress-switch button{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;border:0;background:transparent;padding:10px;color:var(--muted);font:500 12px var(--body);min-height:46px;cursor:pointer}
.progress-switch button svg{width:17px;height:17px;flex-shrink:0;fill:currentColor}
.progress-switch button[aria-pressed=true]{color:var(--accent-ink)}
/* v4.6.161: no pull-up. v4.6.130 pulled the greeting up 20px from a 40px gap
   (leaving 20); v4.6.153 made the gap 18px, and the same pull-up then parked
   the greeting ~2px under the header. It now sits at the page's 18px, like the
   first element of every other screen. */
.today-focus{margin-top:0}
/* v4.6.135: the pull-up is for the top of the page; after a card (the milestone moment) it put the greeting under that card */
.card + .today-focus{margin-top:0}
.today-focus .su-hello-row{margin:0 0 6px}
.today-focus-card.card{padding:20px 18px}
.today-focus-title{display:flex;align-items:center;justify-content:space-between;gap:12px}
.today-focus-card .today-focus-title h3{font-size:18px;margin:8px 0 11px}
.today-focus summary{list-style:none;cursor:pointer}
.today-focus summary::-webkit-details-marker{display:none}
.today-focus summary::marker{content:''}
.today-focus summary>.ic,.today-focus-title>.ic{flex-shrink:0;color:var(--muted);transition:transform .2s ease}
.today-focus details[open]>summary>.ic,.today-focus-plan[open] .today-focus-title>.ic{transform:rotate(90deg)}
.today-focus-plan-content{margin-top:13px}
.today-focus-plan-content>.card{box-shadow:none;padding:0;background:transparent;margin:0}
.today-focus-later>summary::after{display:none}
.today-focus-later>summary,.today-focus-more>summary{display:flex;justify-content:space-between;align-items:center;gap:12px}
@media(prefers-reduced-motion:reduce){.today-focus summary>.ic,.today-focus-title>.ic{transition:none}}
.today-focus-card h3{font:600 23px/1.2 var(--body);letter-spacing:-.5px;margin:7px 0 10px;overflow-wrap:anywhere}
.today-focus-kicker{font:400 11px var(--body);color:var(--muted)}
.today-focus-meta{font-size:11px}
.today-focus-copy{font:400 12px/1.457 var(--body);color:var(--muted);margin:10px 0 18px}
.today-focus-card .btn{display:inline-flex;width:auto;margin:0;padding:12px 20px;font-size:13px;align-items:center;gap:10px;flex-shrink:0}
.today-focus-next{display:flex;justify-content:space-between;align-items:center;margin-top:16px;padding-top:16px;border-top:1px solid var(--line);gap:15px}
.today-focus-next>div{min-width:0;overflow-wrap:anywhere}
.today-focus-next small{display:block;font-size:11px;color:var(--muted);margin-bottom:3px}
.today-focus-tools{display:flex;justify-content:center;flex-wrap:wrap;gap:8px 10px;margin:14px 0 11px}
.today-focus-tools button{width:auto;margin:0}
#view .today-focus .today-focus-rest.btn{display:block;margin:8px 0 0;width:100%;min-height:52px;padding:14px 18px;border:1px solid var(--line);border-radius:16px;background:var(--surface);box-shadow:0 2px 4px #00000003;color:var(--chalk);font-weight:500}
.today-focus-more{border-bottom:1px solid var(--line);font:400 11px var(--body);color:var(--muted);margin:6px 0}
.today-focus-more>summary{padding:18px 4px;cursor:pointer;min-height:44px}
/* v4.6.148: the one future line -- the count, then the next day, quiet, clipped rather than wrapped */
.today-focus-more>summary .tfm-count{font:500 12px var(--body);color:var(--chalk);white-space:nowrap}
.today-focus-more>summary .tfm-next{flex:1;min-width:0;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--muted)}
/* the days inside get the same air as the single upcoming row, and the same inset */
.today-focus-more .pw-later-day{background:transparent;border-top:1px solid var(--line)}
.today-focus-more .pw-later-day>summary{padding:18px 4px}
.today-focus-upcoming .pw-later-day{background:transparent;border-bottom:1px solid var(--line)}
.today-focus-upcoming .pw-later-day>summary{padding:20px 4px}
.today-focus-upcoming .pw-plan-totals{display:none}
.today-focus-secondary{display:flex;margin:6px auto}
@media(prefers-reduced-motion:reduce){.progress-switch:before{transition:none}}
/* Woven is an additive, read-only Stats experiment. All surfaces inherit the
   actual app theme; no tab-bar, existing chart or shared-token overrides. */
.woven-card .woven-top,.woven-card .woven-tools,.woven-card .woven-key,.woven-card .woven-pages,.woven-card .woven-dayhead{display:flex;align-items:center;justify-content:space-between;gap:10px}
.woven-card .woven-top{margin-bottom:11px}
.woven-card .woven-period{font:500 11px var(--mono);color:var(--muted)}
.woven-card button{border:0;background:transparent;color:var(--muted);font:500 11px var(--body);cursor:pointer;min-height:44px}
.woven-card button:focus-visible,.woven-card input:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.woven-card button:disabled{opacity:.3;cursor:default}
.woven-card .woven-hide{padding:0 8px}
.woven-card .woven-tools{margin-bottom:19px;flex-wrap:wrap}
.woven-card #wovenCount{font:500 11px var(--mono);color:var(--muted)}
.woven-card .woven-mode{display:flex;background:var(--surface2);border-radius:12px;padding:3px}
.woven-card .woven-mode button{padding:0 12px;border-radius:9px;transition:background var(--dur-move) var(--settle),color var(--dur-move) var(--settle)}
.woven-card .woven-mode button[aria-pressed="true"]{background:var(--surface);color:var(--chalk);box-shadow:0 1px 4px #00000010}
.woven-card .woven-chart{width:100%;min-width:0;cursor:crosshair}
.woven-card .woven-chart svg{display:block;width:100%;height:auto;overflow:visible}
.woven-card .woven-chart text{font:400 9px var(--mono);fill:var(--muted)}
.woven-card .woven-label{font:500 9px var(--body);fill:var(--muted)}
.woven-card .woven-thread{fill:none;stroke:var(--muted);stroke-width:1;opacity:.28;transition:opacity var(--dur-move) var(--settle)}
.woven-card .woven-day.selected .woven-thread{opacity:.85;stroke-width:1.5}
.woven-card .woven-chart .woven-number{font:600 9px var(--mono);fill:var(--chalk);pointer-events:none}
.woven-card .woven-band{transition:transform var(--dur-move) var(--settle);pointer-events:none}
.woven-card .woven-key{font:400 9px var(--mono);color:var(--faint);flex-wrap:wrap;gap:3px 4px;margin:0 0 19px}
.woven-card .woven-scrub{display:flex;flex-direction:column;gap:3px 4px}
.woven-card .woven-scrub input{display:block;width:100%;height:44px;margin:0;accent-color:var(--chalk);cursor:ew-resize}
.woven-card .woven-scrub span{font:400 9px var(--mono);text-align:center;color:var(--faint)}
.woven-card .woven-pages{margin:6px 0 13px;font:400 10px var(--mono);color:var(--muted)}
.woven-card .woven-pages button,.woven-card .woven-arrows button{width:44px;font:400 22px var(--body);border:1px solid var(--line);border-radius:50%}
.woven-card .woven-receipt{border-top:1px solid var(--line);padding-top:24px}
.woven-card .woven-dayhead{align-items:flex-start;margin-bottom:16px}
.woven-card #wovenDayTitle{font:600 15px/1.269 var(--body);margin:0 0 4px;letter-spacing:-.3px;color:var(--chalk)}
.woven-card #wovenDayCount{font:400 10px/1.41 var(--mono);color:var(--muted)}
.woven-card .woven-arrows{display:flex;flex-shrink:0;gap:5px}
.woven-card .woven-ex{display:grid;grid-template-columns:5px minmax(0,1fr) auto;align-items:start;gap:10px 12px;padding:15px 0;border-top:1px solid var(--whisper)}
.woven-card .woven-ex:first-child{border-top:0;padding-top:0}
.woven-card .woven-ex>i{width:5px;height:5px;border-radius:50%;margin-top:6px}
.woven-card .woven-exname{font:600 13px/1.363 var(--body);color:var(--chalk);overflow-wrap:anywhere}
.woven-card .woven-lines{font:400 10px/1.692 var(--mono);color:var(--muted);margin-top:4px;overflow-wrap:anywhere}
.woven-card .woven-setcount{font:400 10px/1.692 var(--mono);color:var(--faint);white-space:nowrap}
.woven-card .woven-empty{font:400 11px/1.598 var(--body);color:var(--muted);margin:0;padding-bottom:12px}
@media(max-width:360px){.woven-card .woven-dayhead{flex-wrap:wrap}.woven-card .woven-key{font-size:8px}.woven-card .woven-ex{gap:8px}}
@media(prefers-reduced-motion:reduce){.woven-card .woven-band,.woven-card .woven-thread,.woven-card .woven-mode button{transition:none}}
/* v3.3.536: fixed-frame 4/12-session ranges, isolated from header/nav. */
.progression-section{min-width:0}
.progression-card.card{padding:22px 16px 16px;overflow:hidden}
.progression-card .pg-title-row{display:flex;align-items:center;gap:12px;margin:0 3px 16px}
.progression-card .pg-title{font:600 16px/1.316 var(--body);color:var(--chalk);letter-spacing:-.3px;margin:0;overflow-wrap:anywhere;flex:1;min-width:0}
.progression-card select{font:500 11px/1.316 var(--mono);color:var(--chalk);border:1px solid var(--line);border-radius:10px;background:var(--surface);padding:10px;min-height:44px;max-width:100%}
.progression-card select.pg-ex{width:100%;min-width:0;flex:1;font:600 16px/1.4 var(--body);margin:0}
.progression-card .pg-toolbar{margin:0 0 18px}
.progression-card .pg-modes{display:grid;grid-template-columns:1fr 1fr;gap:2px 3px;padding:3px;background:var(--surface2);border-radius:12px}
.progression-card button{min-width:44px;min-height:44px;border:0;cursor:pointer;background:transparent;color:var(--muted);font:500 11px var(--mono);border-radius:9px;transition:background 180ms,color 180ms}
.progression-card .pg-modes button{padding:0 4px;white-space:nowrap}
.progression-card .pg-modes button[aria-pressed=true]{background:var(--surface);color:var(--accent-ink);box-shadow:0 1px 4px #00000012}
.progression-card .pg-share{flex:0 0 44px;display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--surface2)}
.progression-card .pg-share svg{display:block;width:19px;height:19px}
.progression-card .pg-share:hover{color:var(--chalk)}
.progression-card .pg-empty{font:400 11px/1.598 var(--body);color:var(--muted);margin-bottom:14px}
.progression-card .pg-range-head{margin:16px 0 5px;color:var(--muted)}
.progression-card .pg-range-nav{display:grid;grid-template-columns:44px minmax(0,1fr) 44px;align-items:center;gap:6px 8px;min-width:0;height:48px}
.progression-card .pg-range-nav button{display:grid;place-items:center;width:44px;height:44px;padding:0;font:400 19px var(--body);border-radius:50%;border:1px solid color-mix(in srgb,var(--chalk) 15%,var(--surface));background:var(--surface2);color:var(--chalk);box-shadow:0 2px 3px #00000006;transform:none}
.progression-card .pg-range-nav button:not(:disabled):hover{background:var(--surface);box-shadow:0 3px 8px #00000012}
.progression-card .pg-range-nav button:not(:disabled):active{background:var(--surface2);box-shadow:inset 0 1px 3px #00000012}
.progression-card .pg-period{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px 3px;min-width:0;height:48px;text-align:center;font-variant-numeric:tabular-nums}
.progression-card .pg-period-main{font:500 12px/18px var(--body);color:var(--chalk);white-space:nowrap}
.progression-card .pg-period-year{font:400 10px/14px var(--mono);color:var(--muted);white-space:nowrap}
.progression-card .pg-axis-unit{text-align:right;font:400 10px/15px var(--mono);color:var(--muted);margin:6px 0;white-space:nowrap}
.progression-card .pg-history-row{height:40px;display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 3px 4px}
.progression-card .pg-available{font:400 10px/15px var(--mono);color:var(--muted);min-width:0}
.progression-card .pg-available[aria-hidden=true]{visibility:hidden}
  /* v4.5.23: nothing to show, so almost no height. 14px rather than 0 keeps the
     readout from jumping when Latest reappears -- it grows by 26px instead of
     materialising out of nothing. */
  .progression-card .pg-history-row.pg-history-empty{height:14px;margin-bottom:0}
.progression-card .pg-latest{min-width:72px;min-height:36px;height:36px;padding:0 9px;display:flex;align-items:center;justify-content:center;gap:4px 5px;border-radius:18px;background:color-mix(in srgb,var(--accent) 13%,transparent);color:var(--accent-ink);font:500 9px var(--mono);letter-spacing:.02em;white-space:nowrap}
.progression-card .pg-latest:hover{background:color-mix(in srgb,var(--accent) 19%,transparent)}
.progression-card .pg-latest[hidden]{visibility:hidden;pointer-events:none}
.progression-card .pg-latest span{font:500 11px var(--body)}
.progression-card .pg-scroll{overflow:hidden;touch-action:pan-y;user-select:none;-webkit-user-select:none}
.progression-card .pg-plot{display:block;height:auto;width:100%;max-width:100%;overflow:visible}
.progression-card .pg-grid{stroke:var(--line);stroke-width:.8;stroke-dasharray:2 4}
.progression-card .pg-leader{stroke:var(--muted);stroke-width:.7;opacity:.5;fill:none;pointer-events:none}
.progression-card .pg-axis,.progression-card .pg-date{fill:var(--muted);font:400 9px var(--mono)}
.progression-card .pg-date{font-weight:500}
.progression-card .pg-selection-band{fill:var(--accent);opacity:.045}
.progression-card .pg-set{cursor:pointer}
.progression-card .pg-detail .pg-set circle,.progression-card .pg-detail .pg-set rect{fill:var(--surface2);stroke:transparent;stroke-width:1}
.progression-card .pg-set text{font:400 10px var(--mono);fill:var(--muted);pointer-events:none}
.progression-card .pg-set.pg-best text{font-weight:400;fill:var(--accent-ink)}
.progression-card .pg-detail .pg-best circle,.progression-card .pg-detail .pg-best rect{fill:var(--accent);fill-opacity:.09}
.progression-card .pg-dots .pg-set circle{fill:var(--muted);opacity:.68}
.progression-card .pg-dots .pg-best circle{fill:var(--accent-ink);opacity:.85}
.progression-card .pg-set.pick circle,.progression-card .pg-set:focus-visible circle,.progression-card .pg-set.pick rect,.progression-card .pg-set:focus-visible rect{stroke:var(--accent);stroke-width:1.5}
.progression-card .pg-pages{display:flex;align-items:center;justify-content:space-between;margin:-4px 4px 0;font:400 10px var(--mono);color:var(--muted)}
.progression-card .pg-pages button{font:400 21px var(--body)}
.progression-card button:disabled{opacity:.28;cursor:default}
.progression-card .pg-read-slot{min-height:52px;display:flex;justify-content:center;align-items:flex-start}
  /* v4.5.23: min-height, not height. A fixed 52px meant padding could not do
     anything -- the box was already exactly as tall as it was allowed to be, so
     more padding just squeezed the text. The slot below follows for the same
     reason. */
.progression-card .pg-read{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px 3px;min-width:190px;max-width:100%;min-height:52px;padding:10px 12px;border-radius:12px;background:var(--surface2);text-align:center;font-variant-numeric:tabular-nums;pointer-events:none}
.progression-card .pg-read-date{font:400 10px/13px var(--mono);color:var(--muted);white-space:nowrap}
.progression-card .pg-read-value{font:500 14px/17px var(--body);color:var(--chalk);overflow-wrap:anywhere}
.progression-card .pg-unmeasured{font:400 10px/1.598 var(--mono);color:var(--muted);margin:10px 4px 0}
.progression-card .pg-unmeasured summary{min-height:44px;cursor:pointer;padding:12px 0}
.progression-card button:focus-visible,.progression-card select:focus-visible,.progression-card input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.progression-card .pg-scrubber{--pg-progress:100%;appearance:none;-webkit-appearance:none;display:block;width:100%;height:44px;margin:4px 0 0;background:transparent;cursor:ew-resize;padding:0;border:0;box-shadow:none}
.progression-card .pg-scrubber::-webkit-slider-runnable-track{height:4px;border-radius:9px;background:linear-gradient(to right,var(--chalk) 0%,var(--chalk) var(--pg-progress),var(--line) var(--pg-progress),var(--line) 100%)}
.progression-card .pg-scrubber::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:15px;height:15px;border-radius:50%;background:var(--chalk);margin-top:-4px;box-shadow:0 0 0 3px var(--surface)}
.progression-card .pg-scrubber::-moz-range-track{height:4px;border-radius:9px;background:var(--line)}
.progression-card .pg-scrubber::-moz-range-progress{height:4px;background:var(--chalk)}
.progression-card .pg-scrubber::-moz-range-thumb{width:15px;height:15px;border:0;border-radius:50%;background:var(--chalk)}
@media(max-width:350px){.progression-card .pg-modes button{font-size:10px}}
@media(max-width:359px){.progression-card .pg-period-main{font-size:11px}.progression-card .pg-read-value{font-size:14px}}
@media(prefers-reduced-motion:reduce){.progression-card button{transition:none}}
:root{--rest-top:color-mix(in srgb,var(--rest) 22%,var(--ground));--sat:env(safe-area-inset-top,0px);--sab:env(safe-area-inset-bottom,0px);--kb:0px;--sheen-mask:linear-gradient(transparent,transparent);--sheen-w:100%;--sheen-h:100%;--sheen-x:0px;--sheen-clip:0px;
    /* v3.3.270 — THE MOTION VOICE. One settle curve, three speeds, spoken
       everywhere something arrives or changes state. The curve starts fast
       and lands long — things settle like architecture, they do not snap.
       --dur-quick  feedback: presses, toggles. Reaction stays instant.
       --dur-move   state changes: fades, small slides, chart emphasis.
       --dur-arrive arrivals: cards, views, meters. Composed, unhurried.
       Three curves keep their own physics by design and are allowlisted in
       buildcheck: the save spring, the weight-wheel spring, and the iOS
       band-back overshoot. Everything else speaks with one voice; a new
       ad-hoc cubic-bezier anywhere in this file fails the build. */
    --settle:cubic-bezier(.22,1,.36,1);
    --dur-quick:.16s; --dur-move:.26s; --dur-arrive:.42s; --dur-slow:.6s;   /* v3.3.462: the nav's settle after a scrub */
    color-scheme:dark;
    /* v3.3.202 (Dark Mode V3, step 2): surfaces and the two fills that carry
       white or coloured text.
       - the canvas drops and the card lifts, so a card reads off the screen
         without needing a border (1.11 -> 1.15). The handoff's own control
         surface (#273242) was REJECTED: at that lightness --faint falls to
         4.32 and --record to 3.43 on it. Separation you can only buy by
         pushing text under the floor is not separation worth having, so
         --surface2 holds at its ceiling.
       - --accent as a FILL carried white text at 3.67:1 — below AA, on every
         selected chip and segmented control in the app. The handoff's
         accentFill value fixes it at 4.65 and still clears the 3:1 graphic
         floor against the canvas (4.27). --accent-ink keeps accent-as-text.
       - --record sat at 3.94:1 on --surface2, unguarded because the contrast
         loop only ever checked it against --surface. Lifted along its own
         hue to clear 4.5 on both. */
    /* v3.3.298: CHARCOAL. The dark theme was blue-tinted (--surface #151B25
       is 16 points bluer than it is red); the maker asked for Claude's
       charcoal, which is neutral. Each surface and ink was neutralised by
       holding its RELATIVE LUMINANCE exactly and solving for the grey with
       the same value — so every WCAG pair is arithmetically unchanged
       (measured: max drift 0.06 across the nine ink/ground pairs) and the
       v3.3.92 contrast guard still governs. Only the hue is gone.
       Not copied hexes: Claude's exact stops are not readable from here, so
       these are derived, and the derivation is the honest part. --ground
       lands on #0A0A0A beside charcoal's own near-black anchor. */
    --ground:#0A0A0A; --surface:#1B1B1B; --surface2:#272727; --line:#424242;
    --whisper:#2A2A2A; /* v3.3.328: below --line in the ink ladder — a divider
       between rows that already read as one card, ~1.2:1 against the surface.
       --edge separates a pill FROM a card, --line divides regions, --whisper
       only keeps neighbours from touching. */
    --accent:#4C6BE3; --accent-soft:#3A4A8C; --accent-dim:#95A4E8;
    --record:#E36758;                       /* personal records */
    --rest:#2E7D32;                         /* v3.3.81: declared rest — green's ONE meaning (wash/frame grade).
                                               v3.3.441: sage #3E5C46 → leaf. The maker's verdict on the sage,
                                               once the square wore it too: "too grayish". Same hue in both
                                               themes now, so the state is one colour whatever the ground. */
    --rest-ink:#A5D6A7;                     /* v3.3.92: rest as TEXT — same hue, text-grade. A wash colour
                                               cannot also be an ink: 1.69:1 on its own wash proved it */
    /* v3.3.264: Session Build returns to the maker's Google Sheets chart
       language. The palette is categorical on purpose: Shoulder blue, Back
       red, Legs green, Chest yellow, with teal and purple extending the same
       family for Triceps and Sixpack. These variables are data fills reached
       only through PART_COLORS in Session Build; red and green keep their
       LIVE / rest meanings everywhere else.
       v3.3.325 moved Biceps orange -> magenta by hue arithmetic (21 degrees
       from Back's red); the maker rejected it on sight -- HARDER to read,
       blending into Sixpack's purple and Back's red both. Hue angle was the
       wrong instrument twice: red, magenta and purple are one perceptual
       family whatever the degrees say.
       v3.3.326 established the method: CIEDE2000, calibrated against the
       pairs the maker's own screenshots graded (orange/red 17 and
       magenta/purple 21 failed; purple/blue 25 and teal/blue 30 read fine).
       Every bright hue slot is taken, so darkness is the palette's one free
       axis -- and maximising separation inside that finding landed on
       #605024, a dark khaki the maker called a turd. He is right: the search
       optimised the only thing it was told to, and legibility is necessary
       but not sufficient for a colour someone looks at every day.
       v3.3.327: Biceps is PINE #00584A. Same metric, different objective --
       the most separated colour among those that are actually CHROMATIC
       (Lab chroma >= 28, excluding the olive/khaki hue band 50-110 where
       dark warm colours go muddy). Pine clears 32.1 from every fill in both
       rosters, nearest Legs, second-nearest 37.3 -- more margin than the
       palette's own tightest existing pair (purple/blue at 25.0). Contrast
       2.35:1 dark and 7.32:1 light. Verified by eye on rendered stacks of
       every adjacency that has failed, both themes, before shipping. The one
       deliberate departure from the Google set, which had no readable slot
       left. */
    /* v4.6.16 supersedes the historical palettes above: user-selected Normal
       and opt-in Neon. Normal Chest is deepened slightly for light-ground contrast. */
    --p-chest:#D6A209; --p-back:#E76236; --p-shoulder:#4169C8; --p-legs:#56A344;
    --p-biceps:#CE588E; --p-triceps:#19AEBB; --p-sixpack:#9261CF; --p-run:#7C899B;
    --chart-soft:#616EA3;                   /* v3.3.92: 2025's year line — accent-soft was 2.10:1 as a stroke */
    --live:#C93A2B;                         /* the header while your heart is pumping */
    /* v3.3.201: text ramp lifted per the Dark Mode V3 handoff. The failure
       being fixed is --faint, which sat at 4.06:1 on --surface and 2.60:1 on
       light --surface2 — BELOW the 4.5 floor, while carrying unit-faint
       grammar, day counts and chart annotations: the exact text that has to
       survive a dim gym at low brightness. Values verified against every
       ground each token actually sits on (surface, surface2, ground), not
       just the one the handoff tabulated. Worst-ground: chalk 14.39,
       muted 8.74, faint 4.97. */
    --chalk:#FBFBFB; --muted:#C6C6C6; --faint:#959595; --shadow:none;
    /* v3.3.322: --edge is the line a PILL wears. --line is a divider between
       things that already differ; a row nested in a card is the same colour
       as the card, so the border is the only thing separating them and 1.7:1
       is not enough to do that job. ~2.5:1 in both themes: visible as an
       edge, still quiet enough not to become a grid. */
    --edge:#6B6B6B;
    /* v3.3.201: accent as INK, split from accent as FILL — the handoff's one
       genuinely new idea. One blue cannot do both jobs: a fill needs to be
       dark enough for white text, an ink light enough to read on a dark
       card. --accent stays the fill; this is for accent-coloured TEXT. */
    --accent-ink:#8BA5FF;
    --track-num:-.02em; --track-label:.08em; --track-wide:.16em;
    --disp:'IBM Plex Sans',system-ui,sans-serif;
    /* v3.3.378: THE SQUARE. Every day in this app is drawn as one shape at
       one corner ratio -- a heatmap cell, a coverage mark, today in the
       ceremony, the empty one on day one. They had drifted to six different
       ratios (.227 .250 .261 .273 .292 .300): near-misses nobody can name and
       everybody feels. A PERCENTAGE, not a pixel value, so the 10px mark and
       the 44px hero are provably the same object rather than six things that
       look similar at their own sizes.
       The square means A DAY. Never a bullet, a button, a badge or a
       container -- the moment it wraps something that is not a day it becomes
       a motif and stops meaning anything. */
    --sq:28%;
    /* v3.3.243: reserved height for the fixed header. Defined here so the
       first paint is right before JS measures; syncHeaderHeight() then
       overwrites it on documentElement with the real px. */
    --hdr-h:calc(64px + max(12px,calc(env(safe-area-inset-top,0px) - 4px)));   /* v4.6.152: = --bar-top + the 64px header */
    --body:'IBM Plex Sans',system-ui,sans-serif;
    --mono:'IBM Plex Mono',ui-monospace,monospace;
  }
  :root[data-theme="light"]{
    color-scheme:light;
    --rest-top:color-mix(in srgb,#388E3C 72%,var(--ground));   /* v4.6.444: deeper, so the white crew reads on it */
    /* v3.3.202: light's --record was 4.41:1 on --surface2 — the same
       unguarded gap as dark's, found by the same new check. Deepened along
       its hue to 4.88. Surfaces are unchanged: light already separates by
       1.20 and darkening --surface2 further would push --faint under. */
    /* v3.3.301: the light theme joins the charcoal — one neutral grey story
       in both modes. It was blue (--muted 20 points bluer than red, --line
       12), which is what read as "off" beside a now-neutral dark theme.
       Neutralised the same luminance-preserving way, then ONE deliberate
       change beyond hue: the elevation ladder is inverted to match dark.
       Dark lifts a card ABOVE the page; light was sinking it BELOW (surface2
       #E8EAF0 darker than ground #F2F3F6), which is why the rows read as
       flat grey slabs. Now the page is the darkest thing (#EFEFEF), cards
       are white, and surface2 sits between — so it lifts off the page AND
       recedes inside a card, which is what an elevation step has to do.
       Measured: every ink pair clears AA (tightest faint-on-ground 4.70).
       v3.3.305: --line lifted #DDDDDD -> #C9C9C9. The dark hairline reads at
       1.71:1 against its own card and the light one only managed 1.36, so
       the rows looked like pills in dark and like nothing in light. 1.66 now
       — level with dark, and with the iOS system separator at 1.71. */
    --ground:#EFEFEF; --surface:#FFFFFF; --surface2:#F7F7F7; --line:#C9C9C9;
    --whisper:#E7E7E7;
    --accent:#2F4BD8; --accent-soft:#C3CCF5; --accent-dim:#8892C4;
    --record:#B83528;
    --rest:#66BB6A;                         /* declared rest, light theme (wash/frame grade). v3.3.441: leaf, was sage #79A884 */
    --rest-ink:#1B5E20;                     /* rest as text, light. v3.3.441: was #3B5742; #2E7D32 (B's ink as mocked) was 3.09:1 on the wash, so one step darker */
    /* The Sheets values remain intact in light mode except Chest: Google's
       #FABB05 yellow is only 1.55:1 on ShowUp's near-white ground, so it is
       deepened along the same hue to #E0A000 (2.06:1). The colour still
       reads as the same yellow while every stacked fill clears the 2:1
       chart floor enforced by buildcheck. */
    /* v3.3.301: the light page went lighter (#F2F3F6 -> #EFEFEF), so the two
       palest part colours fell under the v3.3.121 2.0:1 floor against it —
       chest to 1.99, triceps to 1.95. Both stepped down the smallest amount
       that clears it, hue held. The guard found these; I did not. */
    --p-chest:#D6A209; --p-back:#E76236; --p-shoulder:#4169C8; --p-legs:#56A344;
    --p-biceps:#CE588E; --p-triceps:#19AEBB; --p-sixpack:#9261CF; --p-run:#7C899B;
    --chart-soft:#7F859F;                   /* 2025's line, light — C3CCF5 was 1.58:1 on white */
    --live:#BE3324;
    /* v3.3.92: muted 4.10:1 on surface2 and faint 2.62:1 on white failed AA;
       darkened along their own hue, nothing else moved.
       v3.3.201: the handoff is dark-only, so light's counterpart was derived
       here rather than copied. --faint was still 2.60:1 on --surface2; the
       new value clears 4.5 on the WORST ground it sits on (surface2, 4.51).
       --muted moved with it: at the old value the two levels collapsed to a
       1.25x step and the hierarchy stopped reading. Now 1.71x, matching
       dark's ladder. */
    --chalk:#1B1B1B; --muted:#525252; --faint:#6A6A6A; --shadow:0 1px 3px rgba(0,0,0,.06);
    --edge:#A3A3A3;                       /* v3.3.322, see the dark theme */
    --accent-ink:#2A44C8;
  }
  /* Normal is the default, including absent/unknown preferences. Neon changes
     categorical fills only: semantic status colors and mascot remain intact. */
  :root[data-body-palette="neon"]{
    --p-chest:#E9D929; --p-back:#FF6544; --p-shoulder:#5278FF; --p-legs:#83D43B;
    --p-biceps:#F04AA3; --p-triceps:#16CFDC; --p-sixpack:#AA68FA; --p-run:#8D9AA7;
  }
  /* Keep the bright hues legible on pale chart grounds (2:1 fill floor). */
  :root[data-body-palette="neon"][data-theme="light"]{
    --p-chest:#B9AC21; --p-legs:#74BC34; --p-triceps:#14BBC7;
  }
  .body-palette-preview{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 16px;margin:11px 0}
  .body-palette-preview span{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--chalk)}
  .body-palette-preview i{width:12px;height:12px;border-radius:3px;flex:none}
  *{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
  html,body{margin:0;background:var(--ground);color:var(--chalk)}
  /* v3.3.110: this is an APPLICATION shell, not a document. Every surface
     in it owns a gesture — charts scrub/pinch/double-tap, tiles tap-to-delete
     and hold-to-edit, chips select — and iOS resolves a tie between "select
     text" and "run the gesture" in favour of selection, popping the
     Copy/Look Up/Translate callout over the UI mid-drag. Selection is off by
     default and restored deliberately below.
     Two spot-fixes (.settile, .readyhead) previously did this reactively,
     one element at a time, after each one broke; they are subsumed here.
     The share-card overlay mounts on <body>, OUTSIDE #app, so long-press to
     save the image keeps working. */
  #app{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
  /* restored where the content is genuinely the user's, or an identifier
     worth copying. inputs are not optional: three code paths call .select()
     programmatically (bodyweight, rep fields, the Sheets clipboard fallback). */
  input,textarea,select,.selectable{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default}
  /* v3.3.128: the share overlay mounts on <body>, OUTSIDE #app, so the
     v3.3.110 rule never reached it — which is why its buttons could be
     text-selected. Covered explicitly. The IMAGE keeps its callout, since
     long-press to save the card is the one long-press that should work. */
  /* v4.6.45: the live-workout bar joins this list. It is fixed furniture mounted
     outside #app, and furniture must not be text-selectable -- a long press during
     a set should not raise a selection handle over the thing you are tapping. */
  #repOv,.calreturn,#liveWorkoutBar,#icDefs{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
  #repOv img{-webkit-touch-callout:default}
  #app{
    max-width:520px;margin:0 auto;min-height:100vh;
    background:var(--ground);color:var(--chalk);
    font-family:var(--body);font-size:13px;line-height:1.363;
    /* the fixed header no longer occupies flow space; reserve it. The default
       covers the first paint before JS measures the real height. */
    padding-top:var(--hdr-h);
    padding-bottom:78px;position:relative;overflow-x:clip;
  }
  /* v3.3.243: FIXED, not sticky. iOS fades the top edge of scrolling content
     under the status bar, and a sticky header rides inside that scroller — so
     the top ~100pt of the bar washed toward the page ground. It showed as a
     pale band on the coloured rest header and, on every other day, as the
     date text rendering soft: measured, the glyph TOPS came back at 192 grey
     while their BOTTOMS were 27. The proof it is the scroller and not our
     paint: nav has always been position:fixed at the opposite edge, and its
     glyphs measure a clean 83 with no falloff. Fixed elements attach to the
     viewport, outside the faded scroll content. #app compensates with
     padding-top exactly as it already does with padding-bottom for nav. */
  /* v3.3.245: the fixed bar itself is TRANSPARENT and paints nothing.
     Safari 26 (Liquid Glass) scans fixed/sticky elements near the viewport
     edges and reads background-color and backdrop-filter OFF THAT ELEMENT to
     tint the status bar — then composites its own progressive blur over the
     top of the page. That blur is what has been washing out the date and the
     settings button. Safari explicitly does NOT sample position:absolute
     children, so every visual property moves to .hglass below and the bar
     itself declares nothing for Safari to find. */
  header{
    position:fixed;top:0;left:0;right:0;max-width:520px;margin:0 auto;
    z-index:20;background:transparent;
    padding:calc(14px + env(safe-area-inset-top,0px)) 18px 12px;
    /* v3.3.390: CENTRED, not top-hung. flex-start dates from the two-line
       header, where the identity column was taller than the controls and had
       to hang from the top edge so the date's line stayed level with the
       gear. Since v3.3.389 the day header IS one line, so flex-start simply
       pinned every element to the top and left the week and its count sitting
       above the gear's centre. One line means one axis. */
    display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:10px;
    /* v3.3.55: NEVER wrap the outer columns. A long exercise title used to
       push the timer and gear onto another row. The title truncates instead;
       v3.3.386's two-line identity column remains one stable flex item. */
    transition:background .35s var(--settle),border-color .35s var(--settle);
  }
  header.live .hglass{background:var(--live);border-color:var(--live);
    box-shadow:0 6px 24px -6px color-mix(in srgb,var(--live) 45%,transparent)}
  /* v3.3.81: the mirror state. LIVE burns at 92% solid; declared rest is a
     28% wash of the one green in the palette — the day feels different, the
     data stays uncoloured. */
  header.resting .hglass{background:color-mix(in srgb,var(--rest) 52%,var(--ground));
    border-color:color-mix(in srgb,var(--rest) 70%,var(--line));
    animation:restbreathe 7s ease-in-out infinite}
  /* v3.3.82: the wash breathes — a resting breath, ~7s, dimming to 19% and
     back. The live pulse is 1.6s and sharp; rest is four times slower and
     never sharp. Border stays still: only the air moves. */
  @keyframes restbreathe{0%,100%{background:color-mix(in srgb,var(--rest) 52%,var(--ground))}
    50%{background:color-mix(in srgb,var(--rest) 28%,var(--ground))}}
  /* v3.3.21 frosted chrome: only where backdrop-filter actually works, so the
     solid fallback above stays readable everywhere else. Colors are mixed from
     the theme vars, so any future ground change carries through for free.
     header.live keeps 92% solid red — live must never whisper. */
  /* v3.3.242: the HEADER is opaque. It was frosted glass since v3.3.21, and
     the effect never worked at the top of the viewport: a backdrop blur has
     no pixels ABOVE the first row to sample, so iOS clamps at the edge and
     the strip behind the status bar washes out. The seam landed exactly on
     env(safe-area-inset-top) — which reads as "the top of the header is
     blurred and everything shifted up". v3.3.241 fixed the resting colour
     but the artifact is the glass itself, not the mix, so the glass goes.
     The solid backgrounds above were always the fallback and are now the
     only path: same colours, same breathing rest wash, no bleed-through.
     NAV keeps its frost — it sits at the BOTTOM edge with content scrolling
     beneath it, where a backdrop blur has a full backdrop to sample. */
  /* v3.3.497: THE FROST COMES OFF THE NAV. See the note on nav::before below:
     a backdrop-filter on the one fixed element at the bottom edge is the
     remaining suspect for every piece of fixed chrome scrolling with the
     page. The solid background above -- always the declared fallback -- is
     the only path again, exactly as it already is for the header. */
  .h-sub.donetoday::before{content:'✓ ';color:var(--accent-ink);font-weight:600;
    font-size:12px;letter-spacing:0}
  .strip i.pend{background:transparent;border:1.5px dashed var(--muted);opacity:.8}
  #onb{position:fixed;inset:0;z-index:90;background:var(--ground);color:var(--chalk);font-family:var(--body);display:flex;align-items:center;justify-content:center;overflow:auto}
  #onb[hidden]{display:none}
  .onbwrap{width:min(420px,92vw)}
  .onbcard{padding:8px 6px}
  .onblogo{font-size:30px;font-weight:800;letter-spacing:var(--track-num);margin-bottom:5px}
  .onbtag{margin:0 0 18px;line-height:1.41}
  .onbcard h3{margin:0 0 5px;font-size:18px}
  .onbnote{font-size:11px;color:var(--muted);margin:6px 0 11px}
  .onbbtn{display:block;width:100%;padding:13px;margin:6px 0;border-radius:12px;border:1px solid var(--line);background:var(--surface);color:var(--chalk);font-size:13px;font-weight:600}
  .onbbtn.pri{background:var(--accent);border-color:var(--accent);color:#fff}
  .onbbtn.ghost{background:transparent;color:var(--muted)}
  .onbchips{display:flex;flex-wrap:wrap;gap:6px 8px;margin:10px 0 14px}
  .onbchip{padding:9px 14px;border-radius:999px;border:1.5px solid var(--line);background:var(--surface);color:var(--muted);font-size:12px;font-weight:600}
  .onbchip.sel{border-color:var(--accent);color:var(--accent);background:color-mix(in srgb,var(--accent) 12%, transparent)}
  .onbrow{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:11px 0;font-size:12px}
  .onbrow input{width:90px;padding:9px;border-radius:10px;border:1px solid var(--line);background:var(--surface);color:var(--chalk);text-align:right;font-family:var(--mono)}
  .onbseg button,.seg button{padding:8px 14px;border:1px solid var(--line);background:var(--surface);color:var(--muted);font-weight:600}
  .onbseg button.sel,.seg button.sel{background:var(--accent);border-color:var(--accent);color:#fff}
  .onbseg button:first-child,.seg button:first-child{border-radius:10px 0 0 10px}
  .onbseg button:last-child,.seg button:last-child{border-radius:0 10px 10px 0}
  .seg button{flex:1;padding:8px 0;font-family:var(--mono);font-size:11px}
  /* v3.3.343: the rep/sec switch. It reuses .seg -- the app already has one
     segmented control and a second vocabulary for the same gesture would be
     two. What it overrides is only SIZE: this one is a compact pill sitting
     right-aligned in the row a weighted lift spends on its equipment line,
     so a bodyweight exercise costs no height its neighbours were not already
     spending. */
  .suline{display:flex;justify-content:flex-end;padding:0 2px 10px;margin-top:-3px}
  .seg.suseg{display:inline-flex;width:auto}
  .seg.suseg button{flex:0 0 auto;padding:5px 14px;font-size:10px}
  /* lives as the header's first child: bleeds over the header's own top/side
     padding (incl. its safe-area allowance), repaints that zone red, and the
     sticky header carries it — no fixed positioning, no body offsets. */
  /* v4.6.287: a small pill above the tab bar (the old red strip sat hidden behind the glass header) */
  #demoBar{position:fixed;z-index:150;left:50%;transform:translateX(-50%);bottom:96px;display:flex;align-items:center;gap:10px;
    padding:5px 5px 5px 14px;border-radius:999px;background:var(--record);color:#fff;font:600 12px/1 var(--body);letter-spacing:var(--track-label);white-space:nowrap;box-shadow:0 4px 14px rgba(0,0,0,.25)}
  #demoBar[hidden]{display:none}
  #demoBar button{padding:6px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.5);background:transparent;color:#fff;font-size:11px;font-weight:600}
  .emptyhero{text-align:center;padding:34px 20px}
  .emptyhero h3{margin:0 0 6px;font-size:18px}
  .onbctas{margin-top:14px}
  /* ---- v3.3.372: day one ------------------------------------------------
     One question per screen. The tiles are big because this is the only
     screen in the app where nobody knows what to press yet. */
  .d1card{padding:22px 18px;margin-top:8px}
  .d1hero{text-align:center;padding:34px 18px 26px}
  .d1sq{display:block;width:46px;height:46px;margin:0 auto 18px;border-radius:var(--sq);
    background:var(--surface2);border:1.5px dashed var(--line)}
  .d1h{font-size:17px;margin:0 0 5px}
  .d1p{margin:0 0 18px;font-size:11px}
  .d1soon{opacity:.45;cursor:default}
  .d1link{display:block;width:100%;margin-top:13px;background:none;border:0;
    font-family:var(--mono);font-size:10px;color:var(--faint);cursor:pointer}
  .d1q{font-size:14px;margin:2px 0 13px}
  .d1back{background:none;border:0;color:var(--muted);font-size:15px;
    padding:0 0 8px;cursor:pointer}
  .d1grid{display:grid;grid-template-columns:1fr 1fr;gap:8px 10px}
  .d1tile{padding:18px 10px;border-radius:12px;border:1px solid var(--line);
    background:var(--surface2);color:var(--chalk);font-size:12px;cursor:pointer}
  .d1list{display:flex;flex-direction:column;gap:6px 8px}
  .d1row{text-align:left;padding:14px 14px;border-radius:11px;border:1px solid var(--line);
    background:var(--surface2);color:var(--chalk);font-size:12px;cursor:pointer}
  .d1bar{display:flex;align-items:center;justify-content:space-between;gap:10px;
    padding:8px 12px;border-radius:10px;background:var(--surface2);
    border:1px solid var(--line);font-size:10px;color:var(--muted);
    letter-spacing:var(--track-label);text-transform:uppercase}
  /* v3.3.138: 26px -> 14px, matching .zone. Every other card stacked in the
     Lift view sits 14px below the one above it; this one sat 12px lower, so
     the gap under "Logged today" read as a section break that was never
     meant to be there. One rhythm down the column. */
  .lastcard{margin-top:11px;padding:14px 14px 12px;border:1px solid var(--line);border-radius:14px;background:var(--surface)}
  .lasthead{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;letter-spacing:var(--track-label);color:var(--muted);font-weight:600;margin-bottom:6px}
  /* v3.3.273: the selected part chip, restated in miniature at the head of
     every section it scopes. Same white-on-accent as .partcard.sel — one
     vocabulary, no new colour. Uppercase comes from the headings it sits in. */
  .scopepill{display:inline-block;background:var(--accent);color:#fff;
    font-family:var(--mono);font-size:9px;font-weight:700;
    letter-spacing:var(--track-label);padding:2px 8px 3px;border-radius:6px;
    text-transform:uppercase;vertical-align:1px;margin-right:2px}
  .lasthead .ago{letter-spacing:0;font-weight:600;font-family:var(--mono)}
  .lastrow{display:flex;align-items:center;gap:12px;padding:5px 0;border-top:1px dashed var(--line)}
  .lastrow:first-of-type{border-top:none}
  .lastw{min-width:74px;font-weight:700;font-size:13px}
  .lastw .u{font-size:10px;color:var(--muted);font-weight:600}
  .lastreps{display:flex;flex-wrap:wrap;gap:4px 5px}
  .repchip{font-style:normal;font-family:var(--mono);font-size:11px;font-weight:700;padding:3px 8px;border-radius:8px;background:color-mix(in srgb,var(--accent) 13%, transparent);color:var(--accent-ink)}
  /* v3.3.341: a held set is the same chip, wider, because its label carries a
     unit mark and sometimes a minute. Not a different colour: a set is a set,
     and the app has one word for that. */
  .repchip.hold{min-width:38px;letter-spacing:-.02em}
  .lastfoot{margin-top:6px;font-size:10px;color:var(--muted)}
  .runhist .lastrow{display:grid;grid-template-columns:1fr auto auto auto;gap:10px 12px;align-items:baseline}
  .runhist .runD{color:var(--muted);font-size:11px}
  .runhist .runT{font-size:11px}
  .runhist .runP{color:var(--muted);font-size:11px;min-width:74px;text-align:right}
  header.live{cursor:pointer}
  .mgrid{display:grid;grid-template-columns:30px repeat(12,1fr);gap:2px 3px;align-items:center}
  .mg-h{font-size:8px;color:var(--faint);text-align:center;font-family:var(--mono)}
  .mg-y{font-size:9px;color:var(--muted)}
  .mg-c{height:24px;border-radius:5px;display:flex;align-items:center;justify-content:center;font-size:9px;color:var(--chalk)}
  .mg-c.cur{outline:1.5px dashed var(--accent);outline-offset:-1.5px;color:var(--muted)}
  /* v3.3.230: attendance is one story — lifetime promise, current receipt.
     v3.3.307: the receipt is a YEAR HEATMAP whose runs join up, not a month
     calendar. Weeks are columns and weekdays are rows, so consecutive days
     run down a column — and a trained day that touches another drops the
     radius on that side and overlaps a hair, so a streak paints as ONE
     unbroken stroke instead of a stack of squares. */
  .crcard{padding:14px}
  .crhead{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
  .crtotal{display:flex;align-items:baseline;gap:8px;min-width:0}
  .crtotal>b{font-size:26px;line-height:1;color:var(--accent-ink);letter-spacing:var(--track-num);font-weight:500}
  .crtotal>small,.crstreak,.crsince{font-family:var(--mono);letter-spacing:.02em}
  .crtotal>small{font-size:10px;color:var(--faint)}
  .history-totals{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;border-top:1px solid var(--line);padding-top:14px;margin-top:11px}
  .history-totals>span:last-child{text-align:right}
  .history-totals strong{font-weight:600}
  .history-totals small{display:block;font-size:10px;color:var(--muted);margin-top:2px}
  .crstreak{font-size:10px;color:var(--muted);white-space:nowrap}
  .crsince{font-size:9px;color:var(--faint);margin-top:2px}
  /* v4.6.128: REPLAY. The button rides the since-line, so the card gains no
     height; the attendance card only. The canvas (js/heat-replay.js) covers the
     since-line and the frame while it plays; the real cells are hidden under it,
     never removed, and are revealed on the frame the canvas leaves. The labels
     around the grid come back with the settle voice. Reduced motion: no button,
     so the card is only ever its end state. */
  .crcard{position:relative}
  .crsince:has(.heat-replay){display:flex;align-items:center;justify-content:space-between;gap:10px}
  .crsince .heat-acts{display:flex;align-items:center;gap:8px;flex:none}
  /* the plate card's Replay and Share, restated under their own names: the plate
     card's scripts find ITS buttons by class, so borrowing the class would let
     the first match on the page be this card's */
  .crsince .heat-replay{display:inline-flex;align-items:center;justify-content:center;gap:6px;flex:none;
    min-height:32px;margin:0;padding:4px 11px;border:1px solid var(--line);border-radius:18px;
    background:var(--surface2);color:var(--chalk);cursor:pointer;font:500 11px var(--body);letter-spacing:0}
  .crsince .heat-share{width:32px;height:32px;display:grid;place-items:center;padding:0;position:relative;
    border:0;border-radius:50%;background:var(--surface2);color:var(--muted);cursor:pointer}
  .crsince .heat-share svg{width:15px;height:15px}
  .crsince .heat-replay:focus-visible,.crsince .heat-share:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .crsince .heat-share::after{content:'';position:absolute;inset:-6px}
  .heat-replay-canvas{position:absolute;pointer-events:none;z-index:3}
  .crcard.heat-replaying .heatgrid,.crcard.heat-replaying .wdrail,.crcard.heat-replaying .heat-periods,
  .crcard.heat-replaying .crsince,.crcard.heat-replaying .heatframe::after{visibility:hidden}
  .crcard.heat-replayed .wdrail,.crcard.heat-replayed .heat-periods,.crcard.heat-replayed .crsince{
    animation:hrback .3s var(--settle) both}
  @keyframes hrback{from{opacity:0}to{opacity:1}}
  @media (prefers-reduced-motion:reduce){.crsince .heat-replay{display:none}}
  /* v3.3.333: the sheen belongs to the WINDOW, not to what slides past it.
     It was .heatwrap::after -- absolutely positioned inside the scroll
     container, so its containing block was the scrollable overflow area, not
     the visible box. The highlight was therefore as wide as all 35 weeks and
     it TRAVELLED with the grid: scroll left and the band slides away with the
     squares and dies halfway across, which is exactly what the maker saw.
     .heatframe does not scroll, so inset:0 means the visible box and the
     sheen sweeps across the window while the calendar moves underneath. */
  /* v3.3.335: the frame is a flex row -- static rail, then the scroller.
     --hcell is ONE number for the cell, read by the grid, the two label rows
     AND the rail; the rail can only stay aligned with the grid if both are
     sized from the same token rather than from the same literal typed four
     times. --heatyear-h is the year row's exact height, which the rail pads
     by so its M lands on the Monday row rather than on the year label.
     The tracks are FIXED at --hcell rather than minmax(--hcell,1fr): a
     stretching column makes a taller cell (aspect-ratio:1) and the rail,
     which cannot stretch with it, would desync. Squares stay square. */
  .heatframe{--hcell:11px; --heatyear-h:20px;
    position:relative;margin-top:16px;display:flex;gap:6px;align-items:flex-start}
  .wdrail{flex:0 0 auto;display:grid;grid-template-rows:repeat(7,var(--hcell));row-gap:2px;
    padding-top:var(--heatyear-h);font-family:var(--mono);font-size:9px;
    color:var(--faint);text-align:right}
  .wdrail span{line-height:var(--hcell);height:var(--hcell)}
  .heatwrap{flex:1;min-width:0;overflow-x:auto;overflow-y:hidden;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none;
    overscroll-behavior-x:contain}
  .heatwrap::-webkit-scrollbar{display:none}
  /* --hw and --c are written inline by the builder; declared here so they are
     defined even if a render is interrupted, and so the cssvar guard can see
     them (it reads CSS, not JS) */
  .heatgrid{--hw:35;display:grid;grid-auto-flow:column;grid-template-rows:repeat(7,var(--hcell));
    /* v3.3.334: 7px was sized for a 35-week window that had to fit a phone
       without scrolling. The grid spans the whole ledger now and has scrolled
       sideways since v3.3.307, so the cell is sized to be SEEN rather than to
       fit: 11px reads as a square you can aim a thumb at, and seven of them
       make a column tall enough that a week has shape. */
    grid-template-columns:repeat(var(--hw),var(--hcell));
    /* The gaps are NOT equal: two cells side by side are a WEEK apart, two
       stacked are consecutive days, and the layout should say so. v3.3.308
       introduced this when a uniform 2px gap let a row of Mondays read as one
       continuous run. v3.3.314 removed the joining entirely — every day is
       its own square now — but the reason to separate weeks more than days
       outlives the join, so the unequal gaps stay. */
    column-gap:4px;row-gap:2px;min-width:100%}
  /* v3.3.310: the gap between days is PAINTED, not pulled. The join closed a
     2px row-gap with negative margins — but rows are repeat(7,1fr) on a grid
     with no explicit height, so 1fr resolves from the items: the tracks
     shrank by the margin while every cell went on painting at its
     aspect-ratio height. ~4px of overflow per cell, compounding down a run,
     until neighbours bled together and read as one block.
     Now row-gap is 0, each cell reserves 1px of transparent padding top and
     bottom, and the fill is clipped to the content box. Two separate days
     leave a 2px channel; a joined pair drops the padding on the touching
     side and the fills meet exactly. Nothing leaves its track. */
  .heatgrid .hc{display:block;width:100%;aspect-ratio:1;border-radius:var(--sq);
    background-color:var(--surface2);font-style:normal}
  .heatgrid .hc.on{background-color:var(--accent)}
  /* v3.3.469: the inverse card lights rest days in the rest green; the number
     wears it too. Everything else -- size, radius, gap, rail, ticks -- is the
     same rule set, because it is the same markup.
     v3.3.470: its run line is two lines (resting N / longest M) -- the rest
     wording runs longer than "streak" and crowded the number. */
  .crcard.resting .heatgrid .hc.on{background-color:var(--rest)}
  .crcard.resting .crtotal b{color:var(--rest-ink)}
  /* v4.5.14: the green ARRIVES. Declaring rest replaces the card's markup, so a
     CSS transition has nothing to travel from -- the new cells are simply born
     green. These keyframes give them the journey instead, growing left to right
     across the week at the same 40ms-per-step the other staggers use, so the
     grid fills like a breath rather than flicking over. --c is the column index,
     already written on every cell by the builder. Capped so a two-year grid does
     not take a minute to finish; the number changes colour on its own, once. */
  @keyframes restfill{from{background-color:var(--surface2)}to{background-color:var(--rest)}}
  @keyframes restink{from{color:var(--accent-ink)}to{color:var(--rest-ink)}}
  .crcard.resting .heatgrid .hc.on{
    animation:restfill .42s var(--settle) both;
    animation-delay:calc(min(var(--c,0),22) * 18ms)}
  .crcard.resting .crtotal b{animation:restink .5s var(--settle) both}
  @media (prefers-reduced-motion:reduce){
    .crcard.resting .heatgrid .hc.on,.crcard.resting .crtotal b{animation:none}}
  /* v4.5.11: BOTH cards stack. The rest card has read as two right-aligned
     lines since v3.3.469; the attendance card kept one long nowrap string
     ("streak 2 days \u00b7 best 10") that crowded the card's right edge. Same
     markup, same rule, no .resting in the selector. */
  .crcard .crstreak{display:flex;flex-direction:column;align-items:flex-end;line-height:1.35}
  /* v3.3.471: today's breathing ring is the rest ink on the rest card -- the
     same darker green the header's and the bar's rings wear. */
  .crcard.resting .heatgrid .hc.tod::after{border-color:var(--rest-ink)}
  /* ---- v3.3.512: the week shape, under the rest grid ----
     Every selector here is a descendant of .restweek on purpose. The rest
     greens are held to one meaning by test-rest, which reads the SELECTOR:
     a rule may use --rest only if it says rest. That discipline is right, and
     a card about resting that called itself .wshape was quietly opting out of
     it -- so the name changed rather than the rule. */
  .restweek{margin-top:18px;padding-top:20px;border-top:1px solid var(--line)}
  .restweek .rwline{margin:0;font-family:var(--disp);font-weight:600;font-size:17px;
    letter-spacing:-.3px;color:var(--chalk);line-height:1.2}
  .restweek .rwsub{margin:5px 0 0;font-size:10px;color:var(--muted)}
  .restweek .rwbars{display:grid;grid-template-columns:repeat(7,1fr);gap:6px 8px;margin-top:14px;
    align-items:end}
  .restweek .rwbar{display:flex;flex-direction:column;align-items:center;gap:0}
  /* the trough is the full height so the seven columns read as one comparison
     rather than seven unrelated marks; the fill is the answer */
  .restweek .rwbar i{display:block;width:100%;height:86px;border-radius:11px;
    background:var(--whisper);position:relative;overflow:hidden}
  .restweek .rwbar i::after{content:'';position:absolute;left:0;right:0;bottom:0;
    height:var(--h,4%);border-radius:11px;background:var(--rest);opacity:.55}
  .restweek .rwbar.lead i::after{background:var(--rest-ink);opacity:1}
  .restweek .rwbar b{margin-top:7px;font-family:var(--mono);font-size:11px;font-weight:500;color:var(--muted)}
  .restweek .rwbar.lead b{color:var(--chalk);font-weight:700}
  .restweek .rwbar small{margin-top:2px;font-family:var(--mono);font-size:9px;color:var(--faint)}
  .restweek .rwfoot{display:flex;justify-content:space-between;gap:12px;margin-top:13px;
    font-size:9px;color:var(--muted)}
  /* v3.3.513: THE COLUMNS GROW IN. The first cut put a transition on height,
     which animates nothing: the bar is born at its final height, and a
     transition needs a CHANGE to run. It has to be a keyframe, from zero.
     Staggered left to right by --j so the week reads as a week rather than
     seven things appearing at once -- the same 40ms-per-step the cards' own
     entrance uses.
     Gated on #view:not(.norise), which is the app's existing signal for "this
     is an in-place repaint, not an arrival" (v3.3.492). Without it the columns
     would re-grow every time anything on Today re-rendered, which is the
     flicker three releases went into removing. */
  @media (prefers-reduced-motion:no-preference){
    @keyframes rwgrow{from{height:0}to{height:var(--h,4%)}}
    /* v3.3.514: .grown, not merely present. The animation used to run at paint,
       which is when this card is still a screen and a half below the fold --
       so it was always over before the maker reached it. It waits to be SEEN
       now; an observer adds .grown the first time the card scrolls in.
       It fails OPEN, which is the rule v3.3.496 had to go and build for the
       float: the resting state here is the column at its FULL height, and the
       keyframe is the only thing gated. If the observer never speaks -- no
       IntersectionObserver, a browser that stays quiet, a card that is never
       scrolled to -- the week is still fully drawn and simply does not
       animate. Nothing is ever hidden by omission. */
    #view:not(.norise) .restweek.grown .rwbar i::after{
      animation:rwgrow var(--dur-arrive) var(--settle) both;
      animation-delay:calc(var(--j,0)*45ms)}
  }
  .heatgrid .hc.fut{background-color:transparent;box-shadow:inset 0 0 0 1px var(--line);opacity:.5}
  /* v3.3.348: TODAY BREATHES. The mark was inset 1.5px of --accent-ink --
     a DARK ring painted INSIDE an 11px square, so on a day you trained it
     was dark-on-dark, and it shrank the one cell you look for, making today
     read as slightly smaller and muddier than its neighbours. It hid.
     The ring moves OUTSIDE the square, into the 4px gutter the grid already
     leaves between weeks, and takes --accent instead of ink. The square
     itself never changes size or colour: nothing about the DATA moves, only
     the light around it. Works the same whether today is filled or empty,
     which the inset ring could not do.
     position:relative on the cell, and the ring is a ::after rather than a
     box-shadow, so it can sit outside without the grid reserving space for
     it. One animation on the app's own --settle family, ~3.2s, which is slow
     enough to read as breathing rather than blinking. */
  .heatgrid .hc{position:relative}
  .heatgrid .hc.tod::after{content:'';position:absolute;inset:-2px;border-radius:var(--sq);
    border:1.4px solid var(--accent);animation:todbreath 3.2s var(--settle) infinite}
  @keyframes todbreath{
    0%   {opacity:.85; inset:-1.5px; border-radius:4px}
    60%  {opacity:.30; inset:-3.5px; border-radius:6px}
    100% {opacity:.85; inset:-1.5px; border-radius:4px}
  }
  /* still marked when motion is unwelcome -- the halo simply holds still */
  @media (prefers-reduced-motion:reduce){
    .heatgrid .hc.tod::after{animation:none;opacity:.85;inset:-2px}
  }
  .heatticks{--hw:35;display:grid;grid-auto-flow:column;
    grid-template-columns:repeat(var(--hw),var(--hcell));column-gap:4px;
    margin-top:7px;font-family:var(--mono);font-size:10px;color:var(--faint)}
  .heatticks span{--c:0;grid-column:calc(var(--c) + 1);white-space:nowrap}
  /* v3.3.334: the YEAR row. Same template as the grid and inside the same
     scroller, so a year label sits over its own columns and travels with
     them -- the month row already proved that geometry in v3.3.332. Louder
     than the months because it is the coarser mark: chalk, not faint. */
  .heatyears{--hw:35;display:grid;grid-auto-flow:column;
    grid-template-columns:repeat(var(--hw),var(--hcell));column-gap:4px;
    margin-bottom:6px;font-family:var(--mono);font-size:11px;font-weight:600;
    line-height:14px;color:var(--chalk);min-width:100%}   /* 14 + 6 = --heatyear-h */
  .heatyears span{--c:0;grid-column:calc(var(--c) + 1);white-space:nowrap}
  /* the count is the app's unit-faint grammar: the year is the value, the
     days are its unit, and the pair is the only place you can compare one
     year against another at a glance */
  .heatyears small{font-weight:400;color:var(--faint);margin-left:6px}
  /* Weight stays available without claiming a full dashboard section until
     opened. A first value is still recorded in Settings. */
  .bwcollapse{padding:0;overflow:hidden}
  .bwcollapse>summary{list-style:none;display:flex;justify-content:space-between;align-items:center;
    min-height:58px;padding:0 14px;cursor:pointer;color:var(--chalk)}
  .bwcollapse>summary::-webkit-details-marker{display:none}
  .bwcollapse>summary>span:last-child{font-family:var(--mono);font-size:11px;color:var(--muted)}
  .bwcollapse>summary b{font-size:16px;color:var(--chalk)}
  .bwcollapse>summary i{font-style:normal;font-size:8px;margin-left:7px;color:var(--faint)}
  .bwcollapse[open]>summary{border-bottom:1px solid var(--line)}
  .bwbody{padding:14px}
  .bwtools{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:3px}
  /* Same-date year race: the numbers carry the verdict; the filled field
     makes the size and direction of the gap impossible to miss. */
  .conrace{padding:16px 14px 10px}
  .conkick{text-align:center;font-family:var(--mono);font-size:8px;letter-spacing:.12em;color:var(--muted);margin:1px 0 10px}
  /* v3.3.232: the scoreboard is the toggle — it looks tappable and keeps
     one height whichever unit is showing. */
  /* v3.3.236: the pace chart owns the gesture, and its readout keeps a
     fixed slot so revealing a month never resizes the card. */
  .pacescrub{touch-action:none;-webkit-user-select:none;user-select:none;cursor:crosshair}
  .pacecap{--pc-fs:11px;--pc-lh:1.5;
    text-align:center;font-family:var(--mono);font-size:var(--pc-fs);
    line-height:var(--pc-lh);color:var(--muted);
    min-height:calc(var(--pc-fs) * var(--pc-lh));margin-top:2px}
  .pacecap b{color:var(--chalk);font-weight:600;font-variant-numeric:tabular-nums}
  .pacehalo,.pacevline{transition:opacity .08s linear}
  .conscore[data-raceswap]{cursor:pointer;-webkit-tap-highlight-color:transparent}
  .conscore[data-raceswap]:active{opacity:.72}
  .conscore{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:8px 10px}
  .conscore>span{display:flex;flex-direction:column;align-items:center;gap:1px 1px}
  .conscore small{font-family:var(--mono);font-size:8px;color:var(--muted);font-weight:500}
  .conscore b{font-family:var(--mono);font-size:25px;line-height:1;color:var(--chalk);letter-spacing:var(--track-num)}
  .congap{min-width:88px;padding:10px 9px;border:1px solid color-mix(in srgb,var(--record) 55%,var(--line));border-radius:11px;
    color:var(--record);font-family:var(--mono);font-size:11px;font-weight:700;line-height:1.1;text-align:center}
  .congap small{display:block;margin-top:3px;color:inherit;font-size:8px;letter-spacing:.08em;text-transform:uppercase}
  .congap.up{border-color:color-mix(in srgb,var(--accent) 65%,var(--line));color:var(--accent-ink);background:color-mix(in srgb,var(--accent) 9%,transparent)}
  .conrace .conzoom{margin-top:8px}
  .conrace svg{display:block;width:100%;height:auto;margin-top:0}
  .conrace .conend{transition:opacity .1s ease}
  .conrace.scrubbing .conend{opacity:0}
  .mpacecard{padding:14px 14px 12px}
  /* v3.3.473/474: the daily-runs card. Head carries the unit caption on the
     left and the mi/km switch on the right; the chart is an axis column beside
     the same scroller What you did uses, so the labels stay put while the bars
     move under them. */
  .drcard{padding:14px 14px 12px}
  .drcard .pmixhead{display:flex;justify-content:space-between;align-items:center;margin:-3px 0 3px}
  /* v3.3.476: the caption is a control now (mi/km), so it takes a button's
     reset and a pointer; it stays a caption in weight and colour. */
  .drcard .drunit{font-size:10px;letter-spacing:.04em;color:var(--faint);text-transform:uppercase;font-family:var(--mono)}   /* v3.3.481: a label again */
  /* v3.3.481: the scrub. The wrap is the positioning context for the readout;
     while scrubbing the unpicked dots and the line fade so the pick reads. */
  .drwrap{position:relative}
  .drwrap.scrubbing .drdot:not(.pick){opacity:.35}
  .drwrap.scrubbing .drline,.drwrap.scrubbing .drfill{opacity:.55}
  .drwrap .drdot.pick{r:4.2;fill:var(--accent)}
  /* v3.3.511: the progression chart scrubs on the same gesture, so it dims and
     picks the same way. The PR dot keeps its record colour when picked -- red
     is the one thing on this chart that means something on its own, and a
     scrub must not overwrite a fact. touch-action:pan-y lets the page still
     scroll vertically through the chart while a horizontal hold arms it. */
  .pgwrap{touch-action:pan-y}
  .pgwrap.scrubbing .pgdot:not(.pick){opacity:.35}
  .pgwrap.scrubbing polyline{opacity:.45}
  .pgwrap.scrubbing text{opacity:.35}
  .pgwrap .pgdot.pick{r:4.2}
  .pgwrap .pgdot.pick:not([data-pr]){fill:var(--accent)}
  .pgread{color:var(--chalk);font-size:10px}
  .drwrap .drdot.out{opacity:.7}
  /* v3.3.486: the readout moved into the HEAD (This year vs last's way); the
     bubble is gone. .armed marks the hold that turned a touch into a scrub. */
  .drcard .drread{font-size:10px;letter-spacing:.02em;color:var(--muted);font-family:var(--mono);flex:1;padding:0 8px;text-align:left}
  .drcard .drread b{color:var(--chalk);font-weight:600}
  .drcard .drread[hidden],.drcard .drunit[hidden]{display:none}
  .drwrap.armed{cursor:col-resize}
  .drcard .drdot.newest{filter:none}
  .draxis span{white-space:nowrap}
  .drrow{display:flex;align-items:stretch;gap:6px}
  /* v3.3.475: the axis column holds the year as well as the numbers, so the
     year stays put while the days scroll. Top padding clears the totals row. */
  .draxis{position:relative;display:flex;flex-direction:column;justify-content:space-between;flex:0 0 auto;
    padding:14px 0 46px;font-family:var(--mono);font-size:7px;color:var(--faint);text-align:right;min-width:26px}
  .draxis .dryr{position:absolute;left:0;top:2px;font-size:7px;font-weight:700;color:var(--muted);text-align:left}
  .drwrap{min-width:0}
  .drwrap > svg{display:block}
  .mpacecard svg{display:block;width:100%;height:auto}
  /* v3.3.111: .driftrow/.dr-* and .rephead removed with their sections.
     .repline below SURVIVES — the month grid's expand still renders it. */
  .chip.livego{background:var(--live);border-color:var(--live);color:#fff}
  /* v3.3.517: the day count stays neutral, even in the evening. Risk still
     belongs to the pending square and its message, not the numeral. */
  #hStreak.atrisk{color:var(--chalk);font-weight:600}
  #hStreak.restchip{color:var(--rest-ink);font-weight:700}  /* v3.3.92: ink grade — the wash grade was 1.69:1 on its own wash */
  .strip i.pend.atrisk{border-color:var(--record);opacity:1}
  .atriskTxt{color:var(--record)}
  .notei{position:relative;display:inline-block}   /* v3.3.54: lives beside titles now — no note-row top margin, and the ibtn's own 6px gap applies */
  .repline{font-size:11px;color:var(--muted);margin-bottom:8px}
  .btn.ghost{background:var(--surface2);color:var(--chalk);border-color:var(--line)}
  .onbges{display:flex;align-items:center;gap:14px;padding:11px 2px;border-top:1px dashed var(--line)}
  .onbges:first-of-type{border-top:none}
  .onbges .gi{font-family:var(--mono);font-size:13px;flex:0 0 34px;text-align:center;color:var(--accent-ink)}
  .onbges b{font-weight:600}
  .onbges .muted{display:block;font-size:11px}
  .onbnote{font-size:11px;color:var(--muted);margin-top:7px;line-height:1.41}
  /* v3.3.148 pinned the button's height so the two-line volume preview could
     not resize it under your thumb. v3.3.290 DELETED that preview, and the
     pinned box stays: a fixed 51px means the button cannot change size when
     the reps count goes from one digit to two either. */
  #addrep{height:51px;padding:0 15px;display:flex;flex-direction:column;
    align-items:center;justify-content:center;line-height:1.2}

  #msOv{position:fixed;inset:0;z-index:95;display:none;align-items:center;justify-content:center;
    background:var(--ground);cursor:pointer}
  .msIn{text-align:center;animation:msin var(--dur-arrive) var(--settle)}
  @keyframes msin{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:none}}
  @media (prefers-reduced-motion:reduce){.msIn{animation:none}.item.logrow.enter{animation:none}}
  .msNum{font-size:67px;font-weight:700;letter-spacing:var(--track-num);color:var(--chalk);line-height:1}
  .msUnit{font-size:11px;letter-spacing:var(--track-wide);color:var(--muted);margin-top:8px}
  .msRule{border:none;border-top:1px solid var(--line);width:120px;margin:18px auto}
  .msSub{font-size:11px;color:var(--muted)}
  .msTap{font-family:var(--mono);font-size:9px;color:var(--faint);margin-top:27px;letter-spacing:var(--track-wide);text-transform:uppercase}
  .mg-c[data-mk]{cursor:pointer}
  #mexp .mexpIn{margin:8px 0 3px;padding-top:10px;border-top:1px dashed var(--line)}
  .mexpDots{display:flex;flex-wrap:wrap;gap:3px 3.5px;margin-top:6px}
  .mexpDots i{width:15px;height:15px;border-radius:4.5px;background:var(--accent)}
  .mexpDots i:not(.t){background:transparent;border:1.5px solid var(--line)}
  .mexpDots i.f{border-style:dashed;opacity:.6}
  .lastrow[data-lw]{cursor:pointer}
  .lastrow[data-lw]:active{background:var(--surface2)}
  /* ---- v3.3.144: THIS SESSION — one card, one grammar ---- */
  /* the .tight log zone: the "Log a set" caption is gone, so the padding it
     justified goes with it. Tap targets untouched. */
  .zone.tight{padding:14px 14px 12px}   /* v4.6.420: the top matches the sides */
  .zone.tight .loadline{padding:5px 10px;margin-top:6px;margin-bottom:6px}
  .zone.tight .wsel{margin-top:0}
  .sessedit{background:none;border:1px solid var(--line);border-radius:8px;
    padding:3px 10px;color:var(--muted);cursor:pointer;font-size:10px}
  .sessedit:active{background:var(--line)}
  /* today reads full-strength; last time steps back — the same move past
     years make on the Consistency chart. Dimmed, not restyled: one grammar,
     two distances. */
  .cd.bf{border:1px dashed var(--line);color:var(--faint);cursor:pointer}
  .cd.bf:active{background:var(--surface2)}
  .lbread{font-size:11px;color:var(--chalk);min-height:16px;margin-bottom:3px}
  .lbsvg .lbbar{cursor:pointer}
  .mgbar{height:8px;border-radius:4px;background:var(--surface2);overflow:hidden;margin:2px 0 6px}
  .mgbar i{display:block;height:100%;background:var(--accent);border-radius:4px}
  .sess-then{margin-top:10px;padding-top:10px;border-top:1px solid var(--line);opacity:.62}
  /* ---- v3.3.151: LAST TIME at the part level — the session's shape ---- */
  .partlast{margin-top:11px}
  .partlast>.lasthead{margin-bottom:8px}
  /* v3.3.274: fold affordance — the date link and the chevron share the
     head's right edge; folded, the head is the whole card. */
  .lastacts{display:inline-flex;align-items:baseline;gap:10px}
  /* v3.3.430: ONE FOLD CONTROL. This was a pair of text glyphs -- U+25B8 and
     U+25BE -- swapped for each other: a different shape from the plan's fold,
     a different weight, and rotating by SUBSTITUTION rather than rotation.
     Two controls doing one job, drawn two ways. It is the icon system's
     chevron now, rotated, exactly as the plan's fold does it. */
  .plfold{background:none;border:0;color:var(--faint);
    line-height:0;padding:2px 4px;cursor:pointer;display:inline-flex;align-items:center}
  .plfold .ic{transition:transform .18s var(--settle)}
  .partlast.plfolded>.lasthead{margin-bottom:0}
  .plrow{padding:8px 10px;margin:0 -10px;border-radius:10px;cursor:pointer}
  .plrow .lasthead{margin-bottom:2px}
  .plrow:active{background:var(--surface2)}
  .plrow+.plrow{border-top:1px dashed var(--line);border-radius:0 0 10px 10px}
  /* repeated today: settled, not shouting — the ✓ in the row says it */
  .plrow.pldone{opacity:.55}
  .plrow.pldone:active{background:none}
  .sess-then .lasthead{margin-bottom:3px}
  .sess-now{margin-bottom:2px}
  /* the save flash lives on the newest chip now — the settile that used to
     carry it only exists in EDIT mode */
  .repchip.fresh{animation:chipflash .9s ease-out 1}
  @keyframes chipflash{0%{background:var(--accent);color:#fff}100%{}}
  @media (prefers-reduced-motion:reduce){.repchip.fresh{animation:none}}
  .tipbubble.float{position:fixed;z-index:96;top:auto;left:auto;right:auto;bottom:auto}
  button,input,select,textarea{font-family:inherit}
  .chip.on.livego,.btn.livego{background:var(--live);border-color:var(--live);color:#fff;
    animation:livepulse 1.8s ease-in-out infinite}
  @keyframes livepulse{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--live) 45%,transparent)}
    50%{box-shadow:0 0 0 9px color-mix(in srgb,var(--live) 0%,transparent)}}
  @media (prefers-reduced-motion:reduce){.chip.on.livego,.btn.livego{animation:none}}
  .delta.linkdate,.ago.linkdate{background:none;border:none;padding:0;cursor:pointer;
    text-decoration:underline dotted;text-underline-offset:3px}
  .delta.linkdate{color:var(--accent-ink)} .delta.linkdate.down{color:var(--record)}
  .legend1 [data-yr]{cursor:pointer;border-radius:8px;padding:2px 5px;flex:0 0 auto}
  .legend1 [data-yr].selY{background:var(--surface2);outline:1px solid var(--line)}
  .card[data-ysel] svg [data-yr]{opacity:.14;transition:opacity var(--dur-move) var(--settle)}
  .card[data-ysel] svg [data-yr].selY{opacity:1;stroke-width:2.6}
  .card[data-ysel] svg text[data-yr].selY{opacity:1}
  .cd[data-hd]{cursor:pointer}
  .cd[data-hd]:active{transform:scale(.92)}
  .lbNow{transform-box:fill-box;transform-origin:center bottom;
    animation:lbpulse 1.8s ease-in-out infinite}
  @keyframes lbpulse{0%,100%{opacity:1}50%{opacity:.8}}
  @media (prefers-reduced-motion:reduce){.lbNow{animation:none}}
  /* v3.3.149: the timer shows itself. It used to be gated on header.live,
     which is the OTHER half of why completing a part hid it — loosening
     tickRest alone would have written text into a display:none span.
     Colour follows the header it sits on: white on the red live bar, muted
     on the idle one, where #fff would have been invisible. */
  .resttimer{display:none;font-family:var(--mono);font-size:14px;font-weight:700;color:var(--muted)}
  .resttimer.on{display:inline}
  header.live .resttimer{color:#fff}
  .resttimer::before{content:'●';font-size:7px;margin-right:6px;vertical-align:2px;
    animation:pulse 1.2s ease-in-out infinite}
  /* the workout is complete: still counting, no longer claiming to be live */
  .resttimer.done::before{animation:none;opacity:.55}
  @media (prefers-reduced-motion:reduce){.resttimer::before{animation:none}}
  /* the context the timer carries for landscape: no height in portrait */
  .resttimer .rt-ctx,.resttimer .rt-sub,.resttimer .rt-detail{display:none}

  /* ---- v3.3.426: LANDSCAPE, LIVE ONLY ---------------------------------
     Turned sideways during a live session, the header's timer stops being a
     chip and BECOMES the screen. It is the same element with the same text:
     one truth, no second timer to drift, and nothing new that ticks.
     The manifest now allows rotation (it said portrait, which Android obeyed
     and iOS ignored), so every other screen is locked back to portrait here.
     The app is portrait; this is the one exception, and only while live. */
  @media (orientation:landscape){
    /* everything else stays as it was, upright */
    body{ }
    /* v3.3.427: the timer is MOVED to <body> while live and landscape (see
       derive.js), so it is styled where it lands -- not inside the header,
       whose z-index:20 stacking context trapped it. */
    /* v3.3.429: THE TYPE REBALANCED. The clock was 22vw and shouted over
       everything; the exercise -- the thing you actually want to read at a
       glance -- was a caption. The clock steps back to 15vw and to a lighter
       weight, the exercise comes up to a size and weight that carry, and the
       gaps are proportional to the elements they separate rather than a flat
       10px between three things of wildly different size. */
    html.bigtimer body > #hTimer.on{
      position:fixed;inset:0;z-index:1000;margin:0;   /* above the portrait veil (999) */
      background:#770511;color:#fff;
      display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.18em;
      font-family:var(--mono);font-size:clamp(52px,15vw,132px);font-weight:400;
      letter-spacing:-.01em;line-height:1;overflow:hidden;
      padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    }
    html.bigtimer body > #hTimer.on::before{display:none}
    html.bigtimer body > #hTimer.on::after{
      content:'';position:absolute;inset:-4%;pointer-events:none;z-index:0;
      background:radial-gradient(ellipse 81% 180% at 50% 110%,#ffac72 0%,#fa7146 20%,#df392e 43%,#a51420 68%,rgba(119,5,17,0) 100%);
      animation:rtSunPulse 6s ease-in-out infinite;
    }
    @keyframes rtSunPulse{
      0%,100%{opacity:.76;transform:scale(1)}
      50%{opacity:1;transform:scale(1.035)}
    }
    html.bigtimer body > #hTimer.on .rt-ctx,html.bigtimer body > #hTimer.on .rt-detail{
      display:block;text-align:center;line-height:1.2;letter-spacing:var(--track-label)}
    html.bigtimer body > #hTimer.on > :is(.rt-ctx,.rt-time,.rt-detail){position:relative;z-index:1}
    html.bigtimer body > #hTimer.on .rt-ctx{order:-1;opacity:.95;
      font-family:var(--mono);font-size:.12em;font-weight:400;margin:0;max-width:85%;text-wrap:balance}
    html.bigtimer body > #hTimer.on .rt-sub{display:none}
    html.bigtimer body > #hTimer.on .rt-detail{opacity:.9;font-size:.12em;font-weight:400;margin:0;top:.45em}
    html.bigtimer body > #hTimer.on .rt-detail:empty{display:none}
    html.bigtimer body > #hTimer.on .rt-time{display:flex;align-items:center;justify-content:center;line-height:1;font-variant-numeric:tabular-nums}
    html.bigtimer body > #hTimer.on .rt-colon{position:relative;display:inline-block;flex:none;color:transparent;width:.42em;height:.76em}
    html.bigtimer body > #hTimer.on .rt-colon::before,
    html.bigtimer body > #hTimer.on .rt-colon::after{
      content:'';position:absolute;left:50%;width:.058em;height:.058em;border-radius:50%;background:#fff;
      transform:translateX(-50%)}
    html.bigtimer body > #hTimer.on .rt-colon::before{top:.18em}
    html.bigtimer body > #hTimer.on .rt-colon::after{bottom:.18em}
    html.bigtimer body > #hTimer.on .rt-buddy{
      display:block;position:absolute;z-index:2;right:2.6%;bottom:2.5%;width:14vw;height:14vw;max-height:24vh;max-width:24vh;object-fit:contain;
      animation:none;pointer-events:none}
    html.bigtimer body > #hTimer.on .rt-buddy.rt-roaming{
      right:0;bottom:2.5%;width:100%;height:min(11.111vw,24vh);max-width:none;max-height:none;object-fit:contain;object-position:center bottom}
    @keyframes rtBuddyHop{
      0%,2.5%,16%,100%{transform:translateY(0) rotate(0) scale(1)}
      4%{transform:translateY(0) scale(1.08,.9)}
      8%{transform:translateY(-1.8vw) rotate(6deg) scale(1)}
      12%{transform:translateY(0) rotate(-3deg) scale(1.06,.94)}
    }
  }
  @media (orientation:landscape) and (prefers-reduced-motion:reduce){
    html.bigtimer body > #hTimer.on::after{animation:none;opacity:.88}
    html.bigtimer body > #hTimer.on .rt-buddy{animation:none}
  }
  .resttimer .rt-buddy,.resttimer .rt-wordmark,.resttimer .rt-quiet,.resttimer .rt-sync,.resttimer .rt-crew-toggle{display:none}
  @media (orientation:landscape){
    html.bigtimer body > #hTimer.on .rt-crew-toggle{display:flex;position:absolute;right:max(24px,env(safe-area-inset-right));bottom:max(18px,env(safe-area-inset-bottom));z-index:4;align-items:center;justify-content:center;min-width:58px;height:44px;padding:0 14px;margin:0;border:1px solid #ffffff20;border-radius:24px;background:#ffffff0d;box-shadow:none;touch-action:manipulation}
    html.bigtimer body > #hTimer.on .rt-crew-toggle img{width:30px;height:30px;object-fit:contain;pointer-events:none}
    html.bigtimer body > #hTimer.on .rt-crew-toggle[aria-pressed="false"]{opacity:.55}
    /* v4.6.373: hidden, the walking crew keeps its place in the walk; Show fades it back in where it is */
    html.bigtimer body > #hTimer.on .rt-buddy{transition:opacity .22s ease}
    html.bigtimer body > #hTimer.on.rt-crew-hidden .rt-buddy{opacity:0;visibility:hidden;transition:none}
    html.bigtimer body > #hTimer.on.rt-crew-hidden .rt-sync{display:none!important}
    @media (prefers-reduced-motion:reduce){html.bigtimer body > #hTimer.on .rt-buddy,html.bigtimer body > #hTimer.on.rt-crew-hidden .rt-buddy{transition:none}}
  }
  @media (orientation:landscape){
    html.bigtimer body > #hTimer.on.recovery{
      --rt-crew-y:calc(78% - 10px);
      background:radial-gradient(ellipse at 50% 110%,#444f9e 0%,#252d65 35%,#141b40 68%,#0c122a 100%);
      font-size:clamp(52px,17vw,184px)}
    html.bigtimer body > #hTimer.on.recovery::after{
      display:block;background:radial-gradient(ellipse at 50% 110%,#8b9cff 0%,rgba(110,132,236,.35) 40%,transparent 75%);
      animation:rtRecoveryPulse 8s ease-in-out infinite}
    @keyframes rtRecoveryPulse{
      0%,100%{opacity:.04;transform:scale(1)}
      50%{opacity:.12;transform:scale(1.02)}
    }
    html.bigtimer body > #hTimer.on.recovery > :is(.rt-quiet,.rt-wordmark,.rt-sync){z-index:1}
    html.bigtimer body > #hTimer.on.recovery :is(.rt-ctx,.rt-detail,.rt-sub){display:none}
    html.bigtimer body > #hTimer.on.recovery .rt-time{display:none}
    html.bigtimer body > #hTimer.on.recovery .rt-quiet{display:block;position:absolute;top:30%;left:4%;width:92%;padding:0;margin:0;border:0;border-radius:0;background:transparent;box-shadow:none;color:#f7f8ff;text-align:center;touch-action:manipulation}
    html.bigtimer body > #hTimer.on.recovery :is(.rt-caption,.rt-period){display:block;font:400 clamp(11px,2.1vw,23px)/1.4 var(--mono);letter-spacing:.04em;color:#c6cde6;margin-bottom:1.1vw}
    html.bigtimer body > #hTimer.on.recovery :is(.rt-caption,.rt-period):empty{display:none}
    html.bigtimer body > #hTimer.on.recovery .rt-digits{display:block;font:400 clamp(32px,11.8vw,128px)/1.1 var(--mono);letter-spacing:-.065em;font-variant-numeric:tabular-nums;white-space:nowrap}
    html.bigtimer body > #hTimer.on.recovery .rt-buddy{top:var(--rt-crew-y,78%);bottom:auto;transform:translateY(-50%) scale(.8)}
    html.bigtimer body > #hTimer.on.recovery.rt-synced .rt-buddy{display:none}
    html.bigtimer body > #hTimer.on.recovery.rt-synced .rt-sync{display:block;position:absolute;left:0;top:var(--rt-crew-y,78%);transform:translateY(-50%);width:100%;height:auto;pointer-events:none}
    html.bigtimer body > #hTimer.on.recovery .rt-wordmark{
      display:block;position:absolute;top:7%;bottom:auto;left:50%;transform:translateX(-50%);
      width:6vw;max-width:64px;min-width:36px;height:auto;opacity:.8}
  }
  @media (orientation:landscape) and (prefers-reduced-motion:reduce){
    html.bigtimer body > #hTimer.on.recovery::after{animation:none;opacity:.06;transform:none}
  }
  /* v4.6.353: THE RED STAGE WEARS THE BLUE CLOCK'S LAYOUT (Sungjee, 2026-10-18). Same wordmark,
     same one-line caption at the same height, same digit size and spacing, the font's own colon
     (still blinking), and the crew at the blue clock's size and height. Only the colour differs. */
  @media (orientation:landscape){
    html.bigtimer body > #hTimer.on:not(.recovery){justify-content:flex-start;gap:0;padding-top:30vh}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-ctx{font:400 clamp(11px,2.1vw,23px)/1.4 var(--mono);letter-spacing:.04em;color:#ffd6cc;opacity:1;margin:0 0 1.1vw}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-time{font:400 clamp(32px,11.8vw,128px)/1.1 var(--mono);letter-spacing:-.065em;color:#fff8f6}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-colon{color:inherit;width:auto;height:auto}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-colon::before,
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-colon::after{display:none}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-wordmark{display:block;position:absolute;z-index:1;top:7%;left:50%;transform:translateX(-50%);width:6vw;max-width:64px;min-width:36px;height:auto;opacity:.8}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-buddy{top:var(--rt-crew-y,78%);bottom:auto;transform:translateY(-50%) scale(.8)}
    html.bigtimer body > #hTimer.on:not(.recovery) .rt-buddy.rt-roaming{transform:translateY(-50%);height:min(8.889vw,19.2vh)}
  }
  html[data-mascot-motion="still"].bigtimer body > #hTimer.on .rt-buddy{animation:none}
  html[data-mascot-motion="off"].bigtimer body > #hTimer.on .rt-buddy{display:none}
  .lastset.now{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
  .tipline{display:block;margin-top:6px;padding-top:7px;border-top:1px solid rgba(255,255,255,.18);
    font-size:10px;opacity:.85}
  .notecard{background:var(--surface2);border:1px dashed var(--line);box-shadow:none}
  .notecard .smeter{background:var(--surface)}
  .smeter{height:9px;border-radius:5px;background:var(--surface2);overflow:hidden;position:relative}
  .smeter i{display:block;height:100%;border-radius:5px;background:var(--accent);opacity:.75;transition:width .5s var(--settle)}
  .smeter i.over{opacity:1;box-shadow:0 0 8px color-mix(in srgb,var(--accent) 60%,transparent)}
  /* v3.3.34: the part meter is a live bar — red while the session is in
     motion, accent once it's sealed. Same rule as the header and the fire
     needle: if it's red, something is still moving. */
  .smeter i.live{background:var(--live)}
  .smeter i.over.live{box-shadow:0 0 8px color-mix(in srgb,var(--live) 60%,transparent)}
  .settile.editing{outline:2px solid var(--accent);outline-offset:1px}
  .editcard .row input{width:100%}
  .nudge{position:relative;display:flex;align-items:center;gap:12px;margin-top:8px;
    padding:11px 36px 11px 13px;border-radius:12px;
    background:color-mix(in srgb,var(--accent) 8%,var(--surface));
    border:1px solid var(--accent-soft);font-size:11px;line-height:1.316}
  .nudge>span{flex:1;min-width:0}
  .nudge b{font-weight:700}
  .nudge .nudgego{flex:none;width:auto;margin:0;padding:9px 13px;font-size:11px;
    font-family:var(--mono);white-space:nowrap;
    background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
  .nudge .nudgex{position:absolute;top:50%;right:8px;transform:translateY(-50%)}
  .lschip{position:relative;display:inline-block}   /* v3.3.103: badge flush at the corner, never negative — see .lsx and the v3.3.49 buildcheck guard */
  .lschip .lsx{position:absolute;top:0;right:0;width:16px;height:16px;border-radius:50%;
    background:var(--surface2);border:1px solid var(--line);color:var(--muted);
    font-size:9px;line-height:1;padding:0;z-index:1;
    display:flex;align-items:center;justify-content:center}   /* v3.3.50: fully inside the corner — zero overhang (respects the clip guard), reps cleared by chip padding below */
  .zone.mini .lasthead{margin-bottom:5px}   /* v3.3.147: the strip's head is a .lasthead now */
  .zone.mini .lastsets{display:flex;flex-wrap:nowrap;overflow-x:auto;gap:6px 8px;padding:4px 0 2px;
    -webkit-overflow-scrolling:touch;scrollbar-width:none}
  .zone.mini .lastsets::-webkit-scrollbar{display:none}
  .zone.mini .lastset{padding:7px 11px;font-size:11px;flex:none}
  #gearBtn{position:relative}
  /* v4.6.126: the "not syncing" dot hangs on the ICON, not the button, and
     not on the button's ::after. That pseudo-element is also the selected
     tab's shimmer (nav button.on::after, inset:0), so opening Settings moved
     the dot from the top-right corner to the gear's left side. On .ng it has
     one position in every state. Smaller and softer (the maker's pick B):
     worth a glance, not an alarm. */
  #gearBtn.warn .ng{position:relative}
  #gearBtn.warn .ng::after{content:'';position:absolute;top:-1px;right:-2px;width:5px;height:5px;
    border-radius:50%;background:var(--record);opacity:.7;pointer-events:none}
  .btn.done{background:var(--accent);border:1px solid var(--accent);color:#fff;font-weight:600;margin-top:10px}
  /* v3.3.33: the open-part exit pair. Two filled blues side by side would be
     indistinguishable when the session isn't live, so Complete takes the
     outline and Continue keeps the fill — which also matches how often each
     is tapped. .ghost on Complete is doing double duty: it excludes the
     button from the sheen system, which has no business on a transparent
     surface. */
  .btnrow{display:flex;gap:8px;margin-top:10px}
  .btnrow .btn{margin-top:0;flex:1;min-width:0;white-space:nowrap}
  .btn.ghost.done{background:none;border:1px solid var(--accent);color:var(--accent-ink);
    font-size:13px;font-weight:600}
  .btn.done:active{filter:brightness(.9)}
  /* v3.3.371: THE END OF THE DAY IS ALWAYS REACHABLE. This button is the only
     route to the day-done moment, and it lived wherever Today happened to
     end -- past the plan, the parts and the chips, usually below the fold.
     Sticky rather than fixed: it keeps its place in the flow, so it does not
     cover the content above it and does not need the page to reserve room. It
     rests just above the nav, and it is the one filled button on the screen;
     everything else offering to continue is a chip. */
  /* v3.3.375: NOT sticky. A sticky element with a `bottom` offset is pinned
     UPWARDS when its natural position is below the pin line, so on a short
     page this lifted off the end of the document and painted over the part
     chips above it -- reachable, and in the way. It sits in flow now, directly
     under the session cards, and earns its prominence from being the only
     filled button on the screen rather than from floating. */
  /* v3.3.376: the finished day, in the place the button had. Speaks the
     ceremony's language -- the filled square, the same sentence -- so the
     moment and its aftermath are one thing rather than two. */
  /* v3.3.377: the share image wipes in from the top. The <img> already holds
     the finished canvas -- this only uncovers it, so the reveal can never show
     something other than what Share sends. */
  /* v4.6.166: THE SHARE PREVIEW FITS BETWEEN THE SAFE AREAS. A tall card (the
     consistency poster) used to overflow the centred overlay at both ends: the
     format row slid under the Dynamic Island, Share and Close onto the home bar.
     The overlay's padding now holds the insets and the card shrinks to what is
     left after the button row (48) and the gap (14) -- every share card, one rule. */
  #repImg{max-height:calc(100dvh - var(--sat) - var(--sab) - 24px - 62px);flex:0 0 auto}
  #repImg{clip-path:inset(0 0 0 0);transition:clip-path 1.2s var(--settle)}
  #repImg.revealing{clip-path:inset(0 0 100% 0)}
  @media (prefers-reduced-motion:reduce){
    #repImg{transition:none}
    #repImg.revealing{clip-path:inset(0 0 0 0)}
  }
  /* v3.3.388: ONE LINE, TWO ANCHORS. Date holds the left edge; rhythm holds
     the right edge immediately before settings. The open middle is deliberate
     balance, not a connection between the two. Subtitle stays below. */
  .h-toprow{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}
  .h-weekrow{position:static;display:flex;align-items:center;gap:7px;flex:0 0 auto}
  /* v3.3.384: the sweep runs ONCE, left to right across the days you trained,
     and stops. Not a loop: the header is on screen always, so a permanent
     animation is motion nobody can dismiss -- and a shimmering row is the
     web's universal LOADING skeleton, which is the opposite of what a record
     should say. Each square is delayed by its position, so the light crosses
     the week rather than flashing it. */
  .h-week .hwd.on{position:relative;overflow:hidden}
  /* v3.3.385: WIDER AND SLOWER. The first pass was a thin hard line at 0.5s
     -- over a 10px square that is a flicker, not a sweep. The band is now
     most of the square's width and the pass takes more than twice as long,
     with a longer stagger so the light travels ACROSS the week instead of
     hitting every square at once. */
  .h-week .hwd.on::after{content:'';position:absolute;inset:0;
    background:linear-gradient(100deg,transparent 0%,rgba(255,255,255,.92) 50%,transparent 100%);
    transform:translateX(-160%);animation:hwsheen 1.15s var(--settle) 1 both}
  .h-week .hwd:nth-child(2).on::after{animation-delay:.10s}
  .h-week .hwd:nth-child(3).on::after{animation-delay:.20s}
  .h-week .hwd:nth-child(4).on::after{animation-delay:.30s}
  .h-week .hwd:nth-child(5).on::after{animation-delay:.40s}
  .h-week .hwd:nth-child(6).on::after{animation-delay:.50s}
  .h-week .hwd:nth-child(7).on::after{animation-delay:.60s}
  @keyframes hwsheen{to{transform:translateX(160%)}}
  /* v3.3.384: today breathes only while it is still EMPTY -- "this day is
     open". The moment a set lands the square fills and the ring goes still,
     because a finished day that keeps pulsing is decoration. Only the outline
     COLOUR animates, so nothing reflows. */
  .h-week .hwd.tod:not(.on){animation:hwpulse 2.4s ease-in-out infinite}
  /* v3.3.437: the breath means "this day is still open". Declaring rest
     answers it, so the square goes still and green in the same beat. Without
     this the pulse fights the fill for the outline colour and the square
     flickers between green and blue forever. */
  .h-week .hwd.tod.resting{animation:none}
  @keyframes hwpulse{
    0%,100%{outline-color:var(--accent)}
    50%{outline-color:color-mix(in srgb,var(--accent) 22%,transparent)}
  }
  @media (prefers-reduced-motion:reduce){
    .h-week .hwd.on::after{animation:none;opacity:0}
    .h-week .hwd.tod:not(.on){animation:none}
  }
  .h-week{display:flex;gap:5px;align-items:center}
  .h-week .hwd{width:10px;height:10px;border-radius:var(--sq);
    background:var(--surface2);display:block}
  .h-week .hwd.on{background:var(--accent)}
  /* v4.1.8: A DAY THAT HAS NOT ARRIVED. One rule carries the whole strip --
     filled means it happened, outlined means it has not -- so a future day is
     a dotted container rather than a grey absence. A row of grey on a Monday
     morning would read as a week already failed; a row of outlines reads as a
     week ahead of you. background:none so it cannot be mistaken for the
     missed fill at 10px. */
  .h-week .hwd.ahead{background:none;
    box-shadow:inset 0 0 0 1.2px color-mix(in srgb,var(--chalk) 20%,transparent)}
  /* v4.1.11: LIGHT MODE HAD THE HIERARCHY INVERTED. A missed day is
     --surface2 (#F7F7F7) on a --ground of #EFEFEF: 1.06:1, the weakest mark
     the strip can make. The ahead outline resolved to about #C3C3C3 at
     1.53:1. So the loudest square in the week was the one where NOTHING had
     happened, and the quietest was a fact.
     Measured, not guessed: missed goes to 1.22:1 and ahead down to 1.13:1, so
     a day that happened reads stronger than a day that has not.
     Scoped to the strip and to light: --surface2 dresses half the app, and
     dark has the opposite arithmetic already (#272727 on #0A0A0A is a clear
     step), so neither is touched. */
  /* v4.1.12: :not(.on) -- WITHOUT IT THIS PAINTED OVER EVERY TRAINED DAY.
     `:root[data-theme="light"] .h-week .hwd` and `.h-week .hwd.on` carry the
     same specificity, and this rule sits later in the file, so it won and the
     whole week went grey. The count kept saying 3d because the count is JS
     and only the paint was broken -- which is exactly why a test reading hex
     values out of the stylesheet did not notice.
     Second time in three releases that specificity let me overwrite something
     that was already right. The narrow rule is the honest one: this is the
     MISSED fill, so it should say so and match nothing else. */
  /* v4.1.13: header:not(.live) -- the light greys are for the ORDINARY
     header. Without it this rule (0,4,0) outranks `header.live .h-week .hwd`
     (0,3,1) and the live strip keeps the light greys instead of its dark
     marks. Third cascade fault in this corner; caught by resolved style
     before it shipped, which is the whole reason those assertions exist. */
  /* Stronger neutral marks at header size: filled days remain more prominent
     than future outlines, with trained days and the today ring unchanged. */
  :root[data-theme="light"] header:not(.live) .h-week .hwd:not(.on){background:#D4D4D4}
  :root[data-theme="light"] header:not(.live) .h-week .hwd.ahead{background:none;
    box-shadow:inset 0 0 0 1.2px #DDDDDD}
  /* Light Rest header: matcha past days, fresh sage future outlines. */
  :root[data-theme="light"] header.resting:not(.live) .h-week .hwd:not(.on){background:#B2C3A0}
  :root[data-theme="light"] header.resting:not(.live) .h-week .hwd.ahead{background:none;
    box-shadow:inset 0 0 0 1.2px #82AD85}
  :root[data-theme="light"] header.resting:not(.live) .h-week .hwd.tod.resting:not(.on){
    background:#2E7D32;outline-color:#2E7D32;animation:restSelectionPulse 2.4s ease-in-out infinite}
  :root[data-theme="light"] header.resting:not(.live) #hStreak.restchip{color:#2E7D32}
  @keyframes restSelectionPulse{
    0%,100%{outline-color:#2E7D32}
    50%{outline-color:rgba(46,125,50,.35)}
  }
  @media(prefers-reduced-motion:reduce){
    :root[data-theme="light"] header.resting:not(.live) .h-week .hwd.tod.resting:not(.on){animation:none}
  }
  /* v4.1.9 REMOVES what v4.1.8 added here. Today ALREADY had its ring -- an
     `outline` with a 2px offset (below), whose colour hwpulse breathes. I gave
     it a second one as an inset shadow and set background:none, so an open
     today went from a filled square wearing a halo to an empty box wearing two
     rings, and the sheen had nothing left to sweep across.
     The rule "outlined means it has not happened" is carried by the halo that
     was always there; it did not need restating in a second property. */
  /* v3.3.380: the ring is an OUTLINE, not a spreading box-shadow. A 4px
     spread on a 10px square is an 18px footprint inside a 5px gap, so the
     ring overlapped the square before it and ran into the numeral. An
     outline with an offset draws outside the box without occupying layout,
     and the trailing margin buys it the room it needs. */
  .h-week .hwd.tod{outline:1.5px solid var(--accent);outline-offset:2px}
  /* v3.3.437: RESTING NOW. The fill and the outline are the same --rest the
     header wash is mixed from, so the square and the header state one thing
     in one colour. It is a LIVE grade, exactly as red is: it lands on tap and
     is gone at midnight, when the day joins the record as a plain rested day.
     Nothing in the heatmap or in Stats gains a third fill. */
  .h-week .hwd.tod.resting{background:var(--rest);outline-color:var(--rest);
    transition:background .4s ease,outline-color .4s ease}
  header.resting .h-week .hwd.tod.resting{background:var(--rest-ink);outline-color:var(--rest-ink)}
  .h-week .hwd:last-child{margin-right:4px}
  /* ---- v4.1.13: THE LIVE STRIP ------------------------------------------
     A skipped day was white at 28% and a trained day white at 100% -- the
     same colour at two strengths, so a skip read as a DIM TRAINED DAY rather
     than as a different thing. Separation 3.50:1 against the trained white.
     A dark mark on the red takes that to 8.14:1 and, more to the point,
     gives the skip its own language instead of borrowing the trained day's.
     .ahead had no live rule at all and was drawing its outline from --chalk:
     near-black on a red wash. Fixed here rather than left for later. */
  header.live .h-week .hwd{background:rgba(0,0,0,.22)}
  header.live .h-week .hwd.on{background:#fff}
  header.live .h-week .hwd.ahead{background:none;
    box-shadow:inset 0 0 0 1.2px rgba(255,255,255,.32)}
  /* TODAY OWNS THE ROW while a session is open: the ring it always had, a
     halo outside it, and a soft bloom behind -- all breathing on the 2.4s
     beat the ring already uses, so there is one pulse in the header, not two.
     It grows by TRANSFORM, never by width: the seven-square window has not
     changed shape since it was fixed, and a strip that reflows when a session
     starts would break that for a flourish. transform-origin is the centre so
     the neighbours are not nudged. */
  header.live .h-week .hwd.tod{outline:1.5px solid #fff;outline-offset:2px;
    transform:scale(1.18);transform-origin:50% 50%;
    /* v4.1.14: :not(.on) on the FILL only. Without it this beat
       `header.live .h-week .hwd.on` -- same specificity, later in the file --
       and today, once trained, went translucent mid-session. The ring, the
       scale and the bloom still apply to today whether trained or not; only
       the fill steps aside. Fourth time specificity has let me overwrite
       something already right, so the rule is now: add :not() the moment a
       new selector shares ground with an existing one. */
    animation-name:hwlive;
    /* BLUR ONLY, NO SPREAD. The first cut had a hard 4px halo ring and
       test-rest caught it: the squares sit 5px apart, so a 4px ring -- on a
       square already scaled 1.18 -- crowds its neighbours. That guard exists
       because a ring must be drawn outside the box without spreading into the
       gap, and it was right. A blurred bloom FADES into the gap instead of
       filling it, which is the same flourish without the crowding. */
    box-shadow:0 0 10px rgba(255,255,255,.50);
    animation:hwlive 2.4s ease-in-out infinite}
  header.live .h-week .hwd.tod:not(.on){background:rgba(255,255,255,.35)}
  /* THE SHEEN WAS WHITE ON WHITE. A trained day is #fff in the live header
     and the sweep is rgba(255,255,255,.92) -- the shimmer has been running
     invisibly the whole time a session is open. On the red wash the band has
     to be the RED, not more white: the same light travelling across the week,
     read as a shadow instead of a highlight. */
  header.live .h-week .hwd.on::after{
    background:linear-gradient(100deg,transparent 0%,
      color-mix(in srgb,var(--live) 62%,transparent) 50%,transparent 100%)}
  @keyframes hwlive{
    0%,100%{box-shadow:0 0 10px rgba(255,255,255,.50)}
    50%{box-shadow:0 0 16px rgba(255,255,255,.22)}
  }
  @media (prefers-reduced-motion:reduce){
    header.live .h-week .hwd.tod{animation:none}
  }
  /* the timer joins the facts it belongs with. On a 390 header the date
     column, seven squares, the numeral, the flame AND the timer come to
     333px in 298px -- the timer is a session fact, not a record fact, so it
     moves to the line with the sets and parts rather than the line with the
     date and the history. */
  /* v3.3.389: the subtitle line belongs to EXERCISE MODE alone. On the day
     header it is empty, and an empty line must cost no height -- the whole
     point of one line is one line. The timer moved up into the week row and
     SWAPS with the count: #hTimer.on hides .streak, so the slot has two
     tenants and never both. */
  .h-subrow{display:flex;align-items:center;gap:7px;min-width:0}
  .h-sub:not(:empty){margin-top:2px}
  .h-sub:empty{display:none}
  .hbtns .resttimer{font-size:13px}
  /* the slot-swap survives the move: the timer and the count are no longer
     siblings, so :has() carries what "~" used to. Still one tenant at a time
     -- date, week, count, timer and gear do not fit one line together. */
  header:has(#hTimer.on) .streak{display:none}
  .h-subrow .h-sub{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* v3.3.412: a plan on a closed day is a receipt, not a to-do. Ink recedes;
     nothing is struck through or recoloured -- the check marks already say
     which lines were kept. */
  .plspent{opacity:.62}
  /* v3.3.413: tomorrow's plan, read tonight. Same card as the week view's
     day plans; the row above it is the fold. Quieter than today's plan
     because it is not yet the one being trained against. */
  .planahead{opacity:.9;margin-top:5px}
  /* v3.3.432: the app's voice is labelled, so a verified note cannot be
     mistaken for the writer's opinion */
  .plannotes .pnhead{font-size:8px;letter-spacing:var(--track-label);text-transform:uppercase;
    color:var(--faint);margin-bottom:3px}
  /* v3.3.421: Clear lives behind the Edit door now, in the record red --
     destructive, and already past the point where you decided to change the
     plan. It was the far-edge glyph you never wanted to hit by accident. */
  .btn.ghost.danger{color:var(--record);border-color:color-mix(in srgb,var(--record) 45%,transparent)}
  /* v3.3.414: one line, no wrap. Day left, count and chevron right, all on
     the row's centre line. The chevron is a flex item beside the count, not
     an inline glyph hanging off its baseline. */
  .planfoldrow{display:flex;align-items:center;justify-content:space-between;gap:10px 12px;
    width:100%;padding:11px 14px;text-align:left;white-space:nowrap;font-size:11px}
  .planfoldrow .pp-day{color:var(--muted)}
  .planfoldrow .pp-right{display:inline-flex;align-items:center;gap:8px;color:var(--faint)}
  .planfoldrow .ic{color:var(--faint);flex:0 0 auto}
  /* v3.3.452: the fold. grid-template-rows 1fr -> 0fr animates to the
     content's own height with no measured number anywhere; the inner div
     needs min-height:0 and overflow:hidden or the row refuses to shrink
     below its content. Opacity leaves a little ahead of the height so the
     rows dim as they close rather than being guillotined. */
  .planfold{display:grid;grid-template-rows:1fr;
    transition:grid-template-rows var(--dur-arrive) var(--settle)}
  .planfold.shut{grid-template-rows:0fr}
  .planfold-in{min-height:0;overflow:hidden;
    transition:opacity .28s ease}
  .planfold.shut .planfold-in{opacity:0}
  .pfchev{display:inline-flex;transition:transform var(--dur-arrive) var(--settle)}
  .pfchev.open{transform:rotate(90deg)}
  @media (prefers-reduced-motion:reduce){
    .planfold,.planfold-in,.pfchev{transition:none}}
  /* v3.3.501: ONE GAP, NOT THREE MARGINS. The maker asked for the count to sit
     centred between the square and the date, with the two spaces identical.
     They were not, and the margins were not why: the square carried 17px
     below it and the date 5px above it, but the count is TEXT, and it was
     inheriting the app's 1.45 line-height. At 22px that is a ~32px line box
     around ~22px of digits, so about 5px of leading padded each side, and the
     date added ~2.6px of its own. Measured to the ink that is ~22px above the
     count and ~12.6px below -- nearly double, from spacing nobody wrote.
     Hand-tuning two margins against invisible leading is how it drifts again
     the next time a font size moves. So the card becomes a flex column with a
     single gap, and both text elements get line-height:1 so their boxes hug
     their own type. The gap that is declared is now the gap you see, once,
     for both spaces. 18px keeps the card almost exactly the height it was.
     What remains is each font's own space above its caps -- about 3px for the
     count, 1.7px for the date. That residue is inside the glyph box, not in
     the layout, and at these sizes it is under two pixels. */
  .card.dayclosed{display:flex;flex-direction:column;align-items:center;gap:14px 18px;
    width:100%;text-align:center;padding:24px 16px 20px;
    margin-bottom:14px;border:1px solid var(--line);cursor:pointer;position:relative}
  /* A quiet orbit celebrates completion without moving the card or its content.
     Only the gradient angle changes; rotating a rectangle would clip its corners. */
  @property --completed-edge-angle{syntax:'<angle>';initial-value:0deg;inherits:false}
  .card.dayclosed::after{content:'';position:absolute;inset:-1px;border-radius:inherit;
    padding:1.6px;pointer-events:none;
    background:conic-gradient(from var(--completed-edge-angle),transparent 0deg,
      transparent 240deg,rgba(52,69,220,.25) 280deg,#3445dc 322deg,#a0c9ff 344deg,transparent 360deg);
    -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
    mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
    -webkit-mask-composite:xor;mask-composite:exclude;
    animation:completed-edge-orbit 7s linear infinite}
  @keyframes completed-edge-orbit{to{--completed-edge-angle:360deg}}
  @media(prefers-reduced-motion:reduce){.card.dayclosed::after{animation:none;opacity:.35}}
  .dayclosed .dcsq{display:block;width:32px;height:32px;margin:0;
    border-radius:var(--sq);background:var(--accent)}
  /* v3.3.505: THE DESCENDER TRIM. v3.3.501 made the two declared gaps one
     number, and they still did not LOOK equal -- the maker marked the lower
     one. The residue that release named as "under two pixels" was measured
     wrong. With line-height:1 the count's box is exactly its font size, and
     the baseline sits about 0.78 of the way down it: roughly 1.8px of air
     above the cap of "961", but 4.8px of empty descender space below a string
     of digits that has no descenders. So the space above the count reads
     ~19.8px and the space below ~23.7px, from one 18px value.
     A symmetric fix cannot correct an asymmetric cause -- trimming the line
     box takes the same amount off both sides and leaves the difference
     exactly where it was. So the dead space below the baseline is cancelled,
     and only that: -4px, which is the descender of this font at this size,
     not a number chosen by eye. The gap stays a single declared value; this
     subtracts space the font added, rather than adding space the layout did
     not ask for. */
  .dayclosed .dcn{display:block;margin:0 0 -3px;line-height:1;font-family:var(--disp);font-weight:600;
    font-size:19px;letter-spacing:-.45px;color:var(--chalk)}
  /* v3.3.506: THE DATE IS A CAPTION, NOT A THIRD PEER. This reverses the rule
     from v3.3.501 on the maker's second look, and the reversal is the point:
     that release read his ask as "three elements, two identical spaces", and
     evenly spaced is what it built. Evenly spaced is what he then marked twice
     as too loose under the count.
     The card does not have three peers in a row. It has the square, the number
     it stands over, and a date that LABELS that number. A caption belongs to
     the thing above it, and belonging is read as proximity -- so the space
     under the count closes and the space under the square does not. The gap
     stays one declared value for the relationship it still governs; this pulls
     the caption back to its number by an amount that is stated, not tuned. */
  .dayclosed .dcm{display:block;margin:-3px 0 0;line-height:1;font-size:10px;color:var(--muted)}
  /* v3.3.500: .dcr went with the reopen sentence. The card is the square, the
     count and the date; its bottom padding already closes it. */
  .btn.done.dayend{margin-bottom:14px;
    box-shadow:0 6px 20px color-mix(in srgb,var(--accent) 22%,transparent)}
  /* v3.3.245: the header's paint, on an absolute child. Safari 26's status-bar
     tinting samples background-color and backdrop-filter on FIXED elements
     only — absolute children are skipped — so the bar keeps its look while
     Safari finds nothing at the viewport edge to composite a blur over. */
  /* v3.3.247: iOS 26 composites a progressive fade over roughly the top 100pt
     of a STANDALONE web app — measured on device, the header washed from
     rgb(226,232,230) at the very top to its true rgb(203,217,207) only at
     99pt. The same page in Safari is flat from the first pixel, so this is
     the home-screen app treatment and nothing in the page causes it.
     No CSS removes it. What CSS can do is keep the READING out of it: when
     the app is standalone AND the page still sits under the status bar, the
     header's top padding grows so the date and the buttons begin below the
     faded strip. The flag is set in JS only when both are true, so a
     re-installed icon (which picks up the default status-bar style and
     reports a zero inset) drops back to the normal compact header. */
  :root[data-edgefade="1"] header{padding-top:calc(env(safe-area-inset-top,0px) + 44px)}
  .hglass{position:absolute;inset:0;z-index:0;background:var(--ground);
    border-bottom:1px solid var(--line);pointer-events:none}
  /* Approved glass preview: 10% of its 0–40px range = 4px, not 10% opacity.
     v4.6.103: Normal Light uses 20% (8px); all other states stay at 4px.
     Keep the fixed header transparent for Safari's edge sampling; only its
     absolute material layer blurs. Controls stay sharp on their own layer.
     Solid defaults remain readable without backdrop-filter support. */
  header{--header-glass-top:var(--ground);--header-glass-bottom:var(--surface);--header-glass-blur:4px}
  :root[data-theme="light"] header:not(.resting):not(.live){--header-glass-blur:8px}
  header.resting{--header-glass-top:var(--rest-top);--header-glass-bottom:var(--surface)}
  header.live{--header-glass-top:var(--live);--header-glass-bottom:var(--live)}
  header .hglass,header.resting .hglass,header.live .hglass{
    background:var(--header-glass-top);border:0;border-radius:0 0 22px 22px;
    box-shadow:inset 0 -1px 0 rgba(255,255,255,.24),0 5px 16px rgba(0,0,0,.08);
    animation:none}
  @supports ((backdrop-filter:blur(4px)) or (-webkit-backdrop-filter:blur(4px))){
    header .hglass,header.resting .hglass,header.live .hglass{
      background:linear-gradient(160deg,
        color-mix(in srgb,var(--header-glass-top) 78%,transparent),
        color-mix(in srgb,var(--header-glass-bottom) 78%,transparent));
      -webkit-backdrop-filter:blur(var(--header-glass-blur)) saturate(135%);
      backdrop-filter:blur(var(--header-glass-blur)) saturate(135%)}
  }
  @media(prefers-reduced-transparency:reduce){
    header .hglass,header.resting .hglass,header.live .hglass{
      background:var(--header-glass-top);-webkit-backdrop-filter:none;backdrop-filter:none}
  }
  header>.brandrow,header>.hbtns{position:relative;z-index:1}
  .brandrow{display:flex;align-items:flex-start;gap:11px;min-width:0;flex:1 1 auto;overflow:hidden}
  header.live .h-date{color:#fff}
  header.live .h-sub{color:rgba(255,255,255,.92)}
  header.live .h-sub.donetoday::before{color:#fff}
  header.live .streak{color:#fff}
  header.live .icobtn{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.3);color:#fff}
  header.live #gearBtn{background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.24)}
  header.live .icobtn small{color:#fff;opacity:.95}
  header.exmode .h-date{font-size:17px;line-height:1.25}
  /* v3.3.391: exercise mode hides the WEEK, not the row. v3.3.389 moved the
     rest timer into .h-weekrow to share the count's slot, and this rule --
     written when the row held only the record -- then hid the running clock
     the moment you opened an exercise. Mid-session that clock is the most
     useful thing in the header, and the exercise screen is exactly where you
     stand while it runs.
     The squares and the count are the record and still step aside for the
     exercise's own name and subtitle; the timer is not the record, so it
     stays. */
  header.exmode .h-week,header.exmode .streak{display:none}
  header.exmode .h-weekrow{flex:0 0 auto}
  .h-idcol{flex:1 1 auto;min-width:0}
  .h-date{font-family:var(--disp);font-weight:700;font-size:19px;letter-spacing:var(--track-num);
    max-width:100%;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .h-sub{font-family:var(--mono);font-size:11px;color:var(--muted);letter-spacing:var(--track-label);text-transform:uppercase;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .brandrow>div{min-width:0}
  /* v3.3.392: align-self:center so the controls hold the header's axis even
     in exercise mode, where the header hangs its taller identity column from
     the top. The clock lives here now, so this is what keeps its y identical
     on every screen. */
  .hbtns{display:flex;gap:8px;align-items:center;align-self:center;flex:0 0 auto}   /* v3.3.55: controls never shrink and never wrap */
  .icobtn{
    width:44px;height:44px;border-radius:13px;border:1px solid var(--line);
    background:var(--surface);color:var(--chalk);font-size:16px;cursor:pointer;
    display:grid;place-items:center;
  }
  /* v3.3.386: settings is available, not the subject. Its smaller, recessed
     surface leaves the date as the first thing the eye lands on. */
  /* v4.6.125: scoped to the header. Since v4.6.123 the gear lives in the tab
     bar, and this ID rule outranked every nav pill rule -- the selected gear
     wore --surface2 (a dark pill on the white-pill bars) and the tab bar's
     own selected style never reached it. */
  header #gearBtn{width:38px;height:38px;border-radius:12px;background:var(--surface2);
    color:var(--muted);font-size:17px}
  /* The 320px fallback preserves all seven days and the full count. Ordinary
     iPhones keep the 10px marks; only the genuinely narrow edge uses 8px. */
  @media (max-width:340px){
    .h-toprow{gap:8px}
    .h-weekrow{gap:5px}
    .h-week{gap:4px}
    .h-week .hwd{width:8px;height:8px}
    .h-week .hwd:last-child{margin-right:2px}
  }
  /* back appears ONLY inside an exercise; header-scoped selectors out-rank
     .icobtn's display so ordering can never leak it onto other screens again */
  header .hback{display:none;font-size:20px;font-weight:600;padding-bottom:2px}
  header.exmode .hback{display:grid}
  /* v3.3.390: EXERCISE MODE is genuinely two lines (the name, then "Chest .
     3 sets logged"), so there the column is taller than the controls and
     top-hanging is right -- it keeps the exercise name level with the gear
     rather than floating the pair down. The centred rule above is for the
     one-line day header only. */
  header.exmode{align-items:flex-start}
  /* v3.3.380: the count is INK, not the record red. It has been --record
     since the flame sat beside it, where the colour read as fire; alone, a
     red "6d" reads as an alarm on a day that went perfectly. The at-risk rule
     below still turns it red, and now that is the only thing that does. */
  .streak{font-family:var(--mono);font-size:11px;color:var(--chalk);font-weight:600;white-space:nowrap;margin-right:2px}
  html{touch-action:manipulation}   /* no double-tap zoom mid-set; pan/pinch unaffected */
  main{padding:18px 18px calc(28px + env(safe-area-inset-bottom))}
  h2{font-family:var(--disp);font-size:9px;letter-spacing:var(--track-wide);text-transform:uppercase;opacity:.75;
     color:var(--chalk);font-weight:600;margin:19px 0 7px;
     display:flex;align-items:center;gap:8px}
  /* v3.3.512: THE TICK BECOMES A RULE. A 3px accent pip sat before every
     heading, about ten times a screen, in the same colour whatever the section
     was about. It separated nothing and said nothing -- an ornament, which is
     what made it read as a default rather than a decision.
     A hairline running from the label to the edge does the job the pip was
     miming: it SEPARATES. It also lets each heading keep its own trailing
     controls, because the rule takes whatever space is left between them.
     Order, not ::after position, is what puts it in the right place: the rule
     is order:1, so anything that should sit right of it is order:2 and
     everything else stays at 0, ahead of it, where it already was. */
  h2::after{content:'';order:1;flex:1 1 auto;min-width:16px;height:1.5px;
    background:var(--line);border-radius:1px}
  h2 .planedge{order:2}
  /* v3.3.112: actions sit at the far right of every header, so the eye finds
     them in one place instead of hunting mid-title or below the card. */
  /* v3.3.115: back beside the title (no margin-left:auto) — the maker's
     call after living with them hard-right for a release. */
  h2 .hacts{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto}
  h2.quiet::before{background:var(--line)}
  h2:first-child{margin-top:3px}
  h2 .hi{color:var(--accent-ink);font-weight:600}
  /* v3.3.235: chart titles stay clearly separate from the plotting area. */
  h2.charthead{margin-bottom:19px}
  /* v3.3.54: the INFO pill returns to a small "i" dot beside its section
     title. Same affordance, a word's less shouting; the tap target keeps
     ~18px via the circle. lowercase — an "I" dot reads as a letter, not a
     mark. */
  /* v3.3.130: the filled disc is gone. A solid chalk circle beside every
     heading read as a control demanding attention, when it is a footnote you
     consult maybe twice. */
  /* v3.3.131: the bare glyph went too far the other way — it disappeared
     into the heading (maker's verdict, same day). A dim outlined circle
     lands between the two: visibly a button, not shouting. The 22px circle
     IS the tap target now, so the padding/negative-margin trick is gone. */
  .ibtn{border:1px solid var(--line);border-radius:50%;background:none;color:var(--muted);
    font-family:var(--mono);font-size:9px;font-weight:600;cursor:pointer;
    text-transform:lowercase;width:22px;height:22px;line-height:19px;padding:0;
    text-align:center;vertical-align:middle;display:inline-block;
    transition:background var(--dur-quick) var(--settle)}
  .ibtn:active{background:var(--line)}
  @media (prefers-reduced-motion:reduce){.ibtn{transition:none}}
  h2 .hacts{margin-left:1px}
  /* v3.3.266: Copy month + Copy year are one action family. Keep the
     title-to-group breathing room, but use the hacts gap inside the pair. */
  h2 .copyacts{margin-left:10px;gap:6px}
  h2 .copyacts>.dayedit{margin-left:0}
  /* ---- v3.3.130: report card — the app's one share surface ---- */
  /* v3.3.139: the arrows leave the title row and flank the preview at its
     midline, which is where the thumb already is when swiping. The card
     becomes the positioning context; the title reclaims the full width. */
  .repcard{text-align:center;position:relative}
  .repnav{display:flex;align-items:center;justify-content:center;gap:8px}
  .repar{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
    width:34px;height:34px;flex:0 0 auto;border-radius:50%;border:1px solid var(--line);
    background:var(--surface);color:var(--chalk);font-size:15px;line-height:1;cursor:pointer;
    display:flex;align-items:center;justify-content:center;padding:0}
  #repPrev{left:10px} #repNext{right:10px}
  .repar:active{background:var(--line)}
  .repttl{flex:1 1 auto;font-weight:700;font-size:15px;color:var(--chalk);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 42px}
  .repthumbwrap{margin:8px 0 5px;display:flex;justify-content:center;min-height:120px;
    align-items:center;touch-action:pan-y}   /* v3.3.139: vertical scroll still works through it */
  .repthumb{width:min(190px,52%);aspect-ratio:1/1;border-radius:10px;display:block;
    box-shadow:0 6px 18px rgba(0,0,0,.16);pointer-events:none;user-select:none}
  .repdots{margin:0 0 8px;font-family:var(--mono)}
  .repcard .btn{margin:0}
  .repcard .btn:disabled{opacity:.6}
  /* v3.3.513: the version, at the top of Settings where you land */
  .legal-links a{color:inherit;text-underline-offset:2px}   /* v4.6.117 */
  .vertag{margin:2px 0 -5px;text-align:center;font-size:10px;color:var(--faint);
    letter-spacing:.2px}
  #view>.card.rephistory{padding:0;overflow:hidden}
  .rephistory>summary{list-style:none;display:flex;align-items:center;justify-content:space-between;
    min-height:62px;padding:0 16px;cursor:pointer;font-weight:600;color:var(--chalk)}
  .rephistory>summary::-webkit-details-marker{display:none}
  .rephistory>summary .muted{font-size:9px;font-weight:500}
  .rephistory>summary b{color:var(--accent-ink);font-size:12px;margin-left:4px}
  .rephistory[open]>summary{border-bottom:1px solid var(--line)}
  .rephistory .repcard{padding:16px}
  /* v3.3.267: History opens with one small share action instead of ending
     with a second section heading and a 62px card. It grows only when opened. */
  .repquick{margin:0 0 11px;border-radius:12px;background:var(--surface2);box-shadow:none}
  .repquick>summary{min-height:42px;padding:0 12px;font-size:11px}
  .repquick>summary .muted{font-size:8px}
  .repquick>summary b{font-size:11px;margin-left:3px}
  .repquick[open]{background:var(--surface)}
  /* v3.3.152: explanatory text in the BODY face, not the mono display face —
     the rulebook's readability rules (15px, 1.45 line height, sentence case)
     apply to prose, and these bubbles are the app's only prose. Mono stays
     everywhere it labels data. */
  .tipbubble{position:absolute;top:26px;left:0;z-index:35;width:min(300px,80vw);
    background:#16181D;color:#F4F5F8;font-family:var(--body);font-size:13px;font-weight:400;
    line-height:1.363;letter-spacing:0;text-transform:none;text-align:left;
    padding:14px;border-radius:11px;box-shadow:0 8px 24px rgba(0,0,0,.42)}
  .tipbubble::before{content:'';position:absolute;top:-5px;left:24px;width:10px;height:10px;
    background:#16181D;transform:rotate(45deg)}
  /* flip up when the bottom nav would clip it; flip left at the screen edge */
  /* the visible (i) stays 22px; its TAP target is 44px, per the rulebook */
  .ibtn.tipi{position:relative}
  .ibtn.tipi::after{content:'';position:absolute;inset:-11px}
  /* v3.3.152: the plast tip became this inline line (rulebook's own fix) */
  .inlinehelp{font-family:var(--body);font-size:11px;line-height:1.316;color:var(--muted);
    margin:2px 0 8px;text-align:left}
  .tipbubble.up{top:auto;bottom:26px}
  .tipbubble.up::before{top:auto;bottom:-5px}
  .tipbubble.right{left:auto;right:0}
  .tipbubble.right::before{left:auto;right:24px}
  /* v3.3.92: keyboard focus is visible app-wide. :focus-visible only fires
     for keyboard/AT navigation, so touch behaviour is untouched. */
  button:focus-visible,[role="button"]:focus-visible,input:focus-visible,
  select:focus-visible,a:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .card{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:12px;box-shadow:var(--shadow)}
  .row{display:flex;align-items:center;gap:10px}
  .spread{justify-content:space-between}
  .muted{color:var(--muted)} .mono{font-family:var(--mono)}
  .meter{height:6px;border-radius:3px;background:var(--surface2);overflow:hidden;margin-top:10px}
  .meter i{display:block;height:100%;background:var(--accent);transition:width var(--dur-arrive) var(--settle)}
  /* chips */
  .chips{display:flex;flex-wrap:wrap;gap:6px 8px}
  .chip{font:inherit;font-size:12px;background:var(--surface);color:var(--chalk);
    border:1px solid var(--line);border-radius:999px;padding:9px 15px;cursor:pointer;box-shadow:var(--shadow);
    white-space:nowrap}   /* v3.3.276: a chip is a chip — its label never wraps */
  .chip .n{font-family:var(--mono);font-size:10px;color:var(--muted);margin-left:6px}
  .chip.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
  .chip.on .n{color:inherit;opacity:.7}
  .chip.due{border-color:var(--accent);color:var(--accent)}
  .chip.due .n{color:var(--accent);opacity:.75}
  /* list items */
  .item{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;
    background:var(--surface);border:1px solid var(--edge);border-radius:12px;
    padding:13px 14px;margin-bottom:6px;color:var(--chalk);font:inherit;text-align:left;cursor:pointer;box-shadow:var(--shadow)}
  .item .sub{font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:2px}
  /* v3.3.299: Today's session rows adopt B1 — the same slim raised row the
     go-to list took in v3.3.298, at the same metrics. What does NOT come
     across is the flattening: on the go-to list all five rails were the same
     accent and signalled nothing, but here the rail is the live/finished
     distinction, which is real. So the edge KEEPS its meaning — it just
     becomes the row's own 0.5px border instead of a 3px slab on one side. */
  /* v3.3.322: 1px, not 0.5px. The base .item and .card both use 1px; these
     rows overrode it down to a hairline, and since v3.3.304 put them on the
     SAME white as the card behind them, that hairline was the only thing
     separating a row from its container. It was doing all the work at half
     the weight. Padding and gap step down one to match. */
  .item.todayrow{padding:9px 14px;border-radius:10px;margin-bottom:3px;gap:10px;
    background:var(--surface);border:1px solid var(--live);box-shadow:none}
  .item.todayrow b{font-size:12px}
  .item.todayrow .sub{font-size:9px;color:var(--faint);margin-top:2px}
  .item.todayrow.fin{border-color:var(--edge);background:var(--surface)}
  .item.todayrow b::before{content:'● ';color:var(--live);font-size:8px;vertical-align:1px;
    animation:pulse 1.6s ease-in-out infinite;display:inline-block}
  .item.todayrow.fin b::before{content:'✓ ';color:var(--muted);font-size:10px;animation:none}
  /* the sets count is a column, so it lines up down the card */
  .item.todayrow .tsets{flex:0 0 52px;text-align:right;font-family:var(--mono);
    font-size:10px;color:var(--muted)}
  /* v3.3.300: Train's session rows show the day's volume for that lift —
     a right-aligned column so the numbers stack down the card, and the name
     keeps the whole line above its sets. */
  .item.todayrow .logmain{flex:1;min-width:0}
  .item.todayrow .logmain b{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .item.todayrow .tvol{flex:0 0 74px;text-align:right;font-size:10px;color:var(--muted)}
  .item.todayrow:active{background:var(--surface2);transform:scale(.995);
    transition:transform var(--dur-quick) var(--settle),background var(--dur-quick) var(--settle)}
  @media (prefers-reduced-motion:reduce){
    .item.todayrow b::before{animation:none}
    header.resting .hglass{animation:none}}   /* v3.3.82: after the @supports branch, so none wins */
  .item b{font-weight:500}
  .readyrow{display:flex;align-items:center;gap:10px;width:100%;background:none;border:0;
    color:var(--chalk);font:inherit;text-align:left;cursor:pointer;padding:9px 4px;
    border-bottom:1px solid var(--line)}
  .readyrow:last-of-type{border-bottom:0}
  .readyrow .rname{flex:0 0 88px;font-size:12px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .readyrow .rbar{flex:1;height:8px;border-radius:4px;background:var(--surface2);overflow:hidden}
  .readyrow .rbar i{display:block;height:100%;border-radius:4px;background:var(--faint);transition:width var(--dur-arrive) var(--settle)}
  .readyrow .rbar i.due{background:var(--accent)}
  .readyrow .rmeta{flex:none;font-family:var(--mono);font-size:10px;color:var(--chalk)}
  .readyrow .rmeta em{font-style:normal;color:var(--muted)}
  .item[data-ex]>span:first-child{flex:1;min-width:0}
  .item[data-ex]>span:last-child{flex:none;white-space:nowrap;margin-left:auto}
  /* v3.3.296: FOUR across, so eight parts land in two rows instead of three.
     The room comes from dropping the ✅ from finished cards (v3.3.296 in
     lift.js) rather than from shrinking type — the card keeps its 13px name
     and its full "3d ago", because the parts you have NOT trained are what
     this grid is scanned for. minmax(0,1fr) so a long name cannot push the
     column wider than its share. */
  .partgrid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px 6px}
  .partcard{display:flex;flex-direction:column;align-items:flex-start;gap:1px 1px;
    background:var(--surface);border:1px solid var(--line);border-radius:10px;
    padding:8px 9px;color:var(--chalk);font:inherit;text-align:left;cursor:pointer;box-shadow:var(--shadow);
    min-width:0;overflow:hidden}
  .partcard b{font-weight:600;font-size:11px;line-height:1.2;
    max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .partcard .ps{font-family:var(--mono);font-size:9px;color:var(--muted);white-space:nowrap;
    max-width:100%;overflow:hidden;text-overflow:ellipsis}
  /* ---- part-tile state table (the rules, in order of application) ----
     not selected : white (recent) · white+thin red border+🔥 (active)
                  · dimmed to .5, no badge (completed — v3.3.296: the fade is
                  the signal) · gray dimmed (cold: never trained, or nothing
                  logged in PART_COLD_DAYS — v3.3.269)
     selected     : RED fill (active) · BLUE fill (everything else)
     suggested    : dashed accent border on idle tiles, never in red mode  */
  .partcard.hot{border:1.5px dashed var(--accent)}
  .partcard.hot b{color:var(--accent)}
  .partcard.run{border-color:var(--accent-dim)}
  .partcard.run b{color:var(--accent-dim)}
  .partcard.liveP{border:2px solid var(--live);animation:liveborder 1.8s ease-in-out infinite}
  .partcard.liveP .ps{color:var(--live);animation:livetext 1.8s ease-in-out infinite}
  /* the fade IS the tick (v3.3.296) — it carries the whole signal now, so it
     goes further than it did when it was only reinforcing a ✅ */
  .partcard.finP{opacity:.5}
  .partcard.sel{background:var(--accent);border-color:var(--accent);opacity:1}
  .partcard.sel b,.partcard.sel .ps{color:#fff;opacity:1}
  .partcard.sel .ps{opacity:.85}
  .partcard.sel.liveP{background:var(--live);border-color:var(--live);
    animation:livefill 1.8s ease-in-out infinite}
  /* pulsating COLOR, never a whole-tile brightness dim: the red oscillates,
     the text stays solid and legible */
  @keyframes liveborder{0%,100%{border-color:var(--live)}
    50%{border-color:color-mix(in srgb,var(--live) 55%,var(--surface))}}
  @keyframes livetext{0%,100%{color:var(--live)}
    50%{color:color-mix(in srgb,var(--live) 60%,var(--muted))}}
  @keyframes livefill{0%,100%{background:var(--live);border-color:var(--live)}
    50%{background:color-mix(in srgb,var(--live) 78%,#000);
        border-color:color-mix(in srgb,var(--live) 78%,#000)}}
  @media (prefers-reduced-motion:reduce){
    .partcard.liveP,.partcard.liveP .ps,.partcard.sel.liveP{animation:none}}
  .partcard.dead{opacity:.45}
  .partcard.sel.dead{opacity:1}   /* selection is the single authority */
  .item.dim{opacity:.45}
  .item.logrow{gap:8px;padding:10px 10px 10px 14px}
  /* v3.3.64 added a chevron because the flat go-to card had no affordance.
     v3.3.298 DELETES it: it was the middle child of a space-between row, so
     it landed in a different place on every card, and the affordance is now
     carried by the row's own edge and press state — which is what the maker
     chose when he picked B1 over the chevron variant. */
  .logmain{flex:1;min-width:0;background:none;border:0;color:var(--chalk);font:inherit;text-align:left;cursor:pointer;padding:0}
  .logmain b{font-weight:500}
  .xbtn{flex:0 0 40px;height:40px;border-radius:10px;border:1px solid var(--line);
    background:var(--surface2);color:var(--muted);font-size:12px;cursor:pointer;
    display:inline-flex;align-items:center;justify-content:center}   /* v4.6.73: holds the bin */
  .xbtn:active{border-color:var(--record);color:var(--record)}
  /* v3.3.298: the go-to list is B1 — slim raised rows, copied from the
     mockup the maker approved, metric for metric. The old design was a
     ~130px card per exercise with a 3px accent rail on ALL of them (five
     identical accent marks signalling nothing) and a chevron floating in
     the middle of a three-child space-between row — the same defect fixed
     on the plan rows in v3.3.292. One row leads at 16px with the accent on
     its meta line; the rest are 13.5px rows carrying age and weight in
     aligned columns. */
  /* v3.3.304: rows sit on --surface, the same white every other card uses.
     B1 put them on --surface2 to give them an edge, but the light theme's
     new ladder (v3.3.301) already lifts a white card off a grey page — so
     the recessed fill only made these rows read as a different KIND of
     surface from the cards above them. The press now goes the other way:
     resting white, pressing down to --surface2. */
  .item.goto{padding:11px 14px;
    background:var(--surface);border:1px solid var(--edge);border-radius:10px;
    align-items:center;gap:10px;margin-bottom:5px;box-shadow:none}
  .item.goto b{font-size:14px;font-weight:500}
  .item.goto .sub{font-size:9px;color:var(--accent-dim);margin-top:2px}
  /* the ranked rows below the lead: name, age, weight — three columns that
     line up down the whole list.
     v3.3.300: :not(.todayrow) is load-bearing. Train's own "· today" rows
     also carry .logrow, so this rule reached them too and forced their name
     and their set detail onto ONE flex line — the name ellipsised down to
     "E…". A ranked row shows name + weight side by side; a session row
     stacks name over its sets. Different shapes, and the selector has to
     say which it means. */
  .item.logrow:not(.goto):not(.todayrow){padding:10px 14px;gap:10px;align-items:center;
    background:var(--surface);border:1px solid var(--edge);border-radius:10px;
    margin-bottom:5px;box-shadow:none}
  .item.logrow:not(.goto):not(.todayrow) b{font-size:12px;font-weight:500}
  .item.logrow:not(.goto):not(.todayrow) .sub{font-size:9px;color:var(--faint);margin-top:0}
  .item.logrow:not(.goto):not(.todayrow) .logmain{display:flex;align-items:baseline;gap:10px}
  .item.logrow:not(.goto):not(.todayrow) .logmain b{flex:1;min-width:0;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  /* v3.3.303: the weight cell sizes to its CONTENT, floored at 62px. It was
     pinned to 58px, and once v3.3.302 put a space before the unit a 14px
     go-to weight needed 58.8px — so "88.2 lb" broke across two lines. Rows
     carrying a "/ side" line escaped only because that longer line forced
     the cell wider, which is why only some of them wrapped. Right-aligned,
     so the right edges still line up however wide each cell ends up. */
  .item.logrow .pr-cell{flex:0 0 auto;min-width:62px;text-align:right}
  .pr-top{white-space:nowrap}   /* a weight is one token, never two lines */
  /* v3.3.329: an exercise you have never loaded shows a RULE, not a blank.
     The column reads top to bottom; a hole in it looks like a render that
     failed, where a short line says "nothing here yet" in the same breath as
     the numbers around it. Sized to about two digits so it sits in the
     column rather than beside it, and drawn in --line: quieter than any
     number, louder than the whisper between rows. */
  .pr-nil{display:inline-block;width:18px;height:1.5px;border-radius:1px;
    background:var(--line);vertical-align:middle}
  .item.logrow:not(.goto):not(.todayrow) .pr-top{font-size:11px;font-weight:400}
  /* press: the row settles under the thumb — the affordance the flat list
     was missing, bought with an edge and a press rather than a slab */
  .item.logrow:active{background:var(--surface2);transform:scale(.995);
    transition:transform var(--dur-quick) var(--settle),background var(--dur-quick) var(--settle)}
  .item.goto .pr-top{font-size:12px;font-weight:400}
  h2.quiet{color:var(--muted);font-weight:500;margin-top:19px}
  /* v3.3.105: h2.quiet and h2:first-child are an exact specificity tie
     (element+class vs element+pseudo-class), so whichever rule is written
     LATER in the file wins — a coincidence of source order, not a decision.
     .quiet happened to lose that coin flip against the Rhythm heading,
     which genuinely IS #view's first child whenever no session is live,
     producing the 24px gap the maker circled instead of the intended 4px.
     A targeted selector settles it by actual specificity, not by which
     rule got written last. */
  h2.quiet:first-child{margin-top:3px}
  .pr-cell{display:flex;flex-direction:column;align-items:flex-end;flex:0 0 auto;font-family:var(--mono)}
  /* v3.3.302: the weight reads, it does not shout. It was 600 — bold enough
     that the number fought the exercise name for the row — at 12px with the
     unit jammed against the digits ("165.3lb"). The maker asked for the
     mockup's setting: regular weight, a shade larger, and a space before the
     unit so the eye parses "165.3" and "lb" as two things. Mono already, so
     the digits still align down the column. */
  .pr-top{color:var(--chalk);font-size:11px;font-weight:400;letter-spacing:.01em}
  .loadline{font-family:var(--mono);font-size:11px;color:var(--muted);
    background:var(--surface2);border-radius:10px;padding:7px 10px;margin-top:-3px;margin-bottom:8px;
    display:flex;align-items:center;gap:10px}
  .ll-viz{flex:0 0 104px;display:flex;justify-content:center}   /* fixed width: text never shifts */
  .ll-text{flex:1;min-width:0}
  .ll-bar{flex:0 0 auto;background:var(--surface);border:1px solid var(--line);color:var(--muted);
    font-family:var(--mono);font-size:10px;line-height:1.269;border-radius:10px;
    padding:8px 12px;min-height:44px;min-width:64px;cursor:pointer;text-align:center}
  .ll-bar b{color:var(--chalk);font-size:11px}
  .ll-bar.save{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
  /* v3.3.283: while the bar weight is being edited the load line is a small
     form — question, answer, choices — so it drops the single-row flex. */
  /* v3.3.284: the equipment line sits under the stepper it explains. */
  /* v3.3.285: today's parts get their own line under the heading. */
  .todaypartsline{font-size:10px;color:var(--accent-ink);letter-spacing:.06em;
    text-transform:uppercase;margin:-3px 0 8px;line-height:1.41}
  .eqline{display:block;width:100%;text-align:left;background:none;border:0;
    padding:0 2px 10px;margin-top:-3px;cursor:pointer;
    font-family:var(--mono);font-size:9px;color:var(--faint);letter-spacing:.03em}
  .eqline .eqpen{color:var(--muted);margin-left:2px}
  .eqedit{background:var(--surface2);border-radius:10px;padding:12px;margin-bottom:8px}
  .eqedit .eq-q{display:block;font-size:9px;color:var(--muted);
    letter-spacing:var(--track-label);text-transform:uppercase;margin-bottom:6px}
  .eqedit .chips{gap:6px}
  .loadline.editing{display:block;padding:10px 12px 12px}
  .loadline.editing .ll-q{display:block;font-size:9px;color:var(--muted);
    letter-spacing:var(--track-label);margin-bottom:5px;line-height:1.316}
  .loadline.editing .barinput{margin-bottom:6px}
  .loadline.editing .ll-choices{display:grid;grid-template-columns:1fr 1fr auto;gap:6px 8px}
  .loadline.editing .ll-bar{min-width:0;width:100%;white-space:nowrap;
    padding:9px 10px;min-height:40px}
  .barinput{width:100%;background:var(--surface);border:1px solid var(--accent);border-radius:10px;
    color:var(--chalk);font-family:var(--mono);font-weight:700;font-size:18px;padding:9px 11px}
  .barinput:focus{outline:none}
  .barviz{display:flex;align-items:center;gap:2px;height:28px}
  .barviz .bar{height:4px;width:22px;background:var(--muted);border-radius:2px;opacity:.7}
  .barviz .pl{border-radius:2px;background:var(--accent);width:6px}
  /* last session, tappable */
  .zone{background:var(--surface);border:1px solid var(--line);border-radius:14px;
    padding:14px;margin-top:11px;box-shadow:var(--shadow)}
  .zone.prime{border-color:var(--accent-soft)}

  /* v3.3.32: logged = done = quiet, now at container level too. The block
     recedes to the page ground and drops its lift shadow — a well cut into
     the page rather than a card sitting on it. Set tiles keep --surface2, so
     they still step off the tray in BOTH themes (light: tiles darker than
     tray; dark: lighter). Suggested/Last-time zones stay raised: they're
     still-live invitations, and only finished work sinks. */
  .zone.logged{background:var(--ground);box-shadow:none}
  .zonehead{display:flex;justify-content:space-between;align-items:center;
    font-family:var(--disp);font-size:11px;letter-spacing:var(--track-wide);text-transform:uppercase;
    font-weight:600;color:var(--chalk)}
  .zone .lastset{background:var(--ground)}
  .zone .repgrid button,.zone .fld,.zone .repcustom input{background:var(--ground)}
  .zone .wsel{background:var(--ground)}
  .foldrow{display:flex;justify-content:space-between;align-items:center;width:100%;
    background:none;border:1px dashed var(--line);border-radius:12px;padding:11px 14px;
    color:var(--muted);font:inherit;font-family:var(--mono);font-size:11px;cursor:pointer;margin-top:11px}
  .foldrow .hi{color:var(--accent);font-weight:700}
  .chev{background:none;border:0;color:var(--muted);font-size:11px;cursor:pointer;padding:2px 6px}
  /* ---- v3.3.534: the plan card on the exercise screen ---- */
  .planzone .lasthead .ago{font-size:10px;color:var(--faint)}
  .planrows{display:flex;flex-direction:column;margin-top:2px}
  .planrow2{display:flex;align-items:center;gap:10px;width:100%;min-height:46px;
    background:none;border:0;padding:6px 8px;font:inherit;text-align:left;cursor:pointer;
    border-radius:12px;color:var(--chalk)}
  .planrow2+.planrow2{border-top:1px solid var(--whisper)}
  .planrow2.next{background:color-mix(in srgb,var(--accent) 7%,transparent);
    box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--accent) 45%,transparent)}
  .planrow2:active{background:var(--whisper)}
  /* the dial: a ring that fills clockwise by that ROW's own set count, so a
     five-set row moves in fifths and a six-set row in sixths. conic-gradient
     does the arithmetic; --f is the only thing JS writes. */
  .pgd{flex:0 0 auto;width:18px;height:18px;border-radius:50%;
    background:conic-gradient(var(--accent) calc(var(--f,0)*360deg),transparent 0);
    box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--accent) 28%,transparent)}
  .pgd[data-full]{box-shadow:none}
  .p2w{font-family:var(--mono);font-weight:600;font-size:12px;min-width:74px}
  .p2x{font-family:var(--mono);color:var(--muted);font-size:11px}
  .p2r{font-family:var(--mono);font-weight:600;font-size:12px;color:var(--accent-ink)}
  .p2n{font-family:var(--mono);font-size:10px;color:var(--faint)}
  .planrow2 .p2w,.planrow2 .p2r{white-space:nowrap}
  .planrow2.fold{color:var(--faint)}
  .planrow2.fold .p2done{font-family:var(--mono);font-size:11px;flex:0 0 auto}
  .planrow2.fold .p2list{font-family:var(--mono);font-size:11px;color:var(--faint);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto;min-width:0}
  .planrow2.fold .p2show{font-family:var(--mono);font-size:10px;color:var(--muted);flex:0 0 auto}
  .lastsets{display:flex;flex-wrap:wrap;gap:5px 6px;margin-top:6px}
  .lastset{display:flex;align-items:baseline;gap:4px;background:var(--surface);
    border:1px solid var(--line);border-radius:9px;padding:7px 22px 7px 10px;cursor:pointer;
    font-family:var(--mono);color:var(--chalk);box-shadow:var(--shadow)}   /* v3.3.51: denser; right padding still clears the badge (16+3=19 < 22) */
  .lastset:active{border-color:var(--accent);background:var(--accent-soft)}
  .lastset .ls-w{font-weight:700;font-size:13px}
  .lastset .ls-w small{font-size:9px;color:var(--muted);margin-left:1px}
  .lastset .ls-x{color:var(--muted);font-size:10px}
  .lastset .ls-r{font-weight:700;font-size:13px;color:var(--accent)}
  .repcustom{display:flex;gap:8px;margin-top:6px}
  .repcustom input{flex:1;min-width:0;background:var(--surface);border:1px solid var(--line);border-radius:12px;
    color:var(--chalk);font-family:var(--mono);font-size:17px;padding:12px 14px;min-width:0}
  .repcustom input:focus{outline:none;border-color:var(--accent)}
  /* lift */
  .back{background:none;border:0;color:var(--muted);font:inherit;font-size:11px;padding:0 0 12px;cursor:pointer}
  .exhead{font-family:var(--disp);font-weight:600;font-size:18px;letter-spacing:var(--track-num);margin:0 0 3px}
  .lastline{font-family:var(--mono);font-size:11px;color:var(--muted);margin-bottom:2px}
  .lastline b{color:var(--accent);font-weight:500}
  .prline{font-family:var(--mono);font-size:11px;color:var(--chalk);line-height:1.598}
  .prline b{font-weight:700}
  .prline .lbl,.prline .ago{color:var(--muted);font-weight:500}
  .wsel{display:flex;align-items:center;margin:10px 0 8px;
        border:1px solid var(--line);border-radius:12px;overflow:hidden;background:var(--surface)}
  .wsel button{flex:0 0 72px;height:48px;background:var(--surface2);border:0;color:var(--chalk);
    font-family:var(--mono);font-size:19px;cursor:pointer}   /* v3.3.51: 60→48px */
  @keyframes wflash{0%{color:var(--live);transform:scale(1.12)}100%{color:inherit;transform:none}}
  .wsel .val input.wflash{animation:wflash .3s ease}
  .wsel button:active{background:var(--accent-soft)}
  .wsel .val{flex:1;text-align:center;font-family:var(--mono);font-weight:700;font-size:21px}
  .wsel .val input{width:70%;background:none;border:0;color:var(--chalk);text-align:center;
    font-family:var(--mono);font-weight:700;font-size:24px}
  .wsel .val input:focus{outline:none}
  .wsel .unit{font-size:11px;color:var(--muted)}
  /* v3.3.286: THE REP RULER — one continuous scrubber where the tile row was.
     44px notches, centre-snapped; the numbers you actually do stand proud.
     v3.3.287: the band is a sibling ABOVE the scroller (position:absolute in
     .repwrap), never a child of it — the float+sticky version collapsed the
     track. The scroller itself is transparent so the band shows through. */
  .repwrap{position:relative;background:var(--surface2);border-radius:12px;height:74px}
  /* v3.3.293: the numbers fade toward the edges — the depth cue a physical
     dial has, where the far numbers curve away from you.
     Deliberately NOT a mask on .repruler and NOT per-notch opacity in JS.
     A mask on a scrolling element has a long history of killing momentum in
     WebKit, and per-notch opacity would put work straight back into the
     scroll path two releases were spent emptying. This is two static
     overlays painted in the wrapper's own background colour: the scroller
     never knows they exist. */
  .repwrap::before,.repwrap::after{content:'';position:absolute;top:0;bottom:0;
    width:30%;z-index:2;pointer-events:none}
  .repwrap::before{left:0;border-radius:12px 0 0 12px;
    background:linear-gradient(90deg,var(--surface2) 0%,var(--surface2) 12%,transparent 100%)}
  .repwrap::after{right:0;border-radius:0 12px 12px 0;
    background:linear-gradient(270deg,var(--surface2) 0%,var(--surface2) 12%,transparent 100%)}
  .rrband{position:absolute;left:50%;top:6px;transform:translateX(-50%);
    width:52px;height:62px;border-radius:10px;background:var(--surface);
    border:1px solid var(--line);pointer-events:none;z-index:0}
  /* v3.3.289: snap PROXIMITY, not mandatory — mandatory fights momentum and
     arrests a flick at the next notch instead of letting it glide. And no
     scroll-behavior:smooth here: it is for programmatic scrolls, which now
     pass their own behaviour, and leaving it on filters finger scrolling. */
  /* v3.3.291: touch-action:pan-x LOCKS THE AXIS. A drag that starts on the
     ruler may only move it sideways — the page cannot scroll under it, even
     if the finger wanders off the horizontal. The cost is deliberate: you
     can no longer scroll the page by starting your drag on these 74px. */
  .repruler{position:relative;z-index:1;height:74px;background:none;
    touch-action:pan-x;
    /* v3.3.318: MANDATORY again. v3.3.289 loosened this to proximity so a
       flick would carry instead of being arrested at the next notch — but
       proximity also lets the ruler come to rest BETWEEN notches, which is
       what the maker photographed: the band straddling two numbers. A rep is
       a whole number and the ruler must always land on one. The gliding is
       bought back elsewhere (see repTick's velocity gate), not by giving up
       the guarantee. */
    overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;-ms-overflow-style:none;overscroll-behavior-x:contain}
  .repruler::-webkit-scrollbar{display:none}
  .rrtrack{display:flex;flex-wrap:nowrap;align-items:center;height:74px}
  /* v3.3.289: real spacers, because WebKit drops a flex scroller's trailing
     PADDING from the scrollable area and the last notch could never centre. */
  .rrpad{flex:0 0 calc(50% - 22px);height:1px}
  /* the hidden iOS haptic switch: present, togglable, never seen */
  .haptswitch{position:absolute;width:1px;height:1px;opacity:0;
    pointer-events:none;left:-9999px;top:0;overflow:hidden}
  /* v4.6.28: THE NOTCHES SCALE, THEY DO NOT RESIZE. font-size was in the
     transition, so every notch animated a LAYOUT property for 200ms -- during a
     flick the flex track re-measured its forty-odd children continuously, which is
     what "not smooth" was. Each notch is now rendered at its largest size and
     scaled DOWN: the glyph is rasterised at 26px so it stays crisp, and transform
     is composited, so crossing a notch costs no layout at all. Widths are fixed at
     44px and scaling does not affect them, so neighbours never shift and the snap
     points stay exactly where they were. */
  .repruler .rr{flex:0 0 44px;height:74px;scroll-snap-align:center;position:relative;
    background:none;border:0;padding:0;cursor:pointer;color:var(--faint);
    font-family:var(--mono);font-size:23px;font-weight:500;
    /* no will-change here: there are forty-odd notches and promoting each to its
       own layer costs more than the transition saves. */
    transform:scale(.5);transform-origin:50% 50%;
    transition:color var(--dur-quick) var(--settle)}
  .repruler .rr.maj{color:var(--chalk);transform:scale(.654)}   /* reps you actually do */
  .repruler .rr.on{color:var(--chalk);transform:scale(1)}
  @media (prefers-reduced-motion:reduce){.repruler .rr{transition:color var(--dur-quick) var(--settle)}}
  .repruler .rr.maj::after{content:'';position:absolute;left:50%;bottom:13px;
    transform:translateX(-50%);width:14px;height:2px;border-radius:1px;background:var(--line)}
  .repruler .rr.sug::after{content:'';position:absolute;left:50%;bottom:12px;
    transform:translateX(-50%);width:18px;height:3px;border-radius:2px;background:var(--accent)}
  .repgrid{display:grid;grid-template-columns:repeat(8,1fr);gap:4px 5px}   /* v3.3.51: one row of 8 */
  /* v3.3.154: the × is quiet — the number stays the target, the × is the label */
  .repgrid button .rx{color:var(--faint);font-size:10px;margin-right:1px}
  .repgrid button:active .rx{color:#fff}
  .repgrid button{height:40px;background:var(--surface);border:1px solid var(--line);border-radius:9px;
    color:var(--chalk);font-family:var(--mono);font-weight:700;font-size:13px;cursor:pointer;box-shadow:var(--shadow)}
  .repgrid button:active{background:var(--accent);color:#fff;border-color:var(--accent)}
  /* v3.3.141: the suggestion dot. Reps you did last session at this weight,
     marked where you are already looking instead of in a card of their own.
     A dot, not an outline — this is a footnote, not an instruction, and the
     tile has to keep reading as a plain choice among eight. Positioned on
     the tile so the grid never reflows as the marks move with the weight. */
  .repgrid button{position:relative}
  .repgrid button.sug::after{content:'';position:absolute;left:50%;bottom:5px;
    width:4px;height:4px;margin-left:-2px;border-radius:50%;background:var(--accent);opacity:.85}
  .repgrid button.sug:active::after{background:#fff}
  .sets{display:flex;flex-wrap:wrap;gap:6px 7px;margin-top:11px;min-height:4px}
  /* logged = done = quiet: gray fill, muted text, no accent border. Suggested chips
     keep their outline + blue reps, so a glance says which is which (and which one
     deletes on tap). */
  .settile{display:flex;align-items:baseline;gap:6px;padding:9px 11px;border-radius:10px;
    background:var(--surface2);border:1px solid transparent;font-family:var(--mono);font-size:12px;
    cursor:pointer;opacity:.72}
  .settile .w{color:var(--chalk)}
  .settile.saved,.settile.savedpr{opacity:1}
  .settile .w{font-weight:700}
  .settile .w small{font-weight:400;font-size:9px;color:var(--muted);margin-left:1px}   /* v3.3.103: matches .ls-w small — the unit rides WITH its number, no gap */
  .settile .x{color:var(--muted);font-size:10px}
  .settile.pr{border-color:var(--record)}
  .settile.pr .w{color:var(--record)}
  .tot{margin-top:11px;padding-top:12px;border-top:1px dashed var(--line);
    display:flex;justify-content:space-between;font-family:var(--mono);font-size:11px;color:var(--muted)}
  .tot b{color:var(--chalk);font-weight:700}
  .delta.up{color:var(--accent)} .delta.down{color:var(--record)}
  .runrow{display:flex;gap:10px;margin:11px 0}
  .fld{flex:1;background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:10px 12px}
  .fld label{display:block;font-family:var(--mono);font-size:9px;letter-spacing:var(--track-label);
    text-transform:uppercase;color:var(--muted);margin-bottom:3px}
  .fld input{width:100%;background:none;border:0;color:var(--chalk);font-family:var(--mono);font-weight:700;font-size:20px}
  .fld input:focus{outline:none}
  .fld.text input{font-size:12px;font-weight:500;font-family:var(--body)}
  .btn{width:100%;padding:15px;border-radius:12px;border:0;background:var(--accent);color:#fff;
    font:inherit;font-weight:600;font-size:13px;cursor:pointer;margin-top:3px;
    white-space:nowrap}   /* v3.3.279: a button label never wraps, so a button
                             never changes height because of its own text */
  .btn.ghost{font-size:12px;background:none;border:1px solid var(--line);color:var(--muted);font-weight:500}
  /* v4.1.0: Add run and the day's close are one row. Keep this AFTER the
     global .btn rule: the override is visually clearer and jsdom/older WebKit
     no longer report the inherited 100% width even though flex corrected it. */
  .runacts{display:flex;gap:10px;align-items:stretch}
  .runacts .btn{width:auto;margin:0}
  .runacts #addrun{flex:1 1 auto;min-width:0}
  .runacts .runclose{flex:0 1 auto;max-width:33%;min-width:88px;white-space:nowrap}
  /* stats */
  .rhythm .big{font-family:var(--mono);font-size:33px;font-weight:700;line-height:1;letter-spacing:var(--track-num)}
  /* v3.3.86: numbers share a BASELINE, captions share a row — a 2x2 grid is
     the only layout that makes both invisible lines real. */
  .rgrid{display:grid;grid-template-columns:1fr auto;align-items:baseline;column-gap:12px}
  .rgrid .rpct{font-family:var(--mono);font-size:18px;font-weight:700;color:var(--accent);text-align:right}
  .rgrid .rcap{font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:2px}
  .rgrid .rcap.r{text-align:right;font-size:9px}
  .rhythm .big.ok{font-size:18px;color:var(--accent)}
  /* v3.3.106: the day count is the flagship figure, sized and coloured like
     the Stats hero it mirrors. Authored AFTER .big.ok — equal specificity,
     so source order decides (the v3.3.105 lesson, applied deliberately). */
  .rhythm .big.dayn{font-size:25px;color:var(--accent)}   /* v3.3.107: 38→28 — the Stats hero owns a full row, this one shares with the strip */
  .rgrid .rcap .nearms{color:var(--accent);font-weight:700}
  .rhythm .unit{font-family:var(--mono);font-size:11px;color:var(--muted);margin-left:4px}
  /* v3.3.59: the calendar return ticket. Accent, not red — it's navigation,
     not a live signal. Sits above the tab bar + safe area. */
  /* v3.3.65: the app-wide up control. Right-anchored rather than centred so
     it never sits over the content you're reading, and hidden via [hidden]
     so a single boolean drives it. */
  .calreturn{position:fixed;right:14px;
    bottom:calc(var(--bar-bot) + 64px);z-index:60;   /* v4.6.152: 6px above the tab bar, wherever it sits */
    font-family:var(--mono);font-size:12px;letter-spacing:var(--track-label);
    background:var(--accent);color:#fff;border:0;border-radius:999px;
    padding:9px 15px;box-shadow:0 4px 14px rgba(0,0,0,.22);cursor:pointer;
    animation:cardin var(--dur-move) var(--settle) backwards}
  .calreturn[hidden]{display:none}
  .strip{display:flex;gap:3px;margin-top:11px;position:relative}
  /* ---- sheen system (v3.3.27): accent surfaces of meaningful size.
     Excluded by design: anything red/live (signals don't get decorated),
     plain selected chips (state, not invitation), and micro surfaces —
     cal cells, h2 kicker bars, nav bar, fire needle — where motion at
     that size is noise, not light. Groups run desynchronized cycles so
     no two surfaces ever flash together. ---- */
  .chip.on:not(.livego),.btn:not(.ghost):not(.livego),.nudge .nudgego,.ll-bar.save,
  .onbbtn.pri,.vs .vsrow:first-child .bar i,.heatwrap,.mchip.on,
  .partcard.sel:not(.liveP),.cal{position:relative}
  .strip::after,.chip.on:not(.livego)::after,.btn:not(.ghost):not(.livego)::after,
  .nudge .nudgego::after,.ll-bar.save::after,.onbbtn.pri::after,
  .vs .vsrow:first-child .bar i::after,.heatframe::after,.mchip.on::after,
  .partcard.sel:not(.liveP)::after,.cal::after{
    content:'';position:absolute;inset:0;pointer-events:none;border-radius:inherit;
    background:linear-gradient(105deg,transparent 44%,rgba(255,255,255,.35) 50%,transparent 56%);
    background-size:300% 100%;background-position:100% 0;
    animation:sheen 5.4s ease-in-out 1.1s infinite}
  .strip::after{mix-blend-mode:overlay;border-radius:0;animation-duration:4.2s;animation-delay:0s;
    background:linear-gradient(105deg,transparent 44%,rgba(255,255,255,.5) 50%,transparent 56%);
    background-size:300% 100%}
  .chip.on:not(.livego)::after{animation-duration:4.8s;animation-delay:.5s}
  .mchip.on::after{animation-duration:5.2s;animation-delay:.9s}
  .partcard.sel:not(.liveP)::after{animation-duration:5s;animation-delay:.7s}
  .cal::after{mix-blend-mode:overlay;border-radius:0;animation-duration:4.4s;animation-delay:.2s;
    background:linear-gradient(105deg,transparent 44%,rgba(255,255,255,.5) 50%,transparent 56%);
    background-size:300% 100%}
  .heatframe::after{mix-blend-mode:overlay;border-radius:0;animation-duration:4.6s;animation-delay:.3s;
    background:linear-gradient(105deg,transparent 44%,rgba(255,255,255,.5) 50%,transparent 56%);
    background-size:300% 100%}
  .vs .vsrow:first-child .bar i::after{animation-duration:5s;animation-delay:.8s}
  /* v4.6.191: the two grid sheens are seen only through their trained cells
     (js/util.js syncSheenMasks cuts the mask); with no mask yet, nothing shows */
  .cal::after,.heatframe::after{
    -webkit-mask-image:var(--sheen-mask,linear-gradient(transparent,transparent));mask-image:var(--sheen-mask,linear-gradient(transparent,transparent));
    -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
    -webkit-mask-size:var(--sheen-w,100%) var(--sheen-h,100%);mask-size:var(--sheen-w,100%) var(--sheen-h,100%);
    -webkit-mask-position:var(--sheen-x,0px) 0;mask-position:var(--sheen-x,0px) 0}
  .heatframe::after{clip-path:inset(0 0 0 var(--sheen-clip,0px))}
  @keyframes sheen{0%{background-position:100% 0}70%{background-position:0% 0}100%{background-position:0% 0}}
  /* The approved logger keeps its geometry. Light crosses only the plates,
     never the shaft or its arithmetic; the Add-set button retains sheen above. */
  .zone.prime .barviz .pl{position:relative;overflow:hidden}
  .zone.prime .barviz .pl::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
    background:linear-gradient(115deg,transparent 25%,rgba(255,255,255,.65) 50%,transparent 75%);
    background-size:100% 350%;background-position:0 100%;animation:loggerPlateSheen 5.4s ease-in-out 1.1s infinite}
  @keyframes loggerPlateSheen{0%{background-position:0 100%}70%,100%{background-position:0 0}}
  #addrep:active{transform:scale(.988);box-shadow:inset 0 2px 5px #00000020}
  @media(prefers-reduced-motion:reduce){.zone.prime .barviz .pl::after{animation:none;background:none}#addrep:active{transform:none}}
  @media (prefers-reduced-motion:reduce){
    .strip::after,.chip.on:not(.livego)::after,.btn:not(.ghost):not(.livego)::after,
    .nudge .nudgego::after,.ll-bar.save::after,.onbbtn.pri::after,
    .vs .vsrow:first-child .bar i::after,.heatframe::after,.mchip.on::after,
    .partcard.sel:not(.liveP)::after,.cal::after{animation:none;background:none}}
  .strip i{flex:1;height:22px;border-radius:3px;background:var(--surface2);border:1px solid var(--line)}
  .strip i.on{background:var(--accent);border-color:var(--accent)}
  .strip i.now{outline:2px solid var(--chalk);outline-offset:1px}
  /* v3.3.91: rest is signalled by the FRAME. The numbers stay accent —
     recolouring the figures made the card read as a different KIND of data
     rather than the same data on a rest day. Border + today's pending cell
     carry the state; the record keeps its colour. */
  .rhythm.resting{border-color:color-mix(in srgb,var(--rest) 55%,var(--line));
    box-shadow:0 0 0 1px color-mix(in srgb,var(--rest) 22%,transparent),var(--shadow)}
  .rhythm.resting .strip i.pend{border-color:var(--rest);opacity:1}
  .vs{margin-top:11px;padding-top:12px;border-top:1px dashed var(--line)}
  .vsrow{display:flex;align-items:center;gap:8px;margin-bottom:5px}
  .vsrow .y{font-family:var(--mono);font-size:10px;color:var(--muted);flex:0 0 34px}
  .vsrow .bar{flex:1;height:8px;border-radius:4px;background:var(--surface2);overflow:hidden}
  .vsrow .bar i{display:block;height:100%}
  .vsrow .p{font-family:var(--mono);font-size:10px;flex:0 0 32px;text-align:right}
  .kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:6px 8px}
  .kpi.hero{grid-column:1/-1;display:flex;align-items:center;gap:11px 14px;padding:14px 16px 12px}
  .kpi.hero .l{margin-top:0}
  .kpi.hero .d{margin-top:2px}
  .kpi{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:12px 14px;box-shadow:var(--shadow)}
  .kpi .v{font-family:var(--mono);font-weight:700;font-size:21px;line-height:1.15}
  .kpis .kpi.hero .v{font-size:35px;letter-spacing:var(--track-num)}
  .kpi .l{font-family:var(--mono);font-size:9px;letter-spacing:var(--track-label);text-transform:uppercase;color:var(--muted);margin-top:2px}
  .kpi .d{font-family:var(--mono);font-size:9px;margin-top:2px}
  .kpi.accent .v{color:var(--accent)}
  table{width:100%;border-collapse:collapse;font-size:11px}
  td,th{padding:8px 6px;border-bottom:1px solid var(--line);text-align:left}
  th{font-family:var(--mono);font-size:9px;letter-spacing:var(--track-label);text-transform:uppercase;color:var(--muted);font-weight:500}
  td.n{font-family:var(--mono);text-align:right;white-space:nowrap}
  .rec-core td b{font-weight:600;color:var(--chalk)}
  .rec-core td.n b{font-weight:700}
  .rec-other{margin-top:2px}
  .rec-other td{color:var(--muted);font-size:11px;padding:6px}
  .rec-other td.n{font-size:10px}
  .tmove{width:26px;height:26px;margin-left:8px;border-radius:8px;border:1px solid var(--line);
    background:var(--surface2);color:var(--muted);font-size:11px;cursor:pointer;vertical-align:middle}
  .tmove:active{border-color:var(--accent);color:var(--accent)}
  td.n b{color:var(--accent)}
  .heat{display:flex;gap:3px;overflow-x:auto;padding:2px 4px 4px 0}   /* v3.3.46: room for today's outline at the right edge */
  .heat .wk{display:flex;flex-direction:column;gap:3px}
  .heat i{width:12px;height:12px;border-radius:var(--sq);background:var(--surface2);display:block;border:1px solid var(--line)}
  .heat i[data-l="1"]{background:var(--accent-soft);border-color:var(--accent-soft)}
  .heat i[data-l="2"]{background:var(--accent);border-color:var(--accent)}
  .zoom{position:relative;overflow:hidden;border-radius:8px;touch-action:none;cursor:grab}
  .zoom:active{cursor:grabbing}
  .zoom > svg{display:block}
  @keyframes beacon{0%,100%{r:3.2;opacity:1}50%{r:5.2;opacity:.35}}
  .beacon{animation:beacon 1.8s ease-in-out infinite}

  /* ---------- motion pass (v2.07) — every rule below is decoration-free:
     it either confirms an action or shows where a number came from.
     All of it disappears under prefers-reduced-motion. ---------- */
  @media (prefers-reduced-motion:no-preference){
    /* 2. tap feedback — a fast press-in on anything tappable */
    button,.settile,.partcard,.item,.logmain{
      transition:transform .13s cubic-bezier(.2,.9,.3,1.4)}
    button:active,.settile:active,.partcard:active,.item:active,.logmain:active{
      transform:scale(.955)}
    nav button:active{transform:scale(.9)}

    /* 5b. below-fold blocks float up as they scroll into view */
    #view .float-pre{opacity:0;transform:translateY(16px);animation:none!important}
    #view .float-in{opacity:1;transform:none;
      transition:opacity .5s var(--settle),transform .5s var(--settle)}

    /* 5. cards + headers rise in with a stagger (JS sets --i per element) */
    #view>.card,#view>h2,#view>.kpis,#view>table{
      animation:rise var(--dur-arrive) var(--settle) both;
      animation-delay:calc(var(--i,0)*40ms)}
    /* v3.3.492: except on an in-place repaint, where the screen has not been
       arrived at -- only part of its content changed. The rise is an ENTRANCE;
       replaying it for every card because one pill was tapped is the flicker. */
    #view.norise>.card,#view.norise>h2,#view.norise>.kpis,#view.norise>table{
      animation:none}

    /* 3. the set you just logged springs in; a PR gets one red pulse */
    .settile.saved{animation:springin .38s cubic-bezier(.3,1.4,.4,1) both}
    .settile.savedpr{animation:springin .38s cubic-bezier(.3,1.4,.4,1) both,
      prflash 0.9s ease-out .15s 1}

    /* 6. charts draw themselves — lines sweep, bars grow from the baseline */
    svg polyline.draw{stroke-dasharray:var(--len);stroke-dashoffset:var(--len);
      animation:sweep .8s var(--settle) forwards;animation-delay:calc(var(--i,0)*60ms + .15s)}
    svg rect.gbar{transform-box:fill-box;transform-origin:center bottom;
      animation:grow .45s var(--settle) both;animation-delay:calc(var(--i,0)*22ms)}
    .beacon{animation:beacon 1.8s ease-in-out infinite}
  }
  @keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
  @keyframes springin{from{opacity:0;transform:scale(.6)}to{opacity:1;transform:none}}
  @keyframes prflash{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--record) 55%,transparent)}
    100%{box-shadow:0 0 0 14px transparent}}
  @keyframes sweep{to{stroke-dashoffset:0}}
  /* v3.3.57: exercise cards ARRIVE when a part is chosen — a 7px rise with a
     40ms stagger, once. fill-mode:backwards, never both: a finished card must
     leave no transform and no stacking context (the v3.3.16 lesson). */
  .item.logrow.enter{animation:cardin var(--dur-arrive) var(--settle) backwards;
    animation-delay:calc(var(--i,0)*45ms)}
  /* v3.3.64: 7px was invisible on a single-card list. A 14px rise with a
     slight scale reads as arrival without becoming a bounce. */
  @keyframes cardin{from{opacity:0;transform:translateY(14px) scale(.985)}}
  @keyframes grow{from{transform:scaleY(.02)}to{transform:none}}

  /* 1. tab switching — View Transitions API (iOS 18+); a quiet 3px drift + fade */
  /* v4.6.346: ONE CURVE FOR BOTH HALVES. The old page left in .16s and the new one
     arrived over .42s, both on an ease-out -- so for a beat both were mostly
     transparent and the screen washed out pale between tabs. Same duration and
     curve for both (the arrival voice) keeps their opacities summing to one: a true cross-fade. */
  ::view-transition-old(root){animation:vtout var(--dur-arrive) var(--settle) both}
  ::view-transition-new(root){animation:vtin var(--dur-arrive) var(--settle) both}
  @keyframes vtout{to{opacity:0}}
  @keyframes vtin{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
  /* v3.3.442: THE CHROME DOES NOT RIDE THE ROOT. Without a name of their own,
     the header and the tab bar are pixels of `root`, so every tab switch
     dropped them 6px and dipped them through transparency with the view --
     the maker saw the header flicker on every tab. Naming them makes each its
     own transition group with the UA default: old fades out, new fades in,
     blended plus-lighter, so IDENTICAL pixels are invisible and only real
     change (the wash arriving on rest, the nav's active mark moving) shows.
     The view keeps its drift; the chrome stays where chrome belongs. */
  /* A named ancestor forms a backdrop root: naming header itself traps the
     glass against an empty backdrop, rendering transparency without blur.
     Name its paint/control siblings instead, preserving stationary chrome. */
  header{view-transition-name:none}
  header .hglass{view-transition-name:showup-header}
  header>.brandrow{view-transition-name:showup-header-title}
  header>.hbtns{view-transition-name:showup-header-controls}
  nav{view-transition-name:showup-nav}
  ::view-transition-old(showup-header),::view-transition-new(showup-header),
  ::view-transition-old(showup-header-title),::view-transition-new(showup-header-title),
  ::view-transition-old(showup-header-controls),::view-transition-new(showup-header-controls),
  ::view-transition-old(showup-nav),::view-transition-new(showup-nav){animation-duration:var(--dur-quick)}
  @media (prefers-reduced-motion:reduce){
    ::view-transition-old(root),::view-transition-new(root),
    ::view-transition-old(showup-header),::view-transition-new(showup-header),
    ::view-transition-old(showup-header-title),::view-transition-new(showup-header-title),
    ::view-transition-old(showup-header-controls),::view-transition-new(showup-header-controls),
    ::view-transition-old(showup-nav),::view-transition-new(showup-nav){animation:none}}


  /* ---------- pull to refresh (v2.07.2) ---------- */
  /* Top overscroll belongs to pull-to-refresh; the bottom keeps its spring.
     'contain' stops the scroll chaining to the page behind without killing bounce. */
  html{overscroll-behavior-y:contain}
  /* v3.3.309: the transition follows the transform onto #view. Left on body
     it would animate nothing, since body no longer moves. */
  body.banding #view{transition:none}
  body.bandback #view{transition:transform .6s cubic-bezier(.3,1.6,.5,1)}  /* v4.6.121: ~11% overshoot past rest, then settle (was ~0%) */
  body.pulling #view{transition:none}
  body.settling #view{transition:transform .28s var(--settle)}
  #swipehint{position:fixed;top:50%;z-index:55;pointer-events:none;
    transform:translateY(-50%);opacity:0;transition:opacity .12s}
  #swipehint.on{opacity:1}
  #swipehint span{display:block;font-size:84px;line-height:1;font-weight:200;
    color:color-mix(in srgb,var(--chalk) 22%,transparent)}
  #swipehint.l{left:14px} #swipehint.r{right:14px}
  #ptr{position:fixed;left:50%;top:calc(env(safe-area-inset-top,0px) + 6px);z-index:60;
    width:36px;height:36px;margin-left:-18px;border-radius:50%;
    background:var(--surface);border:1px solid var(--line);
    box-shadow:0 3px 12px rgba(0,0,0,.28);
    display:flex;align-items:center;justify-content:center;
    transform:translateY(calc(-58px - env(safe-area-inset-top,0px)));pointer-events:none}
  #ptr svg{width:16px;height:16px;stroke:var(--muted);transition:transform var(--dur-quick) var(--settle),stroke var(--dur-quick) var(--settle)}
  #ptr.arm svg{transform:rotate(180deg);stroke:var(--accent)}
  #ptr.spin svg{animation:ptrspin .7s linear infinite;stroke:var(--accent)}
  @keyframes ptrspin{to{transform:rotate(360deg)}}

  @keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
  .heat i.today{outline:2px solid var(--chalk);outline-offset:1px;animation:pulse 1.6s ease-in-out infinite}
  .strip i.now{animation:pulse 1.6s ease-in-out infinite}
  @media (prefers-reduced-motion:reduce){.beacon,.heat i.today,.strip i.now{animation:none}}
  /* v3.3.129: the hint sits ABOVE the plot, not on top of it. Inside the
     box it overlapped chart furniture (the days-elapsed number on Days by
     month worst of all), and it doubles as the scrub date readout — same
     argument that moved the legend up in v3.3.109. It is a SIBLING of
     .zoom now, so app.js looks for it on the parent card, not the box. */
  .zoomhint{font-family:var(--mono);font-size:9px;color:var(--muted);
    pointer-events:none;opacity:.75;text-align:right;margin:0 2px 4px;
    min-height:11px;line-height:11px}
  /* v3.3.109: WRAPS instead of scrolling — a horizontal scroller silently
     hid years (the current one worst of all, since it sorts last), which is
     unusable when the legend doubles as the scrub readout. Sits above the
     chart now, so a scrubbing hand never covers it. */
  /* v3.3.116: the part-mix chart scrolls sideways and loads older weeks as
     you reach its left edge, so it owns its horizontal gesture. */
  /* v3.3.120: a fixed y-axis beside a scrolling plot, so the scale stays
     readable however far back you are. */
  .pmixbox{position:relative;display:flex;align-items:flex-start;gap:2px}
  .pmixaxis{flex:0 0 auto}
  .pmixwrap{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
    touch-action:pan-x;scroll-behavior:smooth;flex:1 1 auto}
  .pmixwrap > svg{display:block}
  /* jump back to today. Fades rather than pops, and is out of the way of a
     scrolling thumb. */
  .pmixnow{position:absolute;right:6px;bottom:26px;z-index:2;
    width:30px;height:30px;border-radius:50%;border:0;cursor:pointer;
    background:var(--accent);color:#fff;font-family:var(--mono);font-size:12px;
    line-height:28px;text-align:center;padding:0;box-shadow:var(--shadow);
    opacity:0;pointer-events:none;transform:translateX(6px);
    transition:opacity var(--dur-move) var(--settle),transform var(--dur-move) var(--settle)}
  .pmixnow.on{opacity:1;pointer-events:auto;transform:translateX(0)}
  /* v3.3.228: the legend is chart furniture, not a cloud of seven buttons.
     The controls keep 44px transparent hit areas; only the small swatch and
     selected underline are visible. Four stable columns yield a quiet 4+3
     rhythm without hiding any body part behind horizontal scrolling. */
  /* v3.3.277: the sets/weight switch — quiet, mono, right-aligned; the
     active reading in chalk, the other a tap away in muted. */
  /* v3.3.278: today's plan — a note you brought, styled as quietly as one. */
  /* v3.3.297: with no plan yet the pill is an outline, not a fill — the
     section is named without claiming there is something in it. */
  /* v3.3.492: THE UNSELECTED PILL HAS TO BE VISIBLE TO BE A CONTROL. The
     ring was 1px of --line, which is the hairline this app draws BETWEEN
     things -- #E9EAEF on white. As a divider it is right; as the edge of a
     tappable pill it vanished, and the maker could not see the scope he was
     not in. The ring is now mixed from --faint (the ink it carries) so it
     tracks the theme, and the label steps up from --faint to --muted. It is
     still an outline against a fill: the selected pill is the loud one. */
  .scopepill.off{background:none;color:var(--muted);
    box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--faint) 55%,transparent)}
  /* v3.3.282: the plan's management actions ride the heading's right edge —
     margin-left:auto, so they find the corner however long the title runs.
     Quiet mono text, not buttons: they manage the card, they are not part of
     the session it holds. The (i) keeps its v3.3.115 place beside the title. */
  h2 .planedge{margin-left:auto;display:inline-flex;align-items:center;gap:12px;flex:0 0 auto}
  .pedge{background:none;border:0;padding:2px 0;cursor:pointer;
    font-family:var(--mono);font-size:10px;font-weight:500;
    letter-spacing:var(--track-label);color:var(--muted);text-transform:uppercase}
  .pedge:last-child{color:var(--faint)}
  /* the fold chevron leads the group and is a glyph, not a word */
  .pedge.pfold{font-size:11px;color:var(--muted);text-transform:none;padding:2px 2px}
  /* v3.3.400: the writer's door -- the one accent glyph on the line */
  .pedge.pwrite{display:inline-flex;align-items:center;gap:5px;color:var(--accent-ink)}
  .pedge.pwrite .ic{display:block}
  .chips{display:flex;flex-wrap:wrap;gap:5px 6px}
  .chips .chip{margin:0}
  .writecard .seg{display:flex}
  .writecard .seg button{display:flex;flex-direction:column;align-items:center;line-height:1.2;padding:6px 0}
  .writecard .seg button small{font-size:9px;letter-spacing:.04em;opacity:.75}
  .writenext{margin-top:8px;padding:10px 12px}
  /* v3.3.406: the wait is a receipt -- the eight weeks leaving the device,
     drawn as the square that means a day, lighting up as they are read. The
     sparkle breathes on the settle curve; nothing else moves. */
  .writing{min-height:70vh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:21px 26px;padding:24px 0}
  .writing .wspark{color:var(--accent-ink);animation:wbreathe 1.6s var(--settle) infinite alternate}
  .writing .wspark .ic{display:block}
  .writing .wgrid{display:grid;grid-template-columns:repeat(7,16px);gap:4px 5px}
  .writing .wsq{display:block;width:16px;height:16px;border-radius:var(--sq);background:var(--surface2);
    animation:wread var(--dur-arrive) var(--settle) calc(var(--i) * 55ms) both,
              wwave 2.4s var(--settle) calc(3.4s + var(--i) * 40ms) infinite alternate}
  .writing .wline{font-size:11px;color:var(--muted);letter-spacing:.02em;min-height:1.5em;text-align:center}
  .writing .btn.ghost{width:auto;padding:10px 22px;margin-top:5px}
  @keyframes wread{from{background:var(--surface2);transform:scale(.6)}to{background:var(--accent);transform:none}}
  @keyframes wbreathe{from{transform:scale(1);opacity:.82}to{transform:scale(1.1);opacity:1}}
  /* once every week is lit, a slow read passes over them, row by row */
  @keyframes wwave{from{opacity:.55}to{opacity:1}}
  @media (prefers-reduced-motion:reduce){.writing .wsq{animation:none;background:var(--accent)}.writing .wspark{animation:none}}
  /* v3.3.398: the edge speaks in glyphs. Each is an inline SVG on
     currentColor at the edge's own ink, 17px so it survives a phone at arm's
     length (rev 3 of the spec drew them at 14 and they vanished). A pill is
     a button now, because the week scope made it one. */
  .pedge.pglyph{display:inline-flex;align-items:center;padding:2px 1px;color:var(--muted)}
  .pedge.pglyph:last-child{color:var(--faint)}
  .pedge.pglyph .ic{display:block}
  button.scopepill{border:0;cursor:pointer;line-height:inherit}
  .rangeline{font-size:10px;letter-spacing:.04em;padding:2px 14px 8px;color:var(--faint)}
  /* one card per day, the ground between them: a day has edges */
  .weekstack{display:flex;flex-direction:column;gap:5px 6px}
  /* :root[data-skin] rules restyle every .card at higher specificity, so the
     day card and its body are pinned under :root too, for every skin */
  :root .card.daycard{padding:0;overflow:hidden}
  .daycard.past{filter:opacity(.45)}
  /* the day's rows are the day card's body, not a card inside a card */
  :root .daycard .card.plancard{box-shadow:none;background:none;border:0;margin:0;padding:0 0 2px;border-radius:0}
  /* v3.3.422: TIGHTER, AND ONE LINE. The head was 12px of padding around a
     label that wrapped to two right-aligned lines, then 6px more, then the
     rows' own padding -- the maker circled every one of those gaps. The label
     is one line that truncates; the head is 9px; an open head gives its
     bottom padding to the rows beneath. */
  .dayhead{all:unset;box-sizing:border-box;display:flex;width:100%;justify-content:space-between;align-items:center;gap:12px;
    padding:9px 14px;cursor:pointer;font-size:10px;letter-spacing:var(--track-label);font-weight:600;color:var(--muted)}
  .daycard.open .dayhead{padding-bottom:2px}
  .daycard.today .dayhead .dn{color:var(--chalk)}
  .dayhead .dn{white-space:nowrap;text-transform:uppercase}
  .dayhead .dt{display:inline-flex;align-items:center;gap:8px;letter-spacing:0;font-weight:500;color:var(--muted);min-width:0;flex:1 1 auto;justify-content:flex-end}
  .dayhead .dt .dtl{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;font-size:9px;color:var(--faint)}
  .dayhead .dt .ic{flex:none;color:var(--faint)}
  /* v3.3.491: the chevron sits in a .pfchev span now so the turn can be a
     class that TRANSITIONS (shared with the day fold) instead of a rotation
     baked into the icon's own style. It must not shrink beside the truncating
     label, and it carries the ink the bare .ic used to be given. */
  .dayhead .dt .pfchev{flex:none;color:var(--faint)}
  /* the open head gives its bottom padding to the rows; a shut one keeps it.
     v3.3.491: this used to arrive with a repaint, so it was never seen to
     move. It changes on the same tap as the fold now, so it eases with it. */
  .daycard .dayhead{transition:padding-bottom var(--dur-arrive) var(--settle)}
  @media (prefers-reduced-motion:reduce){ .daycard .dayhead{transition:none} }
  .planpv.day .pb b{font-size:11px;letter-spacing:var(--track-label);text-transform:uppercase;color:var(--muted)}
  .plancard{padding:6px 0}
  /* v3.3.281: the plan is reference material you glance at between sets, not
     a headline — it reads one step down from the ledger's own type. */
  /* v3.3.292: names start at a common left edge. space-between with THREE
     children parked the tick left and the numbers right and left the name
     floating in whatever space remained — so every exercise began at a
     different x and the column read as ragged. The name now takes the free
     space (flex:1) and the numbers are pushed right by margin-left:auto, so
     the left edge is fixed by the tick column and nothing else. */
  /* v3.3.313: the row's own padding is the ONLY left indent (.plancard has
     none), so 14px was the whole gap between the card edge and a name. Down
     to 10. On the right the padding stacked with the tick column — 14 + 12 +
     8 = 34px — which is what made the numbers look stranded. The row's right
     padding drops to 4 so the tick's own column supplies the margin, and the
     numbers land 4 + 9 + 5 = 18px in against the name's 10. */
  /* v3.3.328 (maker's pick, option C of four rendered): the divider drops
     below --line into --whisper and INSETS to the text edge — it starts and
     stops at the same 10px the names and numbers live on, so the line
     belongs to the content, not the card frame. Full-bleed at --line was
     table chrome: it sliced the card into cells. A border-bottom cannot
     inset (it spans the padding box), hence the pseudo-element. */
  .planrow{display:flex;width:100%;justify-content:flex-start;align-items:center;
    gap:10px;background:none;border:0;position:relative;
    padding:7px 10px;cursor:pointer;text-align:left;color:var(--chalk);font:inherit}
  .planrow::after{content:"";position:absolute;left:10px;right:10px;bottom:0;
    height:1px;background:var(--whisper)}
  .planrow:last-of-type::after{display:none}
  .planrow .pn{flex:1;min-width:0;text-align:left;font-size:11px;line-height:1.269}
  /* v3.3.295: the weights are a THREE-COLUMN grid, so the \u00d7 lands in the
     same place on every line and the weights right-align to each other.
     (It claimed "within a row and between rows"; only the first was ever
     true, and v3.3.324 below is what makes the second one so.)
     Ink follows the app's own three tiers,
     which is what makes the number findable mid-set: the weight in --chalk,
     the \u00d7 in --faint so it recedes to a separator, the reps in --muted.
     .planrow centres its name against the stack instead of pinning it to the
     first line, which is what left dead space under a two-weight name. */
  /* v3.3.324: the columns are sized by the CARD, not by the row -- --planw
     and --planr come from js/lift.js, measured once across every line in the
     plan. Row-local `auto` columns were what let the \00d7 wander: each row
     sized to its own longest weight, so five rows put the \00d7 at five
     different x. With both outer columns fixed, every row's number block is
     the same width, margin-left:auto lands them all on the same right edge,
     and the \00d7 column falls on one line down the card. mono + the same
     font-size as .pv is what makes `ch` exact rather than approximate. */
  .planrow .pl{flex:0 0 auto;margin-left:auto;display:grid;
    font-family:var(--mono);font-size:9px;
    grid-template-columns:var(--planw,auto) 11px var(--planr,auto);
    column-gap:4px;row-gap:2px;align-items:baseline}
  .planrow .pv{font-size:9px;white-space:nowrap;line-height:1.316}
  .planrow .pw{text-align:right;color:var(--chalk);font-weight:500}
  .planrow .px{text-align:center;color:var(--faint);font-size:9px;line-height:1.316}
  .planrow .pr{text-align:left;color:var(--muted);font-size:9px;
    white-space:nowrap;line-height:1.316}
  /* v3.3.320: in LIGHT the reps drop one tier. Measured, light already
     separates further than dark on paper — chalk to muted is 25.1 in
     perceptual L* against dark's 18.7 — but dark greys compress against a
     white page, so the same arithmetic gap reads as one weight of ink. The
     reps take --faint (L* gap 35.1, still 5.41:1 on white, well clear of
     AA), and the × steps back from them so the tier order survives. No new
     colour: both are tokens this palette already owns. */
  :root[data-theme="light"] .planrow .pr{color:var(--faint)}
  /* v3.3.349: a numeral already in today's record RECEDES. Not struck
     through -- a strike means cancelled or wrong nearly everywhere else, and
     neither is true of a set you just did. Not chipped either: four filled
     chips a line turns a quiet plan into a scoreboard, which is the thing
     this card is built not to be. Dimming is the app's existing word for
     SPENT, the same word .fut speaks in the heatmap.
     No new colour: the rep tier blended toward the card it sits on, the same
     technique the .px override two lines up already uses. And nothing marks
     the numerals still to come -- they simply stay lit. An untouched line
     looks exactly as it did before this release, which is the point: a plan
     you have not started is not a plan you are behind on. */
  .planrow .rp{display:inline}
  .planrow .rp.rspent{color:color-mix(in srgb,var(--muted) 38%,var(--surface))}
  :root[data-theme="light"] .planrow .rp.rspent{color:color-mix(in srgb,var(--faint) 42%,var(--surface))}
  :root[data-theme="light"] .planrow .px{color:color-mix(in srgb,var(--faint) 55%,var(--surface))}
  /* a row whose exercise is in today's ledger. The tick is a fact FROM the
     record, not a score against the plan: it says "this is logged", never
     "you are N% through". Same grammar as the Last time card's checkmark. */
  /* v3.3.312: the tick sits at the END of the row, not the start. Leading it
     reserved a 12px column on every row, so every exercise name began indented
     from the card's edge whether or not it had a tick to show — an indent
     paying rent for a mark most rows do not carry. At the end it still holds
     a fixed column, so ticked and unticked rows keep their numbers aligned,
     but nothing is pushed off the left edge. */
  /* v3.3.324: the tick reads as part of the exercise -- "Deadlift \2713" --
     rather than as a column at the far edge. v3.3.312 moved it from the head
     of the row to the tail for a real reason (leading it indented every name,
     ticked or not), but at the tail it reserved a column on the RIGHT instead,
     and that column plus the row's padding is what held the numbers 34px off
     the card edge. Beside the name it reserves nothing: an empty tick is an
     empty inline box, and the numbers now run to the same 10px the name
     starts from. Alignment between rows no longer depends on it -- the number
     block is fixed-width in its own right (--planw/--planr). */
  .planrow .pk{color:var(--accent);font-size:11px;font-family:var(--mono);
    font-style:normal;margin-left:5px}
  .planrow.pdone .pn{color:var(--muted)}
  .planrow.pdone .pw{color:var(--muted)}
  .planrow.pdone .pr,.planrow.pdone .px{color:var(--faint)}
    /* v3.3.321: a "label <gap> value" note line becomes two columns so the
     value sits at the card's right edge, with the rest of the note still
     verbatim. */
  .pnrow{display:flex;align-items:baseline;justify-content:space-between;gap:12px}
  .pnrow .pnval{flex:0 0 auto;text-align:right;white-space:nowrap}
  .pnline{white-space:pre-wrap}
.plannote{white-space:pre-wrap;font-size:9px;color:var(--faint);
    /* v3.3.313: same left edge as the rows, or the card has two margins */
    padding:8px 10px;position:relative;line-height:1.41}
  .plannote::before{content:"";position:absolute;left:10px;right:10px;top:0;
    height:1px;background:var(--whisper)} /* v3.3.328: same inset whisper */
  /* v3.3.279: plan actions sit on the app's own button grammar — full-width
     rows, uniform height. flex:1 made three unequal labels share the width,
     so the longest wrapped to three lines and the row read as three
     different-sized buttons. A grid gives identical cells instead; the
     primary spans the row above its two secondaries. */
  .planacts{display:grid;grid-template-columns:1fr 1fr;gap:6px 8px;padding:10px 14px 4px}
  .planacts .btn{width:100%;margin-top:0}
  .planacts .wide{grid-column:1/-1}
  /* v3.3.443: the PASTE screen's actions are one row, Read it leading at
     twice the width; nothing stacks and nothing fills the card. The preview
     keeps the grid above: its primary label ("Use this for Sat 9/5") is too
     long to share a row and earned its full span in v3.3.279. */
  .planacts.row{display:flex;flex-wrap:wrap}
  .planacts.row .btn{flex:1 1 0;min-width:0;width:auto;padding-left:8px;padding-right:8px}
  .planacts.row .btn:first-child{flex:2 1 0}
  /* v3.3.494: the third door takes a line of its own rather than squeezing
     into the pair. Three across at this width left "Edit as text" in about
     ninety pixels -- the label would wrap or clip, and a door you cannot read
     is the problem this move was meant to solve. The 8px gap is the row gap
     too, so wrapping needs no margin. */
  .planacts.row .btn.plantext{flex:1 0 100%}
  /* the tool row sits flush over the box, quiet, right where the thumb is */
  .plantools{display:flex;gap:8px;padding:0 14px 8px}
  .plantools .chip{font-size:11px;padding:6px 12px}
  /* 16px is the floor under which iOS zooms the page into a focused field */
  /* v3.3.451: the box GROWS with its text and never scrolls inside itself.
     A nested scroll region on a phone is two gestures competing for one
     thumb; the page already scrolls well, so the box becomes as tall as the
     plan and the page does the moving. min-height keeps an empty box
     inviting; overflow hidden + resize none so nothing else fights it. */
  .planta{width:100%;box-sizing:border-box;font-family:var(--mono);font-size:16px;
    line-height:1.504;background:var(--surface2);color:var(--chalk);
    border:1px solid var(--line);border-radius:10px;padding:12px;resize:none;
    overflow:hidden;min-height:11em}
  .planpv{display:flex;align-items:flex-start;gap:10px;padding:10px 14px;
    border-bottom:1px solid var(--line)}
  .planpv:last-of-type{border-bottom:0}
  .planpv .pi{font-family:var(--mono);font-size:11px;width:14px;flex:none;color:var(--muted)}
  .planpv.ok .pi{color:var(--accent)}
  .planpv .pb{display:flex;flex-direction:column;gap:2px 2px;flex:1;min-width:0}
  .planpv .pb b{font-size:12px;font-weight:600}
  .planpv .pb i{font-style:normal;font-size:10px;color:var(--muted)}
  .planpv .pb i.dim{color:var(--faint)}
  .planpv .pc{display:flex;flex-wrap:wrap;gap:5px 6px;justify-content:flex-end}
  /* v3.3.396: candidates go UNDER the name, never beside it. .pb is min-width:0
     so it could shrink to nothing while two long "did you mean" buttons took
     the row -- "Lat Pulldown (wide grip)" rendered one letter per line under
     its own buttons. The ask row wraps and the candidate strip takes the full
     width on its own line, indented to the name. jsdom has no layout: the
     rule is asserted at source in buildcheck. */
  .planpv.ask{flex-wrap:wrap}
  /* v3.3.399: the read-back's new marks. A grip you can hold; NEW and \u2248 in
     accent ink -- the same ink the tick uses, because they are facts about
     the record (nothing in eight weeks; a number nobody has lifted). */
  .pgrip{background:none;border:0;padding:4px 6px 0 0;color:var(--faint);cursor:grab;touch-action:none;flex:none;display:inline-flex}
  .pgrip .ic{display:block}
  .planpv.lifting{position:relative;z-index:2;background:var(--surface2);border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.4),0 0 0 1px var(--line)}
  .planpv.lifting .pgrip{color:var(--accent-ink)}
  .ptag{font-family:var(--mono);font-size:9px;letter-spacing:.08em;color:var(--accent-ink);margin-left:6px;font-style:normal;font-weight:600}
  .planpv .pb i.pest,.pest{color:var(--accent-ink)}
  .planreason{padding:2px 0 10px;margin-bottom:6px;border-bottom:1px solid var(--whisper)}
  .plannotes{padding:10px 14px 2px;border-top:1px solid var(--whisper)}
  .plannotes .mono{font-size:10px;line-height:1.41;color:var(--faint)}
  .planreason .mono{font-size:11px;line-height:1.41}
  .planpv.ask .pc{flex-basis:100%;justify-content:flex-start;padding-left:24px}
  .btn.tiny{font-size:10px;padding:5px 9px;min-height:0}
  .pmixhead{display:flex;justify-content:flex-end;margin-bottom:2px}
  .pmix-measures{position:relative;isolation:isolate;display:inline-grid;grid-template-columns:1fr 1fr;gap:2px 2px;padding:2px;border-radius:10px;background:var(--surface2);flex-shrink:0}
  .pmix-measures::before{content:'';position:absolute;z-index:-1;left:2px;top:2px;bottom:2px;width:calc((100% - 6px)/2);border-radius:8px;background:var(--surface);box-shadow:0 1px 4px #00000012;pointer-events:none;transition:transform .22s var(--settle)}
  .pmix-measures:has([data-pmixmode="weight"][aria-pressed="true"])::before{transform:translateX(calc(100% + 2px))}
  .pmix-measures button{position:relative;min-height:32px;height:32px;min-width:64px;padding:0 13px;border:0;border-radius:8px;background:transparent;color:var(--muted);font:500 11px var(--body);cursor:pointer;transition:color .22s}
  .pmix-measures button::after{content:'';position:absolute;inset:-6px 0}
  .pmix-measures button[aria-pressed="true"]{background:transparent;color:var(--accent-ink);box-shadow:none}
  @media(prefers-reduced-motion:reduce){.pmix-measures button,.pmix-measures::before{transition:none}}
  .pmixmode{background:none;border:0;padding:2px 4px;cursor:pointer;
    font-family:var(--mono);font-size:10px;letter-spacing:.04em;color:var(--faint)}
  .pmixmode span{color:var(--faint)}
  .pmixmode span.on{color:var(--chalk);font-weight:700}
  .pmixmode span+span::before{content:'/';color:var(--faint);font-weight:400;padding:0 4px}
  /* v3.3.306: the legend is ONE line — a colour bar over each name, echoing
     the stacked bars it explains. It was a 4x2 grid of 11px squares and 14px
     labels, ~110px tall, louder than the chart and holed on the second row
     where the eighth cell would be. Seven parts do not fit 340px, so it
     scrolls sideways, and the wrapper fades its right edge so a clipped name
     reads as "there is more" rather than as broken. */
  .pmixlgdwrap{position:relative;margin:0 0 12px;padding:0 0 12px;
    border-bottom:1px solid var(--line)}
  .pmixlgdwrap::after{content:'';position:absolute;right:0;top:0;bottom:12px;width:34px;
    pointer-events:none;background:linear-gradient(270deg,var(--surface) 0%,var(--surface) 18%,transparent 100%)}
  /* v4.5.3: ONE LINE, scrolled. The legend was already scrollable but its
     items were free to wrap, so eight parts became two rows and the second
     row stole height from the chart below it. flex-wrap:nowrap and a fixed
     basis on each item keep it to a single line whatever the part count. */
  .pmixlgd{display:flex;flex-wrap:nowrap;gap:11px 14px;overflow-x:auto;overflow-y:hidden;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none;
    overscroll-behavior-x:contain}
  .pmixlgd>*{flex:0 0 auto;white-space:nowrap}
  .pmixlgd::-webkit-scrollbar{display:none}
  /* v3.3.123: the year rides just outside the plot, top-left, and swaps as
     you scroll past a boundary — so a deep scroll always says where it is. */
  .pmixyr{position:absolute;left:0;top:-2px;z-index:2;
    font-family:var(--mono);font-size:8px;font-weight:700;color:var(--muted);
    background:var(--surface);padding:0 4px;border-radius:3px;pointer-events:none}
  /* v3.3.231: the caption reserves TWO lines whether it needs them or not.
     Unfocused it reads "All strength · 15,708 completed sets across 938
     sessions · 16.7 avg" and wraps; focused on a part it fits one line — so
     tapping a legend chip resized the whole card and shoved everything below
     it up the screen. The height is derived from the same font-size and
     line-height the text uses, so the three can never drift apart. */
  .pmixsum{--sum-fs:9px;--sum-lh:1.5;
    text-align:center;font-family:var(--mono);font-size:var(--sum-fs);
    color:var(--muted);margin:6px 0 0;line-height:var(--sum-lh);
    min-height:calc(var(--sum-fs) * var(--sum-lh) * 2);
    /* block, NOT flex: the focused caption contains a <b> for the part name,
       and a flex container would make that its own item beside the rest of
       the sentence instead of letting it flow inline. */
    text-wrap:balance}
  .pmixsum b{color:var(--chalk)}
  .pmixsum b.up{color:var(--accent)}
  .pmixsum b.dn{color:var(--record)}
  .pmixcol{fill:transparent;cursor:pointer}
  /* v3.3.126: the newest column breathes, so "where is today" needs no
     scrolling to answer. It pulses the COLUMN, not the bars — an animation
     on the bars would override the inline opacity that focus-dimming sets,
     and the two would fight. */
  .pmixcol.latest{fill:var(--accent);animation:pmixlatest 2.4s ease-in-out infinite}
  .pmixcol.latest.live{fill:var(--live)}
  .pmixbricks{shape-rendering:crispEdges}
  .pmixseg.latest{transform-box:fill-box;transform-origin:center bottom;
    animation:pmixrise var(--dur-arrive) var(--settle) both}
  @keyframes pmixlatest{0%,100%{opacity:.13}50%{opacity:.04}}
  @keyframes pmixrise{from{transform:scaleY(0)}to{transform:scaleY(1)}}
  @media (prefers-reduced-motion:reduce){
    .pmixcol.latest,.pmixseg.latest{animation:none}.pmixcol.latest{opacity:.10}}
  /* v3.3.121: tap a name to isolate it. A single-hue ramp cannot be matched
     to a legend by eye — that is the cost of the ramp, and this is the
     answer to it. */
  /* v3.3.229 kept two honest 44px tap ROWS; on one line the tap target is the
     whole column instead — bar, label and the space around them. */
  .pmixlgd button{--pmix-part:var(--muted);flex:0 0 auto;display:flex;flex-direction:column;
    align-items:stretch;gap:4px 5px;min-height:44px;justify-content:center;
    padding:0;border:0;background:transparent;
    color:var(--muted);font-family:var(--mono);font-size:9px;letter-spacing:.02em;
    cursor:pointer;transition:opacity var(--dur-quick) var(--settle),color var(--dur-quick) var(--settle)}
  /* the swatch IS a bar, the same shape as the segments it names */
  .pmixlgd button i{display:block;height:3px;border-radius:2px;background:var(--pmix-part);
    opacity:.75;transition:opacity var(--dur-quick) var(--settle),height var(--dur-quick) var(--settle)}
  .pmixlgd button span{white-space:nowrap}
  .pmixlgd button.on{color:var(--chalk);font-weight:700}
  .pmixlgd button.on i{opacity:1;height:4px}
  .pmixlgd button.off{opacity:.4}
  #pmixWrap rect[data-pt]{transition:opacity var(--dur-quick) var(--settle)}
  @media (prefers-reduced-motion:reduce){
    .pmixlgd button,.pmixlgd button span::after,#pmixWrap rect[data-pt]{transition:none}}
  .pmixlgd i{width:8px;height:8px;border-radius:2px;flex:0 0 8px;background:var(--pmix-part)}
  @media(max-width:390px){.pmixlgd button{gap:5px;font-size:8px;padding:0 2px}}
  @media (prefers-reduced-motion:reduce){
    .pmixwrap{scroll-behavior:auto}
    .pmixnow{transition:none}}
  .legend1{display:flex;gap:5px 6px;flex-wrap:wrap;margin:0 0 6px;padding-bottom:0}
  .legend1 span{font-family:var(--mono);font-size:9px;color:var(--muted);display:flex;align-items:center;
    gap:3px 4px;white-space:nowrap;flex:0 0 auto}
  .legend1 span.cur{color:var(--chalk)}
  /* the value column holds its width so a live scrub doesn't make the whole
     row twitch as 100% becomes 45% */
  .legend1 span b{font-weight:700;min-width:4ch;text-align:right;display:inline-block}
  .legend1 i{width:10px;height:3px;border-radius:2px;display:inline-block}
  /* v3.3.332: .heatwrap{display:flex} and the .wdrail/.heatcols pair are
     GONE. They described a weekday-rail structure the builder never emitted
     -- dead rules that quietly re-declared .heatwrap on top of the scroller
     defined 1200 lines above. .heatscroll survives because it is now real:
     one inline-block that both the grid and the month row size against, so
     the two share a single column geometry. */
  .heatscroll{display:inline-block;min-width:100%}   /* month row + grid scroll together */
  .heatscroll .heatticks{min-width:100%}
  .compscroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .mrow{display:flex;gap:3px;height:12px;margin-bottom:2px}
  .mlab{flex:0 0 12px;font-family:var(--mono);font-size:7px;color:var(--muted);white-space:nowrap}
  .heat i.fut{opacity:.35}
  .wdl{font-family:var(--mono);font-size:9px;color:var(--muted)}
  .wdl.hiw{color:var(--accent);font-weight:600}
  .cov{display:flex;align-items:center;gap:10px;margin-bottom:7px}
  .cov .cn{flex:0 0 74px;font-size:11px}
  .cov .cv{flex:0 0 40px;text-align:right;font-family:var(--mono);font-size:11px}
  /* v3.3.37: part digest — growth reads as one number, so it gets the only
     colour in the card. Up is accent (the app's own voice), down is record
     red, matching how deltas are coloured everywhere else. */
  .pdigest{margin-top:8px}
  .pdigest .mono.up{color:var(--accent)}
  .pdigest .mono.down{color:var(--record)}
  /* v3.3.38: History's three selector rows are navigation, not content — they
     were eating the screen before the data started. Years hold ONE line and
     scroll sideways once they outgrow it (2027 will arrive); months and parts
     just get denser. */
  .ychips{flex-wrap:nowrap;overflow-x:auto;gap:5px;margin-bottom:7px;
    scrollbar-width:none;-ms-overflow-style:none;padding-bottom:1px}
  .ychips::-webkit-scrollbar{display:none}
  .ychips .chip{flex:none;font-size:11px;padding:6px 11px}
  .ychips .chip .n{font-size:9px;margin-left:4px}
  .pchips{gap:5px;margin-bottom:6px}
  .pchips .chip{font-size:11px;padding:6px 11px}
  .pchips .chip .n{font-size:9px;margin-left:4px}
  .mchips{display:grid;grid-template-columns:repeat(6,1fr);gap:4px 5px}
  .mchip{display:flex;flex-direction:column;align-items:center;gap:0;background:var(--surface);
    border:1px solid var(--line);border-radius:8px;padding:4px 2px;color:var(--chalk);
    font:inherit;cursor:pointer;box-shadow:var(--shadow)}
  .mchip span{font-family:var(--mono);font-size:8px;color:var(--muted);line-height:1.269}
  .mchip b{font-family:var(--mono);font-size:10px;font-weight:700;line-height:1.2}
  .mchip.on{background:var(--accent);border-color:var(--accent)}
  .mchip.on span,.mchip.on b{color:#fff}
  .mchip.dim{opacity:.45}
  .cal{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;text-align:center}
  .cal .cw{font-family:var(--mono);font-size:9px;color:var(--muted);padding:2px 0}
  /* v3.3.36: cells were square, so a 6-row month ate ~290px of vertical space
     for information that reads fine at 2/3 the height. Landscape cells keep the
     grid legible while giving the sessions list back a third of the screen. */
  .cal .cd{font-family:var(--mono);font-size:11px;color:var(--muted);
    aspect-ratio:1.45/1;display:grid;place-items:center;border-radius:8px;background:var(--surface2)}
  .cal .cd.on{background:var(--accent);color:#fff;font-weight:600}
  .cal .cd.fut{opacity:.35;background:none}
  .cal .cd.now{outline:2px solid var(--chalk);outline-offset:1px}
  /* v3.3.60: the header is sticky, so a scrollIntoView(block:start) target
     lands UNDERNEATH it — the tapped date's own header line was hidden.
     scroll-margin reserves the header's height (+ safe area + breath) for
     every scroll target in History: the day cards, and the calendar for the
     return-pill trip back. */
  .day,.cal{scroll-margin-top:calc(env(safe-area-inset-top,0px) + 86px)}
  .day{border:1px solid var(--line);border-radius:12px;background:var(--surface);margin-bottom:6px;overflow:hidden;box-shadow:var(--shadow)}
  .day summary{padding:13px 14px;cursor:pointer;list-style:none;display:flex;justify-content:space-between;
    align-items:flex-start;gap:10px}
  /* v3.3.180: the session head is TWO COLUMNS, not one inline flow. The right
     side was a bare text node followed by two buttons, so a long part list
     ("Run · Back · Biceps") grew the left column and the right flow broke
     wherever it happened to land — volume and Share on one line, Edit
     orphaned and centred beneath. Now: the left column may shrink and wrap
     (min-width:0 is what actually permits that inside a flex row), the
     right column is its own flex line that never splits the number from
     its controls, and when the head is genuinely too narrow the controls
     drop BELOW the volume as a unit instead of jumbling. align-items goes
     to flex-start so a two-line left column no longer drags the right one
     down to its centre. */
  .day summary>span:first-child{min-width:0;flex:1 1 auto}
  /* v3.3.361: the actions never wrap. They were flex-wrap:wrap beside the
     day's totals, so on a narrow row the second button dropped to its own
     line -- two controls stacked in a header two lines tall. With the totals
     gone there is room, and nowrap + flex:0 0 auto says so rather than
     leaving it to chance at some width I did not test. */
  .day summary>span:last-child{display:flex;align-items:center;justify-content:flex-end;
    flex:0 0 auto;flex-wrap:nowrap;gap:8px;text-align:right}
  .day summary>span:last-child>.dayedit{margin-left:0}
  .day summary::-webkit-details-marker{display:none}
  .day .d{font-family:var(--mono);font-size:11px}
  .day .s{font-family:var(--mono);font-size:10px;color:var(--muted)}
  /* v3.3.361: the parts are ONE line. "Run · Legs · Sixpack" wrapped under
     the date and dragged the whole header taller. It ellipsises rather than
     wraps: a session names two or three parts, and the row's job is the date
     and what you trained, not to hold every part at any width. min-width:0 on
     the parent is what lets a flex child shrink far enough to clip at all. */
  .day summary .s{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .day .body{padding:0 14px 12px;font-family:var(--mono);font-size:11px;color:var(--muted)}
  .day .body b{color:var(--chalk);font-weight:500}
  /* v3.3.43: session detail reuses the LAST TIME row styles, so it must NOT
     inherit the old one-row-per-set border rule — the grouped rows draw
     their own dashed separators. */
  /* v3.3.61: past-day editing. The Edit control is quiet until used; edit
     mode itself is unmistakable (accent ring on the card) so you always know
     the record is open for writing. */
  .dayedit{margin-left:10px;font-family:var(--mono);font-size:9px;letter-spacing:var(--track-label);
    text-transform:uppercase;background:var(--surface2);border:1px solid var(--line);
    color:var(--muted);border-radius:999px;padding:3px 9px;cursor:pointer}
  /* v3.3.358: the icon variant. Same pill, sized to the glyph instead of to
     a word, and a touch taller so the target does not shrink with the label:
     the text form was ~20px high, this is 26. COPY MONTH / COPY YEAR share
     .dayedit and stay words -- they are not per-row controls and there is no
     symbol for "copy a year" that anyone would read correctly. */
  .dayedit.ico{padding:5px 7px;line-height:0}
  /* ============ v3.3.369 — the day is placed ============
     Maker's pick A. One motion: surface in, square draws itself, one ring
     spent, the count lands. The ring reuses the breathing halo's grammar
     (v3.3.348) at a single beat. Everything rides --settle; nothing loops;
     reduced-motion gets the finished frame, because a marker that vanishes
     for that setting would celebrate nobody. Tap anywhere ends it early. */
  #dayDone{--sq:13px;position:fixed;inset:0;z-index:1001;background:var(--surface);
    display:flex;overflow-y:auto;overscroll-behavior:contain;
    animation:ddin .18s var(--settle) both}
  #dayDone .ddinner{box-sizing:border-box;width:100%;max-width:446px;min-height:100%;margin:auto;
    padding:calc(34px + env(safe-area-inset-top,0px)) 28px calc(26px + env(safe-area-inset-bottom,0px));
    display:flex;flex-direction:column;text-align:center}
  /* v4.2.5: the plate card's date, in the completion moment's own voice */
  .plate-date{font:500 10px var(--mono);letter-spacing:1.65px;color:var(--muted);
    text-transform:uppercase;text-align:center;margin:2px 0 11px}
  #dayDone .dddate{font:500 10px var(--mono);letter-spacing:1.65px;color:var(--muted);text-transform:uppercase}
  #dayDone .ddbody{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:44px 0 38px}
  #dayDone .ddyou{font:500 22px var(--disp);letter-spacing:-.5px;margin:20px 0 14px;
    color:var(--chalk);text-transform:none;opacity:1}
  #dayDone .ddyou::before{display:none}
  #dayDone .ddsummary{font:400 11px/1.692 var(--mono);color:var(--muted)}
  #dayDone .ddactions{display:flex;flex-direction:column}
  #dayDone .ddshare{background:none;border:0;color:var(--muted);font:400 11px var(--disp);min-height:44px;margin-top:6px;padding:10px;cursor:pointer}
  .today-complete.btn,#dayDone .ddactions .btn{min-height:54px;padding:17px 14px;border-radius:17px;
    font:600 13px var(--disp);background:var(--accent);color:#fff;border:0;
    box-shadow:0 5px 16px color-mix(in srgb,var(--accent) 20%,transparent)}
  .today-complete.btn{display:flex;align-items:center;justify-content:center;gap:10px;margin:16px 0 11px}
  #dayDone.out{opacity:0;transition:opacity .3s var(--settle)}
  /* ---- v3.3.424: THE CENTURY BEAT --------------------------------------
     Every hundredth day the square OPENS and the mark rises out of it. Two
     symbols becoming one: the square is what you did, the chevron is where it
     goes. It is the same ceremony with one beat inserted -- no confetti, no
     sound, and NO ESCALATION: day 1,000 gets exactly what day 100 gets,
     because the number escalates itself.
     The mark is clipped INSIDE the square while it emerges, so it reads as
     coming out of the square rather than being laid over it. */
  /* v3.3.425: a stage the size of the square, holding both. The mark is a
     SIBLING of the square so the square's fade cannot drag it grey. */
  #dayDone .ddstage{position:relative;display:block;width:44px;height:44px}
  #dayDone .ddstage .ddsq{position:absolute;inset:0}
  #dayDone.century .ddmk{position:absolute;left:0;top:0;width:44px;height:44px;
    display:grid;place-items:center;color:#fff;
    clip-path:inset(0 0 0 0);animation:ddrise 1.15s var(--settle) .55s both}
  #dayDone.century .ddmk .ic{width:34px;height:34px;display:block;
    /* v3.3.425: the mark casts a shadow onto the square as it lifts -- the
       one place this app is allowed depth, because the whole beat IS the
       chevron coming out of the square. It grows with the rise and settles;
       nothing else in the app carries a shadow. */
    filter:drop-shadow(0 0 0 rgba(0,0,0,0));animation:ddlift 1.15s var(--settle) .55s both}
  @keyframes ddlift{
    0%   {filter:drop-shadow(0 0 0 rgba(0,0,0,0))}
    45%  {filter:drop-shadow(0 2px 3px rgba(0,0,0,.34))}
    100% {filter:drop-shadow(0 10px 14px rgba(0,0,0,.42))}
  }
  @keyframes ddrise{
    0%   {clip-path:inset(0 0 100% 0); transform:translateY(6px)}
    45%  {clip-path:inset(0 0 0 0);    transform:translateY(0)}
    100% {clip-path:inset(0 0 0 0);    transform:translateY(-26px)}
  }
  /* the square recedes as the mark clears it -- it has handed over */
  #dayDone.century .ddsq{animation:ddhand 1.15s var(--settle) .55s both}
  @keyframes ddhand{ 0%,45%{opacity:1;transform:scale(1)} 100%{opacity:.5;transform:scale(.86) translateY(8px)} }
  #dayDone.century .ddn{animation-delay:1.5s}
  @media (prefers-reduced-motion:reduce){
    /* no rise, no recede, no lift: the mark is simply there, which is the fact */
    #dayDone.century .ddmk,#dayDone.century .ddsq,#dayDone.century .ddmk .ic{animation:none}
    #dayDone.century .ddmk{transform:translateY(-26px)}
  }
  #dayDone .ddsq{display:block;width:45px;height:45px;border-radius:var(--sq);background:var(--accent);
    position:relative;animation:ddsq .5s var(--settle) both .12s}
  #dayDone .ddsq::after{content:'';position:absolute;inset:-8px;border-radius:var(--sq);
    border:2px solid var(--accent);opacity:0;animation:ddring .7s var(--settle) .5s}
  #dayDone .ddn{font-family:var(--disp);font-size:74px;font-weight:600;color:var(--chalk);
    letter-spacing:-5px;font-variant-numeric:tabular-nums;
    margin-top:19px;line-height:1.05;animation:ddup .45s var(--settle) both .55s}
  #dayDone .ddn.ddlarge{font-size:67px;letter-spacing:-4px}
  #dayDone .ddu{font-family:var(--mono);font-size:11px;color:var(--faint);
    margin-top:8px;letter-spacing:3px;text-transform:uppercase;animation:ddup .45s var(--settle) both .62s}
  #dayDone .ddt{position:absolute;bottom:34px;font-family:var(--mono);font-size:9px;
    color:var(--faint);letter-spacing:var(--track-label);text-transform:uppercase;
    animation:ddup .5s var(--settle) both .95s}
  @keyframes ddin{from{opacity:0}}
  @keyframes ddsq{from{transform:scale(.25);opacity:0}}
  @keyframes ddring{0%{opacity:.55;inset:-6px}100%{opacity:0;inset:-22px}}
  @keyframes ddup{from{opacity:0;transform:translateY(6px)}}
  @media (prefers-reduced-motion:reduce){
    #dayDone,#dayDone .ddsq,#dayDone .ddn,#dayDone .ddu,#dayDone .ddt{animation:none}
    #dayDone .ddsq::after{animation:none;opacity:0}
  }
  .dayedit.ico svg{display:block;width:15px;height:15px}
  .day.editing{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent-soft)}
  .day.editing .dayedit{background:var(--accent);border-color:var(--accent);color:#fff}
  .hsets{display:flex;flex-wrap:wrap;gap:5px 6px;margin-top:3px}
  .hset{position:relative;display:flex;align-items:baseline;gap:6px;background:var(--surface2);
    border:1px solid var(--line);border-radius:9px;padding:8px 30px 8px 10px;cursor:pointer;
    font-family:var(--mono);font-size:11px;color:var(--chalk)}
  .hset .muted{font-size:11px}
  /* v4.6.73: the tile's corner control is the bin now, not a cross -- a tap
     here deletes the set. 20px so the glyph reads; the tile pads for it. */
  .hsx{position:absolute;top:3px;right:3px;width:20px;height:20px;border-radius:50%;
    background:var(--ground);border:1px solid var(--line);color:var(--muted);
    line-height:1;display:flex;align-items:center;justify-content:center;font-style:normal}
  .hsx svg{width:11px;height:11px}
  .hadd{margin-top:6px;font-family:var(--mono);font-size:10px;color:var(--accent);
    background:none;border:1px dashed var(--line);border-radius:9px;padding:6px 12px;cursor:pointer}
  .day .body .exgrp+.exgrp{margin-top:8px;padding-top:10px;border-top:1px solid var(--line)}
  .day .body .lasthead{font-size:9px;margin-bottom:3px;color:var(--faint)}
  .day .body .lastw{min-width:64px;font-size:12px}
  .day .body .lastrow{padding:4px 0}
  nav{position:fixed;bottom:0;left:0;right:0;z-index:30;max-width:520px;margin:0 auto;
    /* v3.3.481: one non-wrapping row with equal-width buttons, without
       grid tracks on the fixed glass surface. */
    display:flex;flex-flow:row nowrap;align-items:stretch;
    background:var(--surface);border-top:1px solid var(--line);padding-bottom:env(safe-area-inset-bottom)}
  nav button{flex:1 1 0;min-width:0;background:none;border:0;color:var(--muted);font:inherit;font-size:9px;
    font-family:var(--mono);letter-spacing:var(--track-label);text-transform:uppercase;
    padding:11px 4px 13px;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px 4px}
  /* v3.3.458: the glyphs are SVG in currentColor (index.html), so the emoji
     grayscale filter is gone with the emoji. Ink follows the button: muted at
     rest, accent when on. The underline stays: colour alone should not carry
     the active state, and two cues at two weights read as one. */
  /* v3.3.459: THREADS' WEIGHT, OUR GLYPHS. 24px glyphs, labels at 600 so
     they read from arm's length (the maker: "HEAVY or BOLD"), and the active
     state is a soft capsule behind the tab rather than the underline -- a
     SURFACE rather than a mark, which is the app's own grammar. One cue now,
     where 458 had two. */
  /* v3.3.461: ICONS ONLY, 28px, and the active tab in the APP'S accent on a
     capsule of the same blue -- one colour family, no grey. Without labels the
     4.5:1 text gate no longer applies to the bar; a 28px solid glyph is a
     graphic (3:1), which the accent clears over anything the page can scroll
     beneath it. Today's square is hollow until nav.dayclosed; History's frame
     is a stroke, its bar and days are fills. */
  nav button .ng{display:block;width:28px;height:28px;line-height:0}
  nav button .ng svg{width:28px;height:28px;display:block;fill:currentColor}
  nav button{position:relative;border-radius:24px;margin:4px 4px;padding:10px 4px;gap:0;font-size:0}
  /* v3.3.470: THE SELECTED TAB IS INK ON GREY. Black glyph, grey capsule --
     selection is a neutral fact. Colour is reserved for the DAY'S STATE on the
     Today square alone: blue fill once the day is closed, green ring while
     resting (rest-ink, the darker green the header's ring wears), whether or
     not Today is the selected tab. */
  nav button.on{color:var(--pill-chalk,var(--chalk));background:color-mix(in srgb,var(--muted) 16%,transparent)}
  nav button .ng svg .sq{fill:none;stroke:currentColor;stroke-width:2.6}
  nav.dayclosed button[data-v="today"] .ng svg .sq{fill:var(--accent);stroke:none}
  /* v3.3.510: --pill-rest where the bar declares one; --rest-ink remains the
     fallback for any surface where the page theme IS the background. */
  nav.resting button[data-v="today"] .ng svg .sq{stroke:var(--pill-rest,var(--rest-ink));fill:none}

  nav button .ng svg .frame{fill:none;stroke:currentColor;stroke-width:2.4}
  /* v3.3.459: HIDE ON SCROLL. The bar slides off the bottom after ~28px of
     downward travel and returns on ~10px upward; at the top of the page it is
     always shown. Transform only, on the settle curve; the compositing layer
     from v3.3.179 is what makes iOS carry it smoothly. */
  /* v3.3.460: IT SLIDES. 459 faded opacity in .16s while the transform took
     .26s, so the bar was gone before it had moved -- it read as vanishing.
     Opacity is out entirely: the bar leaves by moving, at --dur-arrive, and
     goes far enough (its own height plus the inset) to clear the screen. */
  /* v3.3.470: the bar STAYS. The hide-on-scroll of 459-462 is gone at the
     maker's word; no transition, no scrub, no hidden state. */
  @media (prefers-reduced-motion:reduce){ nav{transition:none} }
  /* v3.3.179: bottom-anchored fixed chrome gets its own compositing layer.
     Reported against Minimal — the nav pill and the "↑ top" button both hung
     mid-screen during a scroll — but the bug is in the BASE and Classic has
     it too: iOS re-anchors position:fixed elements to the visual viewport
     only at the end of a scroll gesture, so anything bottom-anchored lags
     behind the URL-bar resize. At bottom:0 the lag has nowhere visible to
     go; a pill inset from the edge shows it plainly. Promoting both to
     their own layer makes iOS carry them with the viewport instead of
     repainting them after the fact. Fixes Classic as well as Minimal. */
  /* v3.3.509: THE v3.3.179 PROMOTION COMES OFF, and this is the last suspect
     standing. Walking every ancestor of both hanging elements under every body
     class the app can set (tools/demo-fixedchain.js) turns up exactly one
     thing they share and nothing else on the page has: this promotion. #app's
     overflow-x:clip is an ancestor of nav but NOT of .calreturn, so it cannot
     explain both. No ancestor of either carries a transform, filter, contain
     or perspective in any state.
     179 promoted both to their own compositing layers to fix a lag where iOS
     re-anchored fixed elements only at the end of a gesture. Everything built
     on that premise since has failed: 480's translateZ theory, 495's belt,
     497's blur. A promoted layer is precisely the thing that can be composited
     at a stale position, so the fix is now the leading candidate for the bug.
     Off it comes, whole, alone. If the chrome holds, 179's premise expired
     with the iOS version that needed it. If it does not, the next candidate is
     #app's overflow-x:clip -- which explains the nav only, and would mean the
     top button is a separate fault. */
  .calreturn{}
  /* v3.3.480: THE NAV DROPS THE 3D HINT. The 179 symptom came back on the
     device -- pill and top button hanging mid-screen after a scroll -- with
     the 179 fix in place. What changed is that since v3.3.465 the nav's
     backdrop-filter actually renders, and WebKit is known to stop re-anchoring
     a fixed element that carries BOTH an active backdrop filter and a 3D
     transform. So the nav keeps will-change:transform (a layer of its own,
     which is what 179 needed) and loses translateZ(0), which it no longer
     needs for promotion and which conflicts with the glass. The top button
     has no glass and keeps the full 179 rule.
     v3.3.495: the belt is GONE and the .reanchor class with it. navReanchor()
     put translateZ(0) back on the nav 120ms after every scroll -- reapplying,
     on a loop, the exact trigger the rule above removes. On the device the
     pill still hung, and it hung together with the "top" button, which has no
     glass at all. Both are elements this file deliberately promotes to their
     own layer, and that promotion is now the remaining suspect. The belt goes
     first and alone so the next report is readable. */
  /* v3.3.509: nav is a plain fixed element again -- see .calreturn above. */
  .recdate{display:block;font-size:8px;color:var(--muted);font-weight:400;margin-top:1px}
  .mstone{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:6px}
  .mstone .big{font-family:var(--mono);font-size:21px;color:var(--chalk);font-weight:600}
  .mstone .goal{font-family:var(--mono);font-size:10px;color:var(--muted)}
  .mbar{height:8px;border-radius:4px;background:var(--line);overflow:hidden}
  .mbar{position:relative}
  .mbar i{display:block;height:100%;background:var(--accent);border-radius:4px}
  .mbar .pacemark{position:absolute;top:-3px;width:2px;height:14px;background:var(--chalk);
    border-radius:1px;opacity:.75}
  .linky{background:none;border:0;padding:0;color:var(--accent);font:inherit;
    text-decoration:underline;cursor:pointer}
  #portraitveil{display:none}
  /* v3.3.428: THE VEIL STANDS DOWN FOR A LIVE SESSION. This is why the
     landscape timer never appeared: the veil sits at z-index 999 and covered
     it. The veil is right for every other screen -- none of them are designed
     sideways -- but during a live rest the sideways view IS the design, so the
     one exception is written here, where the rule that hid it lives. */
  @media (orientation:landscape) and (pointer:coarse) and (max-height:500px){
    #portraitveil{position:fixed;inset:0;z-index:999;background:var(--ground);
      display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px 6px}
    html.bigtimer #portraitveil{display:none}
    .pv-mark{font-size:30px;color:var(--accent)}
    .pv-text{font-family:var(--disp);font-weight:600;font-size:16px;color:var(--chalk)}
    .pv-sub{font-family:var(--mono);font-size:11px;color:var(--muted)}
  }
  #toast{position:fixed;bottom:86px;left:50%;transform:translateX(-50%) translateY(20px);
    background:var(--chalk);color:var(--ground);font-family:var(--mono);font-size:11px;
    padding:9px 14px;border-radius:999px;opacity:0;transition:var(--dur-move) var(--settle);pointer-events:none;z-index:40;max-width:86%;text-align:center}
  /* v3.3.472: an undo toast is a control, so it says so with the cursor and a brighter word */
  #toast.undo{cursor:pointer}
  /* v4.6.212: ...and it has to take the tap. The toast ignores the pointer so it never blocks the page; an undo toast on screen is the one that must not. */
  #toast.undo.on{pointer-events:auto}
  .pedge.pclear{color:var(--muted)}
  #toast.on{opacity:1;transform:translateX(-50%) translateY(0)}
  .note{font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:6px;line-height:1.504}
  .assetcredits a{color:inherit;text-decoration:underline dotted;text-underline-offset:3px}
  /* v3.3.66 arrival greeting — quiet by design: it is a state, not a banner */
  /* v3.3.98: the milestone moment. Bold by TYPE and MOTION — the voice
     stays dry even at full volume. Thousand tier floods accent and lets the
     record itself cascade in. */
  .msmoment{text-align:center;padding:26px 16px 18px;overflow:hidden;position:relative}
  .msmoment .mskick{font-family:var(--mono);font-size:11px;letter-spacing:.3em;color:var(--muted)}
  .msmoment .msnum{font-family:var(--mono);font-weight:700;font-size:67px;line-height:1.05;
    color:var(--chalk);letter-spacing:var(--track-num);margin:2px 0 3px}
  .msmoment .msline{font-family:var(--mono);font-size:11px;color:var(--muted)}
  .msmoment.thousand{background:var(--accent);border-color:var(--accent)}
  .msmoment.thousand .mskick,.msmoment.thousand .msline{color:rgba(255,255,255,.85)}
  .msmoment.thousand .msnum{color:#fff;font-size:81px}
  .msmoment.thousand .btn.ghost{border-color:rgba(255,255,255,.5);color:#fff}
  .msgrid{display:grid;grid-template-columns:repeat(12,1fr);gap:2px 3px;margin:0 auto 11px;max-width:340px}
  .msgrid i{aspect-ratio:1;border-radius:2px;background:#fff;animation:mscell .5s ease-out both}
  @keyframes mscell{from{opacity:0;transform:scale(.4)}to{transform:scale(1)}}
  @media (prefers-reduced-motion:reduce){.msgrid i{animation:none;opacity:1 !important}}
  .restbtn{margin-top:8px}
  .restbtn.on{border-color:var(--accent-soft);color:var(--chalk)}  /* declared: quiet, not lit */
  .hello{display:flex;align-items:baseline;gap:6px 8px;margin:2px 0 10px;flex-wrap:nowrap}
  .bwnow{font-family:var(--mono);font-weight:700;font-size:23px;color:var(--chalk);line-height:1}
  .bwsub{font-family:var(--mono);font-size:10px;color:var(--muted);margin-top:3px}
  .hello .hi{font-family:var(--disp);font-weight:600;font-size:15px;color:var(--chalk);
    white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .hello .hisub{font-family:var(--mono);font-size:10px;color:var(--muted);white-space:nowrap;flex:0 0 auto}
  @media (prefers-reduced-motion:reduce){*{transition:none!important}}

  /* v3.3.181 rep zones — existing tokens only, so every ink here is already
     under the theme contrast guard: numbers chalk, labels muted, unit words
     faint, bars accent on a soft accent track. NO red anywhere — an empty
     bucket states itself in the same voice as a full one. */
  /* v3.3.188: the primary lifts sit on ONE line and scroll sideways — a
     wrapping chip cloud pushed the chart below the fold on parts with four
     or five staples. */
  /* v3.3.193 — styles for the merge picker (v3.3.191) and the intent-gap
     list (v3.3.192). BOTH shipped unstyled: the edits that should have added
     them anchored on `.rzparts{`, a selector deleted in v3.3.188 when the
     part chips became sections. A string replace whose anchor is gone
     changes nothing and says nothing — buildcheck now asserts these
     selectors exist so the next miss fails the gate instead of the phone. */
  .rzsel{width:100%;font-family:var(--mono);font-size:11px;color:var(--chalk);
    background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:9px 10px}
  :root[data-skin="minimal"] .rzsel{background:var(--ground);border-color:transparent}
  /* v3.3.194 muscle coverage — dots are the last 7 days, oldest left.
     Trained = accent, untrained = a quiet track. No red anywhere. */
  /* v3.3.216: Growth Audit uses the base h2 spacing. Its old 52px exception
     made the gap above it visibly larger than every other card-to-section
     transition on Stats. */
  /* v3.3.350: ONE TEMPLATE, read by the weekday header and by every row, so
     the seven day columns and the three numeric tracks are the same columns
     down the whole card. The tail is split -- count, unit, separator, sets --
     because "1 day" and "2 days" can only share a column if the number and
     its word are measured apart. The ch widths are computed across all rows
     and set once on .mcgrid (v3.3.324's technique). */
  /* v3.3.351: the gap is PER BOUNDARY, not uniform. grid `gap` applies to
     every column edge equally, so the 10px that correctly separates the day
     strip from the numbers was also landing on both sides of the separator --
     a dot that wants one space of air got twenty pixels of it, and the tail
     read as three islands instead of one phrase. Zero gap, and the two
     boundaries that need air ask for it themselves. The tail now sets like
     type: "2 days · 31 sets", one space either side of the dot. */
  /* v3.3.352: ONE GRID, and the tail measures ITSELF.
     v3.3.350 sized the tail in ch on .mcrow -- and ch resolves in the font of
     the element the track is declared on, which is the ROW, inheriting the
     sans. The cells inside render in 11px MONO. So every track was cut for a
     wider character than it would ever hold, and the slack came out as air
     around the separator. v3.3.351 then tuned padding against that wrong
     ruler, which is why the gap shrank but never closed.
     The card is now one grid and .mcrow is display:contents, so each cell is
     a grid item of the CARD and max-content measures it in its own font,
     across all six rows at once. No ch arithmetic, nothing to keep in sync,
     and the three runtime vars are gone from buildcheck's allowlist with it.
     display:contents costs the row its own box: the separator rule and the
     vertical padding move onto the CELLS, which is where they render
     identically. Click delegation is untouched -- closest() walks the DOM,
     not the layout, so [data-mcg] still finds the row. */
  .mcgrid{display:grid;
    grid-template-columns:max-content 1fr max-content max-content max-content max-content;
    align-items:center}
  .mcrow,.mchead{display:contents;cursor:pointer}
  .mcrow>*{padding:10px 0}
  .mchead>*{padding:0 0 6px}
  /* v3.3.354: THE RULE IS ITS OWN ELEMENT, spanning every column.
     v3.3.352 made the row display:contents and moved the separator onto each
     CELL as a border-top. With align-items:center a cell is only as tall as
     its own content, so six cells sat at six different heights and each drew
     its border at its own top edge: the line came out as offset dashes, not a
     rule. A border on a cell describes THAT CELL; a rule across a table is a
     different object and has to be one.
     .mcline is a 1px element at grid-column:1/-1. It cannot fragment, cannot
     drift with cell height, and does not care what the cells do next.
     Note for future me: nothing in the suite could have caught this. jsdom
     computes no layout, so a border that renders in six pieces is identical
     to one that renders whole. It took the maker's screenshot. */
  .mcline{grid-column:1/-1;height:1px;background:var(--line)}
  .mcinner{grid-column:1/-1}
  .mcname{font-family:var(--mono);font-size:11px;color:var(--chalk);
    padding-right:10px;white-space:nowrap}
  /* the day marks ARE the axis now: seven equal columns, one mark centred in
     each, so a column reads down the card as a single day */
  /* v3.3.353: FIXED PITCH, FLUSH EDGES. v3.3.352's columns were 1fr with a
     12px square centred in each -- so at any width beyond the phone, half a
     column of VOID sat at each end of the strip, reading as a gap against the
     name on one side and the count on the other. The columns are now the size
     of the mark's cell, and space-between puts the surplus BETWEEN days --
     first mark flush left, last mark flush right, gaps even, at every width.
     The one deliberate gap (strip to count) is the padding below, alone. */
  /* v3.3.362: the week has a NATURAL WIDTH and stops there. v3.3.353 fixed
     the day cells and pushed the surplus BETWEEN them, which is right up to a
     point -- but space-between divides ALL of it, so past the phone the seven
     columns kept stretching and the marks drifted apart on a laptop. The
     marks stopped growing at v3.3.352; the columns never did.
     Capped, and kept flush against its own numbers, so the extra room lands
     in the LABEL column -- which is the ragged one in any table, and leaves
     the strip glued to the totals it belongs to. Centring it instead would
     split the surplus and put half of it back between the dots and the
     numbers, which is the gap this card was already fixed for once.
     v3.3.363, on the maker's call: CENTRED, not flush right, and the cap
     widened from 216 to 240 -- about 25px between marks instead of 21.
     Centring splits the surplus, so at 520 each side gets ~22px rather than
     one 45px margin. That is the trade I argued against in v3.3.362 and he
     took anyway, and looking at it he is right: a symmetric pair of small
     margins reads as a column sitting in its track, where one large margin
     read as a gap. The failure this card was fixed for in v3.3.351 was a gap
     WIDER than the tail it sat beside, not any space at all.
     The cap never binds on a phone: at 390 the available track is ~155px,
     narrower than either cap, so that layout is untouched by both numbers. */
  .mcdots{display:grid;grid-template-columns:repeat(7,18px);
    justify-content:space-between;align-items:center;
    max-width:240px;margin-inline:auto}
  /* v3.3.352: the mark is a SQUARE, not a stretched pill. It used to be
     width:100% with a 24px cap, so it filled its column -- and two trained
     days in a row merged into one long bar, which is the one thing this card
     must never do. A fixed square centred in its column always leaves the
     gutter between days visible. It does not grow with the window: the marks
     are a legend, not a measurement, so on a wide screen the columns get
     roomier and the squares stay put. */
  .mcdots i{justify-self:center;width:12px;height:12px;border-radius:var(--sq);
    background:var(--surface2);display:block}
  .mcdots i.on{background:var(--accent)}
  .mchead .mcdots i{background:none;width:auto;height:auto;font-style:normal;
    text-align:center;font-family:var(--mono);font-size:8px;color:var(--faint)}
  /* count right, unit left: the digits form one column and the words another,
     so singular and plural stop mattering */
  .mcv{font-family:var(--mono);font-size:11px;font-weight:600;color:var(--muted);text-align:right}
  .mcu,.mcsep,.mcs{font-family:var(--mono);font-size:10px;color:var(--faint);white-space:nowrap}
  .mcu{text-align:left;padding-left:.55ch}
  .mcsep{text-align:center;padding:0 .25ch}
  .mcs{text-align:right}
  /* the one boundary that earns space: the day strip standing off the count */
  .mcdots{padding-right:12px}
  .mcinner{padding:2px 0 10px 78px}
  /* Quiet weekday underline; past marks darker than future outlines. */
  .mccard .mcgrid{grid-template-columns:minmax(72px,1fr) minmax(126px,1.5fr) minmax(55px,.8fr)}
  .mccard .mcdots{grid-template-columns:repeat(7,minmax(0,1fr));width:100%;max-width:none;margin:0;padding:0;align-self:stretch;align-items:stretch}
  .mccard .mccell{display:flex;align-items:center;justify-content:center;min-width:0;padding:12px 0}
  .mccard .mcrow .mccell{min-height:48px}
  .mccard .mchead .mccell{padding:0 0 12px;min-height:34px;position:relative}
  .mccard .mchead .mc-today:after{content:'';position:absolute;bottom:7px;left:calc(50% - 7px);width:14px;height:2px;border-radius:2px;background:var(--accent)}
  .mccard .mchead .mcdots i{font-family:var(--body);font-size:10px;color:var(--muted)}
  .mccard .mchead .mc-today i{color:var(--accent);font-weight:600}
  .mccard .mcrow .mcdots i:not(.on):not(.mc-future){background:color-mix(in srgb,var(--muted) 8%,var(--surface))}
  .mccard .mc-future{background:transparent;border:1px solid color-mix(in srgb,var(--muted) 22%,var(--surface))}
  .mccard .mcname{font-family:var(--body);font-size:11px;padding-right:6px}
  .mccard .mcsummary{text-align:right;white-space:nowrap;padding:9px 0}
  .mccard .mcsummary .mcu{padding-left:0}
  .mccard .v4-note{float:left;margin:0 12px 13px 0;line-height:1.504}
  .mccard .mcgrid{clear:both}
  .mc-position{font:500 11px/1.504 var(--body);color:var(--accent);text-align:right;margin:0 0 13px}
  .mc-key{display:flex;justify-content:space-between;gap:10px 12px;flex-wrap:wrap;margin-top:10px;padding-top:12px;border-top:1px solid var(--line);font:400 10px var(--body);color:var(--muted)}
  .mc-key>span{display:flex;align-items:center;gap:5px}
  .mc-key i{width:9px;height:9px;border-radius:3px;display:inline-block}
  .mc-key-trained{background:var(--accent)}
  .mc-key-future{border:1px solid color-mix(in srgb,var(--muted) 22%,var(--surface))}
  .mcirow{display:flex;justify-content:space-between;padding:4px 0}
  .mciname{font-family:var(--mono);font-size:10px;color:var(--muted)}
  .mciwhen{font-family:var(--mono);font-size:10px;color:var(--faint)}
  /* v3.3.357: a muscle with nothing logged this week recedes -- one tier, not
     a colour. It is an absence, not a warning: nothing here turns amber, and
     the app has no word for "behind". Dimming is what it already says for
     spent (.rspent) and for days not yet arrived (.fut). */
  .mcirow.mcinone .mciname,.mcirow.mcinone .mciwhen{
    color:color-mix(in srgb,var(--faint) 55%,var(--surface))}
  .mciwhen b{color:var(--muted);font-weight:600}
  /* v3.3.211 Growth Audit — three visual signals only. Empty is a native
     CSS dot; Flat and Going Up use optically sized Noun Project masks. */
  .gasel{width:100%;font-family:var(--mono);font-size:11px;color:var(--chalk);
    background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:9px 10px}
  :root[data-skin="minimal"] .gasel{background:var(--ground);border-color:transparent}
  /* v3.3.372: day one's empty square follows the skin like every other
     surface. The skin suite caught this one -- it is decorative, so it was
     easy to forget, and a first screen that ignores the user's chosen skin is
     the worst possible screen to get wrong. */
  :root[data-skin="minimal"] .d1sq{background:var(--ground);border-style:solid;border-color:transparent}
  .gahead{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 0 8px}
  .gahead small{font-family:var(--mono);font-size:9px;color:var(--faint)}
  .gastate,.gabadge{display:block;width:22px;height:22px;flex:0 0 22px;background:currentColor;
    -webkit-mask:var(--ga-icon) center/var(--ga-size,contain) no-repeat;
    mask:var(--ga-icon) center/var(--ga-size,contain) no-repeat}
  .ga-empty{color:var(--faint);opacity:.55;background:
    radial-gradient(circle at center,currentColor 0 2px,transparent 2.25px);-webkit-mask:none;mask:none}
  .ga-flat{--ga-icon:url('../assets/status-flat.png');--ga-size:11px 11px;color:var(--faint);opacity:.55}
  .ga-up{--ga-icon:url('../assets/status-up.png');--ga-size:30px 30px;color:var(--accent-ink)}
  .garows{display:flex;flex-direction:column}
  .garow{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px 12px;align-items:center;padding:10px 0}
  .garow+.garow{border-top:1px solid var(--line)}
  /* v3.3.222: the stepper on a bodyweight lift reads "BW + [10] kg" — the
     number is the belt, and the prefix says so in the same voice as the
     unit that follows it. */
  /* v3.3.223: spelled out, and stacked ABOVE the number rather than beside
     it. "BW +" inline was cryptic and also fought the input for width at
     24px mono; as a small caption line it reads plainly and costs no
     horizontal room at all. The stepper buttons stretch to the taller row so
     the control keeps one clean edge. */
  .wsel .bwtag{display:block;font-family:var(--mono);font-size:9px;
    color:var(--muted);font-weight:600;letter-spacing:var(--track-label);
    text-transform:uppercase;white-space:nowrap;line-height:1;
    margin:5px 0 2px}
  .wsel .bwval input{font-size:22px}
  .wsel button{align-self:stretch;height:auto;min-height:48px}
  /* v3.3.221: the PR receipt. Opens under its row, indented so it reads as
     that row's detail rather than a new row. */
  .garow{cursor:pointer}
  .garow.open{padding-bottom:4px}
  .garcpt+.garow{border-top:1px solid var(--line)}
  .garcpt{padding:2px 0 12px}
  /* v3.3.226: dates belong to the set they describe. The old flexible middle
     column pushed the improvement day to the far card edge; value and date
     now form a compact left-reading sequence after the label. */
  .garcrow{display:grid;grid-template-columns:100px auto minmax(0,1fr);gap:8px 10px;align-items:baseline;padding:4px 0}
  .garck{font-family:var(--mono);font-size:9px;color:var(--faint);
    text-transform:uppercase;letter-spacing:var(--track-label)}
  .garcrow b{font-family:var(--mono);font-size:11px;color:var(--chalk);font-weight:600;white-space:nowrap}
  .garcw{font-family:var(--mono);font-size:10px;color:var(--faint);white-space:nowrap}
  .garcnote{font-family:var(--mono);font-size:10px;color:var(--faint);
    line-height:1.41;padding-top:4px}
  .garow b{display:block;font-size:11px;color:var(--chalk);font-weight:600}
  /* v3.3.217: Growth Audit's recent change sits quietly beside its status mark. */
  .garight{display:flex;align-items:center;justify-content:flex-end;gap:7px;min-width:0}
  .gadelta{font-family:var(--mono);font-size:8px;font-weight:700;color:var(--accent-ink);
    white-space:nowrap;padding:3px 5px;border-radius:6px;background:color-mix(in srgb,var(--accent) 10%,transparent)}
  .runscore{margin-bottom:6px}
  .runmonthhero{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;padding-bottom:14px;border-bottom:1px solid var(--line)}
  .runmonthhero strong{display:block;font-family:var(--mono);font-size:34px;line-height:1;color:var(--accent-ink);letter-spacing:var(--track-num)}
  .runmonthhero strong small{font-size:13px;color:var(--muted);font-weight:500}
  .runmonthhero span{font-family:var(--mono);font-size:10px;color:var(--muted);text-align:right;line-height:1.5}
  .runmonthhero span b{display:block;color:var(--chalk);font-size:15px}
  .runmonthgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:12px}
  .runmonthgrid span{min-width:0;padding:10px 8px;border-radius:10px;background:var(--surface2);text-align:center;
    font-family:var(--mono);font-size:9px;color:var(--muted)}
  .runmonthgrid b{display:block;margin-bottom:3px;font-size:13px;color:var(--chalk);white-space:nowrap}
  .runmonthgoal{padding:14px 0;border-bottom:1px solid var(--line)}
  .runmonthgoal .mstone{margin-bottom:7px}
  .runmonthgoal .big{font-size:19px}
  .runmonthgoal .tot{margin-top:7px}
  .runmonthfoot{display:grid;grid-template-columns:repeat(2,1fr);gap:5px 12px;margin-top:12px;padding-top:11px;border-top:1px solid var(--line)}
  .runmonthfoot span{font-family:var(--mono);font-size:9px;color:var(--muted);line-height:1.4}
  @media(max-width:390px){.garight{gap:5px}.gadelta{display:none}.runmonthgrid{grid-template-columns:repeat(2,1fr)}}

  /* ================= v3.3.168 — MINIMAL, the default skin =================
     A theme LAYER, orthogonal to light/dark: <html data-skin="minimal|classic">
     rides beside data-theme, resolved in applyTheme() and pre-painted by
     index.html. Classic is the entire sheet above, untouched — these rules
     only ever ADD under [data-skin="minimal"], so switching back is exact.
     The grammar (from the Bench Press mockup): floating cards over a quiet
     ground, near-zero borders, sans-forward tabular numerals, value-chalk /
     unit-faint pairs, generous row heights, and a dark pill for the nav in
     BOTH themes. Chrome only — the skin defines no ink tokens, so every
     contrast pair the theme guard clears is still the pair on screen
     (buildcheck enforces the allowlist). Red-means-live is untouched.
     Specificity note: :root[data-skin] prefixes lift every rule past the
     base sheet, which SHADOWS stateful variants (.chip.on, .zone.prime,
     .seg .sel …) — each one the base sheet styles is re-asserted here.
     That list is the price of the layer; pay it in full or a state dies. */
  :root[data-skin="minimal"]{
    /* v3.3.520: neutral grays at the old dividers' perceived brightness. */
    --line:#292929; --whisper:#232323;
    --shadow:0 1px 2px rgba(0,0,0,.28),0 10px 30px rgba(0,0,0,.32);

  }
  :root[data-skin="minimal"][data-theme="light"]{
    --line:#EAEAEA; --whisper:#F2F2F2;
    --shadow:0 1px 2px rgba(22,26,40,.05),0 10px 30px rgba(22,26,40,.09);
    /* v3.3.169: the pill FOLLOWS the theme. v3.3.168 promoted the mockup's
       dark pill to a both-themes invariant; the mockup was a light
       composition and its pill belongs to that composition — a dark slab in
       light mode read as chrome from another app. White pill, theme inks. */

  }
  /* v3.3.477: THE PILL'S TOKENS FOLLOW data-bar, NOT data-theme. The bar may
     be dark in a light app or light in a dark one, so its surface, inks and
     lighting are keyed to the appearance the person chose for it. applyTheme
     resolves data-bar (dark | light | the theme, when Match) and index.html
     paints it before any CSS, so a cold start cannot flash the other one.
     v3.3.169's finding stands for an OPAQUE slab and is why Match is the
     default: nothing changes until it is asked to. */
  /* v3.3.510: THE REST RING FOLLOWS THE BAR, NOT THE PAGE. It read --rest-ink,
     which is defined per THEME -- so a light page with a dark bar resolved
     #1B5E20, the dark green meant for white, at 2.07:1 on #1C202A. Barely
     visible, which is what the maker photographed.
     This is the bug v3.3.478 already fixed once for the selected glyph: the
     bar's background is independent of the page theme, so the bar needs its
     own ink. --pill-chalk, --pill-ink and --pill-accent all live in these
     blocks for that reason; the rest ring never got the treatment.
     The value is the WASH grade (--rest), not the ink grade (--rest-ink),
     chosen off a rendered comparison. Ink grade is for type; a 40px stroked
     square is a graphic, and --rest-ink at 9.91:1 read washed out beside the
     accent blue it alternates with. 6.89:1 dark, 5.13:1 light.
     Declared last in each block, right before --pill-shadow, so the fixed
     character windows the contrast test scans are not disturbed. */
  :root[data-skin="minimal"][data-bar="dark"]{
    --pill-chalk:#FFFFFF;   /* v3.3.478: the BAR's strong ink -- see below */
    --pill:#282828; --pill-ink:#BDBDBD;   /* v3.3.519: neutral charcoal, matching the page's gray family */
    --pill-accent:#95A4E8;   /* v3.3.461: = --accent-dim, the app's accent as INK on dark. Raw --accent
                                (#4C6BE3) is 3.3:1 on ordinary dark content and 2.3:1 over an accent
                                button scrolled under the glass; this is 5.9 / 4.0 (test-rest recomputes) */
    /* v3.3.463: the glass lighting, dark. White light on dark glass, quieter:
       specular rim on top, a rim hairline all round, light pooling in from
       above, shadow pooling inside the bottom, then the drop. */
    --pill-rest:#66BB6A;   /* --rest, dark theme's wash grade; 6.89:1 on #1C202A */
    --pill-shadow:inset 0 1px 0 rgba(255,255,255,.28),inset 0 0 0 0.5px rgba(255,255,255,.14),
      inset 0 14px 20px -12px rgba(255,255,255,.04),inset 0 -10px 18px -12px rgba(0,0,0,.45),
      0 12px 32px rgba(0,0,0,.42),0 1px 2px rgba(0,0,0,.30);
  }
  :root[data-skin="minimal"][data-bar="light"]{
    /* v3.3.478: THE SELECTED GLYPH TAKES THE BAR'S INK, NOT THE THEME'S.
       v3.3.470 painted it --chalk, which is the THEME's strong ink -- fine
       while the bar matched the theme, and black-on-dark the moment the bar
       could be dark in a light app (v3.3.477). --pill-chalk is the bar's own,
       and it lives with --pill/--pill-ink/--pill-accent in the [data-bar]
       block, so every colour on the bar is chosen by the bar's appearance and
       none by the app's. */
    --pill-chalk:#111111;
    --pill:#FFFFFF; --pill-ink:#525252;   /* neutral ink also keeps the selected capsule free of blue tint */
    --pill-accent:#2F4BD8;   /* v3.3.461: = --accent, the app's blue, as asked; 3.7:1 as a graphic worst-case */
    /* v3.3.463: the glass lighting, light. Specular rim on top, a hairline of
       light all round, light pooling in from above, a soft shadow pooling
       inside the bottom edge, then a soft drop and a contact shadow. */
    --pill-rest:#2E7D32;   /* --rest, light theme's wash grade; 5.13:1 on #FFFFFF */
    --pill-shadow:inset 0 1px 0 rgba(255,255,255,.95),inset 0 0 0 0.5px rgba(255,255,255,.75),
      inset 0 14px 20px -12px rgba(255,255,255,.55),inset 0 -10px 18px -12px rgba(0,0,0,.10),
      0 10px 30px rgba(0,0,0,.16),0 1px 2px rgba(0,0,0,.08);
  }
  /* floating cards: the border's job moves to the shadow */
  :root[data-skin="minimal"] :is(.card,.zone,.lastcard,.day,.kpi){
    border-color:transparent;box-shadow:var(--shadow)}
  :root[data-skin="minimal"] :is(.card,.zone,.lastcard){border-radius:18px}
  :root[data-skin="minimal"] .card{padding:16px}
  :root[data-skin="minimal"] .zone.prime{border-color:var(--accent-soft)}   /* re-assert */
  :root[data-skin="minimal"] .zone.logged{background:var(--ground);box-shadow:none}
  /* generous rows */
  :root[data-skin="minimal"] .lastrow{padding:8px 0}
  :root[data-skin="minimal"] .day .body .lastrow{padding:7px 0}
  /* sans-forward numerals — tabular, so nothing moves under a thumb */
  :root[data-skin="minimal"] :is(.rhythm .big,.mstone .big,.bwnow,.msmoment .msnum,.kpi .v,.fld input,.wsel .val input,.lastw){
    font-family:var(--disp);font-variant-numeric:tabular-nums}
  /* value-chalk / unit-faint */
  :root[data-skin="minimal"] :is(.lastw .u,.wsel .unit,.rhythm .unit,.chip .n){color:var(--faint)}
  :root[data-skin="minimal"] .chip.on .n{color:inherit}                     /* re-assert */
  /* chips: filled, borderless — every stateful border re-asserted */
  :root[data-skin="minimal"] .chip{background:var(--surface2);border-color:var(--surface2)}
  :root[data-skin="minimal"] .chip.on{background:var(--accent);border-color:var(--accent)}
  :root[data-skin="minimal"] .chip.due{border-color:var(--accent)}
  :root[data-skin="minimal"] :is(.chip.livego,.chip.on.livego){background:var(--live);border-color:var(--live)}
  /* ghost buttons: filled, borderless — states re-asserted */
  :root[data-skin="minimal"] .btn.ghost{background:var(--surface2);border-color:var(--surface2)}
  :root[data-skin="minimal"] .btn.ghost.done{background:none;border-color:var(--accent)}
  :root[data-skin="minimal"] .restbtn.on{border-color:var(--accent-soft)}
  :root[data-skin="minimal"] .msmoment.thousand .btn.ghost{background:none;border-color:rgba(255,255,255,.5)}
  /* fields sink into the card; segs become a filled track */
  :root[data-skin="minimal"] .fld{background:var(--ground);border-color:transparent}
  :root[data-skin="minimal"] :is(.seg button,.onbseg button){background:var(--surface2);border-color:var(--surface2)}
  :root[data-skin="minimal"] :is(.seg button.sel,.onbseg button.sel){background:var(--accent);border-color:var(--accent)}
  /* the pill — nav floats, and its surface follows the theme (dark pill on
     dark, white pill on light; v3.3.169). Ink is its own token pair,
     guarded at 4.5 like every other ink. Wins over the @supports frosted
     nav on specificity, not order. */
  /* v3.3.463: a little narrower (24px in from each edge, was 14) and the
     tabs a little closer (2px side margin, was 4) -- the maker's ask. The
     bar's own box-shadow moves to the glass layer below so the lighting and
     the surface are one object. */
  :root[data-skin="minimal"] nav{
    left:20px;right:20px;bottom:var(--bar-bot);   /* v4.6.152: 20px in, like Threads (was 24) */
    max-width:472px;border-top:0;border-radius:26px;background:var(--pill);
    padding:0 8px;box-shadow:var(--pill-shadow);
    -webkit-backdrop-filter:none;backdrop-filter:none}
  :root[data-skin="minimal"] nav button{margin:4px 2px}
  /* v3.3.458: THE PILL IS GLASS. The pill was opaque; content now passes
     beneath it through a blur, which reads as depth without adding a shadow
     -- the Monument Valley way to say "this floats". v3.3.460: the tint is
     72% ("more transparent"), and the pill's own inks moved to keep the gate:
     muted a step darker/lighter, the active accent a step deeper. The WORST
     backdrop is a full accent button scrolled under the bar; with the inks
     retuned every label clears 4.5:1 there in both themes (muted 4.95 / 6.25,
     active on its capsule 5.19 / 6.02) and sits above 6:1 on ordinary
     content. test-rest recomputes these from the file. The v3.3.179 compositing
     layer (translateZ) stays and is what lets iOS carry a blurred fixed
     element with the viewport instead of lagging it. */
  /* v3.3.462: THE GLASS LIVES ON A PSEUDO-ELEMENT. The maker could not see
     the frost. nav carries translateZ(0) + will-change:transform (v3.3.179,
     for iOS scroll anchoring), and WebKit is unreliable about sampling a
     backdrop for an element that is also its own promoted layer -- the blur
     silently does nothing. Moving the tint and the filter to nav::before,
     which has no transform of its own, gives WebKit a plain element to
     sample behind. The pill itself goes transparent inside @supports so the
     pseudo-element's tint is the only surface. Tint 55% ("more transparent"):
     labels are gone, so the gate is the 3:1 graphics one, and the active
     capsule gets a 60% pill base so blue-on-blue still clears (test-rest). */
  /* v3.3.463: LIQUID GLASS, as far as the web can follow it. Over the app's
     own white cards a blur has nothing to blur, so what reads as glass is the
     LIGHTING: a specular rim along the top, light pooling in from above, a
     soft shadow inside the bottom edge, a hairline of light around the whole
     rim, and a clearer, lighter blur (9px, was 20) with more saturation so
     what does pass beneath comes through richer. The tint is a fall-off, more
     at the top than the bottom, like a thick pane lit from above. Every
     lighting term lives in --pill-shadow so the two themes can set their own
     (white light reads on dark glass too, quieter). Safari cannot refract --
     the lensing of the real thing is not available to a web page -- so this
     is the material without its bending. No colour anywhere in the light. */
  @supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
    /* v3.3.465: NO isolation:isolate HERE. v3.3.462 added it to keep the
       glass layer under the buttons -- and it is exactly what kept the glass
       dead: isolation:isolate makes an element a BACKDROP ROOT, so a
       backdrop-filter on its ::before samples only the nav's own contents
       (transparent) instead of the page behind it. The maker's screenshot
       showed "Legs · 3 days" reading straight through the bar: no blur at
       all. The nav is position:fixed with a z-index, which is already a
       stacking context, so ::before at z-index:0 sits under the buttons at
       z-index:1 without isolating anything. */
    /* v3.3.497: THE BLUR COMES OFF, AND THIS IS THE EXPERIMENT THE v3.3.480
       NOTE ASKED FOR AND NOBODY RAN: "if the pill still hangs, the next test
       is turning the blur off on the nav to confirm the trigger."
       The maker's screenshots settle two things. The pill hangs on a PURE
       SCROLL, with no render and no view transition. And it does not hang
       alone: the "top" button and the HEADER hang with it -- in both shots
       the header is simply absent from the top of the screen. All three are
       position:fixed. So this is not one element mis-anchoring, it is fixed
       positioning failing across the page, and the fix must be something
       page-wide.
       There is exactly one backdrop-filter in this stylesheet and it is on
       the nav. An element with a backdrop-filter needs a BACKDROP ROOT, and
       with nothing bounding it that root is the document -- which is enough,
       on iOS WebKit, to change how the whole page's fixed layer is composited.
       The timeline fits to the release: v3.3.462 added isolation:isolate,
       which bounded the root and, as v3.3.465 discovered, made the blur do
       nothing at all. v3.3.465 removed it so the glass would finally render.
       The hanging is reported from there. v3.3.480 saw the same link -- it
       says so in the note above -- and blamed translateZ. Removing translateZ
       changed nothing. Removing the belt changed nothing. The blur itself is
       what is left.
       So the pill goes opaque. Without a blur behind it a 66-88% tint over
       scrolling content is unreadable, so the tint closes up; --pill already
       follows the theme and the v3.3.477 Match/Light/Dark setting still picks
       the surface, which is the v3.3.169-safe way to be opaque. The lighting
       in --pill-shadow stays: the rim, the pooled light and the inner shadow
       are what read as glass over the app's own white cards anyway -- v3.3.463
       said so itself. What is lost is the refraction of content passing under,
       which Safari could not do properly in the first place.
       If the chrome holds still after this, the blur was the cause and we can
       argue about how to get it back. If it still hangs, the blur is
       eliminated and the last suspect is the v3.3.179 layer promotion. */
    :root[data-skin="minimal"] nav{background:transparent;box-shadow:none}
    :root[data-skin="minimal"] nav::before{content:'';position:absolute;inset:0;border-radius:inherit;z-index:0;
      background:linear-gradient(180deg,var(--pill),color-mix(in srgb,var(--pill) 97%,transparent));
      box-shadow:var(--pill-shadow)}
    :root[data-skin="minimal"] nav button{z-index:1}
    /* the active tab is a glass bead on the glass: its own top highlight and
       a whisper of drop, so it sits ON the surface rather than in it */
    :root[data-skin="minimal"] nav button.on{box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 70%,var(--pill)),0 1px 3px color-mix(in srgb,#000 10%,transparent)}
  }
  :root[data-skin="minimal"] nav button{color:var(--pill-ink);padding:10px 4px}
  /* v3.3.461: --pill-accent IS the app accent now (light) and the app's
     accent-as-ink (dark); the deeper v3.3.460 value protected labels that
     no longer exist. It stays a token because the skin's pill trio is guarded
     by buildcheck (v3.3.168) and the skin block is chrome-only. */
  /* v3.3.462: the capsule stands on a 60% pill base under its 12% accent, so the active blue glyph clears 3:1 even over a blue button (4.30 light, 4.50 dark) */
  :root[data-skin="minimal"] nav button.on{color:var(--pill-chalk);background:color-mix(in srgb,var(--pill) 60%,var(--pill-ink) 16%)}   /* v3.3.470: ink on grey; v3.3.478: the BAR's ink */
  /* v3.3.470: on the pill the closed-day fill is --pill-accent: the accent in light, the accent-as-ink in dark, where the raw accent is 2.3:1 on the grey capsule over a blue button */
  :root[data-skin="minimal"] nav.dayclosed button[data-v="today"] .ng svg .sq{fill:var(--pill-accent)}
  /* v3.3.484: selected completed Today on a dark bar is white, edged in the
     original pale blue. Unselected, open-day and rest states stay unchanged. */
  :root[data-skin="minimal"][data-bar="dark"] nav.dayclosed button[data-v="today"].on .ng svg .sq{fill:var(--pill-chalk);stroke:var(--pill-accent);stroke-width:2}
  /* v3.3.331: the TRAIN TAB joins the skin. Everything above was written
     against the Bench Press mockup, which was a Today composition -- cards,
     zones, fields, the nav pill. The two surfaces that FILL the Train tab,
     .partcard (the body-part grid) and the .item.logrow list beneath it,
     were never in the skin's list, so switching skins changed the nav pill
     and almost nothing else on the screen.
     SCOPE, deliberately narrow. .item.todayrow is NOT here. It carries
     border:1px solid var(--live) -- it IS the live row, red-means-live, and
     a blanket border-color:transparent across .item would have erased that
     law. Its .fin sibling is likewise left alone: those rows sit INSIDE a
     card in the same colour as the card, so v3.3.322's edge is the only
     thing separating them from it. A list of cards and a list inside a card
     are different grammars; only the first is being restyled.
     SPECIFICITY, the v3.3.323 lesson repeated: .item.goto and the ranked
     .item.logrow each re-declare the whole `border` shorthand, so they are
     named explicitly rather than swept up by .item -- and every state the
     base sheet paints as a BORDER (hot, run, liveP, sel) is re-asserted
     below, or it dies under this layer. That is the price of the skin. */
  :root[data-skin="minimal"] :is(.partcard,.item.goto,.item.logrow:not(.goto):not(.todayrow)){
    border-color:transparent;box-shadow:var(--shadow)}
  :root[data-skin="minimal"] .partcard{border-radius:16px;padding:11px 12px;gap:2px}
  :root[data-skin="minimal"] .item.goto{border-radius:16px;padding:15px 16px;margin-bottom:6px}
  :root[data-skin="minimal"] .item.logrow:not(.goto):not(.todayrow){
    border-radius:16px;padding:13px 16px;margin-bottom:6px}
  /* --- states re-asserted: each of these is a BORDER in the base sheet -- */
  :root[data-skin="minimal"] .partcard.hot{border-color:var(--accent)}
  :root[data-skin="minimal"] .partcard.run{border-color:var(--accent-dim)}
  :root[data-skin="minimal"] .partcard.liveP{border-color:var(--live)}
  :root[data-skin="minimal"] .partcard.sel{background:var(--accent);border-color:var(--accent)}
  :root[data-skin="minimal"] .partcard.sel.liveP{background:var(--live);border-color:var(--live)}
  /* found by the coverage guard, not by looking: the gear sits on EVERY
     screen, and onboarding is the first surface a new user ever sees. Both
     re-assert their painted states (header.live inverts the gear; .pri and
     .ghost are the onboarding button's own). */
  :root[data-skin="minimal"] :is(.icobtn,.onbbtn){
    border-color:transparent;box-shadow:var(--shadow)}
  :root[data-skin="minimal"] .icobtn{border-radius:15px}
  :root[data-skin="minimal"] header.live .icobtn{
    background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.3);box-shadow:none}
  :root[data-skin="minimal"] .onbbtn{border-radius:14px}
  :root[data-skin="minimal"] .onbbtn.pri{background:var(--accent);border-color:var(--accent)}
  :root[data-skin="minimal"] .onbbtn.ghost{background:transparent;box-shadow:none}
  /* the pull-to-refresh dot, also found by the guard rather than by eye */
  :root[data-skin="minimal"] #ptr{border-color:transparent;box-shadow:var(--shadow)}
  :root[data-skin="minimal"] .rzbar{background:color-mix(in srgb,var(--accent) 10%,var(--ground))}
  /* v3.3.514: 110 -> 134. The floating pill's top edge sits about 96px above
     the viewport bottom (10px offset + the home indicator's safe area + a 52px
     pill), so 110 left roughly fourteen pixels of air under the last line.
     That was enough while the last thing on a screen was a chart you glance
     at; the rest card now ends in a row of small type -- weekday, percentage,
     longest run -- that has to be READ, and reading something fourteen pixels
     off a floating bar is what the maker means by hidden behind it. */
  :root[data-skin="minimal"] #app{padding-bottom:134px}
  :root[data-skin="minimal"] #toast{bottom:118px}

  /* -----------------------------------------------------------------
     Modern Minimal preview — URL gated with ?design=modern.
     This layer changes no app state and leaves the saved skin untouched.
     It is intentionally a preview token pass, not a second component tree:
     the exact same buttons, cards, charts and routes remain underneath it.
     ----------------------------------------------------------------- */
  :root[data-design-preview="modern"]{
    --preview-r-sm:8px;
    --preview-r-md:11px;
    --preview-r-lg:14px;
    --preview-shadow-card:0 1px 2px rgba(0,0,0,.18),0 8px 24px rgba(0,0,0,.16);
    --preview-shadow-float:0 8px 28px rgba(0,0,0,.28);
    --shadow:0 1px 2px rgba(0,0,0,.18);
  }
  :root[data-design-preview="modern"][data-theme="light"]{
    --ground:#F5F6F8;
    --surface:#FFFFFF;
    --surface2:#ECEEF3;
    --line:#E1E3E9;
    --chalk:#17191F;
    --muted:#535866;
    --faint:#737987;
    --accent:#334FD6;
    --accent-ink:#2E48C8;
    --accent-soft:#DCE2FF;
    --preview-shadow-card:0 1px 2px rgba(22,26,40,.05),0 8px 24px rgba(22,26,40,.07);
    --preview-shadow-float:0 8px 28px rgba(22,26,40,.16);
    --shadow:0 1px 2px rgba(22,26,40,.06);
  }
  :root[data-design-preview="modern"] #app{
    font-size:13px;
    line-height:1.335;
    padding-bottom:104px;
  }
  :root[data-design-preview="modern"] header{
    padding:calc(13px + env(safe-area-inset-top,0px)) 18px 12px;
    border-bottom-color:color-mix(in srgb,var(--line) 84%,transparent);
  }
  :root[data-design-preview="modern"] .h-date{
    font-size:21px;
    font-weight:600;
    letter-spacing:-.035em;
  }
  :root[data-design-preview="modern"] .h-sub{
    margin-top:2px;
    font-size:10px;
    letter-spacing:.11em;
  }
  :root[data-design-preview="modern"] .icobtn{
    width:42px;
    height:42px;
    border-radius:var(--preview-r-md);
    border-color:var(--line);
    box-shadow:var(--shadow);
  }
  :root[data-design-preview="modern"] main{
    padding:18px 16px calc(30px + env(safe-area-inset-bottom));
  }
  :root[data-design-preview="modern"] h2{
    margin:22px 0 8px;
    font-size:10px;
    letter-spacing:.12em;
    opacity:1;
    color:var(--muted);
    gap:6px;
    padding-left:1px;
  }
  /* v3.3.512: the modern preview had its own tick; the rule replaces both. */
  :root[data-design-preview="modern"] h2::after{background:var(--line)}
  :root[data-design-preview="modern"] h2 .hi{color:var(--accent-ink)}
  :root[data-design-preview="modern"] :is(.card,.zone,.lastcard,.day,.kpi){
    border:1px solid var(--line);
    border-radius:var(--preview-r-lg);
    box-shadow:var(--preview-shadow-card);
  }
  :root[data-design-preview="modern"] .card{padding:16px}
  :root[data-design-preview="modern"] .rhythm{
    padding:17px 16px 14px;
  }
  :root[data-design-preview="modern"] .rhythm .big.dayn{
    font-family:var(--disp);
    font-variant-numeric:tabular-nums;
    font-size:31px;
    font-weight:600;
    letter-spacing:-.05em;
  }
  :root[data-design-preview="modern"] .rgrid .rpct{
    font-family:var(--disp);
    font-variant-numeric:tabular-nums;
    font-size:21px;
    font-weight:600;
    letter-spacing:-.035em;
  }
  :root[data-design-preview="modern"] .rgrid .rcap{margin-top:4px}
  :root[data-design-preview="modern"] .strip{gap:4px;margin-top:13px}
  :root[data-design-preview="modern"] .strip i{
    height:24px;
    border:0;
    border-radius:4px;
  }
  :root[data-design-preview="modern"] .strip::after{display:none}
  :root[data-design-preview="modern"] .item{
    min-height:50px;
    border-radius:var(--preview-r-md);
    box-shadow:none;
    transition:background .12s ease,transform .12s ease,border-color .12s ease;
  }
  /* The old left status rail/check combination is the generic card pattern
     this preview is testing away. The whole row remains one clear target. */
  :root[data-design-preview="modern"] .item.todayrow,
  :root[data-design-preview="modern"] .item.todayrow.fin,
  :root[data-design-preview="modern"] .item.goto{
    border-left:1px solid var(--line);
  }
  :root[data-design-preview="modern"] .item.goto{
    background:color-mix(in srgb,var(--accent) 6%,var(--surface));
    border-color:color-mix(in srgb,var(--accent) 24%,var(--line));
  }
  :root[data-design-preview="modern"] .item.goto .gochev{
    width:24px;
    height:24px;
    right:8px;
    display:grid;
    place-items:center;
    border-radius:50%;
    background:var(--accent-soft);
    color:var(--accent-ink);
    font-size:12px;
    font-weight:600;
    opacity:1;
  }
  :root[data-design-preview="modern"] .item.todayrow,
  :root[data-design-preview="modern"] .item.todayrow.fin{
    background:var(--surface2);
  }
  :root[data-design-preview="modern"] .item.todayrow b::before,
  :root[data-design-preview="modern"] .item.todayrow.fin b::before{display:none}
  :root[data-design-preview="modern"] .item:active{
    transform:scale(.985);
    background:color-mix(in srgb,var(--accent) 10%,var(--surface));
    border-color:color-mix(in srgb,var(--accent) 36%,var(--line));
  }
  :root[data-design-preview="modern"] .chip{
    min-height:42px;
    border-radius:var(--preview-r-md);
    padding:9px 14px;
    box-shadow:none;
    background:var(--surface2);
    border-color:var(--surface2);
    transition:transform .12s ease,background .12s ease;
  }
  :root[data-design-preview="modern"] .chip:active{transform:scale(.97)}
  :root[data-design-preview="modern"] .chip.on{
    background:var(--accent);
    border-color:var(--accent);
  }
  :root[data-design-preview="modern"] .partcard{
    border-radius:var(--preview-r-md);
    box-shadow:none;
    transition:transform .12s ease,background .12s ease,border-color .12s ease;
  }
  :root[data-design-preview="modern"] .partcard:active{transform:scale(.98)}
  :root[data-design-preview="modern"] .btn{
    min-height:48px;
    border-radius:var(--preview-r-md);
    font-weight:600;
    transition:transform .12s ease,filter .12s ease;
  }
  :root[data-design-preview="modern"] .btn:active{transform:scale(.985);filter:brightness(.96)}
  :root[data-design-preview="modern"] .btn.ghost{
    background:var(--surface2);
    border-color:var(--surface2);
  }
  :root[data-design-preview="modern"] :is(.kpi,.day,.cal,.mchip,.fld,.settile,.repcard){
    border-radius:var(--preview-r-md);
  }
  :root[data-design-preview="modern"] .kpi .v,
  :root[data-design-preview="modern"] .kpis .kpi.hero .v{
    font-family:var(--disp);
    font-variant-numeric:tabular-nums;
    font-weight:600;
  }
  :root[data-design-preview="modern"] .day{box-shadow:var(--shadow)}
  :root[data-design-preview="modern"] nav{
    left:12px;
    right:12px;
    bottom:calc(8px + env(safe-area-inset-bottom,0px));
    max-width:496px;
    border:1px solid color-mix(in srgb,var(--line) 85%,transparent);
    border-radius:18px;
    background:var(--surface);   /* v3.3.497: no backdrop-filter on fixed chrome */
    padding:5px 4px;
    box-shadow:var(--preview-shadow-float);
  }
  :root[data-design-preview="modern"] nav button{
    min-height:52px;
    padding:7px 4px 8px;
    gap:5px;
    border-radius:13px;
    font-size:8px;
    letter-spacing:.1em;
    transition:transform .12s ease,background .12s ease,color .12s ease;
  }
  :root[data-design-preview="modern"] nav button:active{
    transform:scale(.94);
    background:var(--surface2);
  }
  :root[data-design-preview="modern"] nav button.on{
    background:color-mix(in srgb,var(--accent) 10%,transparent);
  }
  :root[data-design-preview="modern"] nav button.on::after{display:none}
  :root[data-design-preview="modern"] nav button span{
    display:block;
    width:19px;
    height:19px;
    font-size:0;
    line-height:1;
    filter:none;
    background:currentColor;
    -webkit-mask-position:center;
    -webkit-mask-repeat:no-repeat;
    -webkit-mask-size:contain;
    mask-position:center;
    mask-repeat:no-repeat;
    mask-size:contain;
  }
  :root[data-design-preview="modern"] nav button[data-v="today"] span{
    -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='3' width='18' height='18' rx='4'/%3E%3Cpath d='m8 12 2.5 2.5L16.5 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%3Crect x='3' y='3' width='18' height='18' rx='4'/%3E%3Cpath d='m8 12 2.5 2.5L16.5 8.5'/%3E%3C/svg%3E");
  }
  :root[data-design-preview="modern"] nav button[data-v="lift"] span{
    -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='M7 8v8M4 9.5v5M17 8v8M20 9.5v5M7 12h10'/%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='M7 8v8M4 9.5v5M17 8v8M20 9.5v5M7 12h10'/%3E%3C/svg%3E");
  }
  :root[data-design-preview="modern"] nav button[data-v="stats"] span{
    -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 19V10M10 19V5M16 19v-7M22 19H2'/%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 19V10M10 19V5M16 19v-7M22 19H2'/%3E%3C/svg%3E");
  }
  :root[data-design-preview="modern"] nav button[data-v="history"] span{
    -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='M6 3h12v18H6zM9 8h6M9 12h6M9 16h4'/%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='M6 3h12v18H6zM9 8h6M9 12h6M9 16h4'/%3E%3C/svg%3E");
  }
  @media (prefers-reduced-motion:reduce){
    :root[data-design-preview="modern"] :is(.item,.chip,.partcard,.btn,nav button){transition:none}
  }

  /* v3.3.516: approved TODAY/TRAIN refinement. One opt-out removes this
     entire layer; the existing skins and all record/charts stay authoritative.
     No global card, header, navigation, logger, Stats or History restyling. */
  :root[data-flow="refined"] #view[data-flow-view="today"] h2:not(.ddyou),
  :root[data-flow="refined"] #view[data-flow-view="lift"][data-flow-screen="browse"] h2{
    gap:8px;letter-spacing:.13em}
  :root[data-flow="refined"] #view:is([data-flow-view="today"],[data-flow-view="lift"][data-flow-screen="browse"]) h2::before{display:none}
  :root[data-flow="refined"] #view:is([data-flow-view="today"],[data-flow-view="lift"][data-flow-screen="browse"]) h2::after{
    content:'';height:1px;min-width:12px;flex:1;background:var(--line);order:1}
  /* v3.3.527: the glyph sits with its word, on the same gap the sparkle uses
     before WRITE -- the two controls now read as a pair rather than one run-on
     string of capitals. */
  .pedge[data-planpaste]{display:inline-flex;align-items:center;gap:5px}
  .pedge[data-planpaste] .ic{flex:0 0 auto}
  :root[data-flow="refined"] .flow-paste{order:3;min-height:44px;min-width:44px;color:var(--accent-ink)}
  :root[data-flow="refined"] #view[data-flow-view="today"] h2.planhead{column-gap:4px;row-gap:2px;flex-wrap:wrap}
  :root[data-flow="refined"] #view[data-flow-view="today"] h2.planhead::after{min-width:0}
  :root[data-flow="refined"] #view[data-flow-view="today"] h2.planhead>.notei{flex-shrink:0}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planedge{gap:2px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .pedge{min-height:44px;min-width:30px;display:inline-flex;align-items:center;justify-content:center}
  :root[data-flow="refined"] #view[data-flow-view="today"] .pglyph{width:30px}
  :root[data-flow="refined"] .flow-dayone-plan{display:flex;align-items:center;justify-content:center;gap:18px;margin-top:16px;padding-top:16px;border-top:1px solid var(--line)}
  :root[data-flow="refined"] .flow-dayone-plan .planedge{display:flex;gap:12px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .plancard{padding:4px 18px}
  /* v3.3.528: the expanded plan is a compact reference, below the action
     hierarchy. Keep the numeric readout and whole-row tap target readable. */
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow{display:block;padding:12px 0;border-radius:0;min-height:64px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow::after{left:0;right:0}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow .pn{display:flex;align-items:center;justify-content:space-between;font-size:13px;font-weight:500;gap:12px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow .pk{display:inline-flex;align-items:center;gap:5px 6px;white-space:nowrap}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow .pl{margin:5px 0 0;grid-template-columns:minmax(72px,var(--planw)) 12px minmax(0,1fr);gap:2px 10px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow :is(.pv,.px,.pr){font-size:11px;line-height:1.41}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow .pr{white-space:normal;overflow-wrap:anywhere}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planrow .pw{color:var(--muted)}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planfoldrow{margin-bottom:0;padding:16px 18px;min-height:54px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planfold:not(.shut) .plancard{margin-top:6px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .daycard .plancard{margin-top:0}
  /* v3.3.532: Week is an overview, one step quieter than the focused day.
     Keep the same numeric type, aligned columns and fold motion. */
  :root[data-flow="refined"] #view[data-flow-view="today"] .weekstack .daycard .plancard{padding:0 12px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .weekstack .planrow{padding:8px 0;min-height:56px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .weekstack .planrow .pn{font-size:12px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .weekstack .planrow .pl{margin-top:3px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planta{border-radius:12px;padding:14px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .writecard .seg button{min-height:54px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .writecard .seg button small{font-size:10px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planpv{padding-top:17px;padding-bottom:17px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planpv .pb b{font-size:14px}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planpv .pb i{font-size:11px;line-height:1.551}
  :root[data-flow="refined"] #view[data-flow-view="today"] .planpv :is(.lsx,.pgrip){min-width:36px;min-height:44px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partgrid{gap:7px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partcard{align-items:flex-start;text-align:left;border-radius:12px;padding:10px 8px;min-height:54px;box-shadow:none}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partcard b{font-size:11px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partcard .ps{font-size:10px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partcard:not(.sel):not(.liveP) .ps{color:var(--faint);font-weight:400}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partlast{margin-top:14px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partlast>.lasthead{margin-bottom:0;min-height:44px;align-items:center}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partlast>.lasthead>span{display:inline-flex;align-items:center;gap:8px;line-height:1.222}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partlast .linkdate{display:inline-flex;align-items:center;min-height:44px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partlast .plfold{min-width:44px;min-height:44px;justify-content:center}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .partlast .inlinehelp{padding-top:12px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup{background:var(--surface);border-radius:18px;padding:2px 17px;box-shadow:var(--shadow);margin-bottom:10px}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .item.logrow{background:none;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:0;margin:0;box-shadow:none;gap:4px;animation:none}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .item.logrow:last-child{border-bottom:0}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .item.logrow.enter{animation:cardin var(--dur-move) var(--settle) backwards;animation-delay:calc(var(--i,0)*.018s)}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .logmain{display:flex;align-items:center;gap:10px;padding:18px 0;min-height:76px;width:100%}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exname{flex:1;min-width:0}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .logmain b{display:block;font-size:14px;line-height:1.269;white-space:normal;overflow:visible}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .logmain .sub{display:block;margin-top:4px;font-size:10px;line-height:1.504;color:var(--faint);font-weight:400;white-space:normal}
  :root[data-flow="refined"] #view[data-flow-view="lift"] :is(.flow-goto,.fresh-group .item.logrow.goto) .logmain b{font-size:15px;font-weight:600}   /* v4.6.367: a first visit's starters are go-to rows */
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .pr-top{font-size:11px;color:var(--muted)}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exchev{flex:0 0 auto;color:var(--faint);transition:transform var(--dur-quick) var(--settle)}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .logmain:active .flow-exchev{transform:translateX(2px)}
  :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .logmain:active{transform:scale(.99)}
  /* v3.3.517: one slow sweep on the selected capsule only. Keep the icon
     above the light, and avoid filter/transform changes on the fixed bar. */
  :root[data-flow="refined"] nav button.on::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:0;
    background:linear-gradient(105deg,transparent 44%,rgba(255,255,255,.22) 50%,transparent 56%);
    background-size:300% 100%;background-position:100% 0;animation:sheen 6.8s ease-in-out 1.3s infinite}
  :root[data-flow="refined"] nav button .ng{position:relative;z-index:1}
  /* v3.3.519: neutral surfaces, slightly deeper light shading and modest
     dark translucency. Keep the fixed nav blur/transform-free (v3.3.497). */
  :root[data-flow="refined"][data-skin="minimal"][data-bar="light"] nav::before{
    /* v3.3.523: Airy Silver. Keep the clearest edge below the icons.
       Middle alphas are .03 denser than the preview to protect gray glyphs
       over black content, without dimming the approved lighter icon ink. */
    background:linear-gradient(180deg,rgba(255,255,255,.97) 0%,rgba(251,251,251,.94) 15%,rgba(241,241,241,.95) 48%,rgba(230,230,230,.94) 78%,rgba(192,192,192,.48) 100%);
    box-shadow:inset 0 1px 0 #FFFFFF,inset 0 0 0 .5px rgba(255,255,255,.79),inset 0 -1px 0 rgba(0,0,0,.19),inset 0 9px 12px -10px #FFFFFF,0 10px 30px rgba(0,0,0,.20),0 1px 2px rgba(0,0,0,.08)}
  :root[data-flow="refined"][data-skin="minimal"][data-bar="light"] nav button{color:#797979}
  :root[data-flow="refined"][data-skin="minimal"][data-bar="light"] nav button.on{
    color:var(--pill-chalk);
    /* v3.3.531: approved B, luminous rim. Light is below the existing icon
       and shimmer layers; the bar, header and navigation geometry stay put. */
    background:radial-gradient(ellipse at 50% -35%,#FFFFFF 0%,#FFFFFF55 43%,transparent 72%),linear-gradient(180deg,#F7F7F7 0%,#DFDFDF 42%,#B9B9B9 100%);
    box-shadow:inset 0 1.5px 0 #FFFFFF,inset 1px 0 .8px #FFFFFFF0,inset -1px 0 .8px #FFFFFFBF,inset 0 -1px 0 #FFFFFF99,0 0 0 .7px #FFFFFFC9,0 -2px 9px 1px #FFFFFFC2,0 3px 6px #00000029}
  :root[data-flow="refined"][data-skin="minimal"][data-bar="dark"] nav::before{
    /* v3.3.521: approved Polished Graphite, with a clearer lower half.
       Lighting belongs to the surface, never opacity on the icon layer. */
    background:linear-gradient(180deg,rgba(78,78,78,.98) 0%,rgba(46,46,46,.98) 15%,rgba(29,29,29,.94) 48%,rgba(21,21,21,.82) 100%);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.52),inset 0 0 0 .5px rgba(255,255,255,.19),inset 0 -1px 0 rgba(0,0,0,.4),0 12px 24px rgba(0,0,0,.33),0 2px 3px rgba(0,0,0,.2)}
  :root[data-flow="refined"][data-skin="minimal"][data-bar="dark"] nav button.on{
    background:linear-gradient(180deg,#4C4C4C,#303030);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.8),0 1px 3px rgba(0,0,0,.2)}
  /* v3.3.537: approved B Silver + Porcelain ONLY for dark content + light bar.
     The brighter selected pill is distinct from the quieter neutral bar.
     Keep all other combinations on their existing Airy/Graphite rules. */
  :root[data-flow="refined"][data-skin="minimal"][data-theme="dark"][data-bar="light"] nav::before{
    background:linear-gradient(180deg,rgba(215,215,215,.92) 0%,rgba(181,181,181,.85) 43%,rgba(151,151,151,.88) 100%);
    box-shadow:inset 0 1px 1px rgba(255,255,255,.75),inset 0 0 0 .5px rgba(255,255,255,.40),inset 0 -1px 2px rgba(0,0,0,.16),0 12px 25px rgba(0,0,0,.40)}
  :root[data-flow="refined"][data-skin="minimal"][data-theme="dark"][data-bar="light"] nav button{color:#424242}
  /* Ink grades for the darker silver, not changes to the page's accents.
     Selected Today still uses the original blue on its porcelain surface. */
  :root[data-flow="refined"][data-skin="minimal"][data-theme="dark"][data-bar="light"] nav button:not(.on){--pill-accent:#2032A5;--pill-rest:#18491F}
  :root[data-flow="refined"][data-skin="minimal"][data-theme="dark"][data-bar="light"] nav button.on{
    color:#181818;
    background:linear-gradient(175deg,rgba(255,255,255,.99),rgba(237,237,237,.98) 55%,rgba(205,205,205,.97));
    box-shadow:inset 0 1px 1px rgba(255,255,255,.90),inset 0 -1px 2px rgba(255,255,255,.28),0 2px 7px rgba(0,0,0,.15)}
  /* v3.3.538: approved A Porcelain, only light content + light bar.
     Background only: keep the existing rim, shimmer, icons and geometry. */
  :root[data-flow="refined"][data-skin="minimal"][data-theme="light"][data-bar="light"] nav button.on{
    background:radial-gradient(ellipse at 50% -35%,#FFFFFF 0%,#FFFFFF55 43%,transparent 72%),linear-gradient(180deg,#FFFFFF 0%,#F8F8F8 42%,#EDEDED 100%)}
  @media(max-width:380px){
    :root[data-flow="refined"] #view[data-flow-view="today"] h2{flex-wrap:wrap}
    :root[data-flow="refined"] #view[data-flow-view="today"] h2 .planedge{gap:2px}
    :root[data-flow="refined"] #view[data-flow-view="today"] .planpv{gap:6px}
    :root[data-flow="refined"] #view[data-flow-view="today"] .planpv .pb b{font-size:13px}
  }
  @media(prefers-reduced-motion:reduce){
    :root[data-flow="refined"] nav button.on::after{animation:none;background:none}
    :root[data-flow="refined"] .flow-exchev{transition:none}
    :root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .item.logrow.enter{animation:none}
  }
/* Persistent workout action, independent of plans and the four navigation tabs. */
:root{--live-workout-extra:72px}
/* v4.6.59: THE BAR POSITIONS ITSELF. positionLiveWorkout() copied the NAV's
   measured rectangle into inline left/width/bottom on every render, one frame
   late. Measure during a transition -- a tab switch, a soft render -- and a
   transient rectangle got written into durable inline styles, where it stayed
   until the next render or resize. That is how one bad frame became a bar stuck
   off the right edge, and why the nav beside it looked wrong at the same moment:
   they were reading the same instant. The nav is fixed, centred and capped at
   520px by CSS; the bar now states the SAME constraints instead of copying the
   result of them, so there is nothing to measure and nothing to get stale. */
/* v4.6.63: THE BAR IS THE LIVE SURFACE. The bar only exists while a workout is
   open -- syncLiveWorkout() hides it the moment there are no sets or the day is
   closed -- so "live" is not a state the bar can be in, it is the only state it
   has. That means the red needs no state class, and it also means v4.6.60's
   pill-token paint job is now a dead constraint: it chose the bar's charcoal
   against the NAV's appearance, and the bar is no longer charcoal in any skin.
   Removed rather than left to rot, per the named pattern.

   --live-bar is NOT var(--live). The theme grades of --live (#C93A2B dark,
   #BE3324 light) are tuned for red AS INK ON THE PAGE; this is white ink ON
   red, which is the opposite problem. #BE3324 is the grade where the 11px meta
   line clears 4.5:1 (4.89:1); the dark grade lands at 4.40:1 and misses. The
   bar's appearance is chosen independently of the page, so it takes one value
   in both themes. Title 5.69:1, meta 4.89:1, Finish ink on its cap 7.6:1. */
:root{--live-bar:#BE3324;--live-bar-ink:#FFFFFF;--live-bar-meta:#F8EBE9;
  --live-bar-cap:#9C2A1E;--live-bar-quiet:#F3D4D0}
#liveWorkoutBar{position:fixed;z-index:31;box-sizing:border-box;display:flex;align-items:center;gap:6px;padding:8px 6px 8px 14px;background:var(--live-bar);color:var(--live-bar-ink);border:1px solid rgba(255,255,255,.20);border-radius:18px;
  left:0;right:0;margin:0 auto;width:min(520px,100%);max-width:100%;
  bottom:calc(58px + env(safe-area-inset-bottom, 0px) + 10px);
  animation:live-workout-breathe 4.2s ease-in-out infinite}
/* the minimal skin floats its nav 10px off the floor and narrows it to 472px */
:root[data-skin="minimal"] #liveWorkoutBar{width:min(472px,calc(100% - 40px));
  bottom:calc(var(--bar-bot) + 58px + 10px)}
#liveWorkoutBar[hidden]{display:none}
#liveWorkoutBar button{font-family:var(--body);border:0;margin:0;cursor:pointer;min-height:44px}
#liveWorkoutResume{flex:1;min-width:0;text-align:left;padding:0;background:none;color:var(--live-bar-ink)}
.live-workout-title{display:flex;align-items:center;gap:7px;font-size:11px;font-weight:500;line-height:1.316}
.live-workout-title i{width:7px;height:7px;flex-shrink:0;border-radius:50%;background:var(--live-bar-ink);animation:live-workout-pulse 2.8s ease-in-out infinite}
.live-workout-meta{display:block;color:var(--live-bar-meta);font-size:10px;line-height:1.316;margin:2px 0 0 14px;font-variant-numeric:tabular-nums}
#liveWorkoutFinish{display:flex;align-items:center;justify-content:center;gap:7px;padding:8px 12px;flex-shrink:0;background:var(--live-bar-cap);color:var(--live-bar-ink);border-radius:12px;font-size:11px;font-weight:500}
#liveWorkoutBar.lw-anim{overflow:hidden;white-space:nowrap}   /* v4.6.70: no reflow while the fold is mid-squeeze */
#liveWorkoutFold{display:flex;align-items:center;justify-content:center;width:38px;flex-shrink:0;background:none;color:var(--live-bar-quiet);padding:0}

/* v4.6.63: FOLDED. Same element, same red, same breath -- less of it. The fold
   gives back the screen the bar was covering; it does not stop saying live,
   because a workout is still open and the app must not pretend otherwise.
   Folded, the whole bar is one target and it unfolds; Finish is reached by
   unfolding, which is why nothing destructive hides behind the fold. */
/* v4.6.68: the fold never shrank on the minimal skin. Its width rule lost the
   cascade to `:root[data-skin="minimal"] #liveWorkoutBar` (1,2,0 beats 1,1,0),
   so the capsule the mock showed shipped as a full-width bar. Stated at the
   skin's own specificity, for both skins. */
#liveWorkoutBar.folded,:root[data-skin="minimal"] #liveWorkoutBar.folded{width:fit-content;max-width:calc(100% - 40px);padding:0 16px;gap:9px;
  animation-name:live-workout-breathe-sm}
#liveWorkoutBar.folded #liveWorkoutResume{flex:0 0 auto;display:flex;align-items:center;gap:9px}
#liveWorkoutBar.folded .live-workout-title{gap:9px}
#liveWorkoutBar.folded .lw-label,
#liveWorkoutBar.folded .live-workout-meta,
#liveWorkoutBar.folded #liveWorkoutFinish,
#liveWorkoutBar.folded #liveWorkoutFold{display:none}
#liveWorkoutBar.folded .lw-brief{display:inline;font-variant-numeric:tabular-nums}
.lw-brief{display:none}

@keyframes live-workout-breathe{
  0%,100%{box-shadow:inset 0 1px 0 rgba(255,255,255,.30),inset 0 -10px 18px -12px rgba(0,0,0,.40),
    0 3px 16px rgba(190,51,36,.32),0 0 34px rgba(190,51,36,.18),0 1px 2px rgba(0,0,0,.30)}
  50%{box-shadow:inset 0 1px 0 rgba(255,255,255,.30),inset 0 -10px 18px -12px rgba(0,0,0,.40),
    0 5px 22px rgba(190,51,36,.46),0 0 48px rgba(190,51,36,.30),0 1px 2px rgba(0,0,0,.30)}
}
@keyframes live-workout-breathe-sm{
  0%,100%{box-shadow:inset 0 1px 0 rgba(255,255,255,.28),0 2px 10px rgba(190,51,36,.30),0 0 22px rgba(190,51,36,.16)}
  50%{box-shadow:inset 0 1px 0 rgba(255,255,255,.28),0 3px 14px rgba(190,51,36,.42),0 0 32px rgba(190,51,36,.26)}
}
:root.workout-active.workout-active #app{padding-bottom:calc(134px + var(--live-workout-extra,72px))}
:root.workout-active .pw-save-dock{bottom:calc(96px + var(--live-workout-extra,72px))}
#workoutFinishDialog,#planMoveDialog{position:fixed;inset:auto 0 0;margin:0 auto;width:100%;max-width:520px;box-sizing:border-box;padding:14px 24px calc(24px + env(safe-area-inset-bottom,0px));border:1px solid var(--line);border-radius:26px 26px 0 0;background:var(--surface);color:var(--chalk);font-family:var(--body);max-height:90%;overflow:auto}
#workoutFinishDialog::backdrop,#planMoveDialog::backdrop{background:#0007}
#workoutFinishDialog h2,#planMoveDialog h2{font:500 20px/1.2 var(--body);letter-spacing:-.4px;margin:0 0 10px;text-transform:none;opacity:1;display:block}
#planMoveDialog h2::after,#planMoveDialog h2::before{content:none}
#workoutFinishDialog p,#planMoveDialog p{font:400 12px/1.41 var(--body);color:var(--muted);margin:10px 0 18px}
#workoutFinishDialog .btn{display:flex;align-items:center;justify-content:center;gap:8px;min-height:48px}
#workoutKeepTraining{margin-top:6px}
.workout-finish-handle{width:34px;height:4px;background:var(--line);border-radius:4px;margin:0 auto 19px}
@keyframes live-workout-pulse{50%{opacity:.4}}
@media(prefers-reduced-motion:reduce){
  .live-workout-title i{animation:none}
  /* the bar still has to READ as live without moving: the breath settles at
     its own midpoint instead of stopping at whichever frame it was on. */
  #liveWorkoutBar{animation:none;box-shadow:inset 0 1px 0 rgba(255,255,255,.30),
    inset 0 -10px 18px -12px rgba(0,0,0,.40),0 3px 18px rgba(190,51,36,.36),
    0 0 38px rgba(190,51,36,.22),0 1px 2px rgba(0,0,0,.30)}
  #liveWorkoutBar.folded{animation:none;box-shadow:inset 0 1px 0 rgba(255,255,255,.28),
    0 2px 12px rgba(190,51,36,.32),0 0 26px rgba(190,51,36,.18)}
}

/* v4.6.107: a set loaded from the Your sets table lands in the entry panel
   with one small pop -- the weight scales and takes an accent wash, the rep
   band rings once as the ruler settles. Decoration over an honest swap; the
   values themselves are never animated. Reduced motion never adds the class. */
@keyframes plArriveVal{0%{transform:scale(1);background:transparent}
  30%{transform:scale(1.07);background:color-mix(in srgb,var(--accent) 14%,transparent)}
  100%{transform:scale(1);background:transparent}}
@keyframes plArriveBand{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 50%,transparent)}
  100%{box-shadow:0 0 0 12px color-mix(in srgb,var(--accent) 0%,transparent)}}
.zone.prime .wsel .val.pl-arrive{animation:plArriveVal .52s var(--settle) both;border-radius:12px}
.zone.prime .repwrap .rrband.pl-arrive{animation:plArriveBand .6s var(--settle) both}
@media (prefers-reduced-motion:reduce){.pl-arrive{animation:none!important}}

/* v4.6.123: one floating chrome surface. The absolute paint layer keeps
   fixed chrome out of the backdrop root. Header and nav share material tokens. */
:root{
  --chrome-material:linear-gradient(180deg,rgba(255,255,255,.97) 0%,rgba(251,251,251,.94) 15%,rgba(241,241,241,.95) 48%,rgba(230,230,230,.94) 78%,rgba(192,192,192,.48) 100%);
  --chrome-shadow:inset 0 1px 0 #fff,inset 0 0 0 .5px rgba(255,255,255,.79),inset 0 -1px 0 rgba(0,0,0,.19),inset 0 9px 12px -10px #fff,0 10px 30px rgba(0,0,0,.20),0 1px 2px rgba(0,0,0,.08);
  --chrome-ink:#181818;--live-workout-extra:0px;
  /* v4.6.152: how far the floating bars sit from the screen edges. Tighter, like
     Threads: the header 4px INTO the top safe area (10pt under the Dynamic
     Island), the tab bar 12px into the bottom one (22pt off the screen edge,
     clear of the home indicator). Floors keep the old 12/10px on screens with
     no safe area (desktop, home-button iPhones), where "into" would be off-screen.
     Everything that sits against a bar reads these two tokens. */
  --bar-top:max(12px,calc(env(safe-area-inset-top,0px) - 4px));
  --rest-glow-core:#148447;--rest-glow-mid:#0b6035;--rest-glow-edge:#0b3320;--rest-glow-y:calc(var(--bar-top) + 32px);
  --bar-bot:max(10px,calc(env(safe-area-inset-bottom,0px) - 12px));
}
:root[data-bar="dark"]{
  --chrome-material:linear-gradient(180deg,rgba(78,78,78,.98) 0%,rgba(46,46,46,.98) 15%,rgba(29,29,29,.94) 48%,rgba(21,21,21,.82) 100%);
  --chrome-shadow:inset 0 1px 0 rgba(255,255,255,.52),inset 0 0 0 .5px rgba(255,255,255,.19),inset 0 -1px 0 rgba(0,0,0,.4),0 12px 24px rgba(0,0,0,.33),0 2px 3px rgba(0,0,0,.2);
  --chrome-ink:#f5f5f5;
}
:root[data-theme="dark"][data-bar="light"]{
  --chrome-material:linear-gradient(180deg,rgba(215,215,215,.92) 0%,rgba(181,181,181,.85) 43%,rgba(151,151,151,.88) 100%);
  --chrome-shadow:inset 0 1px 1px rgba(255,255,255,.75),inset 0 0 0 .5px rgba(255,255,255,.40),inset 0 -1px 2px rgba(0,0,0,.16),0 12px 25px rgba(0,0,0,.40);
}
html body #nav::before{background:var(--chrome-material);box-shadow:var(--chrome-shadow)}
/* v4.6.125: a missed day on the silver header of dark theme + light bar.
   It kept dark's --surface2 (#272727) and read as a black square on silver.
   The maker picked #858585 of six shades: plainly lighter, still a square. */
:root[data-theme="dark"][data-bar="light"] body #app header:not(.live) .h-week .hwd:not(.on):not(.ahead){background:#858585}
/* v4.6.126: the same fix for the other mixed pairing -- light theme, dark bar.
   The missed day kept light's #D4D4D4 and was the brightest mark on the
   graphite header, louder than the days trained. #5C5C5C, the maker's pick. */
:root[data-theme="light"][data-bar="dark"] body #app header:not(.live) .h-week .hwd:not(.on):not(.ahead){background:#5C5C5C}
/* v4.6.125: the day count takes the header's ink. It kept the page's --chalk,
   so on the silver header of dark theme + light bar it was white on silver. */
html body #app header:not(.live) .streak:not(.restchip){color:var(--chrome-ink)}
html body #app header{
  top:var(--bar-top);left:18px;right:18px;width:auto;max-width:484px;
  min-height:64px;height:auto;margin:auto;padding:10px 12px 10px 8px;gap:8px;border-radius:24px;
  background:transparent;box-shadow:none;border:0;color:var(--chrome-ink);
}
html body #app header .hglass{
  border:0;border-radius:inherit;background:var(--chrome-material);box-shadow:var(--chrome-shadow);
  backdrop-filter:none;-webkit-backdrop-filter:none;animation:none;
}
html body #app header.live .hglass{
  background:linear-gradient(180deg,rgba(199,71,56,.97) 0%,rgba(187,53,39,.94) 15%,rgba(171,43,31,.95) 48%,rgba(154,35,25,.94) 78%,rgba(128,29,21,.70) 100%);
  box-shadow:var(--chrome-shadow),0 0 0 1px rgba(226,68,48,.72),0 0 12px rgba(225,57,40,.36),0 5px 25px rgba(219,51,35,.22);
}
html[data-theme="dark"] body #app header.live .hglass{
  background:linear-gradient(180deg,rgba(177,57,44,.98) 0%,rgba(154,40,30,.98) 15%,rgba(135,32,24,.94) 48%,rgba(110,26,20,.82) 100%);
}
html body #app header .brandrow{flex:1;min-width:0;gap:7px;align-items:center}
html body #app header .h-date{font-size:14px;line-height:1.25;letter-spacing:-.3px;white-space:normal;overflow:visible;text-overflow:clip;color:var(--chrome-ink)}
html body #app header .h-subrow,html body #app header.live .h-weekrow{display:none}
html body #app header .hback{width:28px;min-width:28px;height:44px;padding:0;background:transparent;border:0;box-shadow:none;color:var(--chrome-ink)}
html body #app header .hbtns{gap:8px;flex:0 0 auto;align-items:center}
html body #app header #hTimer{font-size:12px;font-weight:500;min-width:37px;white-space:nowrap;justify-content:center;color:var(--chrome-ink)}
html body #app header #hTimer::before{display:none}
html body #app header:not(.live) #hTimer{display:none}
html body #app header.live .h-date,html body #app header.live .hback,html body #app header.live #hTimer{color:#fff}
html body #app header:not(.live){padding-left:18px;padding-right:18px}
/* v4.6.144: with no back arrow, the title's inset matches Finish's on the right (12px) */
html body #app header.live:not(.exmode){padding-left:12px}
html body #app header #liveWorkoutBar{
  position:static;inset:auto;width:auto;min-width:0;max-width:none;height:auto;min-height:0;margin:0;padding:0;
  display:flex;flex:0 0 auto;gap:0;border:0;border-radius:0;background:none;box-shadow:none;animation:none;transform:none;
}
html body #app header #liveWorkoutBar[hidden]{display:none}
html body #app header #liveWorkoutBar > :not(#liveWorkoutFinish){display:none}
html body #app header #liveWorkoutBar #liveWorkoutFinish{
  display:flex;align-items:center;justify-content:center;gap:6px;height:44px;min-height:44px;min-width:0;
  font:500 13px var(--body);padding:0 11px;border:0;border-radius:13px;
  background:rgba(255,255,255,.16);box-shadow:inset 0 1px 0 rgba(255,255,255,.22);color:#fff;white-space:nowrap;
}
html body #nav{grid-template-columns:repeat(4,minmax(0,1fr))}
/* v4.6.125: no colour or background here -- the gear is a tab like the other
   four, so the per-bar nav rules (nav button / nav button.on) dress it. */
html body #nav #gearBtn{width:auto;height:auto;min-width:0;flex:1;border:0;padding:10px 4px;border-radius:24px}
html body #nav #gearBtn .ng{display:grid;place-items:center}
html body #nav #gearBtn:not(.on){background:transparent;box-shadow:none}
.nav-settings-gear{display:block;width:24px;height:24px;background:currentColor;
  /* v4.6.134: an EVEN gear. The hand-traced clip-path it replaces had teeth of
     different widths at uneven angles and a bulging left side. This is generated:
     8 identical teeth every 45deg (tips 17deg, roots 27deg, r 11.4 / 8.6 of a
     24 box), a 3.4 hole, corners softened by a 1.1 round-join stroke. The maker's
     pick A of two (v4.6.134). Regenerate, don't hand-edit. */
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' stroke='%23000' stroke-width='1.1' stroke-linejoin='round' d='M9.99 3.64 L10.31 0.73 L13.69 0.73 L14.01 3.64 L16.49 4.67 L18.78 2.84 L21.16 5.22 L19.33 7.51 L20.36 9.99 L23.27 10.31 L23.27 13.69 L20.36 14.01 L19.33 16.49 L21.16 18.78 L18.78 21.16 L16.49 19.33 L14.01 20.36 L13.69 23.27 L10.31 23.27 L9.99 20.36 L7.51 19.33 L5.22 21.16 L2.84 18.78 L4.67 16.49 L3.64 14.01 L0.73 13.69 L0.73 10.31 L3.64 9.99 L4.67 7.51 L2.84 5.22 L5.22 2.84 L7.51 4.67ZM15.40 12A3.4 3.4 0 1 0 8.60 12A3.4 3.4 0 1 0 15.40 12Z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' stroke='%23000' stroke-width='1.1' stroke-linejoin='round' d='M9.99 3.64 L10.31 0.73 L13.69 0.73 L14.01 3.64 L16.49 4.67 L18.78 2.84 L21.16 5.22 L19.33 7.51 L20.36 9.99 L23.27 10.31 L23.27 13.69 L20.36 14.01 L19.33 16.49 L21.16 18.78 L18.78 21.16 L16.49 19.33 L14.01 20.36 L13.69 23.27 L10.31 23.27 L9.99 20.36 L7.51 19.33 L5.22 21.16 L2.84 18.78 L4.67 16.49 L3.64 14.01 L0.73 13.69 L0.73 10.31 L3.64 9.99 L4.67 7.51 L2.84 5.22 L5.22 2.84 L7.51 4.67ZM15.40 12A3.4 3.4 0 1 0 8.60 12A3.4 3.4 0 1 0 15.40 12Z'/%3E%3C/svg%3E") center/contain no-repeat;
}
@media(max-width:350px){
  html body #app header{left:14px;right:14px;gap:5px;padding-left:5px;padding-right:9px}
  html body #app header .brandrow{gap:4px}
  html body #app header .hbtns{gap:5px}
  html body #app header.live:not(.exmode){padding-left:9px}   /* v4.6.144: matches padding-right here */
}

/* v4.6.145: Continue with Apple, in Apple's own two styles: black on light, white on dark.
   The logo is the system font's own glyph (U+F8FF), which only Apple devices draw --
   and the button only exists in the iOS app. Same size and weight as Google's, above it. */
.btn.apple-btn{display:flex;align-items:center;justify-content:center;gap:8px;margin:0 0 8px;background:#000;color:#fff;border-color:#000}
[data-theme="dark"] .btn.apple-btn{background:#fff;color:#000;border-color:#fff}
.apple-btn .apple-glyph{font:500 17px -apple-system,system-ui,sans-serif;line-height:1;margin-top:-2px}

/* v4.6.151: EDGE BLUR. A progressive Gaussian blur above the header and below
   the tab bar, so content fades out of focus as it slides under the chrome
   instead of reading sharp through it. Four stacked layers, each blurrier and
   masked closer to the screen edge (1/3/7/14px), plus a light ground wash on
   the outermost. The bands sit under the header (z 20) and the nav (z 30) and
   take no taps.
   Built around the chrome lessons above: the fixed band paints nothing and
   declares no backdrop-filter itself (v3.3.245 -- Safari samples those on
   fixed elements); the blur lives on absolute children. This IS the first
   backdrop-filter near fixed chrome since v3.3.497 took the nav's off, owner-
   approved as C in the 2026-09-28 mock with a device test to follow: if the
   header, nav or "top" button hang on scroll on iOS again, this block is the
   first suspect -- delete it (and the two divs in index.html) to go back.
   buildcheck allows exactly these four blur values here and nowhere else. */
.edgeblur{position:fixed;left:0;right:0;z-index:19;pointer-events:none;-webkit-user-select:none;user-select:none}
.edgeblur.top{top:0;height:calc(var(--bar-top) + 82px)}   /* header 64 + the 18px gap, so the first card stays sharp at rest */
.edgeblur.bot{bottom:0;height:calc(var(--bar-bot) + 80px)}   /* tab bar 58 + 22 */
.edgeblur i{position:absolute;inset:0}
.edgeblur.top i{--eb-dir:0deg}
.edgeblur.bot i{--eb-dir:180deg}
.edgeblur i:nth-child(1){-webkit-mask:linear-gradient(var(--eb-dir),transparent 0%,#000 25%);mask:linear-gradient(var(--eb-dir),transparent 0%,#000 25%)}
.edgeblur i:nth-child(2){-webkit-mask:linear-gradient(var(--eb-dir),transparent 20%,#000 50%);mask:linear-gradient(var(--eb-dir),transparent 20%,#000 50%)}
.edgeblur i:nth-child(3){-webkit-mask:linear-gradient(var(--eb-dir),transparent 45%,#000 75%);mask:linear-gradient(var(--eb-dir),transparent 45%,#000 75%)}
.edgeblur i:nth-child(4){-webkit-mask:linear-gradient(var(--eb-dir),transparent 70%,#000 100%);mask:linear-gradient(var(--eb-dir),transparent 70%,#000 100%);
  background:linear-gradient(calc(var(--eb-dir) + 180deg),color-mix(in srgb,var(--ground) 45%,transparent),transparent)}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
.edgeblur i:nth-child(1){-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}
.edgeblur i:nth-child(2){-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}
.edgeblur i:nth-child(3){-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px)}
.edgeblur i:nth-child(4){-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
}
/* Retro is flat by design (no glass anywhere), and reduced transparency means none. */
:root[data-appearance="retro"] .edgeblur{display:none}
@media(prefers-reduced-transparency:reduce){.edgeblur{display:none}}

/* v4.6.163: Best lifts (Progress). Two columns: the lift and its note on the
   left; weight x reps on one right edge that the total shares. Values sit on
   the name's line, not centred on two. Mock C, 2026-09-29. */
.bl-head{display:flex;align-items:baseline;gap:8px}
.bl-head small{font:400 11px var(--mono);color:var(--muted);letter-spacing:0;text-transform:none}
.card.bl-card,#view.stats-system .stats-card.bl-card{padding:0 18px;overflow:hidden}   /* the stats layer pads every card by id */
.bl-row,.bl-total{display:grid;grid-template-columns:minmax(0,1fr) 44px 34px 10px;column-gap:6px;align-items:start;width:100%;padding:14px 0;border:0;border-top:1px solid var(--line);background:none;color:inherit;font:inherit;text-align:left;cursor:pointer}
.bl-card>.bl-row:first-child{border-top:0}
.bl-name{min-width:0}
.bl-name strong{display:flex;align-items:center;gap:7px;font:600 13px/19px var(--body);color:var(--chalk)}
.bl-name small{display:block;margin-top:2px;font:400 10px/14px var(--mono);color:var(--muted);overflow-wrap:anywhere}
.bl-tag{padding:2px 7px;border-radius:999px;font:500 9px/1.222 var(--mono);color:var(--accent-ink,var(--accent));background:color-mix(in srgb,var(--accent) 14%,transparent)}
.bl-row b,.bl-total b{font:600 13px/19px var(--mono);font-size:13px;color:var(--chalk);text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.bl-row i,.bl-total i{font-style:normal;font:400 13px/19px var(--mono);color:var(--chalk);white-space:nowrap}
.bl-row svg{width:8px;height:14px;margin-top:2px;fill:none;stroke:var(--muted);stroke-width:2}
.bl-none b{color:var(--muted)}
.bl-total{margin:0 -18px;width:calc(100% + 36px);padding:14px 18px 16px;cursor:default;background:color-mix(in srgb,var(--chalk) 3%,transparent)}
.bl-total b{font:700 13px/19px var(--mono)}
.bl-total i{color:var(--muted)}

/* v4.6.174 — Include / Avoid. One glyph (.xp-ic), one quiet treatment: an
   avoided exercise is set apart in ink, not alarm colour -- red already means
   "training right now" in this app. */
.xp-ic{width:15px;height:15px;flex:0 0 auto;vertical-align:-2px}
.xp-row{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:11px 0 3px;padding:12px 14px 12px 16px;border-radius:18px;background:var(--surface);border:1px solid var(--line)}
.xp-row>span{font:400 11px var(--mono);color:var(--muted);letter-spacing:.02em}
.xp-seg{--xp-in:var(--accent);--xp-out:#2C2C2C;--xp-out-ink:#fff;position:relative;display:grid;grid-template-columns:1fr 1fr;flex:0 0 auto;width:196px;padding:3px;border-radius:21px;background:color-mix(in srgb,var(--chalk) 7%,transparent);isolation:isolate}
:root[data-theme="dark"] .xp-seg{--xp-out:#E6E7EC;--xp-out-ink:#191B24}
/* v4.6.177: the thumb. It slides (transform only -- nothing else on the page
   reflows) with the app's settle curve, and takes the colour of the choice:
   brand blue for Include, soft charcoal for Avoid (a light slate in dark). */
.xp-seg::before{content:"";position:absolute;z-index:0;top:3px;bottom:3px;left:3px;width:calc(50% - 3px);border-radius:17px;background:var(--xp-in);box-shadow:0 1px 3px rgba(0,0,0,.16),0 2px 8px color-mix(in srgb,var(--xp-in) 30%,transparent);
  transition:transform var(--dur-arrive,.42s) var(--settle),background-color var(--dur-quick,.16s) ease,box-shadow var(--dur-quick,.16s) ease}
.xp-seg[data-state="avoid"]::before{transform:translateX(100%);background:var(--xp-out);box-shadow:0 1px 3px rgba(0,0,0,.18)}
.xp-seg button{position:relative;z-index:1;display:flex;align-items:center;justify-content:center;gap:6px;height:34px;min-height:0;margin:0;padding:0 10px;border:0;border-radius:17px;background:none;color:var(--muted);font:500 12px var(--body);cursor:pointer;-webkit-tap-highlight-color:transparent;transition:color var(--dur-quick,.16s) ease}
.xp-seg button .xp-ic{width:14px;height:14px}
.xp-seg[data-state="include"] [data-expref="include"]{color:#fff}
.xp-seg[data-state="avoid"] [data-expref="avoid"]{color:var(--xp-out-ink)}
@media (prefers-reduced-motion:reduce){.xp-seg::before{transition:none}}
.xp-banner{display:flex;align-items:center;gap:10px;margin:0 0 10px;padding:11px 12px 11px 14px;border-radius:14px;background:color-mix(in srgb,var(--chalk) 6%,transparent);font:400 11px/1.269 var(--body);color:var(--muted)}
.xp-banner b{color:var(--chalk);font-weight:600}.xp-banner .xp-ic{color:var(--chalk);width:16px;height:16px}
.xp-banner button{margin-left:auto;flex:0 0 auto;min-height:0;height:auto;padding:6px 4px;border:0;background:none;color:var(--accent);font:600 11px var(--body);cursor:pointer}
.xp-tag{display:inline-flex;align-items:center;gap:4px;margin-left:8px;font:400 11px var(--mono);color:var(--muted)}.xp-tag .xp-ic{width:12px;height:12px}
.xp-dim{opacity:.55}
.xp-list .xp-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 0;border-top:1px solid var(--line)}
.xp-list .xp-item:first-child{border-top:0;padding-top:2px}
.xp-list .xp-item strong{display:flex;align-items:center;gap:7px;font:600 13px var(--body);color:var(--chalk)}.xp-list .xp-item strong .xp-ic{color:var(--muted);width:14px;height:14px}
.xp-list .xp-item small{display:block;margin-top:2px;font:400 11px var(--mono);color:var(--muted)}
.xp-list .xp-item .btn{flex:0 0 auto;width:auto;min-height:0;height:32px;margin:0;padding:0 12px;border-radius:16px;font:500 11px var(--body)}
/* v4.6.176: a History session's exercise opens it in Train */
.exgrp.hx-open{cursor:pointer;-webkit-tap-highlight-color:transparent;border-radius:12px;transition:background-color var(--dur-quick,.16s)}
.exgrp.hx-open:active{background:color-mix(in srgb,var(--accent) 7%,transparent)}
.exgrp.hx-open:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* v4.6.179: while a pop-up is up, its own scrolling stays its own, and the
   "↑ top" button (a layer above it) steps away -- see syncModalLock() */
.pw-add-sheet,#repOv{overscroll-behavior:contain}
html.modal-open .calreturn{display:none!important}

/* v4.6.187: Train's muscle filter and the muscle tag on a row's second line */
.mf-row{display:flex;flex-wrap:wrap;gap:5px 6px;margin:14px 0 14px}
.mf-chip{padding:8px 14px;border:0;border-radius:999px;font:500 11px/1 var(--body);color:var(--muted);background:color-mix(in srgb,var(--chalk) 6%,transparent);cursor:pointer}
.mf-chip.on{background:var(--accent);color:#fff}
.mtag{display:inline-block;margin-right:8px;padding:2px 7px;border-radius:9px;background:color-mix(in srgb,var(--chalk) 8%,transparent);color:var(--muted);font:600 9px/1.222 var(--mono);letter-spacing:.06em;text-transform:uppercase;vertical-align:1px;white-space:nowrap}
/* v4.6.188: the exercise page's Muscle card */
.xm-card{margin:11px 0 3px;padding:16px 14px 14px 16px;border-radius:18px;background:var(--surface);border:1px solid var(--line)}
.xm-top{display:flex;align-items:center;justify-content:space-between;gap:12px}
.xm-top span{font:400 11px var(--mono);color:var(--muted);letter-spacing:.02em}
.xm-top b{font:600 12px var(--body);color:var(--chalk)}
.xm-chips{display:flex;flex-wrap:wrap;gap:5px 6px;margin-top:10px}
.xm-card small{display:block;margin-top:8px;font:400 11px/1.316 var(--body);color:var(--muted)}
/* v4.6.189: Daily runs -- the row of years */
.dryears{display:flex;gap:4px;margin:2px 0 8px;overflow-x:auto;scrollbar-width:none}
.dryears::-webkit-scrollbar{display:none}
.dry{flex:0 0 auto;min-height:30px;padding:0 11px;border:0;border-radius:999px;background:none;font:400 11px/1 var(--mono);color:var(--faint);cursor:pointer}
.dry.on{background:color-mix(in srgb,var(--chalk) 8%,transparent);color:var(--chalk);font-weight:700}
/* v4.6.194: Hold -- the switch (the Include / Avoid control, its own states), the line under it, the tag */
.xh-seg[data-state="hold"]::before{transform:translateX(100%);background:var(--xp-out);box-shadow:0 1px 3px rgba(0,0,0,.18)}
.xh-seg[data-state="progress"] [data-exhold="progress"]{color:#fff}
.xh-seg[data-state="hold"] [data-exhold="hold"]{color:var(--xp-out-ink)}
.xh-note{margin:2px 0 5px;padding:0 16px;font:400 11px/1.41 var(--body);color:var(--muted)}
.xh-note b{color:var(--chalk);font-weight:600}
.xh-tag{display:inline-flex;align-items:center;gap:3px 4px;margin:0 8px 0 0;padding:2px 7px 2px 6px;border-radius:9px;background:color-mix(in srgb,var(--chalk) 8%,transparent);color:var(--muted);font:600 9px/1.222 var(--mono);letter-spacing:.06em;text-transform:uppercase;vertical-align:1px;white-space:nowrap}
.xh-tag .xp-ic{width:12px;height:12px;flex-shrink:0}
.pf-ex-summary .xh-tag{margin:0 0 0 6px;vertical-align:2px}
/* v4.6.220 — vertical attendance. Isolated from Rest and the floating header. */
.attendance-card{padding:26px 24px 18px;overflow:hidden;--at-blue:#3049dc;--at-story-opacity:1}
[data-theme="dark"] .attendance-card{--at-blue:#7188ff}
.attendance-card .at-owner{font:400 12px/1.5 var(--body);color:var(--muted);margin-bottom:20px;min-height:18px}
.at-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:0}
.attendance-card .at-heading .at-owner{margin:0;min-width:0;overflow-wrap:anywhere}
.at-years-toggle{display:flex;align-items:center;gap:6px 8px;white-space:nowrap;min-height:44px;padding:8px 12px;border:1px solid var(--line);border-radius:14px;background:var(--surface2);color:var(--chalk);font:500 11px var(--body)}
.at-years-panel{padding:16px;margin:0 0 14px;border:1px solid var(--line);border-radius:20px;background:var(--surface)}
.at-years-panel[hidden]{display:none}
.at-years-panel>strong{font:500 13px var(--body)}
.at-years-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px 8px;margin-top:11px}
.at-years-grid button{display:flex;align-items:center;justify-content:space-between;gap:4px;min-height:44px;padding:8px 10px;border:1px solid var(--line);border-radius:12px;background:var(--surface2);color:var(--chalk);font:400 11px var(--mono)}
.at-years-grid button[aria-pressed="true"]{border-color:var(--at-blue);color:var(--at-blue);background:color-mix(in srgb,var(--at-blue) 9%,var(--surface))}
.at-years-bottom{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:11px;color:var(--muted);font:400 11px var(--body)}
.at-years-apply{min-height:44px;padding:8px 20px;border:0;border-radius:14px;background:var(--at-blue);color:#fff;font:500 12px var(--body)}
.at-years-apply:disabled{opacity:.45}
.at-composition{position:relative;height:414px;isolation:isolate}
.at-story{position:absolute;left:0;top:60px;z-index:2;pointer-events:none;max-width:44%}
.at-total{font:500 clamp(37px,13vw,60px)/1 var(--body);letter-spacing:-.055em;color:var(--at-blue);font-variant-numeric:tabular-nums;transition:color .3s ease}
.attendance-card[data-focused="true"] .at-total{color:var(--chalk)}
.at-unit{font:400 13px/1.41 var(--body);color:var(--muted);margin-top:6px}
.at-focus{margin-top:21px;font:500 12px/1.504 var(--body);color:var(--chalk);transition:color .3s ease}
.at-focus>b,.at-focus>.at-focus-part{display:block}.at-focus b{font-weight:500;white-space:nowrap}
.at-focus-count{color:var(--at-blue);transition:color .3s ease}.at-focus-days{color:var(--chalk);transition:color .3s ease}
.at-streak{margin-top:29px;font:400 10px/1.692 var(--mono);color:var(--muted)}
.at-timeline{position:absolute;right:0;top:0;width:57%;max-width:164px}
.at-year-label{display:block;position:relative;width:fit-content;height:38px;text-align:left;margin-left:28px}
.at-year-label::after{content:"";position:absolute;right:3px;top:13px;width:6px;height:6px;border-right:1.5px solid var(--muted);border-bottom:1.5px solid var(--muted);transform:rotate(45deg);pointer-events:none}
.attendance-card select{font:500 12px/1.4 var(--body);color:var(--chalk);background:var(--surface);border:1px solid var(--line);border-radius:9px;max-width:100%;min-height:36px;padding:5px 9px}
.attendance-card .at-year{appearance:none;-webkit-appearance:none;font:500 14px var(--mono);border:0;padding:0 20px 0 0;text-indent:0;height:36px;min-height:36px}
.at-weekdays{display:grid;grid-template-columns:repeat(7,1fr);margin-left:28px;height:24px;align-items:center;font:400 8px var(--mono);color:var(--muted);text-align:center}
.at-scroll{height:352px;overflow:auto;scrollbar-width:none;overscroll-behavior-y:contain;padding:0 4px 4px 0;scroll-behavior:auto;mask-image:linear-gradient(transparent,#000 9px,#000 calc(100% - 5px),transparent)}
.at-scroll::-webkit-scrollbar{display:none}.at-calendar{position:relative}
.at-cell{position:absolute;width:calc((100% - 28px)/7 - 3px);max-width:12px;height:12px;border-radius:30%;background:var(--surface2)}
.at-cell.on,.at-mini.on{background:var(--at-blue)}.at-cell.dim,.at-mini.dim{opacity:.24}
.at-cell.today,.at-mini.today{outline:1.5px solid var(--at-blue);outline-offset:2px}
.at-month{position:absolute;left:0;font:400 9px/12px var(--mono);color:var(--muted)}
/* Keep the 330px calendar intact; reclaim space above it, not from its marks.
   The 84px heading reserve also matches the replay geometry in heat-replay.js. */
.at-overview{display:none;position:absolute;left:0;right:0;top:84px;height:330px;overflow:visible}
.at-atlas-label{font:400 10px/1 var(--mono);color:var(--muted);white-space:nowrap}
.at-pages{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px;color:var(--muted);font:400 11px/1.316 var(--body)}
.at-pages button{min-width:44px;min-height:44px;border:1px solid var(--line);border-radius:16px;background:var(--surface2);color:var(--chalk);font:500 16px var(--body)}
.at-pages button:disabled{opacity:.35}.at-pages span{text-align:center}
.at-share-pages{flex-wrap:wrap;max-width:min(90vw,540px);width:100%;margin:0 auto 10px}.at-pages .at-share-all{font-size:11px;padding:8px 12px}
.at-mini{border-radius:30%;background:var(--surface2);min-width:0}
.at-blank{border-radius:30%;background:var(--surface2);opacity:.45}
.attendance-card[data-overview="true"] .at-timeline{display:none}
.attendance-card[data-overview="true"] .at-overview{display:grid}
.attendance-card[data-overview="true"] .at-story{top:0;max-width:100%;display:flex;align-items:baseline;gap:4px 12px;flex-wrap:wrap}
.attendance-card[data-overview="true"] .at-total{font-size:50px}
.attendance-card[data-overview="true"] .at-streak{display:none}
.attendance-card[data-overview="true"] .at-focus{margin:0;flex-basis:100%;display:flex;gap:8px}
.attendance-card .at-focus[hidden]{display:none}
.at-date,.at-range{border-top:1px solid var(--line);margin-top:10px;min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--muted)}
.at-date strong{font:400 21px var(--mono);color:var(--chalk)}.at-date span,.at-range span{font:400 10px/1.504 var(--mono)}
.at-return{border:0;background:transparent;color:var(--at-blue);font:400 11px var(--body);min-height:44px;white-space:nowrap;padding:0}
.attendance-card [hidden]{display:none}
.at-actions{display:flex;gap:10px;margin-top:5px}
.attendance-card .at-actions button{display:flex;align-items:center;justify-content:center;gap:7px;min-height:44px;border:1px solid var(--line);background:var(--surface2);color:var(--chalk);border-radius:24px;font:500 13px var(--body);padding:8px 16px}
.at-actions svg{width:17px;height:17px}.at-actions button:disabled{opacity:.4}
.at-options{display:grid;grid-template-columns:1fr;gap:10px 12px;margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
.at-options label{font:400 10px/1.41 var(--body);color:var(--muted);min-width:0}.at-options select{display:block;width:100%;margin-top:4px}
.attendance-card .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.at-replay-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none}
.at-playing .at-timeline,.at-playing .at-overview{visibility:hidden}
.at-playing .at-story{top:0;max-width:100%;display:flex;align-items:baseline;gap:4px 12px;flex-wrap:wrap}
.at-playing .at-streak{display:none}
.at-playing .at-total{font-size:44px}
/* One masked, composited light sheet. Day marks themselves never animate. */
.at-group-sheen{position:absolute;inset:0;pointer-events:none;z-index:1;mask-size:100% 100%;-webkit-mask-size:100% 100%;overflow:hidden}
.at-group-sheen i{display:block;position:absolute;inset:0 auto 0 0;width:320%;background:linear-gradient(90deg,#ffffff0a 0%,#ffffff38 25%,#ffffff8a 50%,#ffffff38 75%,#ffffff0a 100%);background-size:50% 100%;animation:at-group-light 6s linear infinite;will-change:transform}
.at-today-beacon{position:absolute;pointer-events:none;z-index:2;border-radius:30%}
.at-today-beacon::after{content:"";position:absolute;inset:-3px;border:1.5px solid var(--at-blue);border-radius:30%;box-shadow:0 0 9px color-mix(in srgb,var(--at-blue) 40%,transparent);animation:at-ambient-pulse 2.8s ease-in-out infinite;will-change:transform,opacity}
.at-playing .at-today-beacon,.attendance-card[data-animation="Off"] .at-today-beacon{display:none}
.attendance-card[data-visible="false"] .at-today-beacon::after,.at-document-hidden .at-today-beacon::after,.at-document-hidden .at-group-sheen i{animation-play-state:paused}
@keyframes at-ambient-pulse{0%,100%{transform:scale(1);opacity:.8}50%{transform:scale(1.7);opacity:.18}}
@media(prefers-reduced-motion:reduce){.at-today-beacon{display:none}}
.at-playing .at-group-sheen,.attendance-card[data-animation="Off"] .at-group-sheen{display:none}
.attendance-card[data-visible="false"] .at-group-sheen i{animation-play-state:paused}
.at-parts{position:relative;display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;scroll-snap-type:x proximity;margin:0 0 5px;padding:2px 0 4px;overscroll-behavior-x:contain}
.at-parts::-webkit-scrollbar{display:none}
.attendance-card .at-parts button{flex:0 0 auto;min-height:40px;scroll-snap-align:center;border:0;border-radius:20px;padding:8px 13px;background:transparent;color:var(--muted);font:500 12px var(--body);white-space:nowrap;transition:color .22s,background-color .22s}
.attendance-card .at-parts button[aria-pressed="true"]{background:color-mix(in srgb,var(--at-blue) 10%,transparent);color:var(--at-blue)}
.attendance-card .at-parts button:focus-visible{outline:2px solid var(--at-blue);outline-offset:-2px}
.at-cell,.at-mini{transition:opacity .3s ease}
.at-playing .at-focus{margin:0;flex-basis:100%;display:flex;gap:8px;font-size:12px}.at-playing .at-focus>b,.at-playing .at-focus>.at-focus-part{display:inline}
@keyframes at-group-light{from{transform:translate3d(-50%,0,0)}to{transform:translate3d(0,0,0)}}
@media(prefers-reduced-motion:reduce){.at-group-sheen{display:none}.attendance-card .at-parts button,.at-cell,.at-mini,.at-total,.at-focus,.at-focus-count,.at-focus-days{transition:none}}
@media(max-width:360px){.attendance-card{padding:22px 18px 16px}.at-total{font-size:40px}.at-options{gap:8px}.at-date span{font-size:9px}}
@media(prefers-reduced-motion:reduce){.attendance-card .heat-replay{display:none}.attendance-card *{scroll-behavior:auto!important}}

/* ============ v4.6.262: the first run (js/today.js onbRender) ============ */
#onb[data-step]{display:block;align-items:initial;justify-content:initial;--ob-d:0s}
/* v4.6.263: the line above outranked #onb[hidden] (same specificity, later), so a finished first run stayed on screen over Train */
#onb[hidden]{display:none!important}
#onb[data-step="1"]{background:#3049DC;color:#fff}
#onb .ob{min-height:100%;width:100%;max-width:520px;margin:0 auto;box-sizing:border-box;display:flex;flex-direction:column;padding:calc(env(safe-area-inset-top,0px) + 18px) 24px calc(env(safe-area-inset-bottom,0px) + 22px);font-family:var(--body);color:inherit;position:relative;isolation:isolate}
#onb .ob button{-webkit-tap-highlight-color:transparent;font-family:var(--body)}
@keyframes obRise{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
#onb .ob-rise{opacity:0;animation:obRise .5s var(--settle) var(--ob-d,0s) forwards}
#onb.ob-still .ob-rise,#onb.ob-still .ob-days i,#onb.ob-still .ob-lock,#onb.ob-still .ob-motto span,#onb.ob-still .ob-door-btns,#onb.ob-still .ob-jump .su-mascot,#onb.ob-still .ob-lines,#onb.ob-still .ob-bubble{animation:none!important;opacity:1!important;transform:none!important}
#onb.ob-still .ob-lock{transform:scale(1.4)!important}
/* the door */
#onb .ob-days{display:grid;grid-template-columns:repeat(7,26px);grid-auto-rows:26px;gap:6px 7px;justify-content:center;margin-top:38px;-webkit-mask:linear-gradient(#000 55%,transparent 100%);mask:linear-gradient(#000 55%,transparent 100%)}
#onb .ob-days i{border-radius:7px;background:rgba(255,255,255,.12);opacity:0;animation:obDay .5s ease-out calc(var(--i)*40ms + 200ms) forwards}
#onb .ob-days i.on{animation-name:obDayOn}
@keyframes obDay{from{opacity:0;transform:scale(.6)}to{opacity:1;transform:none}}
@keyframes obDayOn{0%{opacity:0;transform:scale(.6);background:rgba(255,255,255,.12)}60%{transform:scale(1.08)}100%{opacity:1;transform:none;background:rgba(255,255,255,.92)}}
#onb.ob-still .ob-days i.on{background:rgba(255,255,255,.92)}
#onb .ob-hero{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;padding:24px 0 34px;min-height:220px}
#onb .ob-lock{opacity:0;transform:scale(1.4);margin:0 0 40px;animation:obLock .5s 1.3s ease-out forwards}
@keyframes obLock{from{opacity:0;transform:translateY(14px) scale(1.4)}to{opacity:1;transform:scale(1.4)}}
#onb .ob-lock .showuppp-c .su-mascot{position:absolute;width:184.615%;left:-42.308%;top:-43.83%;max-width:none;margin:0;pointer-events:none}
#onb .ob-lock .showuppp-c .showuppp-lettering{color:#fff}
#onb .ob-lock .showuppp-ground{background:radial-gradient(ellipse,#0006 0%,#0002 48%,#0000 74%)}
#onb .ob-lock .showuppp-burst{background:#fff}
#onb .ob-motto{margin:0;text-align:center;font:700 26px/1.12 var(--body);letter-spacing:-.6px}
#onb .ob-motto span{display:block;opacity:0;animation:obRise .6s var(--settle) 2.9s forwards}
#onb .ob-motto span+span{animation-delay:3.2s}
#onb .ob-door-btns{opacity:0;animation:obRise .6s ease-out 3.6s forwards}
#onb .ob-btn{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:56px;margin:10px 0 0;padding:0 52px;border:0;border-radius:16px;background:#fff;color:#111;font:600 15px var(--body);box-shadow:0 8px 20px rgba(10,20,90,.35),0 1px 2px rgba(10,20,90,.4);cursor:pointer}
#onb .ob-btn:active{transform:translateY(1px) scale(.99);box-shadow:0 4px 10px rgba(10,20,90,.3)}
#onb .ob-btn .ob-lg{position:absolute;left:20px;top:50%;width:22px;height:22px;margin-top:-9px}
#onb .ob-apple{background:#000;color:#fff}
#onb .ob-local{background:rgba(255,255,255,.16);color:#fff;box-shadow:none}
#onb .ob-note{margin:11px 0 0;text-align:center;font:400 11px/1.504 var(--body);color:rgba(255,255,255,.78)}
#onb .ob-link{display:inline;width:auto;min-height:0;margin:0;padding:0;border:0;background:none;box-shadow:none;color:inherit;font:inherit;text-decoration:underline;cursor:pointer}
#onb .ob-note .ob-link{color:#fff}
/* steps */
#onb .ob-top{display:flex;align-items:center;gap:14px;height:32px;margin-top:18px}
#onb .ob-back,#onb .ob-skip{width:auto;min-width:28px;min-height:0;margin:0;padding:4px 0;border:0;background:none;box-shadow:none;color:var(--chalk);font:400 23px/1 var(--body);cursor:pointer}
#onb .ob-skip{font:500 13px var(--body);color:var(--muted)}
#onb .ob-prog{flex:1;display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:5px 6px}  /* v4.6.314: four steps since v4.6.311, one row */
#onb .ob-prog i{height:3px;border-radius:2px;background:color-mix(in srgb,var(--chalk) 14%,transparent)}
#onb .ob-prog i.on{background:var(--chalk)}
#onb .ob-h{font:700 28px/1.12 var(--body);letter-spacing:-.8px;margin:22px 0 6px}
#onb .ob-sub{font:400 14px/1.363 var(--body);color:var(--muted);margin:0 0 19px}
#onb .ob-sp{flex:1;min-height:24px}
#onb .ob-go{display:flex;align-items:center;justify-content:center;width:100%;height:56px;margin:0;border:0;border-radius:16px;background:var(--accent);color:#fff;font:600 15px var(--body);box-shadow:none;cursor:pointer}
#onb .ob-go:active{transform:scale(.99)}
#onb .ob-parts{display:grid;grid-template-columns:1fr 1fr;gap:8px 10px}
#onb .ob-part{display:flex;align-items:center;justify-content:space-between;width:auto;height:60px;min-height:0;margin:0;padding:0 16px;border-radius:16px;border:1px solid color-mix(in srgb,var(--chalk) 14%,transparent);background:none;box-shadow:none;color:var(--muted);font:600 15px var(--body);cursor:pointer}
#onb .ob-part.on{background:var(--chalk);color:var(--ground);border-color:transparent}
#onb .ob-part svg{width:18px;height:18px;flex:none}
#onb .ob-count{font:500 12px var(--body);color:var(--muted);margin:14px 2px 0}
/* about you */
@keyframes obBob{0%,100%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-6px) rotate(3deg)}}
@keyframes obPop{0%{transform:scale(.4) rotate(-6deg);opacity:0}100%{transform:none;opacity:1}}
@keyframes obBump{0%{transform:scale(1)}40%{transform:scale(1.06)}100%{transform:scale(1)}}
#onb .ob-hello{display:flex;align-items:flex-end;margin:14px 0 8px}
#onb .ob-hello .su-mascot{width:124px;flex:none;margin-left:-18px;animation:obBob 1.6s ease-in-out infinite}
#onb .ob-bubble{position:relative;flex:1;min-width:0;margin:0 0 37px -6px;padding:16px 20px 15px;border-radius:24px 24px 24px 6px;background:var(--accent);color:#fff;box-shadow:0 10px 30px color-mix(in srgb,var(--accent) 35%,transparent);transform-origin:0 100%;animation:obPop .55s cubic-bezier(.3,1.6,.5,1) .2s both}
#onb .ob-bubble.bump{animation:obBump .22s ease-out}
#onb .ob-bubble:after{content:"";position:absolute;left:-7px;bottom:0;width:16px;height:16px;background:var(--accent);clip-path:polygon(100% 0,100% 100%,0 100%)}
#onb .ob-hi{display:block;font:700 26px/1.05 var(--body);letter-spacing:-.6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#onb .ob-hi2{display:block;font:500 13px var(--body);opacity:.85;margin-top:4px}
#onb .ob-lbl{display:block;font:600 15px var(--body);color:var(--chalk);margin:0}
#onb .ob-name{display:block;width:100%;box-sizing:border-box;margin:6px 0 0;padding:4px 0 10px;border:0;border-bottom:2px solid color-mix(in srgb,var(--chalk) 18%,transparent);border-radius:0;background:none;box-shadow:none;outline:none;color:var(--chalk);font:600 25px var(--body);letter-spacing:-.4px}
#onb .ob-name.filled,#onb .ob-name:focus{border-bottom-color:var(--accent)}
#onb .ob-name::placeholder{color:color-mix(in srgb,var(--chalk) 28%,transparent)}
#onb .ob-row{display:flex;justify-content:space-between;align-items:center;margin-top:26px}
#onb .ob-seg{display:grid;grid-template-columns:1fr 1fr;width:132px;padding:3px;border-radius:12px;background:color-mix(in srgb,var(--chalk) 8%,transparent)}
#onb .ob-seg button{height:34px;min-height:0;margin:0;padding:0;border:0;border-radius:9px;background:none;box-shadow:none;color:var(--muted);font:600 13px var(--body);cursor:pointer}
#onb .ob-seg button.on{background:var(--chalk);color:var(--ground)}
#onb .ob-opt{display:block;font:600 15px var(--body);color:var(--chalk);margin:22px 0 10px}
#onb .ob-opt span{font:400 13px var(--body);color:var(--muted)}
#onb .ob-field{display:flex;height:60px;border-radius:14px;border:1px solid color-mix(in srgb,var(--chalk) 22%,transparent);overflow:hidden}
#onb .ob-field input{flex:1;min-width:0;height:100%;margin:0;padding:0 18px;border:0;background:none;box-shadow:none;outline:none;color:var(--chalk);font:500 18px var(--body)}
#onb .ob-field input::placeholder{color:var(--muted);font-weight:400}
#onb .ob-field b{display:flex;align-items:center;justify-content:center;width:72px;border-left:1px solid color-mix(in srgb,var(--chalk) 22%,transparent);font:600 15px var(--body)}
#onb .ob-why{font:400 13px/1.363 var(--body);color:var(--muted);margin:10px 0 0}
#onb .ob-notnow{align-self:flex-start;margin-top:19px;font:600 14px var(--body);color:var(--accent-ink,var(--accent))}
:root[data-theme="dark"] #onb .ob-notnow{color:#7d92ff}
/* you're in */
#onb .ob-in{font:700 28px/1.12 var(--body);letter-spacing:-.8px;margin:22px 0 5px}
#onb .ob-q{font:400 16px var(--body);color:var(--chalk);opacity:.85;margin:0}
#onb .ob-jump{position:relative;display:flex;justify-content:center;height:120px;margin:11px 0}
@keyframes obJump{0%{opacity:0;transform:translateY(60px) scale(.7)}60%{opacity:1;transform:translateY(-18px) scale(1.04)}100%{opacity:1;transform:none}}
@keyframes obFlick{0%,100%{opacity:1}50%{opacity:.35}}
@keyframes obNudge{0%,100%{transform:none}50%{transform:translateX(5px)}}
#onb .ob-jump .su-mascot{width:170px;opacity:0;animation:obJump .9s .25s cubic-bezier(.3,1.6,.5,1) forwards,obBob 1.8s 1.2s ease-in-out infinite}
#onb .ob-lines{position:absolute;left:calc(50% + 62px);top:6px;width:30px;height:30px;color:var(--chalk);opacity:0;animation:obRise .4s 1s ease-out forwards,obFlick 1.4s 1.5s ease-in-out infinite}
#onb .ob-card{display:flex;align-items:center;gap:14px;width:100%;min-height:0;margin:10px 0 0;padding:18px;border-radius:20px;border:1px solid var(--line);background:var(--surface);box-shadow:none;color:var(--chalk);text-align:left;cursor:pointer}
#onb .ob-card:active{transform:scale(.99)}
#onb .ob-ic{flex:none;width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--chalk) 9%,transparent)}
#onb .ob-ic svg{width:22px;height:22px}
#onb .ob-ct{flex:1;min-width:0}
#onb .ob-ct b{display:block;font:600 16px var(--body)}
#onb .ob-ct span{display:block;font:400 12px var(--body);color:var(--muted);margin-top:2px}
#onb .ob-ar{width:22px;height:22px;flex:none}
#onb .ob-card.pri{background:var(--accent);border-color:transparent;color:#fff;box-shadow:0 10px 26px color-mix(in srgb,var(--accent) 40%,transparent)}
#onb .ob-card.pri .ob-ic{background:rgba(255,255,255,.22)}
#onb .ob-card.pri .ob-ct span{color:rgba(255,255,255,.85)}
#onb .ob-card.pri .ob-ar{animation:obNudge 1.4s 2s ease-in-out infinite}
#onb .ob-card.plain{background:none;border-color:transparent}
#onb .ob-card.plain .ob-ic{background:none}
#onb .ob-foot{text-align:center;font:400 13px var(--body);color:var(--chalk);opacity:.8;margin:0}
@media(max-height:700px){#onb .ob-days{grid-template-columns:repeat(7,20px);grid-auto-rows:20px;gap:4px 5px;margin-top:22px}#onb .ob-days i{border-radius:5px}#onb .ob-lock{transform:scale(1.2);margin-bottom:27px}@keyframes obLock{from{opacity:0;transform:translateY(14px) scale(1.2)}to{opacity:1;transform:scale(1.2)}}#onb .ob-motto{font-size:23px}#onb .ob-hero{padding-bottom:22px;min-height:180px}#onb .ob-h,#onb .ob-in{font-size:25px;margin-top:16px}#onb .ob-part{height:52px}#onb .ob-jump{height:96px}#onb .ob-jump .su-mascot{width:136px}#onb .ob-card{padding:14px}}
@media(prefers-reduced-motion:reduce){#onb .ob-rise,#onb .ob-days i,#onb .ob-lock,#onb .ob-motto span,#onb .ob-door-btns,#onb .ob-jump .su-mascot,#onb .ob-lines,#onb .ob-bubble,#onb .ob-hello .su-mascot,#onb .ob-card.pri .ob-ar{animation:none!important;opacity:1!important}#onb .ob-days i.on{background:rgba(255,255,255,.92)}}
#onb .ob-field input::-webkit-outer-spin-button,#onb .ob-field input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
#onb .ob-field input[type=number]{-moz-appearance:textfield;appearance:textfield}
@media(max-width:360px){
 #onb .ob{padding-left:20px;padding-right:20px}
 #onb .ob-btn{font-size:14px;padding:0 40px}#onb .ob-btn .ob-lg{left:16px;width:20px;height:20px;margin-top:-8px}
 #onb .ob-h,#onb .ob-in{font-size:24px}
 #onb .ob-sub{font-size:13px}
 #onb .ob-part{padding:0 13px;font-size:14px}
 #onb .ob-hello .su-mascot{width:96px;margin-left:-14px}
 #onb .ob-bubble{padding:13px 15px 12px}
 #onb .ob-hi{font-size:21px;white-space:normal;overflow-wrap:anywhere}
 #onb .ob-hi2{font-size:12px}
 #onb .ob-name{font-size:21px}
 #onb .ob-card{gap:12px;padding:14px}#onb .ob-ic{width:40px;height:40px}#onb .ob-ct b{font-size:14px}#onb .ob-ct span{font-size:11px}
 #onb .ob-foot{font-size:12px}
}
@media(max-height:700px){
 #onb .ob-top{margin-top:8px}
 #onb .ob-hello{margin:8px 0 5px}#onb .ob-bubble{margin-bottom:29px}
 #onb .ob-row{margin-top:18px}#onb .ob-opt{margin:16px 0 8px}#onb .ob-field{height:52px}#onb .ob-notnow{margin-top:13px}
 #onb .ob-q{font-size:14px}#onb .ob-jump{margin:6px 0 5px}
}
#onb .ob-local{padding:0 16px}
/* v4.6.266: a toast during the first run sat under it (z-index 40 vs 90); it shows above, at the top, clear of the buttons */
body:has(#onb:not([hidden])) #toast{z-index:95;top:calc(env(safe-area-inset-top,0px) + 14px);bottom:auto;transform:translateX(-50%) translateY(-12px)}
body:has(#onb:not([hidden])) #toast.on{transform:translateX(-50%)}
/* v4.6.266: the door's lower half, quieter -- a smaller line, slimmer buttons, softer shadows */
#onb .ob-motto{font:700 21px/1.18 var(--body);letter-spacing:-.4px}
#onb .ob-hero{padding-bottom:30px}
#onb .ob-btn{height:50px;margin-top:8px;border-radius:14px;font:600 14px var(--body);letter-spacing:-.1px;box-shadow:0 1px 2px rgba(10,20,90,.25),0 4px 12px rgba(10,20,90,.18)}
#onb .ob-btn .ob-lg{width:19px;height:19px;margin-top:-8px;left:18px}
#onb .ob-local{background:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.38);font-weight:500}
#onb .ob-note{margin-top:13px;font:400 11px/1.504 var(--body);color:rgba(255,255,255,.72)}
#onb .ob-note .ob-link{font-weight:500;text-underline-offset:3px}
@media(max-width:360px){#onb .ob-btn{font-size:13px}#onb .ob-motto{font-size:19px}}

/* ============ v4.6.274: Bring your workouts (js/settings.js impRender) ============ */
#imp{position:fixed;inset:0;z-index:93;overflow:auto;background:var(--ground);color:var(--chalk);font-family:var(--body);-webkit-overflow-scrolling:touch}
body:has(#imp) #toast{z-index:96;top:calc(env(safe-area-inset-top,0px) + 14px);bottom:auto;transform:translateX(-50%) translateY(-12px)}
body:has(#imp) #toast.on{transform:translateX(-50%)}
#imp .im{min-height:100%;max-width:520px;margin:0 auto;box-sizing:border-box;display:flex;flex-direction:column;padding:calc(env(safe-area-inset-top,0px) + 14px) 22px calc(env(safe-area-inset-bottom,0px) + 22px)}
#imp button{font-family:var(--body);-webkit-tap-highlight-color:transparent;cursor:pointer}
#imp .im-top{display:flex;align-items:center;gap:10px;height:44px;font:500 15px var(--body)}
#imp .im-back{width:32px;min-height:0;margin:0;padding:0;border:0;background:none;box-shadow:none;color:var(--chalk);font:400 25px/1 var(--body);text-align:left}
#imp .im-h{font:700 26px/1.12 var(--body);letter-spacing:-.7px;margin:14px 0 5px}
#imp .im-c{text-align:center}
#imp .im-sub{font:400 15px/1.363 var(--body);color:var(--chalk);opacity:.85;margin:5px 0 14px}
#imp .im-sub2{font:400 14px var(--body);color:var(--muted);margin:0 0 13px}
#imp .im-pipwrap{position:relative;display:flex;justify-content:center;height:110px;margin:6px 0}
#imp .im-pipwrap .su-mascot{width:150px}
#imp .im-lines{position:absolute;left:calc(50% + 56px);top:4px;width:28px;height:28px;color:var(--chalk)}
#imp .im-src{display:flex;align-items:center;gap:14px;width:100%;min-height:0;margin:8px 0 0;padding:16px;border-radius:18px;border:1px solid var(--line);background:var(--surface);box-shadow:none;color:var(--chalk);text-align:left}
#imp .im-src>svg{width:20px;height:20px;flex:none;color:var(--muted)}
#imp .im-ic{flex:none;width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--chalk) 9%,transparent)}
#imp .im-ic svg{width:22px;height:22px}
#imp .im-ct{flex:1;min-width:0}#imp .im-ct b{display:block;font:600 15px var(--body)}#imp .im-ct span{display:block;font:400 12px/1.269 var(--body);color:var(--muted);margin-top:2px}
#imp .im-how{margin:11px 2px 0;font:400 12px/1.41 var(--body);color:var(--muted)}#imp .im-how summary{cursor:pointer;color:var(--chalk);font-weight:500}#imp .im-how p{margin:5px 0 0}#imp .im-how b{color:var(--chalk);font-weight:600}
#imp .im-link{align-self:center;width:auto;min-height:0;margin:11px 0 0;padding:6px;border:0;background:none;box-shadow:none;color:var(--accent-ink,var(--accent));font:500 13px var(--body);text-decoration:underline;text-underline-offset:3px}
:root[data-theme="dark"] #imp .im-link{color:#8ea0ff}
#imp .im-sp{flex:1;min-height:20px}
#imp .im-foot{text-align:center;font:400 12px var(--body);color:var(--muted);margin:10px 0 0}
#imp .im-err{margin:3px 0 5px;padding:12px 14px;border-radius:14px;background:color-mix(in srgb,#E5484D 12%,transparent);color:var(--chalk);font:400 12px/1.363 var(--body)}
#imp .im-go,#imp .im-go2{display:flex;align-items:center;justify-content:center;width:100%;height:54px;min-height:0;margin:0;border:0;border-radius:16px;background:var(--accent);color:#fff;font:600 15px var(--body);box-shadow:none}
#imp .im-go:disabled{background:color-mix(in srgb,var(--chalk) 10%,transparent);color:var(--muted)}
#imp .im-go2{margin-top:8px;background:var(--surface);color:var(--chalk);border:1px solid var(--line)}
#imp .im-unit{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 11px;font:500 13px var(--body)}
#imp .im-seg{display:grid;grid-template-columns:1fr 1fr;width:120px;padding:3px;border-radius:12px;background:color-mix(in srgb,var(--chalk) 8%,transparent)}
#imp .im-seg button{height:32px;min-height:0;margin:0;padding:0;border:0;border-radius:9px;background:none;box-shadow:none;color:var(--muted);font:600 13px var(--body)}
#imp .im-seg button.on{background:var(--chalk);color:var(--ground)}
#imp .im-att{display:flex;align-items:center;gap:8px 10px;flex-wrap:wrap;padding:12px 14px;margin:0 0 8px;border-radius:14px;border:1px solid var(--line);background:var(--surface);font:600 13px var(--body);color:#E0A100}
#imp .im-att .im-mini{margin-left:auto}
#imp .im-list{display:flex;flex-direction:column;gap:8px 10px}
#imp .im-item{display:flex;gap:12px;align-items:flex-start;padding:14px;border-radius:16px;border:1px solid var(--line);background:var(--surface)}
#imp .im-item.im-ok{align-items:center;padding:12px 14px}
#imp .im-ready{margin-top:8px}
#imp .im-it{flex:1;min-width:0}#imp .im-it>b{display:block;font:600 14px var(--body)}#imp .im-it>span{display:block;font:400 12px/1.316 var(--body);color:var(--muted);margin-top:2px;overflow-wrap:anywhere}
#imp .im-dot{flex:none;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font:700 12px var(--body)}
#imp .im-dot svg{width:15px;height:15px}
#imp .im-dot.ok{background:#2fb36b;color:#fff}#imp .im-dot.ask{color:var(--accent-ink,var(--accent))}#imp .im-dot.ask svg{width:22px;height:22px}
#imp .im-dot.bad{color:#E5484D}#imp .im-dot.bad svg{width:24px;height:24px}#imp .im-dot.warn{background:#E0A100;color:#000}
#imp .im-btns{display:flex!important;gap:8px;margin-top:10px!important}
#imp .im-b{flex:1;height:42px;min-height:0;margin:0;padding:0 10px;border:1px solid var(--line);border-radius:12px;background:none;box-shadow:none;color:var(--chalk);font:600 13px var(--body)}
#imp .im-b.pri{background:var(--accent);border-color:transparent;color:#fff}
#imp .im-mini{flex:none;width:auto;min-height:0;margin:0;padding:6px 10px;border:1px solid var(--line);border-radius:10px;background:none;box-shadow:none;color:var(--chalk);font:500 11px var(--body)}
#imp .im-chips{display:flex!important;flex-wrap:wrap;gap:5px 6px;margin-top:8px!important}
#imp .im-chips.im-col{flex-direction:column}
#imp .im-chips button{width:auto;min-height:0;margin:0;padding:8px 12px;border:1px solid var(--line);border-radius:12px;background:none;box-shadow:none;color:var(--chalk);font:500 12px var(--body);text-align:left}
#imp .im-chips button.on{background:var(--chalk);color:var(--ground);border-color:transparent}
#imp .im-big{display:flex;align-items:center;gap:18px;margin:11px 0 16px}#imp .im-big span{padding-right:18px;border-right:1px solid var(--line)}#imp .im-big b{display:block;font:800 56px/1 var(--body);letter-spacing:-2px;color:var(--accent-ink,var(--accent))}#imp .im-big span{font:600 17px var(--body)}#imp .im-big em{font:400 15px var(--body);font-size:15px;font-style:normal}
:root[data-theme="dark"] #imp .im-big b,:root[data-theme="dark"] #imp .im-count b{color:#5d78ff}
#imp .im-checks{padding:6px 16px;border-radius:18px;border:1px solid var(--line);background:var(--surface)}#imp .im-checks p{display:flex;align-items:center;gap:12px;margin:0;padding:12px 0;font:400 14px var(--body);border-top:1px solid var(--line)}#imp .im-checks p:first-child{border-top:0}
#imp .im-count{text-align:center;margin:6px 0 10px}#imp .im-count b{display:block;font:800 56px/1 var(--body);letter-spacing:-2px;color:var(--accent-ink,var(--accent))}#imp .im-count span{font:400 15px var(--body)}
#imp .im-heat{display:flex;justify-content:center;gap:3px;margin:6px 0 8px}#imp .im-wk{display:flex;flex-direction:column;gap:2px 3px}#imp .im-wk i{width:9px;height:9px;border-radius:2.5px;background:color-mix(in srgb,var(--chalk) 12%,transparent)}#imp .im-wk i.on{background:var(--accent)}
@media(max-width:360px){#imp .im{padding-left:18px;padding-right:18px}#imp .im-h{font-size:23px}#imp .im-wk i{width:7px;height:7px}#imp .im-big b,#imp .im-count b{font-size:46px}}
.note [data-imp-undo]{display:inline;width:auto;min-height:0;margin:0;padding:0;border:0;background:none;box-shadow:none;color:var(--accent-ink,var(--accent));font:inherit;text-decoration:underline;cursor:pointer}

/* v4.6.277: first-run tips (js/today.js). Ink pill: black on light, white on dark. */
.tipn{--tip-ax:40px;position:fixed;z-index:180;display:flex;gap:6px;align-items:center;max-width:min(330px,calc(100vw - 28px));padding:8px 16px 8px 6px;border-radius:18px;
  background:var(--chalk);color:var(--ground);font:600 13px/1.25 var(--body,system-ui);box-shadow:0 12px 28px rgba(0,0,0,.28);
  animation:tipIn .52s cubic-bezier(.3,1.6,.5,1) both}
.tipn.out{animation:tipOut .26s ease-in both}
.tipn:after{content:'';position:absolute;left:var(--tip-ax,40px);width:14px;height:14px;background:var(--chalk);border-radius:2px;transform:rotate(45deg)}
.tipn.up:after{top:-6px}.tipn.down:after{bottom:-6px}
.tipn .tipt{animation:tipTxt .4s ease-out .18s both}
.tipn small{display:block;margin-top:2px;font-weight:500;font-size:11px;opacity:.68}
.tipn .tip-pip{display:block;flex:none;width:60px;height:auto;margin:-8px -6px -8px -4px;transform-origin:50% 80%;animation:tipWave 1.1s ease-in-out .35s both,tipBob 2.6s ease-in-out 1.6s infinite}
.tipn button{all:unset;cursor:pointer;margin-left:6px;padding:8px 13px;border-radius:999px;white-space:nowrap;background:color-mix(in srgb,var(--ground) 16%,transparent);font:600 11px/1 var(--body,system-ui)}
.tipr{--tip-r:20px;--tip-sx:1.06;--tip-sy:1.2;position:fixed;z-index:179;pointer-events:none;border-radius:var(--tip-r,20px)}
/* v4.6.280: smooth on the phone. A steady outline holds the target; two rings ripple out from it.
   Only transform and opacity move (the GPU's job; animating `inset` re-laid out every frame and
   stuttered on iOS), each ring fades in so a loop's restart can't be seen, and the second waits
   invisibly (fill both) instead of sitting still for its delay and then jumping. */
.tipr{outline:2px solid var(--accent);outline-offset:2px}
.tipr:before,.tipr:after{content:'';position:absolute;inset:-4px;border-radius:calc(var(--tip-r,20px) + 4px);border:2px solid var(--accent);opacity:0;will-change:transform,opacity;animation:tipRing 2.4s ease-out infinite both}
.tipr:after{animation-delay:1.2s}
.tipr.out{opacity:0;transition:opacity .2s}
@keyframes tipIn{0%{opacity:0;transform:scale(.82)}100%{opacity:1;transform:none}}
@keyframes tipOut{to{opacity:0;transform:scale(.9)}}
@keyframes tipTxt{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
@keyframes tipWave{0%,100%{transform:rotate(0)}20%{transform:rotate(-14deg)}40%{transform:rotate(12deg)}60%{transform:rotate(-8deg)}80%{transform:rotate(5deg)}}
@keyframes tipBob{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}
@keyframes tipRing{0%{transform:scale(1);opacity:0}12%{opacity:.6}100%{transform:scale(var(--tip-sx,1.06),var(--tip-sy,1.2));opacity:0}}
:root[data-mascot-motion="still"] .tipn .tip-pip{animation:none}
@media (prefers-reduced-motion:reduce){.tipn,.tipn .tipt,.tipn .tip-pip{animation:none}.tipr:before,.tipr:after{animation:none;opacity:0}}

/* v4.6.287: "Just looking around" -- sample data, offered quietly on a fresh Today */
.today-sample{all:unset;cursor:pointer;display:block;margin:14px auto 0;font:500 12px/1 var(--body);color:var(--muted);text-decoration:underline;text-underline-offset:3px}
/* v4.6.318: the part shelf */
.ps-card{--ps-off:color-mix(in srgb,var(--chalk) 11%,var(--surface))}
.ps-card button{font:inherit;border:0;background:transparent;color:var(--muted);cursor:pointer}
.ps-seg{display:flex;gap:4px;background:var(--surface2);border-radius:12px;padding:3px}
.ps-seg button{flex:1;font-weight:500;font-size:11px;padding:7px 0;border-radius:9px}
.ps-seg button[aria-pressed="true"]{background:var(--surface);color:var(--chalk);box-shadow:0 1px 3px rgba(0,0,0,.1)}
.ps-row{display:flex;justify-content:space-between;align-items:center;margin:10px 0 2px;gap:8px}
.ps-pill{display:flex;background:var(--surface2);border-radius:999px;padding:2px}
.ps-pill button{font-weight:500;font-size:11px;padding:5px 11px;border-radius:999px}
.ps-pill button[aria-pressed="true"]{background:var(--chalk);color:var(--surface)}
.ps-shelf{display:grid;grid-template-columns:repeat(4,1fr);gap:14px 4px;margin-top:10px}
.ps-t{display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0}
.ps-fig{width:66px;height:112px}
.ps-t b{font-size:20px;font-weight:600;line-height:1;margin-top:3px;font-variant-numeric:tabular-nums;color:var(--chalk)}
.ps-t small{font:500 9px var(--mono,'IBM Plex Mono',monospace);color:var(--faint);margin-top:2px}
.ps-t span{font-size:11px;color:var(--muted);margin-top:3px}
.ps-t.ps-zero b{color:var(--faint)}
.ps-foot{display:flex;justify-content:space-between;gap:10px;font-size:11px;color:var(--muted);margin-top:11px;padding-top:12px;border-top:1px solid var(--line)}
.ps-foot span:last-child{text-align:right}
.ps-foot i{font-style:normal;color:var(--chalk);font-weight:500}
.ps-card{--ps-glow:color-mix(in srgb,var(--chalk) 35%,transparent)}
.ps-card:not(.ps-played) .ps-clip{transform:translateY(100%)}   /* v4.6.404: the figure's own height, whichever figure */
@media (prefers-reduced-motion:reduce){.ps-card .ps-clip{transform:none!important}}
.ps-shelf .ps-t{padding:4px 0 6px;border-radius:16px;font:inherit;color:inherit;-webkit-tap-highlight-color:transparent;transition:transform .15s,background-color .15s}
.ps-shelf .ps-t:active{transform:scale(.96);background:var(--surface2)}
.ps-shelf .ps-t:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
dialog.ps-sheet{position:fixed;inset:auto 0 0 0;margin:0 auto;width:100%;max-width:520px;max-height:88vh;overflow:auto;border:0;border-radius:24px 24px 0 0;background:var(--surface);color:var(--chalk);padding:8px 20px calc(22px + env(safe-area-inset-bottom));box-shadow:0 -8px 40px rgba(0,0,0,.18)}
dialog.ps-sheet::backdrop{background:rgba(0,0,0,.36)}
.ps-grab{width:38px;height:5px;border-radius:3px;background:var(--line);margin:0 auto 8px}
.ps-sheet{font-family:var(--body)}
.ps-sheet .ps-head{display:flex;justify-content:space-between;align-items:flex-start}
.ps-sheet h3{margin:0;font-size:21px;font-weight:600}
.ps-sheet .ps-head p{margin:2px 0 0;font-size:11px;color:var(--muted)}
.ps-x{font-size:23px!important;line-height:1;width:36px;height:36px;border-radius:50%;background:var(--surface2)!important;color:var(--muted)}
.ps-figs{display:flex;justify-content:center;gap:28px;margin:8px 0 6px}
.ps-figs figure{margin:0;text-align:center}
.ps-figs figcaption{font:500 9px var(--mono,'IBM Plex Mono',monospace);letter-spacing:2px;text-transform:uppercase;color:var(--faint);margin-top:3px}
.ps-big{width:118px;height:200px}
.ps-mrow{display:grid;grid-template-columns:12px 1fr auto;gap:10px 12px;align-items:center;padding:12px 0;border-top:1px solid var(--line)}
.ps-mrow i{width:12px;height:12px;border-radius:4px}
.ps-mrow b{display:block;font-size:13px;font-weight:500}
.ps-mrow span small{display:block;font-size:11px;color:var(--muted);margin-top:2px}
.ps-mrow em{font-style:normal;font-size:18px;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.ps-mrow em small{font-size:11px;font-weight:400;color:var(--muted)}
.ps-mrow.ps-mzero b,.ps-mrow.ps-mzero em{color:var(--faint)}
/* v4.6.319: the unit said once, above the shelf */
.ps-cap{font-size:11px;color:var(--muted);margin:11px 2px 0}
.ps-cap b{font-weight:600;color:var(--chalk)}
.ps-t b small{font-size:10px;font-weight:500;color:var(--faint)}
.ps-t .ps-n{font-style:normal}
.ps-share{margin:5px 0 2px;font-size:11px;color:var(--muted)}
.ps-share b{font-size:13px;color:var(--chalk);font-weight:600}
.ps-mrow em{text-align:right}
.ps-mrow em small{display:block;font-size:10px!important;margin-top:1px}
.ps-note{font-size:10px;color:var(--faint);margin:10px 0 0;line-height:1.316}
  .eqedit .eq-step-q{margin-top:11px}   /* v4.6.331 */
/* v4.6.341: ONE ICON SET. Icons inside a control sit 10% under their box so a
   solid shape doesn't crowd the label beside it; Share, the widest silhouette,
   a further 10% (Sungjee, 2026-10-15). `scale` leaves the layout alone. */
:is(button,a,[role=button],.pw-btn) .ic:not(.ic-brandmark){scale:.9}
:is(button,a,[role=button],.pw-btn) .ic-share{scale:.81}
.ic-glyph{flex:none;vertical-align:-.18em}
/* v4.6.365: a glyph turned icon stays IN the line of text it replaced. Some cards style their svgs
   display:block at a fixed size (the Stats system sizes every svg in #view), so "+ Year" and
   "Latest ↗" broke into an icon stacked over the word, both blown up to 25-31px. The icon is the
   size of its letters and sits on their line, whatever card it lands in. */
.ic.ic-glyph{display:inline-block!important;width:1.1em!important;height:1.1em!important;margin:0 .2em 0 0;vertical-align:-.2em!important}
/* the "+ Year" fold is a pill like the year chips beside it -- no disclosure triangle */
.comparison-years summary{list-style:none;display:inline-flex;align-items:center}
.comparison-years summary::-webkit-details-marker{display:none}
.chart-latest{display:inline-flex;align-items:center;gap:2px}
.chart-latest .ic.ic-glyph{margin:0 0 0 .15em}

/* v4.6.359: TODAY WHILE A SESSION IS LIVE (js/today.js liveTodayHTML). One plan
   card with its progress and the single Next button, then a plain list of what
   is logged. The red header bar is the only live signal; a logged row carries a
   small red dot while its exercise is still open. Sans for words, mono for numbers. */
.lt{display:flex;flex-direction:column}
.pw-home.lt .lt-plan{display:flex;flex-direction:column;gap:13px 16px;padding:20px 18px 18px;margin:0}
.lt .lt-fold>summary.pw-fold-summary{gap:12px}
.lt .lt-head{display:flex;flex-direction:column;gap:2px 3px;min-width:0}
.lt .lt-head small{font:400 12px/1.222 var(--body);color:var(--muted)}
.lt .lt-head b{font:600 18px/1.2 var(--body);color:var(--chalk);text-wrap:balance}
.lt .lt-fold-body{padding-top:12px}
.pw-home.lt .lt-fold-body .pw-home-tools{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px 6px;margin:0 0 11px}
.pw-home.lt .lt-fold-body .pw-home-tools>*{min-width:0;justify-content:center;padding-inline:6px}
.lt .lt-progress{height:8px;border-radius:4px;background:var(--line);overflow:hidden}
.lt .lt-progress i{display:block;height:100%;border-radius:4px;background:var(--accent)}
.lt .lt-count{margin-top:-6px;font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}
.lt .lt-done{margin:0;font:400 12px/1.316 var(--body);color:var(--muted)}
.lt .lt-h{margin:24px 4px 10px}
.pw-home.lt .lt-log{padding:4px 0;margin:0;display:flex;flex-direction:column}
.lt .lt-row{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:56px;box-sizing:border-box;padding:10px 20px;border:0;background:none;color:var(--chalk);text-align:left;cursor:pointer;font-family:var(--body)}
.lt .lt-row+.lt-row{border-top:1px solid var(--line)}
.lt .lt-row b{display:flex;align-items:center;gap:10px;min-width:0;font-size:15px;font-weight:500}
.lt .lt-row.on b::before{content:"";flex:none;width:8px;height:8px;border-radius:50%;background:var(--live)}
.lt .lt-row .mono{flex:none;font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}

/* v4.6.360: TRAIN DURING A LIVE SET (approved mock "Train during a live set").
   Part tiles: one style. A part being worked shows a red dot with "today"; the
   selected live part keeps its pulsing red fill, now with white text (it was
   red on red). The part's sets today sit in one card, red-outlined while open,
   with the Continue button inside. */
.vh{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.partcard.liveP:not(.sel){border-color:transparent;animation:none}
.partcard.liveP .ps{display:inline-flex;align-items:center;gap:6px}
.partcard.liveP .ps::before{content:"";flex:none;width:6px;height:6px;border-radius:50%;background:currentColor}
.partcard.liveP:not(.sel) .ps{color:var(--live);animation:none}
.partcard.sel.liveP .ps{color:#FFFFFF;opacity:1;animation:none}
:root[data-skin="minimal"] .partcard.liveP:not(.sel){border-color:transparent}
.pt-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-top:18px}
.pt-head h2{flex:1;min-width:0}
.pt-head .pt-last{display:inline-flex;align-items:center;gap:2px 2px;flex:none;padding:6px 0;border:0;background:none;color:var(--accent-ink);font:400 11px/1.222 var(--body);text-decoration:none;cursor:pointer;white-space:nowrap}
.pt-head .pt-last .ic{width:14px;height:14px}
.pt-card{display:flex;flex-direction:column;gap:3px 4px;padding:8px;border-radius:18px;background:var(--surface);border:1.5px solid transparent}
.pt-card.on{border-color:var(--live)}
.pt-card .todayrow{margin:0;background:none;box-shadow:none;border:0}
.pt-card .todayrow+.todayrow{border-top:1px solid var(--line);border-radius:0}
.pt-card .todayrow:not(.fin) .logmain>b{display:inline-flex;align-items:center;gap:10px}
.pt-card .todayrow:not(.fin) .logmain>b::before{content:"";flex:none;width:8px;height:8px;border-radius:50%;background:var(--live)}
.pt-card .btnrow{margin:5px 0 0}
.pt-card .btnrow .btn{width:100%;margin:0}
.mtag{display:inline;margin:0;padding:0;border-radius:0;background:none;color:inherit;font:inherit;letter-spacing:0;text-transform:none;vertical-align:baseline}
.mtag::after{content:" · "}

/* v4.6.361: TODAY AFTER THE WORKOUT (js/today.js closedTodayHTML). Shares the live
   stack's card and row styles; a part is a small heading with its total, rows
   sit beneath without boxes or ticks. */
.pw-home.ld .lt-h{margin:18px 4px 8px}
.pw-home.ld .ld-next{padding:18px 18px 0;gap:0}
.pw-home.ld .ld-next>.lt-fold{padding-bottom:16px}
.pw-home.ld .ld-next>.pw-later>summary.pw-fold-summary,.pw-home.lt .lt-fold-body>.pw-later>summary.pw-fold-summary{margin:0 -18px;padding:8px 18px;border-top:1px solid var(--line)}   /* v4.6.404: and the live day's */
/* v4.6.365: the days after tomorrow -- the Tomorrow row's shape, a step smaller, hairlines between */
.pw-home.ld .ld-next>.pw-later>summary.pw-fold-summary,.pw-home.lt .lt-fold-body>.pw-later>summary.pw-fold-summary{align-items:center}
.pw-home.ld .ld-next>.pw-later>summary.pw-fold-summary>small,.pw-home.lt .lt-fold-body>.pw-later>summary.pw-fold-summary>small{margin-left:auto}
.pw-home.lt .ld-later-list{margin:0 -18px;padding:0 18px}
.pw-home.lt .ld-later-day{border-top:1px solid var(--line)}
.pw-home.lt .ld-later-day>summary.pw-fold-summary{padding:14px 0;gap:12px;list-style:none}
.pw-home.lt .ld-later-day>summary::-webkit-details-marker{display:none}
.pw-home.lt .ld-later-day .lt-head b{font-size:15px}
.pw-home.lt .ld-later-day .lt-fold-body{padding:0 0 16px}
.pw-home.ld .ld-log{padding:4px 0 8px}
.ld .ld-part{display:flex;align-items:center;justify-content:space-between;gap:12px;box-sizing:border-box;min-height:40px;padding:12px 20px 0;margin:0}
.ld .ld-part+.lt-row{border-top:0}
.ld .lt-row+.ld-part{margin-top:8px;padding-top:12px;border-top:1px solid var(--line)}
.ld .ld-part b{font:600 14px/1.222 var(--body);color:var(--chalk)}
.ld .ld-part .mono{font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}
.ld .lt-row{min-height:44px;height:44px;padding-block:0}
.ld .lt-row b{font-size:14px;font-weight:400}
.ld .lt-row+.lt-row{border-top:0}

/* v4.6.364 retype applied */
:root{font-size:88%}

/* v4.6.364 (Sungjee's notes on version B, 2026-10-20) */
.today-focus-upcoming .pw-later-day .pw-future-label>strong{font-size:15px}  /* 1B: tomorrow's parts read a step larger */
.pw-home.ld .lt-h{margin:24px 4px 10px}                                     /* 3B: every heading on the finished day sits 24px under what precedes it */
.pw-home.ld>.lt-h:first-child{margin-top:10px}                              /* ...the day card above already holds 14px */
:root[data-flow="refined"] #view[data-flow-view="lift"] .flow-exgroup .logmain{padding:11px 0;min-height:46px}  /* 4B: the compact list rows of 4A */

/* v4.6.367: the fold under Start here. It is the last row of the group, quieter
   than an exercise: a count and a chevron that turns when open. */
.fresh-more{display:flex;align-items:center;gap:8px;width:100%;min-height:52px;padding:0 14px;border:0;background:none;color:var(--accent-ink);font:500 14px var(--body);cursor:pointer;text-align:left}
.flow-exgroup .fresh-more{margin:0;padding:0 6px 0 0;min-height:56px}
.fresh-more>span:first-child{flex:1}
.fresh-more .fresh-n{font:500 12px var(--mono);font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.fresh-more .fresh-chev{display:inline-flex;color:var(--muted);transform:rotate(90deg);transition:transform .2s}
.fresh-more.open .fresh-chev{transform:rotate(-90deg)}

/* v4.6.368: Text size. The preview line is real row type, so it grows with the
   setting; the five A's are fixed sizes (they are the scale, not the text). */
.ts-card{display:flex;flex-direction:column;gap:12px}
.ts-preview{display:flex;flex-direction:column;gap:3px;padding:12px 14px;border-radius:14px;background:color-mix(in srgb,var(--chalk) 5%,transparent)}
.ts-preview b{font:600 15px var(--body);color:var(--chalk)}
.ts-preview span{font:400 10px var(--mono);color:var(--muted)}
.ts-steps{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}
.ts-step{--ts-g:14px;min-height:48px;border:1px solid var(--line);border-radius:12px;background:var(--surface2);color:var(--muted);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0}
.ts-step span{font-family:var(--body);font-weight:600;line-height:1;font-size:var(--ts-g)!important}
.ts-step.sel{background:var(--accent);border-color:var(--accent);color:#fff}
.ts-labels{display:grid;grid-template-columns:1fr 1fr 1fr;margin-top:-6px;font:400 10px var(--mono);color:var(--faint)}
.ts-labels span:nth-child(2){text-align:center}.ts-labels span:nth-child(3){text-align:right}
.ts-auto{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:44px;border:0;border-top:1px solid var(--line);background:none;padding:10px 0 0;color:var(--chalk);font:500 13px var(--body);cursor:pointer;text-align:left}
.ts-auto i{flex:0 0 auto;width:44px;height:26px;border-radius:13px;background:var(--line);position:relative;transition:background .2s}
.ts-auto i::after{content:'';position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:#fff;box-shadow:0 1px 3px #0003;transition:transform .2s}
.ts-auto.on i{background:var(--accent)}.ts-auto.on i::after{transform:translateX(18px)}

/* v4.6.370: CALMER TRAIN DURING A WORKOUT. Red is the top bar's: the card loses
   its red outline, the button is the app's blue, the meter is neutral, and a
   body-part tile in progress keeps only a small red dot -- the selected tile
   is the ordinary selected tile. The usual-session line lives in the card. */
.pt-card.on{border-color:transparent}
.pt-head .pt-last{color:var(--muted)}
.pt-usual{display:flex;flex-direction:column;gap:6px;padding:10px 10px 4px;border-top:1px solid var(--line);margin-top:2px}
.pt-usual .smeter{margin:0}
.pt-usual .smeter i{background:color-mix(in srgb,var(--chalk) 32%,transparent)}
.pt-usual b.pt-over{color:var(--accent-ink)}
.pt-card .todayrow.fin .logmain>b::before{content:'✓';margin-right:7px}
.pt-usual-t{display:flex;justify-content:space-between;gap:10px;font:400 11px var(--mono);color:var(--muted)}
.pt-usual-t b{color:var(--chalk);font-weight:600}
:root[data-skin="minimal"] .partcard.sel.liveP,.partcard.sel.liveP{background:var(--accent);border-color:var(--accent);animation:none}
:root[data-skin="minimal"] .partcard.liveP,.partcard.liveP{border-color:transparent;animation:none}
.partcard.liveP:not(.sel) .ps{color:var(--muted)}
.partcard.liveP .ps::before{content:"";display:inline-block;width:7px;height:7px;margin-right:6px;border-radius:50%;background:var(--live);vertical-align:1px}
.partcard.sel.liveP .ps::before{background:#fff}

/* v4.6.372: the two ways in. Choice cards advance on a tap; the plan screen
   shows the week and today's session before anything is saved. */
#onb .ob-card.ob-big{padding:22px 18px;margin-top:14px}
#onb .ob-card.ob-big .ob-ct b{font-size:18px}
#onb .ob-card.ob-pick{padding:16px 18px}
#onb .ob-card.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
#onb .ob-look{align-self:center;margin:0 0 6px;font:500 14px var(--body);color:var(--muted);text-decoration:none;min-height:44px}
#onb .ob-sub b{color:var(--chalk);font-weight:600}
#onb .ob-week{display:grid;grid-template-columns:repeat(7,1fr);gap:5px;margin:18px 0 0}
#onb .ob-wk{display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 0;border-radius:12px;background:color-mix(in srgb,var(--chalk) 4%,transparent);color:var(--faint)}
#onb .ob-wk b{font:600 11px var(--mono)}
#onb .ob-wk span{font:500 10px var(--body);white-space:nowrap}
#onb .ob-wk.on{background:var(--accent);color:#fff}
#onb .ob-today{margin:14px 0 0;padding:6px 16px 8px;border-radius:18px;background:var(--surface);border:1px solid var(--line)}
#onb .ob-today-h{display:flex;align-items:baseline;gap:8px;padding:10px 0 6px}
#onb .ob-today-h span{font:500 10px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
#onb .ob-today-h b{font:600 16px var(--body);color:var(--chalk)}
#onb .ob-ex{display:flex;justify-content:space-between;gap:12px;padding:11px 0;border-top:1px solid var(--line);font:400 14px var(--body);color:var(--chalk)}
#onb .ob-ex b{font:500 13px var(--mono);font-size:13px;color:var(--muted);white-space:nowrap}
#onb .ob-plan .ob-why{margin-top:12px}
/* v4.6.372 (revised): the fresh path is the planner's Preferences, one section a screen */
#onb .ob-pref .card{margin:22px 0 0;padding:18px}
#onb .ob-pref .pf-seg{margin:0}
#onb .ob-order .card.pf-split{padding:14px 12px}
#onb .ob-order .pf-ses{grid-template-columns:auto 1fr 30px}
#onb .ob-order .ob-dayn{width:auto;min-width:44px;height:30px;padding:0 8px;border-radius:15px;font:600 12px var(--mono);font-size:12px}
#onb .ob-order .pf-rot-tags em{font-size:11px}
#onb .ob-order .pf-pal-chip:disabled{opacity:.45}
#onb .ob-go:disabled{opacity:.4}
#onb .ob-order .pf-ses{grid-template-columns:24px 1fr 30px}
#onb .ob-ses-h{display:flex;align-items:baseline;gap:8px;margin:2px 0 6px;width:100%}
#onb .ob-ses-h b{font:600 13px var(--body);color:var(--chalk)}
#onb .ob-ses-h i{font:500 11px var(--mono);font-size:11px;font-style:normal;color:var(--accent-ink)}
#onb .ob-order .pf-pal{position:static;margin:10px 0 4px;border-radius:14px}

/* v4.6.372: START ME FRESH, four screens. Built from the app's own pieces: the
   onboarding card and button, the planner's segmented control, weekday circles
   and body-part tags. */
#onb .ob-top .ob-stepn{flex:none;font:400 11px var(--mono);font-size:11px;color:var(--muted);letter-spacing:.04em;min-width:44px;text-align:right}
#onb .ob-sec{margin:22px 0 0}
#onb .ob-sec>.ob-lbl{display:block;margin:0 0 10px;font:600 14px var(--body);font-size:14px;color:var(--chalk)}
#onb .ob-sec .pf-seg{margin:0}
#onb .ob-row.ob-sec{display:flex;align-items:center;justify-content:space-between}
#onb .ob-row.ob-sec>.ob-lbl{margin:0}
#onb .ob-more{margin:22px 0 0;border-top:1px solid var(--line)}
#onb .ob-more summary{display:flex;align-items:center;justify-content:space-between;min-height:52px;list-style:none;cursor:pointer;font:600 14px var(--body);font-size:14px;color:var(--chalk)}
#onb .ob-more summary::-webkit-details-marker{display:none}
#onb .ob-more summary i{font:400 12px var(--body);font-size:12px;font-style:normal;color:var(--muted);margin-left:6px}
#onb .ob-more summary .ic{color:var(--muted);transition:transform .2s}
#onb .ob-more[open] summary .ic{transform:rotate(45deg)}
#onb .ob-more-in{display:grid;grid-template-columns:1fr 120px;gap:10px}
#onb .ob-more-in label{display:flex;flex-direction:column;gap:6px;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#onb .ob-in-f{height:46px;padding:0 12px;border:1px solid var(--line);border-radius:12px;background:var(--surface);color:var(--chalk);font:400 16px var(--body);font-size:16px}
#onb .ob-foot2{margin:10px 0 0;text-align:center;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#onb .ob-go:disabled{opacity:.4}
/* 03 Routine */
#onb .ob-rt-plan{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:22px 0 0;padding:16px 18px;border-radius:18px;background:var(--surface);border:1px solid var(--line)}
#onb .ob-rt-plan b{display:block;font:600 17px var(--body);font-size:17px;color:var(--chalk)}
#onb .ob-rt-plan span{display:block;margin-top:3px;font:400 12px var(--mono);font-size:12px;color:var(--muted)}
#onb .ob-rt-link{min-height:44px;padding:0 4px;border:0;background:none;color:var(--accent-ink);font:500 13px var(--body);font-size:13px;cursor:pointer}
#onb .ob-rt-bar{display:flex;align-items:center;justify-content:space-between;margin:18px 0 2px}
#onb .ob-rt-bar>span{font:500 10px var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
#onb .ob-rt-tray{margin:14px 0 4px;padding:14px;border-radius:18px;background:var(--surface);border:1px solid var(--line)}
#onb .ob-rt-tray-h{display:flex;align-items:center;justify-content:space-between}
#onb .ob-rt-tray-h b{font:600 14px var(--body);font-size:14px;color:var(--chalk)}
#onb .ob-rt-tray p{margin:0 0 10px;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#onb .ob-rt-pool{touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;min-height:40px;cursor:grab}
#onb .ob-rt-pool.held{background:var(--accent);border-color:var(--accent);color:#fff}
#onb .ob-rt-list{display:flex;flex-direction:column;gap:8px;margin-top:8px}
#onb .ob-rt-ses{padding:12px 10px 12px 14px;border-radius:18px;background:var(--surface);border:1.5px solid transparent;box-shadow:var(--shadow);transition:border-color .16s,background-color .16s,transform .16s}
#onb .ob-rt-ses.ob-drop{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 8%,var(--surface));transform:scale(1.012)}
#onb .ob-rt-ses.ob-settle{animation:obSettle .26s ease-out}
@keyframes obSettle{50%{transform:scale(1.02)}}
#onb .ob-rt-head{display:flex;align-items:center;gap:10px}
#onb .ob-rt-day{flex:none;width:34px;font:500 12px var(--mono);font-size:12px;color:var(--muted)}
#onb .ob-rt-head b{flex:1;min-width:0;font:600 15px var(--body);font-size:15px;color:var(--chalk)}
#onb .ob-rt-grip{flex:none;width:44px;height:44px;padding:0;border:0;background:none;color:var(--faint);display:inline-flex;align-items:center;justify-content:center;touch-action:none;cursor:grab}
#onb .ob-rt-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
#onb .ob-rt-ses .ob-rt-chips{margin:2px 0 0 44px}
#onb .ob-rt-chips em{font:400 12px var(--body);font-size:12px;font-style:normal;color:var(--muted)}
#onb .ob-rt-chip{gap:4px;height:34px;padding:0 6px 0 12px;border:0;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;cursor:grab}
#onb .ob-rt-chip .ic{width:12px;height:12px;opacity:.7}
#onb .ob-rt-more{min-height:30px;padding:0 6px;border:0;background:none;color:var(--muted);font:500 12px var(--mono);font-size:12px;cursor:pointer}
#onb .ob-rt-add{margin:8px 0 0 44px;min-height:36px;padding:0 12px;border:1px dashed var(--accent);border-radius:12px;background:none;color:var(--accent-ink);font:500 12px var(--body);font-size:12px;cursor:pointer}
#onb .ob-rt-order{display:flex;gap:8px;margin:10px 0 0 44px}
#onb .ob-rt-order button{flex:1;min-height:40px;border:1px solid var(--line);border-radius:12px;background:var(--surface2);color:var(--chalk);font:500 12px var(--body);font-size:12px}
#onb .ob-rt-order button:disabled{opacity:.4}
#onb .ob-rt-addses{align-self:center;margin:10px 0 0;min-height:44px;border:0;background:none;color:var(--accent-ink);font:500 13px var(--body);font-size:13px;cursor:pointer}
#onb .ob-rt-feed{display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:44px;margin-top:8px;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#onb .ob-lifted{opacity:.35}
#onb .ob-rt-ghost{position:fixed;left:0;top:0;z-index:99;pointer-events:none;padding:9px 16px;border-radius:17px;background:var(--accent);color:#fff;font:600 14px var(--body);font-size:14px;white-space:nowrap;box-shadow:0 12px 28px rgba(0,0,0,.28)}
html.ob-dragging,html.ob-dragging *{user-select:none!important;-webkit-user-select:none!important}
#onb .ob-live{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
/* the template sheet */
#onb .ob-sheet-wrap{position:fixed;inset:0;z-index:50;display:flex;align-items:flex-end;justify-content:center;background:rgba(0,0,0,.45);animation:obFadeIn .2s ease-out}
#onb .ob-sheet{width:100%;max-width:480px;max-height:92vh;overflow:auto;padding:10px 20px calc(env(safe-area-inset-bottom) + 20px);border-radius:28px 28px 0 0;background:var(--ground);box-shadow:0 -10px 40px rgba(0,0,0,.25);animation:obSheetIn .24s ease-out}
#onb .ob-sheet-grip{width:40px;height:4px;margin:0 auto 14px;border-radius:2px;background:var(--line)}
#onb .ob-sheet-h{display:flex;align-items:center;justify-content:space-between}
#onb .ob-sheet-t{margin:0;font:700 23px var(--body);font-size:23px;letter-spacing:-.4px;color:var(--chalk)}
#onb .ob-sheet-x{width:40px;height:40px;padding:0;border:0;border-radius:50%;background:var(--surface2);color:var(--chalk);display:inline-flex;align-items:center;justify-content:center}
#onb .ob-sheet .ob-sub{margin:4px 0 12px}
#onb .ob-tpl{display:flex;align-items:center;gap:10px;width:100%;margin:8px 0 0;padding:13px 14px;border:1px solid var(--line);border-radius:16px;background:var(--surface);color:var(--chalk);text-align:left;cursor:pointer}
#onb .ob-tpl>span{flex:1;min-width:0}
#onb .ob-tpl b{display:block;font:600 15px var(--body);font-size:15px}
#onb .ob-tpl small{display:block;margin-top:3px;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#onb .ob-tpl i{flex:none;font:500 10px var(--mono);font-size:10px;font-style:normal;letter-spacing:.06em;text-transform:uppercase;color:var(--accent-ink)}
#onb .ob-tpl.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset;background:color-mix(in srgb,var(--accent) 7%,var(--surface))}
#onb .ob-tpl .ic{flex:none;color:var(--accent-ink)}
#onb .ob-tpl-week{display:grid;grid-template-columns:repeat(auto-fit,minmax(64px,1fr));gap:6px;margin:14px 0}
#onb .ob-tpl-week div{padding:9px 4px;border-radius:12px;background:var(--surface2);text-align:center}
#onb .ob-tpl-week small{display:block;font:500 11px var(--mono);font-size:11px;color:var(--muted)}
#onb .ob-tpl-week span{display:block;margin-top:2px;font:500 12px var(--body);font-size:12px;color:var(--chalk)}
@keyframes obSheetIn{from{transform:translateY(30px);opacity:.6}to{transform:none;opacity:1}}
@keyframes obFadeIn{from{opacity:0}to{opacity:1}}
@media(prefers-reduced-motion:reduce){#onb .ob-rt-ses,#onb .ob-rt-ses.ob-settle,#onb .ob-sheet,#onb .ob-sheet-wrap{animation:none;transition:none}}
/* Today: the rotation, until the first workout */
.today-rot{margin:14px 0 0;padding:16px 16px 14px;border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
.today-rot-h{display:flex;align-items:center;justify-content:space-between}
.today-rot-h span{font:500 10px var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.today-rot-edit{min-height:40px;padding:0 4px;border:0;background:none;color:var(--accent-ink);font:500 13px var(--body);font-size:13px}
.today-rot-days{display:grid;grid-template-columns:repeat(auto-fit,minmax(64px,1fr));gap:6px;margin-top:4px}
.today-rot-days div{padding:9px 4px;border-radius:12px;background:var(--surface2);text-align:center}
.today-rot-days small{display:block;font:500 11px var(--mono);font-size:11px;color:var(--muted)}
.today-rot-days span{display:block;margin-top:2px;font:500 12px var(--body);font-size:12px;color:var(--chalk)}
.today-rot p{margin:10px 0 0;font:400 11px var(--body);font-size:11px;color:var(--muted)}

/* v4.6.374: chips that can be dragged look like it -- a dashed edge, six grip
   dots, and a slow breath that runs through the tray one chip after another.
   It stops on the chip you are holding, and for reduced motion. */
#onb .ob-rt-tray{--k:0}
#onb .ob-rt-pool,#onb .ob-rt-chip{position:relative}
#onb .ob-rt-pool{border:1.5px dashed color-mix(in srgb,var(--accent) 55%,var(--line));padding-left:24px;animation:obChipBreath 2.8s ease-in-out infinite;animation-delay:calc(var(--k,0) * .18s)}
#onb .ob-rt-pool::before,#onb .ob-rt-chip::before{content:"";position:absolute;left:10px;top:50%;width:6px;height:10px;margin-top:-5px;
  background:radial-gradient(circle,currentColor 1.1px,transparent 1.4px) 0 0/3px 3.4px;opacity:.55}
#onb .ob-rt-chip{outline:1.5px dashed color-mix(in srgb,var(--accent) 35%,transparent);outline-offset:-1.5px;padding-left:22px}
#onb .ob-rt-chip::before{left:9px}
#onb .ob-rt-pool.held,#onb .ob-lifted{animation:none}
@keyframes obChipBreath{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 0%,transparent);border-color:color-mix(in srgb,var(--accent) 45%,var(--line))}
  50%{box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 14%,transparent);border-color:var(--accent)}}
@media(prefers-reduced-motion:reduce){#onb .ob-rt-pool{animation:none}}
/* v4.6.374: session cards about 18% shorter */
#onb .ob-rt-list{gap:6px}
#onb .ob-rt-ses{padding:10px 10px 11px 14px}
#onb .ob-rt-grip{height:44px;margin:-4px 0}
#onb .ob-rt-head{min-height:36px}
#onb .ob-rt-ses .ob-rt-chips{margin-top:0}
#onb .ob-rt-ses .pf-rot-tag{height:28px}
#onb .ob-rt-chip{height:30px}
/* v4.6.375: an open sheet redrawn in place does not play its entrance again */
#onb.ob-sheet-still .ob-sheet,#onb.ob-sheet-still .ob-sheet-wrap{animation:none}
#onb .ob-tpl{transition:border-color .2s ease,background-color .2s ease,box-shadow .2s ease}

/* v4.6.376: no weight yet -- the Train tab's dumbbell in outline, at the size of the text it replaces */
.feel-ic{width:1.35em;height:1.35em;vertical-align:-.3em;flex:none;color:color-mix(in srgb,var(--chalk) 38%,var(--surface))}   /* v4.6.454: the set's dumbbell, softer grey */
/* v4.6.376: a card where nothing has a weight yet (a new user's first week) has no numbers to line up: the dumbbell sits under the name */
:root[data-flow="refined"] #view[data-flow-view="today"] .plancard.allfeel .planrow .pl{grid-template-columns:auto 12px minmax(0,1fr)}
.plancard.allfeel .planrow .pv{text-align:left}
/* v4.6.378: a session being dragged is the card itself, lifted; its old place is a dashed slot; the others slide aside */
#onb .ob-rt-lift{position:fixed;left:0;top:0;z-index:99;margin:0;pointer-events:none;border-color:var(--accent);background:var(--surface);transform-origin:50% 40%;scale:1;rotate:0deg;transition:scale .18s var(--settle),rotate .18s var(--settle),box-shadow .18s}
#onb .ob-rt-lift.up{scale:1.04;rotate:-1.6deg;box-shadow:0 22px 44px rgba(0,0,0,.34),0 6px 14px rgba(0,0,0,.18)}
#onb .ob-rt-ses.ob-rt-slot{background:transparent;box-shadow:none;border:1.5px dashed var(--muted);opacity:.7}
#onb .ob-rt-ses.ob-rt-slot>*{visibility:hidden}
.ob-dragging #onb .ob-rt-ses[data-rtcard]{transition:transform .22s var(--settle),border-color .16s,background-color .16s}
@media (prefers-reduced-motion:reduce){#onb .ob-rt-lift,.ob-dragging #onb .ob-rt-ses[data-rtcard]{transition:none}}
/* v4.6.378: sessions are numbered days; an optional Extra day sits apart */
#onb .ob-rt-day{width:46px}
#onb .ob-rt-ses .ob-rt-chips,#onb .ob-rt-order,#onb .ob-rt-xnote{margin-left:56px}
#onb .ob-rt-order{flex-wrap:wrap}
#onb .ob-rt-order .ob-rt-drop{flex-basis:100%;color:var(--muted)}
#onb .ob-rt-xbar{margin-top:14px}
#onb .ob-rt-ses.ob-rt-extra{background:transparent;box-shadow:none;border:1.5px dashed var(--line)}
#onb .ob-rt-extra .ob-rt-day{color:var(--accent-ink)}
#onb .ob-rt-xnote{margin-top:8px;margin-bottom:0;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#onb .ob-rt-addx{align-self:flex-start;margin-top:4px}
#onb .ob-count .pf-dowb{font-variant-numeric:tabular-nums}
:root .pf-workspace .pf-dow.ob-count,:root .pf-workspace .pf-dow.pf-freq{grid-template-columns:repeat(6,1fr)}

/* v4.6.381: the way back to the exercise in the red bar, once, until dismissed */
.lt .lt-tip{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0 0 12px;padding:12px 14px 12px 16px;border:1.5px dashed color-mix(in srgb,var(--muted) 45%,transparent);border-radius:16px;font:400 13px/1.38 var(--body);font-size:13px;color:var(--muted)}
.lt .lt-tip b{color:var(--chalk);font-weight:600}
.lt .lt-tip button{flex:none;min-height:36px;padding:0 4px;border:0;background:none;color:var(--accent-ink);font:500 12px var(--body);font-size:12px;cursor:pointer;white-space:nowrap}
/* the tip points at the bar it is about: an up arrow in the app's own glyph, nudging upward */
.lt .lt-tip span{flex:1;min-width:0}
.lt .lt-tip-up{flex:none;display:grid;place-items:center;width:30px;height:30px;margin:0 -2px 0 -4px;border-radius:50%;background:color-mix(in srgb,var(--live) 12%,transparent);color:var(--live)}
.lt .lt-tip-up .ic{width:16px;height:16px}
@media(prefers-reduced-motion:no-preference){.lt .lt-tip-up .ic{animation:ltTipUp 1.6s var(--settle) infinite}}
@keyframes ltTipUp{0%,100%{transform:translateY(2px)}50%{transform:translateY(-3px)}}
/* v4.6.382: a planned run on the plan card reads its distance and time in the weight column's place */
.planrow .pl.pl-cardio,:root[data-flow="refined"] #view[data-flow-view="today"] .planrow .pl.pl-cardio{display:block}
.planrow .pl-cardio .pv{color:var(--muted)}

/* v4.6.387: the routine nudge under Choose exercise -- a quiet line, not a second button */
.today-routine{all:unset;box-sizing:border-box;cursor:pointer;display:flex;align-items:center;gap:6px;margin-top:16px;font:400 14px/1.35 var(--body);color:var(--muted);-webkit-tap-highlight-color:transparent}
.today-routine b{font-weight:600;color:var(--accent)}
.today-routine svg{color:var(--accent);flex:none}
.today-routine:active{opacity:.6}
.today-routine:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}

/* v4.6.387: a rest day in the order */
#onb .ob-rt-rest{background:transparent;box-shadow:none;border:1.5px dashed color-mix(in srgb,var(--chalk) 20%,transparent)}
#onb .ob-rt-rest .ob-rt-head b{color:var(--muted)}
#onb .ob-rt-rest.ob-drop{border-color:var(--line);background:transparent;transform:none}
#onb .ob-rt-restnote{margin:0 0 0 44px;font:400 13px/1.35 var(--body);color:var(--faint)}
#onb .ob-rt-adds{display:flex;justify-content:center;gap:4px;flex-wrap:wrap}

/* v4.6.387: Customize, calmer. No dashes, no breathing, no grip dots: the tray sorts itself into
   what is not in your week yet (accent, +) and what already is (quiet, a check, ×2 when it repeats) */
#onb .ob-rt-tray{padding:16px 16px 14px;border:0;box-shadow:var(--shadow)}
#onb .ob-rt-tray p{margin:4px 0 2px}
#onb .ob-rt-grp{display:block;margin:14px 0 8px;font:500 10px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
#onb .ob-rt-tray .ob-rt-chips{gap:8px}
#onb .ob-rt-pool{animation:none;gap:6px;height:36px;min-height:36px;padding:0 14px 0 11px;border:0;border-radius:18px;font:500 13px var(--body);transition:background-color .16s,color .16s,box-shadow .16s}
#onb .ob-rt-pool::before,#onb .ob-rt-chip::before{content:none}
#onb .ob-rt-pool .ic{width:13px;height:13px;flex:none}
#onb .ob-rt-pool.fresh{background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent-ink);box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--accent) 40%,transparent)}
#onb .ob-rt-pool.used{background:color-mix(in srgb,var(--chalk) 6%,transparent);color:var(--muted);box-shadow:none}
#onb .ob-rt-pool.used .ic{opacity:.65}
#onb .ob-rt-n{font:500 11px var(--mono);font-size:11px;font-style:normal;color:var(--faint);margin-left:1px}
#onb .ob-rt-pool.held{background:var(--accent);color:#fff;box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 22%,transparent)}
#onb .ob-rt-pool.held .ob-rt-n{color:rgba(255,255,255,.75)}
#onb .ob-rt-pool:active{scale:.96}
#onb .ob-rt-chip{outline:0;padding:0 8px 0 12px;gap:6px}
#onb .ob-rt-chip .ic{width:11px;height:11px;opacity:.55}

/* v4.6.387: DAY COLUMN = HANDLE. Day N on top, the grip under it; the whole column grabs.
   Title and chips take the rest of the card. The grip nudges now and then until you have moved a day. */
#onb .ob-rt-ses{display:flex;align-items:stretch;gap:8px;padding:10px 14px 10px 4px}
#onb .ob-rt-side{all:unset;box-sizing:border-box;flex:none;width:58px;display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px 0 2px;border-radius:14px;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}
#onb .ob-rt-side:active{background:color-mix(in srgb,var(--chalk) 6%,transparent)}
#onb .ob-rt-side:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
#onb .ob-rt-side .ob-rt-day{width:auto;font:500 12px/20px var(--mono);color:var(--muted);white-space:nowrap}
#onb .ob-rt-side .ob-rt-grip{position:static;width:auto;height:auto;flex:1;min-height:30px;display:flex;align-items:flex-start;justify-content:center;padding-top:6px;color:var(--muted);background:none}
#onb .ob-rt-side .ob-rt-grip svg{width:24px;height:24px;animation:obGripNudge 4.2s ease-in-out infinite;animation-delay:calc(1.2s + var(--i,0) * .22s)}
#onb.ob-rt-known .ob-rt-side .ob-rt-grip svg,#onb .ob-rt-ses.ob-rt-moving .ob-rt-grip svg{animation:none}
@keyframes obGripNudge{0%,78%,100%{translate:0 0}82%{translate:0 -3px}86%{translate:0 2px}90%{translate:0 -2px}94%{translate:0 0}}
#onb .ob-rt-side-x{cursor:default;touch-action:auto;justify-content:flex-start}
#onb .ob-rt-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px;padding-top:4px}
#onb .ob-rt-name{font:600 15px/20px var(--body);color:var(--chalk)}
#onb .ob-rt-top{display:flex;align-items:center;gap:8px}
#onb .ob-rt-top .ob-rt-name{flex:1;min-width:0}
#onb .ob-rt-xdel{all:unset;width:32px;height:32px;margin:-6px -6px -6px 0;display:inline-flex;align-items:center;justify-content:center;color:var(--faint);cursor:pointer;border-radius:50%}
#onb .ob-rt-ses .ob-rt-chips,#onb .ob-rt-order,#onb .ob-rt-xnote,#onb .ob-rt-restnote{margin:0}
#onb .ob-rt-ses .ob-rt-chips{gap:6px}
#onb .ob-rt-add{align-self:flex-start;margin:0}
#onb .ob-rt-order{margin-top:2px}
@media(prefers-reduced-motion:reduce){#onb .ob-rt-side .ob-rt-grip svg{animation:none}}
/* the card you are moving is itself, raised -- never an empty slot */
#onb .ob-rt-ses.ob-rt-moving{position:relative;z-index:5;border-color:var(--accent);transition:scale .18s var(--settle),rotate .18s var(--settle),box-shadow .18s;will-change:translate}
#onb .ob-rt-ses.ob-rt-moving.up{scale:1.03;rotate:-1deg;box-shadow:0 22px 44px rgba(0,0,0,.34),0 6px 14px rgba(0,0,0,.18)}
#onb .ob-rt-rest.ob-rt-moving{background:var(--ground)}
/* grip dots: what can be dragged says so */
#onb .ob-dots{flex:none;width:6px;height:10px;background:radial-gradient(circle,currentColor 1.05px,transparent 1.35px) 0 0/3px 3.4px;opacity:.5}
#onb .ob-rt-pool{gap:7px;padding:0 14px 0 12px}
#onb .ob-rt-pool.fresh .ob-dots{opacity:.7}
#onb .ob-rt-chip{gap:6px;padding:0 8px 0 10px}
#onb .ob-rt-chip .ob-dots{opacity:.55}

/* v4.6.389: Your data -- three rows that say what each file is for, then two quiet links */
.data-count{margin:0 0 4px;font:400 13px/1.4 var(--body);color:var(--muted)}
.data-row{display:flex;align-items:center;gap:12px;padding:14px 0;border-top:1px solid var(--line)}
.data-row:first-of-type{border-top:0}
.data-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.data-txt b{font:600 15px/1.25 var(--body);color:var(--chalk)}
.data-txt span{font:400 12.5px/1.4 var(--body);color:var(--muted)}
.data-txt .linkbtn{font-size:inherit}
.data-acts{flex:none;display:flex;flex-direction:column;gap:6px;width:112px}
#view .data-acts .btn{margin:0;width:100%;min-height:38px;padding:0 10px;font-size:13px}
.data-link{all:unset;cursor:pointer;display:inline-block;padding:10px 0 2px;font:500 13px var(--body);color:var(--accent-ink);-webkit-tap-highlight-color:transparent}
.data-link:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.data-owner{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;margin-top:8px;padding-top:10px;border-top:1px dashed var(--line)}
.data-owner>span{font:500 10px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.data-owner .data-link{padding:4px 0}

/* v4.6.390: bring your routine -- the paste step, what the AI read, the found routine */
#onb .ob-paste-box{position:relative;margin-top:18px}
#onb .ob-paste-box textarea{box-sizing:border-box;width:100%;min-height:240px;padding:14px 14px 48px;border-radius:18px;border:1.5px solid var(--line);background:var(--surface);color:var(--chalk);font:400 15px/1.5 var(--mono);resize:vertical;box-shadow:var(--shadow)}
#onb .ob-paste-box textarea:focus{outline:none;border-color:var(--accent)}
#onb .ob-paste-box textarea::placeholder{color:var(--faint)}
#onb .ob-paste-chip{all:unset;position:absolute;right:10px;bottom:10px;display:inline-flex;align-items:center;gap:6px;height:34px;padding:0 12px;border-radius:17px;background:color-mix(in srgb,var(--chalk) 7%,transparent);color:var(--chalk);font:500 13px var(--body);cursor:pointer}
#onb .ob-paste-chip .ic{width:14px;height:14px}
#onb .ob-paste-hint{margin:10px 2px 0;font:400 12.5px/1.45 var(--body);color:var(--muted)}
#onb .ob-paste-ai{display:flex;align-items:center;gap:8px;margin:14px 2px 0;font:500 13px var(--body);color:var(--accent-ink)}
#onb .ob-paste-ai .ic{width:15px;height:15px;animation:aiPulse 1.2s ease-in-out infinite}
@keyframes aiPulse{0%,100%{opacity:.35}50%{opacity:1}}
#onb .ob-go:disabled{opacity:.45}
#onb .ob-rt-exs{margin:0;font:400 12.5px/1.4 var(--body);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#onb .ob-rt-unread{display:flex;flex-direction:column;gap:3px;margin:10px 0 0;padding:12px 14px;border-radius:14px;background:color-mix(in srgb,var(--chalk) 5%,transparent)}
#onb .ob-rt-unread b{font:600 13px var(--body);color:var(--chalk)}
#onb .ob-rt-unread span{font:400 12px/1.4 var(--mono);color:var(--muted)}
@media(prefers-reduced-motion:reduce){#onb .ob-paste-ai .ic{animation:none}}
/* v4.6.390: today's AI allowance -- a quiet line near the end, a short sheet at the end */
.ai-left{display:flex;align-items:center;justify-content:center;gap:6px;margin:8px 0 0;font:400 12px var(--body);color:var(--muted)}
.ai-left .ic{width:12px;height:12px;color:var(--accent-ink)}
.ai-cap{position:fixed;inset:0;z-index:120;display:flex;align-items:flex-end;justify-content:center;padding:0 12px calc(env(safe-area-inset-bottom,0px) + 12px);background:rgba(0,0,0,.32);animation:aiCapIn .18s ease-out}
.ai-cap-card{box-sizing:border-box;width:100%;max-width:440px;padding:22px 22px 18px;border-radius:24px;background:var(--surface);color:var(--chalk);box-shadow:0 18px 50px rgba(0,0,0,.3);text-align:center;animation:aiCapUp .24s var(--settle)}
.ai-cap-ic{display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:50%;background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent-ink)}
.ai-cap-card h3{margin:12px 0 6px;font:600 18px var(--body)}
.ai-cap-card p{margin:0 0 6px;font:400 14px/1.45 var(--body);color:var(--muted)}
.ai-cap-card .btn{margin:14px 0 0;width:100%;min-height:48px;border:0;border-radius:14px;background:var(--accent);color:#fff;font:600 15px var(--body);cursor:pointer}
@keyframes aiCapIn{from{opacity:0}}@keyframes aiCapUp{from{transform:translateY(24px);opacity:0}}
@media(prefers-reduced-motion:reduce){.ai-cap,.ai-cap-card{animation:none}}

/* v4.6.390: reading your routine -- the crew's coffee break */
#onb .ob-reading .ob-read-stage{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding-bottom:40px}
#onb .ob-read-h{margin:0}
#onb .ob-read-line{margin:14px 0 0;min-height:22px;font:500 16px var(--body);color:var(--accent-ink)}
#onb .ob-read-line.in{animation:obLineIn .4s ease-out}
@keyframes obLineIn{from{opacity:0;transform:translateY(6px)}}
#onb .ob-read-sub{margin:8px 0 0;font:400 13px var(--body);color:var(--muted)}
#onb .ob-read-quick{margin-top:22px}
@media(prefers-reduced-motion:reduce){#onb .ob-read-line.in{animation:none}}
#onb .ob-read-quick[hidden]{display:none}
/* v4.6.390: import review -- one answer for every duplicate */
#imp .im-bulk{border:1.5px solid color-mix(in srgb,var(--accent) 40%,var(--line))}
#imp .im-more{margin:4px 4px 8px;font:400 13px var(--body);color:var(--muted);text-align:center}

/* v4.6.390: the x in each day's corner; Move up / Move down keep the menu */
#onb .ob-rt-ses .ob-rt-top{display:flex;align-items:flex-start;gap:8px}
#onb .ob-rt-ses .ob-rt-top .ob-rt-name{flex:1;min-width:0}
#onb .ob-rt-ses .ob-rt-xdel{flex:none;width:36px;height:36px;margin:-8px -8px -8px 0;color:var(--muted);border-radius:50%}
#onb .ob-rt-ses .ob-rt-xdel:active{background:color-mix(in srgb,var(--chalk) 8%,transparent)}
#onb .ob-rt-ses .ob-rt-xdel .ic{width:14px;height:14px}
#onb .ob-rt-order button{display:inline-flex;align-items:center;justify-content:center;gap:6px}
#onb .ob-rt-order button .ic{width:14px;height:14px}

/* v4.6.390: the found routine on one line; Edit with its pencil; the x asks first */
#onb .ob-h.ob-h1{white-space:nowrap;font-size:min(32px,7.4vw);letter-spacing:-.02em}
#onb .ob-rt-bar .ob-rt-link{display:inline-flex;align-items:center;gap:5px}
#onb .ob-rt-bar .ob-rt-link .ic{width:14px;height:14px}
#onb .ob-rt-ask{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 10px;border-radius:12px;background:color-mix(in srgb,var(--chalk) 6%,transparent)}
#onb .ob-rt-ask span{flex:1;min-width:0;font:500 13px var(--body);color:var(--chalk)}
#onb .ob-rt-ask button{all:unset;cursor:pointer;height:32px;padding:0 12px;border-radius:16px;font:600 13px var(--body);color:var(--chalk);background:color-mix(in srgb,var(--chalk) 8%,transparent)}
#onb .ob-rt-ask .ob-rt-ask-go{background:#D64545;color:#fff}
#onb .ob-rt-ask{gap:6px;padding:6px 6px 6px 10px;flex-wrap:nowrap}
#onb .ob-rt-ask span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#onb .ob-rt-ask button{flex:none;padding:0 10px;font-size:12.5px}

/* v4.6.390: the answer-all card is the loudest thing on the screen */
#imp .im-bulk{display:flex;flex-direction:column;align-items:center;text-align:center;margin:4px 0 6px;padding:22px 18px 18px;border-radius:22px;border:2px solid var(--accent);background:color-mix(in srgb,var(--accent) 16%,var(--surface));box-shadow:0 10px 30px color-mix(in srgb,var(--accent) 22%,transparent)}
#imp .im-bulk-k{font:500 10.5px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--accent-ink)}
#imp .im-bulk-n{margin-top:6px;font:700 48px/1 var(--body);font-size:48px;letter-spacing:-.02em;color:var(--chalk);font-variant-numeric:tabular-nums}
#imp .im-bulk-t{margin-top:2px;font:600 17px var(--body);color:var(--chalk)}
#imp .im-bulk p{margin:6px 0 14px;font:400 13px/1.4 var(--body);color:var(--muted)}
#imp .im-bulk-btns{display:flex;flex-direction:column;gap:8px;width:100%}
#imp .im-bulk-btns .im-b{width:100%;min-height:50px;font-size:16px}
#imp .im-one{margin:16px 4px 6px;font:500 10.5px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
#imp .im-bulk-btns .im-b:not(.pri){border:1.5px solid color-mix(in srgb,var(--chalk) 28%,transparent);background:color-mix(in srgb,var(--chalk) 4%,transparent)}

/* v4.6.390: the crew reads your streaks -- a wall of day squares scrolling up past its eyes */
#onb .ob-read-scene{position:relative;width:214px;height:250px;margin-bottom:22px}
#onb .ob-wall{position:absolute;inset:0;overflow:hidden;-webkit-mask:linear-gradient(transparent 0,#000 22%,#000 70%,transparent 100%);mask:linear-gradient(transparent 0,#000 22%,#000 70%,transparent 100%)}
#onb .ob-wall-row{position:absolute;left:0;right:0;top:0;display:grid;grid-template-columns:repeat(7,24px);gap:7px;justify-content:center;will-change:transform}
#onb .ob-wall-row i{height:24px;border-radius:7px;background:color-mix(in srgb,var(--chalk) 9%,transparent);transition:background-color .35s ease,transform .35s var(--settle)}
#onb .ob-wall-row i.fut{background:none;box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--chalk) 10%,transparent)}
#onb .ob-wall-row.read i.yes{background:var(--accent);transform:scale(1.06)}
#onb .ob-read-scene .ob-read-crew{position:absolute;left:50%;bottom:-6px;transform:translateX(-50%);height:auto;margin:0;z-index:2}
#onb .ob-read-scene .ob-read-comp{width:112px;height:112px;animation:obRead 2.8s ease-in-out infinite}
@keyframes obRead{0%,100%{transform:translateY(0) rotate(0)}30%{transform:translateY(-3px) rotate(-2deg)}65%{transform:translateY(-2px) rotate(2deg)}}
@media(prefers-reduced-motion:reduce){#onb .ob-read-scene .ob-read-comp{animation:none}#onb .ob-wall-row i{transition:none}}

/* v4.6.394: a tip is one sentence -- it takes the room it has and breaks evenly */
.tipn .tipt{flex:1;min-width:0;text-wrap:balance}
/* v4.6.399: Muscle this week (js/muscleweek.js) -- the body above, one bar per muscle below (0 to
   20 working sets, a hairline mark at 10). The body's shade and the bar's are the same scale. */
.mw-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin:0 0 4px}
.mw-head strong{font:600 11px/1.3 var(--body);color:var(--chalk)}   /* card head (Every part: "Days trained") */
.mw-head span{font:400 11px/1.3 var(--body);color:var(--muted)}
.mw-stick{position:sticky;top:calc(var(--hdr-h,76px) - 12px);z-index:2;margin:0 -18px;padding:14px 18px 6px;border-radius:0 0 4px 4px;background:var(--surface);
  box-shadow:0 10px 12px -12px color-mix(in srgb,var(--chalk) 22%,transparent)}
.mw-figs{display:flex;justify-content:center;gap:clamp(18px,8vw,40px);margin:0}
.mw-view{margin:0;display:flex;flex-direction:column;align-items:center;gap:6px}
.mw-view svg{display:block;height:150px;width:auto;overflow:visible}
.mw-view figcaption{font:500 11px/1.2 var(--body);color:var(--muted)}   /* label */
.mw-sil{fill:color-mix(in srgb,var(--line) 50%,var(--surface))}
.mw-mu{fill:color-mix(in srgb,var(--line) 80%,var(--surface))}
:root[data-theme="dark"] .mw-sil{fill:color-mix(in srgb,var(--line) 78%,var(--surface))}
:root[data-theme="dark"] .mw-mu{fill:color-mix(in srgb,var(--chalk) 16%,var(--line))}
.mw-tint{fill:var(--accent);transition:opacity var(--dur-quick,.15s) ease}
.mw-tint.off{opacity:.12}
.mw-tint.pick{fill-opacity:1!important}
.mw-tint.pick.zero{fill-opacity:.16!important}   /* picked but no sets: shown where it is, not as trained */
.mw-hit{fill:transparent;cursor:pointer}
.mw-key{display:flex;justify-content:center;gap:16px;margin:12px 0 4px;font:400 11px/1.2 var(--body);color:var(--muted)}   /* legend */
.mw-key b{display:inline-block;width:12px;height:12px;border-radius:3px;margin-right:6px;vertical-align:-2px;background:var(--accent)}
.mw-rows{display:grid;grid-template-columns:max-content 1fr 4.2ch;column-gap:12px;row-gap:3px;align-items:center}
.mw-row{all:unset;box-sizing:border-box;grid-column:1/-1;display:grid;grid-template-columns:subgrid;align-items:center;margin:0 -10px;padding:0 10px;border-radius:12px;cursor:pointer;
  transition:background-color var(--dur-quick,.15s) ease}
.mw-row>span{padding:3px 0;transition:opacity var(--dur-quick,.15s) ease}
/* v4.6.401: the picked row is lifted, not just bolded: a soft wash of the accent behind it, its
   name in accent ink, its bar at full strength -- and every other row steps back */
.mw-row.dim>span{opacity:.35}
.mw-row.sel{background:color-mix(in srgb,var(--accent) 10%,transparent)}
.mw-row.sel .mw-name{color:var(--accent-ink,var(--accent))}
.mw-row.sel .mw-n{color:var(--chalk)}
.mw-row.sel .mw-bar i{opacity:1!important}
.mw-row:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.mw-grp{grid-column:1/-1;margin-top:10px;font:400 10px/14px var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}   /* meta, as a kicker */
.mw-name{font:600 13px/19px var(--body);color:var(--chalk)}   /* row title (Best lifts: the lift) */
.mw-bar{position:relative;height:8px;border-radius:999px;background:color-mix(in srgb,var(--line) 55%,transparent)}
.mw-bar i{position:absolute;left:0;top:0;bottom:0;border-radius:999px;background:var(--accent)}
.mw-bar u{position:absolute;top:-4px;bottom:-4px;width:1.5px;margin-left:-.75px;background:var(--chalk);opacity:.35}
.mw-n{font:600 13px/19px var(--mono);font-size:13px;color:var(--chalk);text-align:right;font-variant-numeric:tabular-nums}   /* row value (Best lifts: the weight) */
.mw-foot{margin:12px 0 0;text-align:center;font:400 10px/14px var(--body);color:var(--faint)}   /* note */
/* v4.6.401: the mark's words sit under the mark itself, centred on its line, and move with it */
.mw-marktrack{position:relative;height:62px;margin-top:6px}
.mw-mark{position:absolute;top:0;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:4px;white-space:nowrap}
.mw-mark label{font:400 11px/1.3 var(--body);color:var(--muted)}   /* legend */
.mw-mark small{font:400 10px/14px var(--body);color:var(--faint)}   /* note */
.mw-why{font:500 10px/14px var(--body);color:var(--accent-ink,var(--accent));text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;padding:2px 4px;margin:-2px -4px}
.mw-pick{-webkit-appearance:none;appearance:none;margin:0 1px;padding:1px 6px;border:0;border-radius:6px;font:600 11px/1.3 var(--mono);color:var(--chalk);
  background:color-mix(in srgb,var(--line) 55%,transparent);cursor:pointer;text-align:center}
.mw-pick:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* v4.6.404: a fold on Today says what it is doing -- the chevron points right while
   the fold is shut and turns down once it is open (Sungjee 2026-10-23: "> means
   it's hidden"). Every fold on the plan home: today's plan, the next plan, "N more
   plans" and each day inside it. */
.pw-home details>summary.pw-fold-summary>.ic-chevron{transition:transform .2s ease}
.pw-home details[open]>summary.pw-fold-summary>.ic-chevron{transform:rotate(90deg)}
@media (prefers-reduced-motion:reduce){.pw-home details>summary.pw-fold-summary>.ic-chevron{transition:none}}

/* v4.6.410: THE VERTICAL SET LOGGER (end of js/lift.js). v4.6.412: the wheels are 50:50,
   the × turns the reps, and the finger drives the wheels (touch-action none: see vlWheel). Two wheels, one band across
   both; the strip under them is the loaded bar and the step. Sizes are written
   at the current (retyped) scale. */
.vlog-zone{display:flex;flex-direction:column;gap:10px}
.vl-picker{--row:38px;position:relative;height:calc(var(--row)*5);border-radius:18px;background:var(--surface2);overflow:hidden;
  display:grid;grid-template-columns:minmax(0,1fr) 24px minmax(0,1fr);padding:0 8px;touch-action:none;isolation:isolate}
/* v4.6.415: ONE WELL, ONE GLASS BAND (Sungjee picked L4 for light, G5 for dark,
   2026-10-24). No divider between weight and reps: the whole well is shaded toward
   its top and bottom so the rows read as turning away, and the band that carries
   "255 LB × 16 REPS" sits on it like a glass tube -- a rounded shine across its top,
   darker ends, a lit edge and a shadow above and below. Dark lifts the band further
   and lets the selected numbers catch the light. */
.vl-picker::before,.vl-well::before{content:'';position:absolute;z-index:0;inset:0;border-radius:18px;pointer-events:none;
  /* v4.6.418: R4 -- a near-white well with lighter shading, so the light logger reads brighter */
  background:linear-gradient(rgba(0,0,0,.05),rgba(0,0,0,0) 34%,rgba(0,0,0,0) 66%,rgba(0,0,0,.05)),color-mix(in srgb,#fff 85%,var(--surface2));box-shadow:inset 0 2px 6px rgba(0,0,0,.02)}
.vlog-zone .vl-band{left:2px;right:2px;height:46px;overflow:visible;
  /* v4.6.416: brighter glass (B1) and a softer shadow, underneath only (S2) */
  background:linear-gradient(90deg,rgba(0,0,0,.05),rgba(0,0,0,0) 6%,rgba(0,0,0,0) 94%,rgba(0,0,0,.05)),linear-gradient(#ffffff 0%,#ffffff 45%,#f5f6f8 100%);
  box-shadow:0 2px 0 #fff inset,0 -2px 4px rgba(0,0,0,.035) inset,0 8px 16px -8px rgba(0,0,0,.14),0 0 0 1px rgba(0,0,0,.09)}
.vlog-zone .vl-band::before{content:'';position:absolute;left:10px;right:10px;top:2px;height:52%;border-radius:12px 12px 20px 20px;pointer-events:none;
  background:linear-gradient(rgba(255,255,255,1),rgba(255,255,255,.55))}
:root[data-theme="dark"] .vl-picker::before,:root[data-theme="dark"] .vl-well::before{background:linear-gradient(rgba(0,0,0,.22),rgba(0,0,0,.04) 40%,rgba(0,0,0,.04) 60%,rgba(0,0,0,.22)),var(--surface2);
  box-shadow:inset 0 6px 14px rgba(0,0,0,.14),inset 0 -6px 14px rgba(0,0,0,.10)}
:root[data-theme="dark"] .vlog-zone .vl-band{background:linear-gradient(color-mix(in srgb,#fff 14%,var(--surface)),var(--surface) 48%,color-mix(in srgb,var(--chalk) 5%,var(--surface)));
  box-shadow:0 1px 0 color-mix(in srgb,#fff 14%,var(--surface)) inset,0 -1px 0 color-mix(in srgb,var(--chalk) 8%,transparent) inset,0 -10px 18px -10px rgba(0,0,0,.35),0 16px 30px -10px rgba(0,0,0,.45),0 0 0 1px color-mix(in srgb,var(--chalk) 10%,transparent)}
:root[data-theme="dark"] .vlog-zone .vl-band::before{display:none}
:root[data-theme="dark"] .vl-wheel li.on{text-shadow:0 1px 0 color-mix(in srgb,#fff 14%,var(--surface))}
.vl-band{position:absolute;z-index:1;left:8px;right:8px;top:50%;height:44px;transform:translateY(-50%);border-radius:14px;background:var(--surface);box-shadow:0 1px 6px var(--shadow);pointer-events:none;overflow:hidden}
.vl-band::after{content:'';position:absolute;inset:0;background:linear-gradient(100deg,transparent 30%,color-mix(in srgb,var(--accent) 22%,transparent) 50%,transparent 70%);background-size:300% 100%;background-position:130% 0;opacity:0}
.vl-band.glint::after{animation:vlGlint .7s ease-out}
@keyframes vlGlint{0%{opacity:1;background-position:130% 0}100%{opacity:1;background-position:-30% 0}}
.vl-u{position:absolute;top:50%;transform:translateY(-50%);z-index:3;pointer-events:none;font-size:9px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.vl-u.w{right:calc(50% + 20px)}
.vl-u.r{right:22px}
.vl-x{position:relative;z-index:2;display:grid;place-items:center;font-size:19px;color:var(--faint);touch-action:none;cursor:grab}
.vl-wheel{position:relative;z-index:2;height:100%;overflow:hidden;touch-action:none;cursor:grab;outline:none;
  -webkit-mask-image:linear-gradient(transparent,#000 26%,#000 74%,transparent);mask-image:linear-gradient(transparent,#000 26%,#000 74%,transparent)}
.vl-wheel:focus-visible{box-shadow:inset 0 0 0 2px var(--accent);border-radius:14px}
.vl-wheel ol{list-style:none;margin:0;padding:calc(var(--row)*2) 0;will-change:transform}
.vl-wheel li{--vl-s:1;--vl-o:1;height:var(--row);display:flex;align-items:center;justify-content:center;gap:7px;cursor:pointer;
  font-family:var(--body);font-size:20px;font-weight:600;letter-spacing:-.01em;font-variant-numeric:tabular-nums;color:var(--muted);
  transform:scale(var(--vl-s));opacity:var(--vl-o);user-select:none;-webkit-user-select:none}
.vl-wheel li.on{color:var(--chalk)}
.vl-wheel.vl-w li{justify-content:flex-start;padding-left:18px;transform-origin:18px 50%}
.vl-tag{font:500 9px var(--mono);letter-spacing:.04em;text-transform:uppercase;padding:2px 5px;border-radius:6px;background:var(--line);color:var(--muted);transform:translateY(-2px)}
.vl-tag.plan{background:var(--accent-soft);color:var(--accent-ink)}
.vl-tag.plan.ping{animation:vlPing .6s ease-out}
@keyframes vlPing{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 50%,transparent)}100%{box-shadow:0 0 0 8px transparent}}
.vl-strip{display:flex;align-items:center;gap:10px;width:100%;min-height:52px;box-sizing:border-box;padding:7px 10px 7px 8px;border:0;border-radius:14px;background:var(--surface2);color:var(--chalk);text-align:left;font:inherit;cursor:pointer}
.vl-viz{flex:none;display:flex;min-width:40px;justify-content:center}
.vl-viz:empty{display:none}
.vl-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.vl-txt b{font:500 12px var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums;font-size:12px}
.vl-txt small{font-size:10px;color:var(--muted)}
.vl-step{flex:none;display:flex;align-items:center;gap:5px;font:400 11px var(--mono);color:var(--muted)}
/* the plates catch the light, as on the horizontal logger */
.vl-viz .barviz .pl,.vl-prev .barviz .pl{position:relative;overflow:hidden}
.vl-viz .barviz .pl::after,.vl-prev .barviz .pl::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:linear-gradient(115deg,transparent 25%,rgba(255,255,255,.65) 50%,transparent 75%);background-size:100% 350%;background-position:0 100%;animation:loggerPlateSheen 5.4s ease-in-out 1.1s infinite}
/* Add set: a slow sheen while it waits, a spring when it lands */
.vlog-zone #addrep{position:relative;overflow:hidden}
.vlog-zone #addrep::after{content:'';position:absolute;inset:0;pointer-events:none;background:linear-gradient(110deg,transparent 35%,rgba(255,255,255,.26) 50%,transparent 65%);background-size:300% 100%;background-position:120% 0;animation:vlBtnSheen 4.6s ease-in-out 2s infinite}
@keyframes vlBtnSheen{0%{background-position:120% 0}45%,100%{background-position:-20% 0}}
.vlog-zone #addrep.vl-hit{animation:vlHit .42s var(--settle)}
@keyframes vlHit{0%{transform:scale(.97)}60%{transform:scale(1.015)}100%{transform:none}}
.vl-float{position:absolute;left:50%;transform:translate(-50%,0);margin-top:-4px;font:600 13px var(--mono);color:var(--accent-ink);pointer-events:none;white-space:nowrap;z-index:2;animation:vlRise 1.1s ease-out forwards}
@keyframes vlRise{0%{opacity:0;transform:translate(-50%,8px)}20%{opacity:1}100%{opacity:0;transform:translate(-50%,-34px)}}
/* the plan's last set: a ring of light runs around the logger twice */
.vlog-zone{position:relative}
.vlog-zone.vl-met::before{content:'';position:absolute;inset:-2px;border-radius:inherit;padding:2px;pointer-events:none;z-index:1;
  background:conic-gradient(from var(--vl-a,0deg),transparent 0 70%,var(--accent) 82%,#F2C14E 90%,transparent 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:vlRing 1.6s linear 2,vlRingOut 3.2s linear 1 forwards}
/* v4.6.445: after its second lap the ring fades out instead of freezing at the corner */
@keyframes vlRingOut{0%,82%{opacity:1}100%{opacity:0}}
@property --vl-a{syntax:'<angle>';inherits:false;initial-value:0deg}
@keyframes vlRing{to{--vl-a:360deg}}
/* a set that beats last time shimmers gold in Your sets */
/* v4.6.445: the gold is the animation, not the cell's style (Sungjee, 2026-10-26: the cell stayed
   yellow while it was the newest set). Three passes of light over a gold wash, then the wash eases
   back to the chip's own colour; the highlight rests off the chip. */
body.vlog .sc-fresh.sc-win .sc-value.sc-now{background-image:linear-gradient(110deg,transparent 30%,rgba(255,255,255,.75) 50%,transparent 70%);
  background-size:300% 100%;background-repeat:no-repeat;background-position:-100% 0;border-radius:8px;
  animation:vlGold 2.4s linear 3,vlGoldWash 8.4s ease-out 1}
@keyframes vlGoldWash{0%,80%{background-color:#F6E2A6;color:#5E4100}}
@keyframes vlGold{from{background-position:100% 0}to{background-position:-100% 0}}
/* Bar & steps */
.vl-scrim{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.35);animation:vlFade .2s ease-out}
.vl-sheet{position:fixed;left:50%;bottom:0;z-index:61;width:min(480px,100%);transform:translateX(-50%);max-height:86vh;overflow-y:auto;box-sizing:border-box;
  background:var(--surface);color:var(--chalk);border-radius:24px 24px 0 0;display:flex;flex-direction:column;gap:12px;
  padding:10px max(16px,env(safe-area-inset-right,0px)) calc(20px + env(safe-area-inset-bottom,0px)) max(16px,env(safe-area-inset-left,0px));animation:vlUp .28s var(--settle)}
@keyframes vlUp{from{transform:translate(-50%,100%)}to{transform:translate(-50%,0)}}
@keyframes vlFade{from{opacity:0}}
.vl-grab{width:40px;height:5px;border-radius:3px;background:var(--line);margin:0 auto 2px}
.vl-sh{display:flex;align-items:center;justify-content:space-between}
.vl-sh h3{margin:0;font-size:16px}
.vl-close{width:44px;height:44px;border:0;border-radius:14px;background:var(--surface2);color:var(--chalk);display:grid;place-items:center}
.vl-lab{font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding:0 2px}
.vl-seg{display:flex;flex-wrap:wrap;gap:6px}
.vl-seg button{min-height:44px;min-width:52px;padding:4px 12px;border:0;border-radius:12px;background:var(--surface2);color:var(--chalk);font:500 14px var(--mono);font-variant-numeric:tabular-nums;font-size:14px;line-height:1.15}
.vl-seg button small{display:block;font:400 9px var(--body);opacity:.75}
.vl-seg button[aria-pressed="true"]{background:var(--chalk);color:var(--surface)}
.vl-chips{display:flex;flex-wrap:wrap;gap:6px}
.vl-other{display:flex;align-items:center;gap:8px}
.vl-other input{width:96px;height:44px;box-sizing:border-box;border:0;border-radius:12px;background:var(--surface2);padding:0 12px;font:500 16px var(--mono);color:var(--chalk)}
.vl-mini{height:44px;border:0;border-radius:12px;padding:0 14px;background:var(--surface2);color:var(--chalk);font:500 13px var(--body)}
.vl-link{align-self:flex-start;min-height:36px;border:0;background:none;padding:0 2px;color:var(--accent-ink);font:500 12px var(--body)}
.vl-prev{display:flex;align-items:center;gap:10px;padding:10px;border-radius:14px;background:var(--surface2)}
.vl-prev .vl-step{display:none}
.vl-note{margin:0;font-size:12px;color:var(--muted)}
.vl-done{flex:0 0 auto;height:52px;min-height:52px;border:0;border-radius:14px;background:var(--chalk);color:var(--surface);font:600 15px var(--body)}
/* Settings: the choice, each with a small picture */
.ls-opts{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.ls-opt{display:flex;flex-direction:column;gap:8px;padding:10px;border:0;border-radius:18px;background:var(--surface);color:var(--chalk);box-shadow:0 0 0 1px var(--line);text-align:left;font:inherit;cursor:pointer}
.ls-opt[aria-checked="true"]{box-shadow:0 0 0 2px var(--accent)}
.ls-opt small{font-size:11px;line-height:1.4;color:var(--muted)}
.ls-name{display:flex;align-items:center;justify-content:space-between;font-size:14px;font-weight:600}
.ls-ck{color:var(--accent)}
.ls-mini{height:86px;border-radius:12px;background:var(--surface2);display:flex;flex-direction:column;justify-content:center;gap:5px;padding:8px;box-sizing:border-box}
.ls-mini i{display:flex;align-items:center;justify-content:center;border-radius:7px;background:var(--surface);font-style:normal}
.ls-mini b{font-size:13px}
.ls-cols{display:grid;grid-template-columns:3fr 2fr;gap:5px;height:48px}
.ls-row{height:22px}
.ls-ruler{height:22px}
.ls-mini .ls-go{height:14px;background:var(--live)}
@media(prefers-reduced-motion:reduce){.vl-band.glint::after,.vlog-zone #addrep::after,.vlog-zone #addrep.vl-hit,.vl-float,.vlog-zone.vl-met::before,.vl-tag.plan.ping,body.vlog .sc-fresh.sc-win .sc-value.sc-now,.vl-sheet,.vl-scrim,.vl-viz .barviz .pl::after,.vl-prev .barviz .pl::after{animation:none}.vl-float{display:none}}
/* v4.6.419: the horizontal strip keeps the same 8 pt above and below it */
.vl-strip.h-strip{margin-bottom:8px}
.vl-seg.vl-steps{flex-wrap:nowrap}
.vl-seg.vl-steps button{flex:1 1 0;min-width:0;padding:4px 2px}   /* v4.6.420: every step on one row */
.vl-seg [data-vlbarother]{font-size:13.5px}   /* v4.6.420: "Other" 1.5 pt smaller */

/* v4.6.421: SPLIT -- the weight card beside the reps wheel. Both sit in the Vertical
   logger's well, and the card's face is the same glass band, grown to hold the number,
   the loaded bar and the plates per side. */
.vl-picker.vl-split{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:10px;padding:0;background:none;overflow:visible;border-radius:0;height:calc(var(--row)*6)}
.vl-picker.vl-split::before{display:none}
.vl-well{position:relative;border-radius:18px;overflow:hidden;isolation:isolate}
.vs-card{display:flex;flex-direction:column;touch-action:none;cursor:grab;outline:none}
.vs-card:focus-visible,.vs-reps .vl-wheel:focus-visible{box-shadow:inset 0 0 0 2px var(--accent)}
.vs-step{position:relative;z-index:2;flex:0 0 42px;height:42px;display:flex;align-items:center;justify-content:center;-webkit-appearance:none;appearance:none;line-height:0;border:0;background:none;color:var(--chalk);padding:0;margin:0;cursor:pointer;touch-action:none;transition:transform .12s var(--settle),opacity .12s}
.vs-step.on,.vs-step:active{transform:scale(.86);opacity:.6}
.vs-face{position:relative;flex:1;min-height:0;margin:0 8px}
.vlog-zone .vs-card .vl-band.vs-band{left:0;right:0;top:0;bottom:0;height:auto;transform:none;border-radius:14px}
.vs-in{position:relative;z-index:2;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;pointer-events:none;padding:0 6px;box-sizing:border-box}
.vs-num{display:flex;align-items:baseline;gap:5px;line-height:1}
.vs-num b{display:inline-block;font:700 36px var(--body);font-size:36px;letter-spacing:-.03em;font-variant-numeric:tabular-nums;color:var(--chalk)}
.vs-num i{font-style:normal;font-size:13px;color:var(--muted)}
/* v4.6.422: a changed weight flashes the way the horizontal stepper's does -- red, a touch
   larger -- and slides in from the side it came from */
.vs-num.up b{animation:vsUp .34s var(--settle)}
.vs-num.dn b{animation:vsDn .34s var(--settle)}
@keyframes vsUp{0%{color:var(--live);transform:translateY(6px) scale(1.08)}35%{color:var(--live)}100%{color:var(--chalk);transform:none}}
@keyframes vsDn{0%{color:var(--live);transform:translateY(-6px) scale(1.08)}35%{color:var(--live)}100%{color:var(--chalk);transform:none}}
.vs-viz{min-width:0}
.vs-line{font:400 10px var(--mono);font-size:10px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.vs-reps .vl-u.r{right:12px}
.vs-line.vs-tap{display:inline-flex;align-items:center;gap:3px;padding:3px 7px 3px 9px;border-radius:999px;background:color-mix(in srgb,var(--accent) 9%,transparent);color:var(--accent);font-weight:500}
.vs-num .vl-tag{align-self:center;margin-left:2px}
.vs-face{cursor:pointer}
@media(prefers-reduced-motion:reduce){.vs-num.up b,.vs-num.dn b{animation:none}}
/* the Settings tile */
.ls-split i:first-child{flex-direction:column;gap:0;line-height:1}
.ls-split em{font-style:normal;font-size:9px;color:var(--muted);line-height:1}
.vs-reps .vl-wheel ol{padding:calc(var(--row)*2.5) 0}   /* six rows tall: the chosen row sits on the band */
/* v4.6.422: THE USUAL IS QUIET, THE EXCEPTION HAS COLOUR. Progress and Include are what nearly every
   exercise is, so they sit on a plain raised thumb in ink; colour arrives only with the exception --
   Hold in the accent, Avoid in a muted red -- and an exercise you have changed shows it at a glance.
   The order stays: the usual first, the exception second, as every two-way picker in the app
   (Sungjee 2026-10-25). */
.xp-seg{--xp-calm:var(--surface);--xp-calm-ink:var(--chalk);--xp-hold:var(--accent);--xp-avoid:#C94A4A}
:root[data-theme="dark"] .xp-seg{--xp-calm:color-mix(in srgb,var(--chalk) 16%,var(--surface));--xp-avoid:#D45C5C}
.xp-seg::before{background:var(--xp-calm);box-shadow:0 1px 2px rgba(0,0,0,.12),0 1px 6px rgba(0,0,0,.06)}
.xp-seg[data-state="include"] [data-expref="include"],.xh-seg[data-state="progress"] [data-exhold="progress"]{color:var(--xp-calm-ink)}
.xp-seg[data-state="avoid"]::before{background:var(--xp-avoid);box-shadow:0 1px 3px rgba(0,0,0,.16),0 2px 8px color-mix(in srgb,var(--xp-avoid) 30%,transparent)}
.xp-seg[data-state="avoid"] [data-expref="avoid"]{color:#fff}
.xh-seg[data-state="hold"]::before{background:var(--xp-hold);box-shadow:0 1px 3px rgba(0,0,0,.16),0 2px 8px color-mix(in srgb,var(--xp-hold) 30%,transparent)}
.xh-seg[data-state="hold"] [data-exhold="hold"]{color:#fff}
/* v4.6.424: A SETTINGS GROUP SAYS IT IS OPEN. The chevron alone turned from > to v in muted grey and
   could be missed (Sungjee 2026-10-25). Open, the header takes a soft accent wash, its title goes accent,
   and the chevron sits down-turned in a filled accent circle; shut, it is the plain row with > as before. */
.club-settings-groups .club-settings-group>summary{margin:0 -18px;padding-left:26px;padding-right:26px;border-radius:0;transition:background-color var(--dur-quick,.16s) ease}   /* the wash runs edge to edge (#view's 18px gutter) */
.club-settings-group>summary>span.club-chev{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;flex:none;transition:transform .2s ease,background-color var(--dur-quick,.16s) ease,color var(--dur-quick,.16s) ease}
.club-settings-group[open]>summary{background:color-mix(in srgb,var(--accent) 8%,transparent)}
.club-settings-group[open]>summary b{color:var(--accent-ink,var(--accent))}
.club-settings-group[open]>summary>span.club-chev:last-child{background:var(--accent);color:#fff;transform:none}
.club-settings-group>summary>.club-chev svg{display:block;transition:transform .2s ease}
.club-settings-group[open]>summary>.club-chev svg{transform:rotate(90deg)}
.club-settings-group[open]{border-bottom-color:transparent}
.club-settings-group[open]>.club-group-content{margin-left:8px;padding-left:10px;border-left:1px solid color-mix(in srgb,var(--accent) 40%,transparent)}
@media(prefers-reduced-motion:reduce){.club-settings-group>summary,.club-settings-group>summary>.club-chev{transition:none}}
/* v4.6.424: every Settings heading starts with its glyph, in the accent */
h2>.sec-ic{display:inline-grid;place-items:center;width:22px;height:22px;margin-right:2px;border-radius:7px;background:color-mix(in srgb,var(--accent) 12%,transparent);color:var(--accent-ink,var(--accent));flex:none}
h2>.sec-ic .ic{width:13px;height:13px}
/* v4.6.424: Google signs in with its own mark, on its own white (dark in dark), as Google asks */
.btn.google-btn{display:flex;align-items:center;justify-content:center;gap:10px;background:#fff;color:#1f1f1f;box-shadow:inset 0 0 0 1px #dadce0}
:root[data-theme="dark"] .btn.google-btn{background:#131314;color:#e3e3e3;box-shadow:inset 0 0 0 1px #8e918f}
.btn.google-btn .ob-lg{position:static;width:18px;height:18px;flex:none}
/* v4.6.424: the routine offer is the second way in, so it reads as one: the size of the line above it,
   in its own soft accent row with the sparkle, the offer in bold accent */
.today-routine{width:100%;margin-top:14px;padding:12px 14px;gap:12px;border-radius:14px;background:color-mix(in srgb,var(--accent) 8%,transparent);font:400 12px/1.457 var(--body);color:var(--chalk)}
.today-routine-ic{display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--accent);color:#fff;flex:none}
.today-routine-ic .ic{color:#fff}
.today-routine-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;color:var(--muted)}
.today-routine-t b{font:600 14px/1.35 var(--body);color:var(--accent-ink,var(--accent))}
.today-routine>svg{color:var(--accent-ink,var(--accent))}
.today-routine:active{opacity:1;background:color-mix(in srgb,var(--accent) 14%,transparent)}
.barviz.dbviz .bar{width:12px}   /* v4.6.425: a dumbbell's handle is short; its discs are 7 pt */
.barviz.dbviz .pl{width:7px}
/* v4.6.426: the nudge toward Plan preferences -- a soft card until saved or closed, then a quiet line */
.pn-card{position:relative;margin:0 0 12px;border-radius:14px;background:color-mix(in srgb,var(--accent) 8%,transparent)}
.pn-go{display:flex;align-items:center;gap:12px;width:100%;margin:0;padding:12px 44px 12px 14px;border:0;background:none;text-align:left;font:inherit;color:var(--accent-ink,var(--accent));cursor:pointer}
.pn-ic{display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--accent);color:#fff;flex:none}
.pn-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;font:400 12px/1.4 var(--body);font-size:12px;color:var(--muted)}
.pn-t b{font:600 14px/1.35 var(--body);font-size:14px;color:var(--accent-ink,var(--accent))}
.pn-x{position:absolute;top:50%;right:6px;transform:translateY(-50%);display:grid;place-items:center;width:36px;height:36px;margin:0;padding:0;border:0;border-radius:50%;background:none;color:var(--muted);cursor:pointer}
.pn-hint{display:block;width:100%;margin:-2px 0 12px;padding:0 12px;border:0;background:none;text-align:center;font:400 13px/1.45 var(--body);font-size:13px;color:var(--muted);cursor:pointer}
.pn-hint b{font-weight:600;color:var(--accent-ink,var(--accent));white-space:nowrap}
.pn-hint b svg{vertical-align:-2px}
/* v4.6.435: History -- several months, several parts, one date, and the export
   sheet. Sizes follow the neighbouring History rules (v4.6.364 scale); tap
   targets keep the 44px floor through padding, not through bigger type. */
.hselrow{display:flex;justify-content:space-between;align-items:center;margin:8px 0 4px;font:400 10px/1.3 var(--body);color:var(--muted)}
.hselrow button{min-height:32px;padding:0 6px;border:0;background:none;color:var(--accent-ink,var(--accent));font:500 11px/1 var(--body);cursor:pointer}
.hselrow button:disabled{color:var(--faint);cursor:default}
h2 .hselhint{margin-left:auto;font:400 10px/1 var(--body);letter-spacing:0;text-transform:none;color:var(--muted)}
.mchip.cur{box-shadow:0 0 0 2px var(--surface),0 0 0 3.5px var(--accent)}
.hcount{display:flex;justify-content:space-between;align-items:center;gap:8px;min-height:36px;margin-top:8px;padding:0 4px 0 12px;border-radius:11px;background:var(--surface2);background:color-mix(in srgb,var(--accent) 10%,var(--surface));font:400 11px/1.3 var(--body);color:var(--accent-ink,var(--accent))}
.hcount b{font-weight:600}
.hview{display:inline-flex;align-items:center;gap:4px;min-height:36px;padding:0 8px;border:0;background:none;color:inherit;font:500 11px/1 var(--body);cursor:pointer}
.hview[hidden]{display:none}
.hcalnav{display:flex;justify-content:space-between;align-items:center;margin:0 0 6px;font-size:10px}
.hcalnav>span:last-child{display:flex;gap:4px}
.hcalbtn{display:grid;place-items:center;width:36px;height:30px;border:1px solid var(--line);border-radius:999px;background:var(--surface2);color:var(--chalk);cursor:pointer;padding:0}
.hcalbtn:disabled{opacity:.35;cursor:default}
.cal .cd[data-hd],.cal .cd[data-backfill]{cursor:pointer}
.cal .cd.sel{outline:2px solid var(--chalk);outline-offset:1px;box-shadow:inset 0 0 0 2px var(--surface)}
.cal .cd:focus-visible,.hcalbtn:focus-visible,.hview:focus-visible,.hselrow button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.hdays{scroll-margin-top:calc(env(safe-area-inset-top,0px) + 86px)}
#hSessions{scroll-margin-top:calc(env(safe-area-inset-top,0px) + 86px)}
.hexport{display:inline-flex;align-items:center;gap:4px;min-height:30px;color:var(--accent-ink,var(--accent))}
.hscope{margin:-4px 0 8px;font:400 11px/1.45 var(--body);color:var(--muted)}
.hdayf{display:flex;justify-content:space-between;align-items:center;gap:8px;min-height:36px;margin:0 0 8px;padding:0 10px;border-radius:11px;background:var(--surface2);background:color-mix(in srgb,var(--accent) 10%,var(--surface));font:400 11px/1.3 var(--body);color:var(--accent-ink,var(--accent))}
.hdayf button{min-height:36px;border:0;background:none;padding:0;color:inherit;font:500 11px/1 var(--body);text-decoration:underline;cursor:pointer}
.hmdiv{margin:14px 0 8px;font-family:var(--mono);font-size:9px;letter-spacing:var(--track-label);text-transform:uppercase;color:var(--muted)}
.hmore{display:block;width:100%;min-height:44px;margin-top:4px;border:1px solid var(--line);border-radius:12px;background:var(--surface);color:var(--accent-ink,var(--accent));font:500 12px/1 var(--body);cursor:pointer}
#histExportDialog{position:fixed;inset:auto 0 0;margin:0 auto;width:100%;max-width:520px;box-sizing:border-box;padding:14px 24px calc(24px + env(safe-area-inset-bottom,0px));border:1px solid var(--line);border-radius:26px 26px 0 0;background:var(--surface);color:var(--chalk);font-family:var(--body);max-height:90%;overflow:auto}
#histExportDialog::backdrop{background:#0007}
#histExportDialog p{font:400 12px/1.45 var(--body);color:var(--muted);margin:10px 0 14px}
#histExportDialog .btn{display:flex;align-items:center;justify-content:center;gap:8px;min-height:48px;width:100%;margin:8px 0 0}
#histExportDialog .btn:disabled{opacity:.4}
.hxs-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.hxs-head h2{font:600 22px/1.2 var(--body);letter-spacing:-.4px;margin:0;text-transform:none;opacity:1}
.hxs-head h2::before,.hxs-head h2::after{content:none;display:none}
.hxs-close{flex:none;display:grid;place-items:center;width:44px;height:44px;border:0;border-radius:50%;background:var(--surface2);color:var(--chalk);cursor:pointer;padding:0}
.hxs-scope{display:flex;flex-direction:column;gap:3px;padding:12px 14px;margin:0 0 6px;border-radius:14px;background:var(--surface2);font:400 11px/1.4 var(--body);color:var(--muted)}
.hxs-scope b{font:600 16px/1.25 var(--body);color:var(--chalk)}
#histExportDialog .hxs-note{margin:6px 0 4px;color:var(--accent-ink,var(--accent))}
.hxs-year{display:flex;justify-content:space-between;align-items:center;width:100%;min-height:44px;margin-top:12px;padding:0 4px;border:0;border-top:1px solid var(--line);background:none;color:var(--accent-ink,var(--accent));font:500 13px/1 var(--body);cursor:pointer}
.hxs-year>span:last-child{display:inline-flex;align-items:center;gap:4px}
.hxs-year:disabled{color:var(--faint);cursor:default}
#histExportDialog .hxs-privacy{margin:10px 0 0;font-size:11px}
.hxs-fb{font:400 12px/1.45 var(--body);color:var(--accent-ink,var(--accent));min-height:0;margin-top:8px}
.hxs-fb:empty{display:none}
.hxs-fallback{display:block;width:100%;box-sizing:border-box;height:140px;margin-top:8px;padding:8px;border:1px solid var(--line);border-radius:10px;background:var(--surface2);color:var(--chalk);font:400 16px/1.35 var(--mono)}
.hxs-fallback[hidden]{display:none}
.hxs-close:focus-visible,.hxs-year:focus-visible,#histExportDialog .btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
#hSessions{opacity:1}
#hSessions .hacts{order:2;margin-left:0}
#hSessions .hexport{font-size:10px;padding:0 12px;background:var(--surface);border-color:color-mix(in srgb,var(--accent) 35%,var(--line))}
/* v4.6.436: Today's two ways to a plan -- the planner's own buttons, two to a row, each with a line saying what it does */
.today-focus-tools.tf-two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 10px;justify-content:stretch}
.today-focus-tools .tf-tile{display:grid;grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto auto;column-gap:6px;row-gap:3px;align-items:center;width:100%;min-height:52px;padding:9px 12px;text-align:left}
.tf-ic{grid-row:1;grid-column:1;display:grid;place-items:center;color:var(--accent-ink,var(--accent))}
.tf-t{display:contents}
.tf-t b{grid-row:1;grid-column:2;font-weight:600}
.tf-t small{grid-row:2;grid-column:1/-1}
.tf-t small{font:400 11px/1.3 var(--body);font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* v4.6.436: the prompt for your own AI, above the paste box -- the paste card's own heading, small print and controls */
.pa-card{display:flex;flex-direction:column;gap:8px}
.pa-card h3{margin:0}
.pa-sub,.pa-note{margin:0 0 4px;font:400 12px/1.504 var(--body);font-size:12px;color:var(--muted)}
.pa-note{margin:0}
.pa-lab{margin-top:4px;font:400 10px/1.4 var(--mono);font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.pa-seg{display:grid;grid-template-columns:1fr 1fr;gap:2px 3px;padding:3px;border-radius:12px;background:var(--surface2)}
.pa-seg.pa-seg5{grid-template-columns:repeat(5,minmax(0,1fr))}
.pa-seg button{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;min-height:40px;margin:0;padding:2px;border:0;border-radius:9px;background:none;color:var(--muted);font:500 12px/1.2 var(--body);font-size:12px;white-space:nowrap}
.pa-seg button small{font:400 10px/1.2 var(--mono);font-size:10px;opacity:.8}
.pa-seg button[aria-pressed="true"]{background:var(--surface);color:var(--accent-ink,var(--accent));box-shadow:0 1px 4px #00000012}
.pa-copy{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:6px}
.pa-see summary{cursor:pointer;font:400 12px/1.5 var(--body);font-size:12px;color:var(--accent-ink,var(--accent));padding:4px 0}
.pa-pre{margin:6px 0 0;max-height:220px;overflow:auto;padding:10px;border-radius:12px;background:var(--surface2);font:400 11px/1.5 var(--mono);font-size:11px;white-space:pre-wrap;word-break:break-word;color:var(--chalk)}
/* PASTE A WEEK: the import, in the setup frame (#sfRoot.sf-aiw) -- its labels, cards, footer and
   type are the setup screens'; these are only the parts it has of its own */
#sfRoot .sf-card.pa-card{gap:8px;display:flex;flex-direction:column}
#sfRoot .aiw-paste{position:relative;padding:12px}
#sfRoot .aiw-ta{display:block;width:100%;min-height:180px;box-sizing:border-box;margin:0;padding:10px 10px 44px;border:0;border-radius:12px;background:var(--surface2);color:var(--chalk);font:400 11px/1.6 var(--mono);font-size:11px;resize:vertical}   /* 12 pt on screen with the app's text scale (Sungjee picked 12) */
#sfRoot .aiw-chip{position:absolute;right:20px;bottom:20px;display:flex;align-items:center;gap:6px;height:32px;padding:0 12px;border:0;border-radius:10px;background:var(--surface);box-shadow:0 0 0 1px var(--line);color:var(--chalk);font:500 12px var(--body);font-size:12px}
#sfRoot .aiw-how{margin-top:10px}
#sfRoot .aiw-err{margin:10px 2px 0;font:500 12px/1.45 var(--body);font-size:12px;color:#D64545}
#sfRoot .aiw-tally{display:flex;justify-content:space-between;margin:0 2px 8px;font:400 12px var(--body);font-size:12px;color:var(--muted)}
#sfRoot .aiw-w{margin-bottom:10px;padding:0;overflow:hidden}
#sfRoot .aiw-w.off .aiw-wt{opacity:.5}
#sfRoot .aiw-wh{display:flex;align-items:center;gap:4px;padding:6px 8px 6px 14px}
#sfRoot .aiw-ck{flex:none;display:grid;place-items:center;width:24px;height:24px;margin:0;padding:0;border:1.5px solid var(--line);border-radius:7px;background:var(--surface);color:#fff}
#sfRoot .aiw-ck[aria-checked="true"]{background:var(--accent);border-color:var(--accent)}
#sfRoot .aiw-wt{flex:1;display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:52px;margin:0;padding:6px 8px;border:0;background:none;text-align:left;color:var(--chalk);font:inherit}
#sfRoot .aiw-wt>span{display:flex;flex-direction:column;gap:2px}
#sfRoot .aiw-wt b{font:600 15px/1.3 var(--body);font-size:15px}
#sfRoot .aiw-wt small{font:400 12px var(--body);font-size:12px;color:var(--muted)}
#sfRoot .aiw-wt em{font-style:normal;color:#B7791F}
#sfRoot .aiw-rows{padding:0 16px 10px;border-top:1px solid var(--line)}
#sfRoot .aiw-row{display:flex;justify-content:space-between;align-items:baseline;gap:10px;padding:10px 0;border-bottom:1px solid var(--line);font:400 13px/1.35 var(--body);font-size:13px}
#sfRoot .aiw-row:last-child{border-bottom:0}
#sfRoot .aiw-row b{flex:none;font:400 12px var(--mono);font-size:12px;color:var(--muted)}
#sfRoot .aiw-row small,#sfRoot .aiw-noterow small{font-size:11px;color:var(--muted)}
#sfRoot .aiw-noterow{color:var(--muted)}
#sfRoot .aiw-undo{margin:0 0 0 6px;padding:0;border:0;background:none;color:var(--accent-ink,var(--accent));font:400 12px var(--body);font-size:12px}
#sfRoot .aiw-check{margin:8px 0;padding:12px;border-radius:12px;background:color-mix(in srgb,#F2A900 12%,var(--surface))}
#sfRoot .aiw-check-k{font:500 10px var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:#9A6700}
#sfRoot .aiw-check p{margin:6px 0 10px;font:400 13px/1.45 var(--body);font-size:13px}
#sfRoot .aiw-sets{font:400 12px var(--mono);font-size:12px;color:var(--muted)}
#sfRoot .aiw-cands{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
#sfRoot .aiw-cand{min-height:36px;margin:0;padding:0 12px;border:0;border-radius:10px;background:var(--surface);box-shadow:0 0 0 1px var(--line);color:var(--chalk);font:500 13px var(--body);font-size:13px}
#sfRoot .aiw-other{display:flex;gap:6px}
#sfRoot .aiw-other select{flex:1;min-width:0;height:36px;padding:0 8px;border:0;border-radius:10px;background:var(--surface);box-shadow:0 0 0 1px var(--line);color:var(--chalk);font:400 13px var(--body);font-size:13px}
#sfRoot .aiw-ghost{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;margin:0;padding:0 12px;border:0;border-radius:10px;background:none;color:var(--accent-ink,var(--accent));font:500 13px var(--body);font-size:13px}
#sfRoot .aiw-sched{padding:4px 16px}
#sfRoot .aiw-srow{padding:12px 0;border-bottom:1px solid var(--line)}
#sfRoot .aiw-srow:last-child{border-bottom:0}
#sfRoot .aiw-sh{display:flex;align-items:center;justify-content:space-between;gap:10px}
#sfRoot .aiw-sh>span{display:flex;flex-direction:column;gap:2px;min-width:0}
#sfRoot .aiw-sh b{font:600 15px/1.3 var(--body);font-size:15px}
#sfRoot .aiw-sh small{font:400 12px var(--body);font-size:12px;color:var(--muted)}
#sfRoot .aiw-date{flex:none;height:40px;padding:0 10px;border:0;border-radius:12px;background:var(--surface2);color:var(--chalk);font:400 13px var(--mono);font-size:13px}
#sfRoot .aiw-when{margin-top:4px;font:400 12px var(--body);font-size:12px;color:var(--muted);text-align:right}
#sfRoot .aiw-k{margin-top:10px}
#sfRoot .aiw-k p{margin:0 0 8px;font:400 12px/1.45 var(--body);font-size:12px;color:var(--muted)}
#sfRoot .aiw-k p b{color:var(--chalk);font-weight:600}
#sfRoot .aiw-kb{display:grid;grid-template-columns:1fr 1fr;gap:8px}
#sfRoot .aiw-kb button{min-height:44px;margin:0;border:0;border-radius:12px;background:var(--surface2);color:var(--chalk);font:500 13px var(--body);font-size:13px}
#sfRoot .aiw-kb button[aria-pressed="true"]{background:var(--accent);color:#fff}
#sfRoot .aiw-note{display:flex;gap:8px;align-items:flex-start;margin-top:14px}
#sfRoot .aiw-note svg{flex:none;margin-top:2px;color:var(--accent-ink,var(--accent))}
#sfRoot .aiw-sum{padding:4px 16px}
#sfRoot .aiw-cr{padding:12px 0;border-bottom:1px solid var(--line)}
#sfRoot .aiw-cr:last-child{border-bottom:0}
#sfRoot .aiw-crh{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}
#sfRoot .aiw-d{font:400 12px var(--body);font-size:12px;color:var(--muted)}
#sfRoot .aiw-badge{padding:2px 8px;border-radius:7px;background:color-mix(in srgb,var(--accent) 10%,transparent);color:var(--accent-ink,var(--accent));font:500 11px var(--body);font-size:11px}
#sfRoot .aiw-crb{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
#sfRoot .aiw-crb b{font:600 15px/1.3 var(--body);font-size:15px}
#sfRoot .aiw-crb span{font:400 12px var(--mono);font-size:12px;color:var(--muted)}
#sfRoot .aiw-cr small{display:block;margin-top:4px;font:400 12px/1.45 var(--body);font-size:12px;color:var(--muted)}
#sfRoot .aiw-undoall{margin-top:12px}
#sfRoot .aiw-rt{margin-top:14px}
#sfRoot.sf-aiw .sf-foot.two .sf-warn{grid-column:1/-1}   /* the reason Next waits gets its own row above Back and Next */
#sfRoot .aiw-ta:focus{outline:2px solid var(--accent);outline-offset:0}

/* v4.6.438: TODAY, CALMER -- one row of four quiet actions under the plan (Edit or Plan with AI,
   Paste, Push a day, Rest day): icon over a short label, equal widths, the app's card surface. */
.tf-acts{--n:4;display:grid;grid-template-columns:repeat(var(--n),1fr);gap:8px;margin:14px 0 0}
.tf-act{all:unset;box-sizing:border-box;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-height:68px;padding:10px 4px;border-radius:16px;background:var(--surface);color:var(--chalk);text-align:center;-webkit-tap-highlight-color:transparent;transition:transform .12s ease,background-color .15s ease}
.tf-act:active{transform:scale(.96)}
.tf-act:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.tf-act-ic{display:grid;place-items:center;color:var(--accent-ink,var(--accent))}
.tf-act-ic .ic{width:20px;height:20px}
.tf-act-t{font:500 12px/1.2 var(--body);font-size:12px;color:var(--chalk);white-space:nowrap}
.tf-act.restbtn{margin:0}
.tf-act.restbtn.on .tf-act-t{color:var(--muted)}
.today-focus .tf-acts+.pn-hint{margin-top:12px}
:root[data-skin="minimal"] .tf-act{background:var(--surface);border-radius:16px}   /* v4.6.438: the one skin */

/* ================= v4.6.439: HISTORY, CALMER =================
   (Sungjee, 2026-10-26: "cluttered -- give it breathing room and the app's elegance")
   Three filter groups under one small label each, 26px apart; month cells without borders, empty
   months blank; the body-part chips carry no counts; Export is a quiet link
   (the session card itself is unchanged: it was set pixel by pixel). */
:root #view .hlab{display:flex;align-items:center;gap:10px;margin:26px 2px 10px;font:500 11px var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
:root #view .hlab::after{content:"";flex:1;height:1px;background:var(--line);order:1}
:root #view .hlab>span:first-child{order:0}
:root #view .hlab-acts{order:2;display:flex;gap:14px;letter-spacing:0;text-transform:none}
:root #view .hlab-acts button{all:unset;cursor:pointer;font:500 12px var(--body);font-size:12px;color:var(--accent-ink,var(--accent))}
:root #view .hlab-acts button:disabled{color:var(--faint);cursor:default}
:root #view .hlab:first-child{margin-top:18px}
:root #view .ychips{margin-bottom:0}
:root #view .mchips{gap:6px}
:root #view .mchip{border:0;background:var(--surface);border-radius:12px;padding:7px 0 6px;min-height:42px;justify-content:center;gap:1px}
:root #view .mchip.on{background:var(--accent)}
:root #view .mchip span{font-size:10px;letter-spacing:.04em}
:root #view .mchip b{font-size:13px;min-height:16px}
:root #view .mchip.dim{opacity:1}
:root #view .mchip.dim span{color:var(--faint)}
:root #view .mchip.on span{color:color-mix(in srgb,#fff 80%,transparent)}
:root #view .pchips{gap:6px;margin-bottom:0}
:root #view .pchips .chip{padding:7px 14px}
:root #view .hnone{margin:22px 2px 0;font:400 13px var(--body);font-size:13px;color:var(--muted)}
:root #view .hcal{margin-top:26px!important}
/* the session list */
:root #view .hdays{margin-top:30px}
:root #view .hdays .hexport{border:0;background:none;padding:0 2px;min-height:32px;font:500 12px var(--body);font-size:12px;letter-spacing:0;text-transform:none;color:var(--accent-ink,var(--accent));box-shadow:none}
:root #view .hdays .hscope{margin:6px 2px 14px;font-size:13px;color:var(--muted)}
/* v4.6.441: TODAY'S FOUR, SIZED BY EYE (Sungjee, 2026-10-26, option 6A). All four come from the
   icon set now (tools/icons). A diagonal pencil carries less ink than a full clipboard, calendar or
   moon in the same box, so the heavier shapes sit a little smaller and the row reads level. */
.tf-act-ic .ic-paste{width:18px;height:18px}
.tf-act-ic .ic-calendarPush{width:17.6px;height:17.6px}
.tf-act-ic .ic-moon{width:18.4px;height:18.4px}
.tf-act-ic{height:20px}   /* one slot height, so the four labels share a baseline whatever each glyph's optical size */
/* v4.6.442: the prompt's choices as the app's sliding toggle (after .xp-seg, the Progress / Hold control) */
.aiw-tg{--i:0;--n:2;position:relative;display:grid;grid-template-columns:repeat(var(--n),minmax(0,1fr));padding:3px;border-radius:21px;background:color-mix(in srgb,var(--chalk) 7%,transparent);isolation:isolate}
.aiw-tg::before{content:"";position:absolute;z-index:0;top:3px;bottom:3px;left:3px;width:calc((100% - 6px) / var(--n));border-radius:17px;background:var(--surface);box-shadow:0 1px 2px rgba(0,0,0,.12),0 1px 6px rgba(0,0,0,.06);transform:translateX(calc(var(--i) * 100%));transition:transform var(--dur-arrive,.42s) var(--settle)}
:root[data-theme="dark"] .aiw-tg::before{background:color-mix(in srgb,var(--chalk) 16%,var(--surface))}
.aiw-tg button{position:relative;z-index:1;display:flex;align-items:center;justify-content:center;height:34px;min-height:0;min-width:0;margin:0;padding:0 2px;border:0;border-radius:17px;background:none;color:var(--muted);font:500 12px var(--body);font-size:12px;white-space:nowrap;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:color var(--dur-quick,.16s) ease}
.aiw-tg button[aria-checked="true"]{color:var(--chalk)}
.aiw-tg button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
@media(prefers-reduced-motion:reduce){.aiw-tg::before{transition:none}}
.pa-note{margin:2px 2px 0;font:400 12px/1.45 var(--body);font-size:12px;color:var(--muted)}
/* v4.6.442: Paste its answer -- one screen, no scroll; the box takes the full width and the height left */
#sfRoot .sf-scroll.aiw-fit{display:flex;flex-direction:column;overflow:hidden}
#sfRoot .sf-scroll.aiw-fit .sf-body{flex:1;display:flex;flex-direction:column;min-height:0}
#sfRoot .aiw-box{position:relative;flex:1;display:flex;min-height:96px}
#sfRoot .aiw-box .aiw-ta{flex:1;height:auto;min-height:0;resize:none;border:1px solid var(--line);background:var(--surface)}
#sfRoot .aiw-box .aiw-chip{right:10px;bottom:10px}
#sfRoot .aiw-s2 .aiw-how{margin:10px 2px 0}
/* v4.6.442: the log toggle on two lines -- the number over its unit */
.aiw-tg2 button{flex-direction:column;gap:1px;height:42px;line-height:1.1}
.aiw-tg2 button small{font:400 10px/1.1 var(--body);font-size:10px}
.aiw-tg2::before{border-radius:16px}
/* v4.6.442: the days move together -- one control, and the list in the Push sheet's own words */
#sfRoot .aiw-sched{padding:12px 16px}
#sfRoot .aiw-move{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px}
#sfRoot .aiw-move .pw-btn{display:inline-flex;align-items:center;gap:4px;margin:0;white-space:nowrap}
#sfRoot .aiw-move .pw-btn:disabled{opacity:.4}
#sfRoot .aiw-start{display:flex;flex-direction:column;align-items:center;gap:1px;text-align:center}
#sfRoot .aiw-start small{font:400 10px var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
#sfRoot .aiw-start b{font:600 13px/1.25 var(--body);font-size:13px;white-space:nowrap}
#sfRoot .aiw-map{margin:12px 0 0}
#sfRoot .aiw-map{list-style:none;margin:12px 0 0;padding:0;font-variant-numeric:tabular-nums}
#sfRoot .aiw-map li{display:flex;align-items:baseline;justify-content:space-between;gap:10px;padding:10px 0;border-top:1px solid var(--line);font:400 13px/1.35 var(--body);font-size:13px}
#sfRoot .aiw-map .pm-from{min-width:0;color:var(--chalk);font-weight:500}
#sfRoot .aiw-map .pm-to{flex:none;max-width:52%;text-align:right}
#sfRoot .aiw-map .pm-to small{font:400 11px/1.35 var(--body);font-size:11px;color:var(--muted)}
#sfRoot .aiw-map .pm-to small.pm-free{color:var(--accent-ink,var(--accent))}
#sfRoot .aiw-map .pm-to small.pm-warn{color:#C94A4A}
/* v4.6.442: the import's dates use the Plan flow's own calendar, key and Move by stepper (.pf-dates-page) */
#sfRoot .aiw-dates .pf-date-sheet{margin:0 0 12px}
#sfRoot .aiw-dates .aiw-k{margin-top:12px}
#sfRoot .sf-how.aiw-note{margin:16px 2px 0}   /* the note under a card, clear of it (the frame's .sf-how loads later and pulled it up) */

/* v4.6.444: REST, CALMER. Folded, the rest card is one small centred line and a
   chevron; open, it is a section heading -- Rest left, chevron (down) right. */
.restfold{margin:6px 0 0}
.restfold>summary{list-style:none;display:flex;align-items:center;justify-content:center;gap:4px;cursor:pointer;padding:10px 0;-webkit-tap-highlight-color:transparent}
.restfold>summary::-webkit-details-marker{display:none}
.restfold .rf-t{display:none;font-family:var(--disp);font-size:9px;letter-spacing:var(--track-wide);text-transform:uppercase;opacity:.75;color:var(--chalk);font-weight:600}
.restfold .rf-n{font:400 11px/1 var(--mono);color:var(--muted)}
.restfold .rf-ch{display:inline-grid;color:var(--muted);transition:transform var(--dur-move) var(--settle)}
.restfold .rf-ch .ic{width:11px;height:11px}
.restfold[open]>summary{justify-content:space-between;padding:13px 0 7px}
.restfold[open] .rf-t{display:block}
.restfold[open] .rf-n{display:none}
.restfold[open] .rf-ch{transform:rotate(90deg)}
.restfold[open] .rf-ch .ic{width:15px;height:15px}
.restfold>.crcard{margin-top:4px}
.restbtn.on#restBtn{display:block;margin:2px auto 0;padding:8px 12px;min-height:0;width:auto;background:none;border:0;box-shadow:none;
  font:400 11px/1.2 var(--body);letter-spacing:0;text-transform:none;color:var(--muted);opacity:.7}
.restbtn.on#restBtn:active{opacity:1}
:root[data-skin="minimal"] .restbtn.on#restBtn{border:0}

/* v4.6.445: double-tap the number to type it -- the field wears the number's own type */
.vs-num .vs-type{width:4.2ch;min-width:0;margin:0;padding:0 2px;border:0;border-bottom:2px solid var(--accent);border-radius:0;background:transparent;
  font:700 36px var(--body);font-size:36px;letter-spacing:-.03em;font-variant-numeric:tabular-nums;color:var(--chalk);text-align:center;outline:none;box-shadow:none;-webkit-appearance:none;appearance:none}
#vsFace.typing .vl-tag{visibility:hidden}

/* v4.6.446: backup status, first in the account card */
.sync-stat{display:flex;align-items:flex-start;gap:10px;margin:4px 0 0;padding:11px 12px;border-radius:12px;background:var(--surface2);border:1px solid var(--line)}
.sync-stat .ss-dot{flex:none;width:10px;height:10px;margin-top:4px;border-radius:50%;background:var(--muted)}
.sync-stat .ss-txt{display:flex;flex-direction:column;gap:3px;min-width:0}
.sync-stat b{font:600 15px/1.2 var(--body);font-size:15px;color:var(--chalk)}
.sync-stat small{font:400 12px/1.35 var(--body);font-size:12px;color:var(--muted)}
.sync-stat.ok .ss-dot{background:#2E9E4F;box-shadow:0 0 0 3px color-mix(in srgb,#2E9E4F 22%,transparent)}
.sync-stat.wait .ss-dot{background:#E0A526;box-shadow:0 0 0 3px color-mix(in srgb,#E0A526 24%,transparent)}
.sync-stat.bad .ss-dot{background:#C94A4A;box-shadow:0 0 0 3px color-mix(in srgb,#C94A4A 22%,transparent)}
.sync-stat.busy .ss-dot{background:var(--accent);animation:ssPulse 1s ease-in-out infinite}
@keyframes ssPulse{50%{opacity:.35}}
@media(prefers-reduced-motion:reduce){.sync-stat.busy .ss-dot{animation:none}}
.row.sync-acts{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px}
.sync-acts #signOutBtn{grid-column:1/-1}
.sync-acts .btn:disabled{opacity:.5}
/* ...and on the closed Account & data row */
.sync-sum{display:inline-flex;align-items:center;gap:6px}
.sync-sum .ss-dot{width:7px;height:7px;border-radius:50%;background:var(--muted);flex:none}
.sync-sum.ok .ss-dot{background:#2E9E4F}.sync-sum.wait .ss-dot{background:#E0A526}.sync-sum.bad .ss-dot{background:#C94A4A}.sync-sum.busy .ss-dot{background:var(--accent)}
.sync-sum.bad{color:#C94A4A}
/* v4.6.448: Settings lists every import, each with its own Delete (tap twice) */
.imp-list{list-style:none;margin:-4px 0 0;padding:0 0 6px}
.imp-row{display:flex;align-items:center;gap:12px;padding:10px 0 10px 12px;border-left:2px solid var(--line)}
.imp-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.imp-t b{font:600 13.5px/1.25 var(--body);color:var(--chalk)}
.imp-t span{font:400 12.5px/1.4 var(--body);color:var(--muted)}
.imp-del{all:unset;flex:none;cursor:pointer;min-height:34px;padding:0 12px;border-radius:999px;border:1px solid var(--line);font:500 13px var(--body);color:var(--muted);transition:background var(--dur-quick) var(--settle),color var(--dur-quick) var(--settle);-webkit-tap-highlight-color:transparent}
.imp-del.on{color:var(--record);border-color:color-mix(in srgb,var(--record) 45%,transparent);background:color-mix(in srgb,var(--record) 10%,transparent)}
.imp-del:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* v4.6.449: the bodyweight strip -- the barbell strip's place and shape, rep/sec at its right */
.vl-strip.vl-bw{cursor:default;padding:7px 8px 7px 14px;gap:12px}
.vl-bw .vl-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.vl-bw .vl-txt b{font:600 14px/1.25 var(--body);color:var(--chalk);white-space:normal}
.vl-bw .vl-txt small{font:400 12px/1.3 var(--body);color:var(--muted)}
.vl-bw .seg{flex:none;margin:0}
.plate-mini[hidden]{display:none}
/* v4.6.450: rep / sec is the Progress / Hold toggle, sized for the strip; the calm thumb slides to the chosen unit */
.vl-bw .xp-seg.su-seg{flex:none;width:124px}
.xp-seg.su-seg[data-state="sec"]::before{transform:translateX(100%)}
.xp-seg.su-seg[data-state="rep"] [data-setunit="r"],.xp-seg.su-seg[data-state="sec"] [data-setunit="s"]{color:var(--xp-calm-ink)}
.vl-bw .vl-txt small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* v4.6.455: TODAY'S ROW AS PILLS (Sungjee, 2026-10-27, option 9B). Icon and word on one line, centred
   under the plan; light enough to read as options, not a second set of big buttons. Same controls,
   same handlers. Glyph sizes keep v4.6.441's optical ratios at the pill's smaller size. */
.tf-acts{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin:16px 0 0}
.tf-act{flex-direction:row;gap:7px;min-height:44px;height:44px;padding:0 16px 0 13px;border-radius:999px;background:var(--surface);box-shadow:var(--shadow)}
:root[data-skin="minimal"] .tf-act{border-radius:999px}
.tf-act-ic{height:auto}
.tf-act-ic .ic{width:15px;height:15px}
.tf-act-ic .ic-paste{width:13.5px;height:13.5px}
.tf-act-ic .ic-calendarPush{width:13.2px;height:13.2px}
.tf-act-ic .ic-moon{width:13.8px;height:13.8px}
.tf-act-t{font:500 13px/1 var(--body);font-size:13px}
@media (max-width:350px){.tf-acts{gap:5px}.tf-act{padding:0 10px 0 9px;gap:5px}.tf-act-t{font-size:11.5px}.tf-act-ic .ic{width:13px;height:13px}.tf-act-ic .ic-paste{width:11.7px;height:11.7px}.tf-act-ic .ic-calendarPush{width:11.4px;height:11.4px}.tf-act-ic .ic-moon{width:12px;height:12px}}
/* v4.6.455 (option 9D): Edit lives inside the expanded plan, under its exercises */
.tf-edit{display:flex;justify-content:center;flex-wrap:wrap;gap:8px;margin:14px 0 2px}
.tf-edit-btn{all:unset;box-sizing:border-box;cursor:pointer;display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:0 20px 0 16px;border-radius:999px;border:1px solid var(--line);color:var(--accent-ink,var(--accent));font:500 13px/1 var(--body);font-size:13px;-webkit-tap-highlight-color:transparent}
.tf-edit-btn:active{transform:scale(.97)}
.tf-edit-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* v4.6.456: later days expanded on Today wear the same tool row as today's expanded plan */
.pw-later-body .pw-home-tools{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px 6px;margin:12px 0 11px}
.pw-later-body .pw-home-tools>*{min-width:0;justify-content:center;padding-inline:6px}
.pw-later-body .pw-home-tools .ic,.pw-home.lt .lt-fold-body .pw-home-tools .ic{flex:none}
@media (max-width:350px){.pw-later-body .pw-home-tools>*,.pw-home.lt .lt-fold-body .pw-home-tools>*{padding-inline:4px;gap:4px;font-size:10.5px}.pw-later-body .pw-home-tools .ic,.pw-home.lt .lt-fold-body .pw-home-tools .ic{width:12px;height:12px}}
/* v4.6.458: ONE CHEVRON RULE (Sungjee, 2026-10-27: "Chevrons should ALWAYS follow the rule"). A chevron
   on anything that expands and contracts -- a <details> summary, or any control carrying aria-expanded
   -- points RIGHT while closed and DOWN while open, everywhere. The app had grown five ways of drawing
   it (a down glyph rotated to the left on open, a right glyph pre-rotated down that flipped up, a
   static right glyph that never turned, wrappers that rotated their own boxes...). This block is the
   one source: it neutralises every wrapper and turns the glyph itself, whichever chevron glyph a
   screen happens to use. Navigation chevrons (a row that goes somewhere) carry no aria-expanded and
   sit in no summary, so they are untouched. Guarded by tools/check-chevrons.cjs, which walks every
   screen, opens and closes every disclosure, and reads the direction actually drawn. */
:is(details>summary,[aria-expanded]) :is(.fresh-chev,.dr-down,.pfchev,.rf-ch,.club-chev,.club-chev svg){transform:none!important}
:is(details>summary,[aria-expanded]) svg.ic:is(.ic-chevron,.ic-chevronR){transform:rotate(0deg)!important;transition:transform .2s ease}
:is(details>summary,[aria-expanded]) svg.ic.ic-chevronD{transform:rotate(-90deg)!important;transition:transform .2s ease}
:is(details>summary,[aria-expanded]) svg.ic.ic-chevronU{transform:rotate(90deg)!important;transition:transform .2s ease}
:is(details[open]>summary,[aria-expanded="true"]) svg.ic:is(.ic-chevron,.ic-chevronR){transform:rotate(90deg)!important}
:is(details[open]>summary,[aria-expanded="true"]) svg.ic.ic-chevronD{transform:rotate(0deg)!important}
:is(details[open]>summary,[aria-expanded="true"]) svg.ic.ic-chevronU{transform:rotate(180deg)!important}
@media (prefers-reduced-motion:reduce){:is(details>summary,[aria-expanded]) svg.ic:is(.ic-chevron,.ic-chevronR,.ic-chevronD,.ic-chevronU){transition:none}}
/* v4.6.467 (12A): the arrow that replaced a navigation chevron is quiet grey; in a primary button it
   takes the button's colour. Blue stays for primary actions. */
svg.ic-go{color:var(--faint,var(--muted));flex:none}
:is(.btn:not(.ghost),.primary,.pw-btn.primary,.im-go,.onbbtn.pri,[data-sf="next"],.tf-edit-btn) svg.ic-go{color:inherit}
/* v4.6.467: History's way to the import -- one quiet row, the same weight as a Settings row */
.hist-import{all:unset;box-sizing:border-box;cursor:pointer;text-align:left;display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:56px;margin:4px 0 14px;padding:10px 16px;border-radius:16px;background:var(--surface);box-shadow:var(--shadow);color:var(--chalk);-webkit-tap-highlight-color:transparent}
.hist-import>span{display:flex;flex-direction:column;gap:3px}
.hist-import b{font:500 14px/1.2 var(--body);font-size:14px}
.hist-import small{font:400 11px/1.3 var(--body);font-size:11px;color:var(--muted)}
.hist-import:active{transform:scale(.99)}
.hist-import:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.hdays+.hist-import{margin-top:18px}
.hist-import-empty{margin:16px 0 0;box-shadow:none;background:var(--surface2)}
/* v4.6.468: a day's tools are pills -- the same as Edit plan / Push a day -- centred above its exercises */
.pw-home-tools.pw-day-tools{display:flex!important;flex-wrap:wrap;justify-content:flex-start;gap:8px;margin:12px 0 12px;grid-template-columns:none}
.pw-home-tools.pw-day-tools.pw-day-tools>.pw-btn.pw-btn{min-height:36px;padding:0 15px 0 12px!important;border-radius:999px;border:1px solid var(--line);background:transparent;color:var(--accent-ink,var(--accent));font:500 12px/1 var(--body);gap:6px;flex:0 0 auto;justify-content:center}
.pw-home-tools.pw-day-tools.pw-day-tools>.pw-btn.pw-btn .ic{flex:none;width:13px;height:13px}
.pw-home-tools.pw-day-tools.pw-day-tools>.pw-btn.pw-btn::after{inset:-4px 0}

/* v4.6.472: Skip for now, on every setup screen from "You're in" on, with where to find it later */
#onb .ob-skip-later{align-self:center;display:block;margin:0 auto 2px;padding:10px 16px;min-height:44px;font:500 15px var(--body);font-size:15px;color:var(--chalk);text-decoration:none}
#onb .ob-skip-note{margin:0 0 4px;font-size:12px;color:var(--muted);opacity:1}
#sfRoot .sf-skip{grid-column:1/-1;display:flex;flex-direction:column;align-items:center;gap:0;margin-top:4px}
#sfRoot .sf-skip-btn{min-height:40px;margin:0;padding:8px 16px;border:0;background:none;color:var(--chalk);font:500 15px var(--body);font-size:15px;cursor:pointer}
#sfRoot .sf-skip p{margin:0;font:400 12px/1.3 var(--body);font-size:12px;color:var(--muted);text-align:center}
/* the crew pick is final on the free plan -- said where the name is given, before Welcome */
.ad-final{display:flex;align-items:flex-start;gap:8px;margin:12px 0 0;padding:10px 12px;border-radius:12px;background:color-mix(in srgb,var(--accent) 10%,transparent);font:400 12.5px/1.4 var(--body);font-size:12.5px;color:var(--chalk);text-align:left}
.ad-final .ic{flex:none;margin-top:2px;color:var(--accent-ink,var(--accent))}
.ad-final b{font-weight:600}
