/* Road to 209 · UI layer merged from direction/ui-designer (springs, cta-pill, goal-bar, streak-barcode, delights, fixes). Paths rewritten to the app root. */
/* ===== springs.css ===== */
/* Road to 209 · springs.css (ui-designer)
   Spring tokens (static, generated by tools/gen_springs.js from springs.js presets),
   the check-off stamp, and the bottom sheet. linear() where supported, cubic-bezier fallback.
   Paths assume direction/ui-designer/. When merging into styles.css change tex/ to tex/. */

:root{
  --sp-press:cubic-bezier(.34,1.56,.64,1);--sp-press-d:637ms; /* k380 c20, overshoot 15.3% */
  --sp-pop:cubic-bezier(.34,1.56,.64,1);--sp-pop-d:717ms; /* k300 c16, overshoot 19.3% */
  --sp-rise:cubic-bezier(.34,1.56,.64,1);--sp-rise-d:675ms; /* k220 c18, overshoot 9.1% */
  --sp-sheet:cubic-bezier(.22,1,.36,1);--sp-sheet-d:508ms; /* k260 c26, overshoot 1.4% */
  --sp-fill:cubic-bezier(.22,1,.36,1);--sp-fill-d:754ms; /* k120 c19, overshoot 0.4% */
  --sp-snap:cubic-bezier(.34,1.3,.64,1);--sp-snap-d:342ms; /* k520 c34, overshoot 3.0% */
  --ease-leave:cubic-bezier(.4,0,1,1);
  --ease-exit:cubic-bezier(.5,0,.75,0);
}
@supports (transition-timing-function: linear(0, 1)){
  :root{
    --sp-press:linear(0,0.0432,0.153,0.3023,0.4681,0.6322,0.7817,0.9083,1.0078,1.0796,1.1252,1.1481,1.1526,1.1434,1.1249,1.1013,1.0758,1.051,1.0288,1.0103,0.996,0.986,0.9798,0.977,0.9768,0.9786,0.9816,0.9854,0.9893,0.993,0.9963,0.999,1.001,1.0024,1.0032,1.0036,1.0035,1.0032,1.0027,1.0021,1);
    --sp-pop:linear(0,0.0435,0.1556,0.3099,0.4831,0.656,0.8144,0.9488,1.0539,1.1283,1.1735,1.1931,1.1917,1.1748,1.1475,1.1148,1.0807,1.0484,1.0202,0.9973,0.9803,0.9692,0.9634,0.9621,0.9643,0.9689,0.9749,0.9815,0.988,0.9939,0.9988,1.0026,1.0052,1.0068,1.0074,1.0072,1.0065,1.0054,1.0041,1.0028,1);
    --sp-rise:linear(0,0.0282,0.1013,0.2035,0.3218,0.4455,0.5666,0.6793,0.7797,0.8657,0.9363,0.9919,1.0333,1.0621,1.08,1.089,1.0908,1.0872,1.0799,1.0702,1.0593,1.048,1.0372,1.0271,1.0183,1.0107,1.0046,0.9998,0.9963,0.9939,0.9925,0.9918,0.9918,0.9922,0.9929,0.9938,0.9948,0.9958,0.9968,0.9977,1);
    --sp-sheet:linear(0,0.0188,0.0673,0.1353,0.2151,0.3003,0.3865,0.4703,0.5494,0.6223,0.6882,0.7466,0.7976,0.8415,0.8787,0.9097,0.9352,0.9559,0.9723,0.9851,0.9948,1.002,1.007,1.0104,1.0125,1.0136,1.0138,1.0135,1.0128,1.0119,1.0108,1.0096,1.0084,1.0072,1.0061,1.0051,1.0042,1.0034,1.0027,1.0021,1);
    --sp-fill:linear(0,0.0189,0.0672,0.1343,0.2121,0.2946,0.3775,0.4577,0.5331,0.6024,0.6651,0.7209,0.7699,0.8123,0.8487,0.8795,0.9054,0.9268,0.9444,0.9587,0.9702,0.9792,0.9863,0.9918,0.9959,0.9989,1.001,1.0025,1.0034,1.0039,1.0042,1.0042,1.004,1.0038,1.0035,1.0032,1.0028,1.0025,1.0021,1.0018,1);
    --sp-snap:linear(0,0.0172,0.0623,0.1269,0.2038,0.2875,0.3734,0.4583,0.5395,0.6154,0.6848,0.7471,0.8019,0.8495,0.8899,0.9238,0.9517,0.9741,0.9917,1.0052,1.0151,1.022,1.0264,1.0289,1.0298,1.0294,1.0282,1.0264,1.0242,1.0217,1.0192,1.0166,1.0142,1.0119,1.0098,1.0079,1.0062,1.0047,1.0035,1.0025,1);
  }
}

/* ===================== check-off ===================== */
/* v2 markup: .chk > svg(rect progress) + span.ms. Add i.chk-stamp + svg.chk-draw (moveRow patch or R209Check.enhance) */
.move .chk{overflow:visible;transition:border-color .2s}
.move.done .chk{background:transparent}            /* v2 filled it with ink: the stamp does it now */
.move.done .chk .ms{opacity:0;animation:none}      /* keep the node: patchRow() writes to it */
.chk-stamp{position:absolute;inset:-1.5px;border-radius:3px;background:var(--ember);opacity:0;transform:scale(.2) rotate(-14deg);
  transition:transform 160ms var(--ease-leave),opacity 120ms linear 40ms;overflow:hidden}
.chk-stamp::after{content:"";position:absolute;inset:0;background:url("grain.b69f3db876.png") 0 0/64px 64px;mix-blend-mode:overlay;opacity:.6}
.chk-draw{position:absolute;inset:-1.5px;width:34px;height:34px;z-index:1;overflow:visible}
.chk-draw path{fill:none;stroke:#1a0b03;stroke-width:3.6;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:1 1;stroke-dashoffset:1;
  transition:stroke-dashoffset 120ms var(--ease-leave)}
.move.done .chk{border-color:var(--ember)}
.move.done .chk-stamp{opacity:1;transform:none;transition:transform var(--sp-pop-d) var(--sp-pop),opacity 60ms linear}
.move.done .chk-draw path{stroke-dashoffset:0;transition:stroke-dashoffset 240ms cubic-bezier(.65,0,.35,1) 120ms}
/* the row dips 3px and springs back as the stamp lands */
.move.done.r209-live{animation:r209Nudge var(--sp-pop-d) var(--sp-pop) both}   /* r209-live: added by springs.js on tap */
@keyframes r209Nudge{from{transform:translateY(3px)}to{transform:none}}
/* text dims and a hairline strike draws across the name */
.mv-name{background:linear-gradient(currentColor,currentColor) 0 58%/0 1.5px no-repeat;transition:color .3s,background-size 160ms var(--ease-leave)}
.move.done .mv-name{color:var(--mute);background-size:100% 1.5px;transition:color .3s,background-size 320ms cubic-bezier(.65,0,.35,1) 180ms}
.mv-cue,.mv-dose{transition:opacity .3s}
.move.done .mv-cue{opacity:.55}
.move.done .mv-dose{opacity:.6}

/* ===================== bottom sheet ===================== */
.sheet{transition:transform var(--sp-sheet-d) var(--sp-sheet);
  /* 60px of sheet color below the edge so the overshoot never shows a gap */
  box-shadow:0 60px 0 0 #121110,0 -20px 50px -10px rgba(6,6,5,.7);will-change:transform}
.sheet.closing{transition:transform 240ms var(--ease-leave)}
.sheet.dragging,.sheet.closed-by-drag{transition:none}
.grab{height:34px;cursor:grab;touch-action:none}
.grab::after{width:44px;height:5px;border-radius:3px;background:#4a453e;transition:transform var(--sp-snap-d) var(--sp-snap),background-color .2s}
.sheet.dragging .grab::after{transform:scaleX(1.25);background:var(--ink-2)}
/* scrim: drop the full screen backdrop-filter, it costs frames on older iPhones */
.scrim{-webkit-backdrop-filter:none;backdrop-filter:none;background:rgba(8,8,7,.72);transition:opacity 280ms cubic-bezier(.2,.8,.2,1)}
.scrim.dragging{transition:none}
/* content staggers in 40ms apart once the sheet starts moving */
.sheet.on .sheet-body > *{animation:r209SheetItem var(--sp-snap-d) var(--sp-snap) backwards;animation-delay:calc(70ms + var(--i,0) * 40ms)}
.sheet-body > *:nth-child(1){--i:0}.sheet-body > *:nth-child(2){--i:1}.sheet-body > *:nth-child(3){--i:2}
.sheet-body > *:nth-child(4){--i:3}.sheet-body > *:nth-child(5){--i:4}.sheet-body > *:nth-child(6){--i:5}
.sheet-body > *:nth-child(7){--i:6}.sheet-body > *:nth-child(8){--i:7}.sheet-body > *:nth-child(n+9){--i:8}
@keyframes r209SheetItem{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion: reduce){
  .chk-stamp,.chk-draw path,.mv-name{transition:none!important}
  .move.done.r209-live{animation:none}
  .chk-stamp{transform:none}
  /* sheet: no movement, a 160ms fade only */
  .sheet,.sheet.on,.sheet.closing{transform:none!important;transition:opacity 160ms linear,visibility 0s linear 160ms!important;opacity:0;visibility:hidden}
  .sheet.on{opacity:1;visibility:visible;transition:opacity 160ms linear!important}
  .sheet.on .sheet-body > *{animation:none}
}
/* ===== cta-pill.css ===== */
/* Road to 209 · cta-pill.css (ui-designer)
   Targets the real v2 markup from pill() in app.js:
     button.pill.tap.pill-grad > span.pill-fx(i.b1 i.b2 i.b3 i.b4) + span.pill-grain
                                + span.pill-txt(b + em*, small) + span.ms.pill-ic
   cta-pill.js adds four small layers once: .pill-surge .pill-flood .pill-done .pill-ok (+ .pill-spin).
   TI-0042: solid warm near-black pill, aurora smeared in from the right edge, baked grain,
   slow drift, spring press with a surge, flood + ring + shards on success.
   Only .pill-grad gets color. .pill-flat stays charcoal (creative director rule).
   Needs springs.css tokens. Paths assume direction/ui-designer/; merged into styles.css use tex/. */

.pill-grad{
  --p-body:#121110; --p-deep:#b3200f; --p-ember:#ff5a1f; --p-amber:#ffb23e; --p-hot:#fff0d6;
  background:var(--p-body);
  box-shadow:inset 0 0 0 1px rgba(255,240,214,.07),inset 0 1px 0 rgba(255,240,214,.09),
    0 16px 34px -16px rgba(255,90,31,.5),0 8px 20px -10px rgba(6,6,5,.9);
  transition:transform var(--sp-press-d) var(--sp-press);
  min-height:44px;
}
.pill:focus-visible{outline:2px solid var(--ember-2);outline-offset:3px}

/* ---- aurora: four streak layers, all anchored to the right edge ---- */
.pill-grad .pill-fx{width:84%;transform-origin:100% 50%;transition:transform var(--sp-press-d) var(--sp-press),opacity .3s;
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.25) 22%,#000 58%);mask-image:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.25) 22%,#000 58%)}
.pill-grad .pill-fx i{border-radius:0;top:-35%;bottom:-35%;height:auto;right:-6%;left:auto;width:100%;will-change:transform}
.pill-grad .pill-fx .b1{ /* deep red into ember: the body of the smear */
  background:radial-gradient(72% 30% at 100% 66%,var(--p-ember) 0%,rgba(255,90,31,.55) 40%,transparent 74%),
             radial-gradient(84% 24% at 98% 84%,var(--p-deep) 0%,rgba(179,32,15,.5) 46%,transparent 80%);
  animation:pDriftA 11s ease-in-out infinite alternate}
.pill-grad .pill-fx .b2{ /* amber streaks */
  background:radial-gradient(58% 12% at 100% 38%,var(--p-amber) 0%,rgba(255,178,62,.4) 42%,transparent 76%),
             radial-gradient(66% 11% at 100% 74%,rgba(255,138,61,.8) 0%,transparent 76%);
  animation:pDriftB 8.5s ease-in-out infinite alternate}
.pill-grad .pill-fx .b3{ /* warm white core bleeding off the top right */
  width:50%;background:radial-gradient(50% 30% at 100% 30%,var(--p-hot) 0%,rgba(255,226,170,.7) 24%,rgba(255,178,62,.3) 52%,transparent 76%);
  animation:pDriftC 9.5s ease-in-out infinite alternate}
.pill-grad .pill-fx .b4{ /* one long thin streak that sells the smear */
  background:radial-gradient(80% 6% at 100% 54%,rgba(255,178,62,.9) 0%,rgba(255,90,31,.35) 50%,transparent 80%);
  animation:pDriftB 12s ease-in-out -4s infinite alternate-reverse}
@keyframes pDriftA{0%{transform:translate3d(0,0,0) scaleX(1)}100%{transform:translate3d(-7%,3%,0) scaleX(1.12)}}
@keyframes pDriftB{0%{transform:translate3d(-3%,2%,0) scaleX(1.05)}100%{transform:translate3d(2%,-4%,0) scaleX(.94)}}
@keyframes pDriftC{0%{transform:translate3d(0,0,0);opacity:.85}100%{transform:translate3d(-5%,6%,0);opacity:1}}
.pill-grad .pill-grain{opacity:.6;-webkit-mask-image:linear-gradient(90deg,rgba(0,0,0,.25) 0%,#000 60%);mask-image:linear-gradient(90deg,rgba(0,0,0,.25) 0%,#000 60%)}

/* ---- added layers ---- */
.pill-surge,.pill-flood{position:absolute;inset:0;pointer-events:none}
.pill-surge{z-index:1;opacity:0;transition:opacity .45s ease;
  background:radial-gradient(62% 130% at 100% 50%,rgba(255,178,62,.5),rgba(255,90,31,.22) 46%,transparent 76%)}
.pill-flood{z-index:1;transform:scaleX(0);transform-origin:100% 50%;
  background:radial-gradient(40% 120% at 96% 20%,rgba(255,240,214,.8),transparent 70%),
             linear-gradient(90deg,var(--p-deep,#b3200f) 0%,var(--p-ember,#ff5a1f) 42%,var(--p-amber,#ffb23e) 88%,#ffd38a 100%)}
.pill-txt{transition:transform 160ms var(--ease-leave),opacity 120ms linear}
.pill-done{position:absolute;z-index:3;left:26px;right:56px;top:0;bottom:0;display:flex;flex-direction:column;justify-content:center;opacity:0;transform:translateY(50%);pointer-events:none}
.pill-done b{display:block;font-family:var(--display);font-size:27px;line-height:1;text-transform:uppercase;letter-spacing:.01em;white-space:nowrap;color:#1a0b03}
.pill-done small{display:block;font-size:12px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:rgba(26,11,3,.72);margin-top:5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pill-sm .pill-done{left:22px;right:44px}
.pill-sm .pill-done b{font-size:24px}
.pill .pill-ic{transition:transform var(--sp-pop-d) var(--sp-pop),opacity .15s}
.pill-ok{position:absolute;z-index:3;right:20px;top:50%;margin-top:-12px;font-size:24px;color:#1a0b03;opacity:0;transform:scale(.3) rotate(-30deg);max-width:none}
.pill-sm .pill-ok{right:18px}
.pill-spin{position:absolute;z-index:3;right:22px;top:50%;width:22px;height:22px;margin-top:-11px;border-radius:50%;border:2.5px solid rgba(255,247,234,.25);border-top-color:#fff7ea;opacity:0}

/* ---- press: 120ms in, spring out with about 15% overshoot ---- */
.pill-grad.tap.is-pressed{transform:scale(.96);transition:transform 120ms cubic-bezier(.3,.7,.4,1)}
.pill-grad.is-pressed .pill-fx{transform:scaleX(1.34);transition:transform 160ms cubic-bezier(.3,.7,.4,1)}
.pill-grad.is-pressed .pill-surge{opacity:1;transition:opacity 90ms linear}
/* legacy flare(): same surge, played once */
.pill-grad.flare .pill-fx{animation:pFlare 700ms var(--sp-press) both}
.pill-grad.flare .pill-fx .b3{filter:none}
@keyframes pFlare{0%{transform:scaleX(1)}25%{transform:translate3d(-12%,0,0) scaleX(1.7)}100%{transform:none}}

/* ---- success: flood 280ms, label swap 90ms in, hold, then settle ---- */
.pill.is-success .pill-flood{transform:scaleX(1);transition:transform 280ms cubic-bezier(.2,.8,.2,1)}
.pill.is-success .pill-txt{opacity:0;transform:translateY(-60%)}
.pill.is-success .pill-done{opacity:1;transform:none;transition:transform var(--sp-pop-d) var(--sp-pop) 90ms,opacity 120ms linear 90ms}
.pill.is-success .pill-ic{opacity:0;transform:scale(.3) rotate(30deg)}
.pill.is-success .pill-ok{opacity:1;transform:none;transition:transform var(--sp-pop-d) var(--sp-pop) 120ms,opacity 100ms linear 120ms}
.pill.is-settled .pill-flood{opacity:0;transition:opacity .5s ease}
.pill.is-settled .pill-done b{color:var(--ink);transition:color .4s}
.pill.is-settled .pill-done small{color:#8d867b;transition:color .4s}
.pill.is-settled .pill-ok{color:#fff6ea;transition:color .4s}

/* ---- loading ---- */
.pill.is-loading{pointer-events:none}
.pill.is-loading .pill-txt{opacity:.55}
.pill.is-loading .pill-ic{opacity:0}
.pill.is-loading .pill-spin{opacity:1;animation:pSpin .7s linear infinite}
@keyframes pSpin{to{transform:rotate(360deg)}}

/* ---- disabled ---- */
.pill-grad:disabled,.pill-grad.tap[disabled]{opacity:1;pointer-events:none;background:#161512;box-shadow:inset 0 0 0 1px var(--line)}
.pill-grad:disabled .pill-fx{opacity:.14}
.pill-grad:disabled .pill-grain{opacity:.2}
.pill-grad:disabled .pill-txt b{color:var(--mute)}
.pill-grad:disabled .pill-txt small{color:var(--dim,#4d4943)}
.pill-grad:disabled .pill-ic{color:var(--mute)}

/* ---- FX overlay for ring + shards (created by cta-pill.js) ---- */
.r209-fx{position:fixed;inset:0;pointer-events:none;z-index:90;overflow:hidden;contain:strict}
.r209-ring{position:absolute;border-radius:999px;box-shadow:0 0 0 2px var(--ember),0 0 22px 2px rgba(255,90,31,.45);will-change:transform,opacity}
.r209-shard{position:absolute;left:0;top:0;width:var(--w);height:var(--h);margin:calc(var(--h) / -2) 0 0 calc(var(--w) / -2);
  background:var(--c);clip-path:polygon(0 50%,100% 0,78% 50%,100% 100%);will-change:transform,opacity}

@media (prefers-reduced-motion: reduce){
  .pill,.pill-grad,.pill-grad .pill-fx,.pill .pill-ic,.pill-txt{transition:none!important}
  .pill-grad .pill-fx i{animation:none!important}
  .pill-grad.tap.is-pressed{transform:none}
  .pill-grad.is-pressed .pill-fx{transform:none}
  .pill-grad.is-pressed .pill-surge{opacity:1}
  .pill-grad.flare .pill-fx{animation:none}
  .pill-flood{transform:none;opacity:0}
  .pill.is-success .pill-flood{opacity:1;transition:none}
  .pill.is-settled .pill-flood{opacity:0}
  .pill-done,.pill.is-success .pill-done,.pill-ok,.pill.is-success .pill-ok{transform:none!important;transition:none!important}
  .pill.is-loading .pill-spin{animation:none;border-color:#fff7ea}
}
/* ===== goal-bar.css ===== */
/* Road to 209 · goal-bar.css (ui-designer)
   Hazard-stripe progress to goal (TI-0043 tee band). Built by setGoalBar() in goal-bar.js.
   Motion is transform only: the fill is a full-width strip translated left, its stripes are
   counter-translated so they never slide, and the marker wrapper rides the same transform.
   The stripe march is a translateX loop of exactly one tile (14px), so it is seamless. */

.gbar{--gb-tile:14px;--gb-rail:14px;position:relative;margin-top:18px;padding-top:30px;overflow:hidden;font-variant-numeric:tabular-nums}
.gbar-top{position:absolute;left:0;right:0;top:0;display:flex;align-items:baseline;justify-content:space-between;gap:10px;height:16px}
.gbar-k{font-size:12px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--mute)}
.gbar-k b{font-family:var(--display);font-weight:700;font-size:15px;letter-spacing:.04em;color:var(--ink-2)}
.gbar-rail{position:relative;height:var(--gb-rail);margin:22px 0 0;border-radius:2px}
/* unfilled: faint dashed centre line inside a hairline box */
.gbar-track{position:absolute;inset:0;border-radius:2px;box-shadow:inset 0 0 0 1px rgba(243,239,230,.07);
  background:repeating-linear-gradient(90deg,rgba(243,239,230,.18) 0 6px,transparent 6px 11px) 0 50%/100% 2px no-repeat,rgba(243,239,230,.025)}
/* filled: hazard stripes, ember on warm black, hard edges */
.gbar-fill{position:absolute;inset:0;overflow:hidden;border-radius:2px;transform:translate3d(-100%,0,0);will-change:transform}
.gbar-counter{position:absolute;inset:0;transform:translate3d(100%,0,0);will-change:transform}
.gbar-hz{position:absolute;top:0;bottom:0;left:calc(var(--gb-tile) * -1);right:0;
  background:linear-gradient(135deg,var(--ember) 0 30%,#0c0c0b 30% 50%,var(--ember) 50% 80%,#0c0c0b 80% 100%) 0 0/var(--gb-tile) var(--gb-tile);
  animation:gbMarch 1.6s linear infinite}
.gbar-hz::after{content:"";position:absolute;inset:0;background:url("grain.b69f3db876.png") 0 0/80px 80px;mix-blend-mode:overlay;opacity:.5}
@keyframes gbMarch{to{transform:translate3d(var(--gb-tile),0,0)}}
/* milestone notches: a cut through the rail plus a tick below it */
.gbar-tick{position:absolute;top:-3px;bottom:-7px;width:2px;margin-left:-1px;background:linear-gradient(var(--card) 0 calc(100% - 7px),var(--line-2) calc(100% - 7px));z-index:2}
.gbar-tick.hit{background:linear-gradient(var(--card) 0 calc(100% - 7px),var(--ember-2) calc(100% - 7px))}
/* goal flag: pole through the rail, slanted orange pennant above (tee block) */
.gbar-flag{position:absolute;right:0;bottom:-7px;width:2px;height:calc(var(--gb-rail) + 7px + 30px);background:var(--ink);z-index:3}
.gbar-pennant{position:absolute;right:0;top:0;height:20px;padding:0 9px 0 11px;display:flex;align-items:center;gap:4px;white-space:nowrap;
  background:var(--ember);color:#1a0b03;clip-path:polygon(7px 0,100% 0,100% 100%,0 100%);
  font-family:var(--display);font-size:16px;line-height:1;letter-spacing:.03em}
.gbar-pennant small{font-family:var(--body);font-weight:700;font-size:12px;letter-spacing:.12em;opacity:.8}
/* current marker: rides the fill edge */
.gbar-mk{position:absolute;inset:0;transform:translate3d(0,0,0);will-change:transform;z-index:4;pointer-events:none}
.gbar-edge{position:absolute;left:-1px;top:-3px;bottom:-3px;width:2px;background:#fff0d6;box-shadow:0 0 10px 1px rgba(255,138,61,.75)}
.gbar-notch{position:absolute;left:-6px;top:-11px;width:12px;height:7px;background:var(--ember);clip-path:polygon(0 0,100% 0,50% 100%)}
.gbar-val{position:absolute;bottom:calc(100% + 11px);left:0;transform:translateX(var(--vx,-50%));white-space:nowrap;
  font-family:var(--display);font-size:20px;line-height:1;color:var(--ink);letter-spacing:.01em}
.gbar-val small{font-family:var(--body);font-size:12px;font-weight:600;color:var(--mute);margin-left:2px;letter-spacing:.06em}
/* labels under the rail */
.gbar-lbls{position:relative;height:18px;margin-top:11px}
.gbar-lbls span{position:absolute;top:0;transform:translateX(-50%);font-family:var(--display);font-size:15px;line-height:1;color:var(--mute);white-space:nowrap;transition:color .3s,opacity .2s}
.gbar-lbls span.first{transform:none}
.gbar-lbls span.end{transform:translateX(calc(-100% + 2px))}
.gbar-lbls span.hit{color:var(--ember-2)}
.gbar-lbls span.hide{opacity:0}

/* timing: spring in on first view and on every change */
.gbar.is-live .gbar-fill,.gbar.is-live .gbar-counter,.gbar.is-live .gbar-mk{transition:transform var(--sp-rise-d) var(--sp-rise)}
.gbar.is-live .gbar-fill{transition-delay:60ms}.gbar.is-live .gbar-counter{transition-delay:60ms}.gbar.is-live .gbar-mk{transition-delay:60ms}
/* delight hook: hazard band flashes white once (used by the DONE moment and milestone unlock) */
.gbar.flash .gbar-hz{animation:gbMarch 1.6s linear infinite,gbFlash 520ms ease-out}
@keyframes gbFlash{0%,30%{filter:brightness(2.4) saturate(.2)}100%{filter:none}}

@media (prefers-reduced-motion: reduce){
  .gbar-hz{animation:none!important}
  .gbar.is-live .gbar-fill,.gbar.is-live .gbar-counter,.gbar.is-live .gbar-mk{transition:none!important}
}
/* ===== streak-barcode.css ===== */
/* Road to 209 · streak-barcode.css (ui-designer)
   30-day streak strip as a barcode (TI-0043). Built by renderStreak() in streak-barcode.js.
   done = warm white bar · rest = dim ember · miss = hairline outline · today = ember, taller, glowing.
   Bars rise in with a staggered spring (16 ms apart). Tap or scrub to read a day. */

.bc{position:relative;margin-top:14px;user-select:none;-webkit-user-select:none}
.bc-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:10px}
.bc-k{font-size:12px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--mute);line-height:1}
.bc-day{font-family:var(--display);font-size:30px;line-height:.8;color:var(--ink);letter-spacing:.01em;font-variant-numeric:tabular-nums}
.bc-day small{font-family:var(--body);font-size:12px;font-weight:600;letter-spacing:.16em;color:var(--ember-2);margin-right:6px;vertical-align:4px}
.bc-bars{position:relative;display:flex;align-items:flex-end;height:52px;padding-top:8px;touch-action:none;cursor:pointer}
.bc-guard{flex:0 0 7px;height:100%;display:flex;align-items:flex-end;justify-content:center}
.bc-guard::after{content:"";width:2px;height:112%;background:var(--mute);opacity:.7}
.bc-bar{flex:1 1 0;height:100%;min-width:0;display:flex;align-items:flex-end;justify-content:center;padding:0;background:none;border:0;position:relative}
.bc-bar span{display:block;width:var(--w,3px);height:calc(var(--h,1) * 100%);background:var(--ink);transform-origin:50% 100%;
  transform:scaleY(0);transition:transform 120ms var(--ease-leave)}
.bc.in .bc-bar span{transform:none;transition:transform var(--sp-rise-d) var(--sp-rise) calc(var(--k,0) * 16ms)}
.bc-bar.is-rest span{background:#7a3a18}
.bc-bar.is-miss span{background:transparent;box-shadow:inset 0 0 0 1px rgba(243,239,230,.2)}
.bc-bar.is-pre span{background:transparent;box-shadow:inset 0 0 0 1px rgba(243,239,230,.08)}
.bc-bar.is-today span{background:var(--ember);height:calc(100% + 8px);width:max(var(--w,3px),5px)}
.bc-bar.is-today::after{content:"";position:absolute;left:50%;bottom:0;width:22px;height:calc(100% + 16px);margin-left:-11px;pointer-events:none;
  background:radial-gradient(50% 50% at 50% 60%,rgba(255,90,31,.55),transparent 70%);opacity:.35;animation:bcGlow 1.8s ease-in-out infinite alternate}
@keyframes bcGlow{to{opacity:1}}
.bc-bar.sel span{background:var(--ember-2)}
.bc-bar.sel.is-miss span{background:rgba(243,239,230,.25)}
/* number row like the digits under a barcode */
.bc-foot{display:flex;justify-content:space-between;margin-top:7px;font-size:12px;font-weight:600;letter-spacing:.2em;color:var(--mute);font-variant-numeric:tabular-nums;line-height:1}
.bc-foot b{color:var(--ink-2);font-weight:600}
/* tooltip */
.bc-tip{position:absolute;top:-6px;left:0;transform:translate(var(--tx,0px),-100%) scale(.9);opacity:0;pointer-events:none;white-space:nowrap;
  padding:7px 10px 6px;border-radius:9px;background:#24201b;border:1px solid var(--line-2);box-shadow:0 10px 24px -8px rgba(6,6,5,.8);
  font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--ink);transform-origin:50% 100%;
  transition:opacity 120ms linear,transform 140ms var(--ease-leave)}
.bc-tip.on{opacity:1;transform:translate(var(--tx,0px),-100%);transition:opacity 90ms linear,transform var(--sp-snap-d) var(--sp-snap)}
.bc-tip i{font-style:normal;color:var(--ember-2)}

@media (prefers-reduced-motion: reduce){
  .bc-bar span,.bc.in .bc-bar span{transform:none!important;transition:none!important}
  .bc-bar.is-today::after{animation:none;opacity:.8}
  .bc-tip,.bc-tip.on{transition:opacity 1ms!important;transform:translate(var(--tx,0px),-100%)!important}
}
/* ===== delights.css ===== */
/* Road to 209 · delights.css (ui-designer)
   One delight per screen, each under 900 ms:
   Today    weight logged: Log pill floods to LOGGED + shards (cta-pill) while the odometer rolls
   Workout  all sets done: DONE block slams in, ember/white shards, chevron bar sweeps white
   History  milestone unlocked: plate flips on a spring, hot amber flash, grain, one sweep of light
   Keypad   digits tick in on a spring, Save is the pill
   Targets v2 markup (.odo/.odo-d/.odo-s, .stamp > span + small, #kpV). Needs springs.css tokens. */

/* ---------- odometer (v2 .odo markup, re-timed) ---------- */
.odo-d{-webkit-mask-image:linear-gradient(transparent 0,#000 8%,#000 92%,transparent 100%);mask-image:linear-gradient(transparent 0,#000 8%,#000 92%,transparent 100%)}
/* low-overshoot spring: overshoot scales with travel, and a 0 to 9 roll on the pop spring ran past the strip (blank digit) */
.odo-s{transition:transform var(--odo-d,780ms) var(--sp-sheet) var(--odo-delay,0ms);will-change:transform}
.odo.is-still .odo-s{transition:none}

/* ---------- DONE stamp: tee orange block + hazard strip (v2 markup: .stamp > span + small) ---------- */
.card .stamp{border:0;border-radius:2px;background:var(--ember);color:#1a0b03;padding:7px 12px 0;overflow:hidden;transform:rotate(-7deg);
  box-shadow:0 12px 26px -10px rgba(255,90,31,.65)}
.card .stamp span{font-size:30px;letter-spacing:.1em;color:#1a0b03}
.card .stamp small{border-top:1.5px solid rgba(26,11,3,.55);color:#1a0b03;font-size:10px;letter-spacing:.24em}
.card .stamp::before{content:"";position:absolute;inset:0;background:url("grain.b69f3db876.png") 0 0/72px 72px;mix-blend-mode:overlay;opacity:.6;pointer-events:none}
.card .stamp::after{content:"";display:block;height:7px;margin:6px -12px 0;
  background:linear-gradient(135deg,#1a0b03 0 30%,transparent 30% 50%,#1a0b03 50% 80%,transparent 80%) 0 0/10px 10px}
.card.complete .stamp{animation:r209Slam 560ms cubic-bezier(.2,.9,.25,1) both}
@keyframes r209Slam{
  0%{opacity:0;transform:translate(10px,-8px) rotate(-26deg) scale(2.5)}
  34%{opacity:1;transform:rotate(-5deg) scale(.9)}
  58%{transform:rotate(-8deg) scale(1.04)}
  100%{opacity:1;transform:rotate(-7deg) scale(1)}}
.card.complete.is-moment{animation:r209Impact 380ms cubic-bezier(.3,1.6,.5,1) 180ms both}
@keyframes r209Impact{0%{transform:none}25%{transform:translateY(4px) scale(.995)}100%{transform:none}}
.card.complete.is-moment::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:radial-gradient(70% 60% at 85% 8%,rgba(255,138,61,.32),transparent 70%);animation:r209Glow 800ms ease-out 180ms both}
@keyframes r209Glow{0%{opacity:0}20%{opacity:1}100%{opacity:0}}
.card.complete.is-moment .stamp::after{animation:r209Hazard 520ms steps(1) 180ms both}
@keyframes r209Hazard{0%{background-color:#fff0d6}18%{background-color:transparent}36%{background-color:#fff0d6}54%,100%{background-color:transparent}}
/* re-renders of an already complete card do not replay the slam */
.card.complete.is-static .stamp{animation:none}

/* ---------- milestone plates (History) ---------- */
.plates{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-top:12px;perspective:700px}
.plate{position:relative;height:84px;border-radius:4px;background:var(--card-2);border:1px solid var(--line);padding:12px 10px 10px;
  display:flex;flex-direction:column;justify-content:space-between;overflow:hidden;transform-style:preserve-3d;backface-visibility:hidden}
.plate b{font-family:var(--display);font-size:32px;line-height:.9;color:#7a7469;letter-spacing:.01em}
.plate small{font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--mute)}
.plate.next{border:1px dashed #6b3a1c}
.plate.next b{color:var(--ink-2)}
.plate.hit{background:var(--ember);border-color:var(--ember)}
.plate.hit b,.plate.hit small{color:#1a0b03}
.plate.goal b{color:var(--ink)}
.plate .p-grain{position:absolute;inset:0;pointer-events:none;opacity:0;background:url("grain.b69f3db876.png") 0 0/72px 72px;mix-blend-mode:overlay}
.plate.hit .p-grain{opacity:.6}
.plate .p-flash{position:absolute;inset:0;pointer-events:none;opacity:0;background:radial-gradient(90% 80% at 50% 40%,#fff0d6 0%,#ffb23e 50%,rgba(255,178,62,0) 100%)}
.plate .p-sweep{position:absolute;top:-10%;bottom:-10%;left:0;width:45%;pointer-events:none;opacity:0;transform:translateX(-120%) skewX(-18deg);
  background:linear-gradient(100deg,transparent,rgba(255,244,222,.75),transparent)}
.plate.unlocking .p-flash{animation:r209Flash 620ms ease-out both}
.plate.unlocking .p-sweep{animation:r209Sweep 640ms cubic-bezier(.3,0,.2,1) 160ms both}
@keyframes r209Flash{0%{opacity:.95}100%{opacity:0}}
@keyframes r209Sweep{0%{opacity:1;transform:translateX(-120%) skewX(-18deg)}100%{opacity:1;transform:translateX(260%) skewX(-18deg)}}

/* ---------- keypad digits (v2 #kpV) ---------- */
.kp-disp .big{display:inline-flex}
.kp-ch{display:inline-block;transform-origin:50% 90%}
.kp-ch.in{animation:r209Tick var(--sp-pop-d) var(--sp-pop) both}
@keyframes r209Tick{from{opacity:0;transform:translateY(.28em) scale(.8)}to{opacity:1;transform:none}}
.kp-ch.out{animation:r209TickOut 120ms var(--ease-leave) both}
@keyframes r209TickOut{to{opacity:0;transform:translateY(-.18em) scale(.9)}}
#kpV.num-bump{animation:none}   /* the old bump is replaced by the per digit tick */

@media (prefers-reduced-motion: reduce){
  .odo-s{transition:none!important}
  .card.complete .stamp,.card.complete.is-moment,.card.complete.is-moment::after,.card.complete.is-moment .stamp::after{animation:none!important}
  .plate.unlocking .p-flash{animation:r209Flash 1ms both}
  .plate.unlocking .p-sweep{animation:none}
  .kp-ch.in,.kp-ch.out{animation:none!important}
}
/* ===== fixes.css (F1 dropped: styles.css ships --mute #908a7e) ===== */
/* Road to 209 · fixes.css (ui-designer)
   Drop-in overrides for the open items in README.md "Fix list". Written against v2 styles.css
   (22:54 build, rechecked against 23:23). Each block names the bug it fixes. Safe to paste at the end of styles.css. */

/* F1 contrast: --mute was 4.16:1 on --card for small caps text. #8d867b is 5.2:1 */

/* F1b --dim (#4d4943, about 2.1:1) is fine for separators but was carrying text: future milestone
   numbers on the chart (18px, below the large text size). Use --mute (5.2:1 on card) */
.msr-t:not(.start):not(.goal):not(.next):not(.touch):not(.hit) b{color:var(--mute)}

/* F2 nothing under 12px. Caps labels keep their tracking, a touch tighter where rows are long */
.label,.rule,.tb-date,.mo-k,.t-undo,.rest .early,.lg-m .sub,.delta,.rest .rl span,.stat .v small,.mv-dose .num small{font-size:12px}
.k,.field label,.m-lbl,.cal .dow,.mv-dose .sub,.logged small,.lg-d .sub,.spec-r span,.demo-flag,.legend{font-size:12px;letter-spacing:.12em}
.wd{font-size:12px}
/* added in the 23:23 build at 10.5 to 11px: */
.today-chip,.inp.tm .tl,.wn-s-r .k{font-size:12px}
.mark-k{font-size:12px;letter-spacing:.22em}
.wk-meta{font-size:12px;letter-spacing:.1em;line-height:1.45}
.pill-txt small,.pill-sm .pill-txt small{font-size:12px;letter-spacing:.14em}
.stamp small{font-size:12px;letter-spacing:.2em}
.msr-t em{font-size:12px;letter-spacing:.06em}
.c-txt{font-size:11.5px}   /* SVG units: the chart renders at about 1.06x, so this lands at 12.2px */

/* F3 tap targets: everything tappable is at least 44px */
.iconbtn,.schip{height:44px}
.iconbtn{width:44px}
.cal-head .iconbtn,.rest .iconbtn{width:44px;height:44px}
.rest .early,.mini{height:44px}
.textbtn{padding:15px 12px}
.t-undo{position:relative}
.t-undo::after{content:"";position:absolute;inset:-7px -4px}   /* 30px chip, 44px hit area */

/* F4 DONE stamp sat on top of the meta line ("NO EQUIPMENT"). Keep text clear of it */
.card.complete .wk-title,.card.complete .wk-meta{padding-right:118px}
.card .stamp{top:34px}

/* F5 a toast fired just before a sheet opened floated above the sheet (z 60 over 41) */
.scrim.on ~ .toast{z-index:39}

/* F6 the weigh-in rule animated width and left (layout every frame). Same look, transform only.
   JS: replace the two style writes in renderStrip() with
   $('#ruleFill').parentNode.style.setProperty('--p', pct);   (pct is 0 to 1) */
.rule-line{container-type:inline-size}
.rule-fill{width:100%!important;transform-origin:0 50%;transform:scaleX(var(--p,0));transition:transform var(--sp-fill-d,1100ms) var(--sp-fill,cubic-bezier(.22,1,.36,1))}
.rule-line b{left:0!important;transform:translateX(calc(var(--p,0) * 100cqw - 50%)) rotate(45deg);transition:transform var(--sp-fill-d,1100ms) var(--sp-fill,cubic-bezier(.22,1,.36,1))}

/* F7 the chart halo pulses forever next to two drifting pills and the hazard march.
   Three pulses after the line draws, then it rests */
.c-halo{animation-iteration-count:3}

/* F8 icon fallback guard (v2 already clips .ms to 1.2em). Keep the glyph box square so a
   missing ligature can never print a word, and never inherit caps or tracking */
.ms{text-transform:none!important;letter-spacing:0!important}

@media (prefers-reduced-motion: reduce){
  .rule-fill,.rule-line b{transition:none}
}

/* ===== app-level glue for the merged UI layer ===== */
/* N: the floating rest or hold timer plus the tab bar never cover the Start or Continue pill */
body.resting #app{padding-bottom:calc(var(--sab) + var(--tabh) + 240px)}
/* keep v2 water barcode on the same rise spring as the streak barcode */
.water .barcode b{animation:bar var(--sp-rise-d) var(--sp-rise) both;animation-delay:calc(var(--i) * 16ms)}
/* goal bar sits where the v2 hazard band was */
#cProof .gbar{margin-top:6px}
.scrim.on ~ .toast.err{z-index:60}
.scrim.on ~ .toast.lift{z-index:60}  /* an Undo stays reachable when a sheet is reopened behind it (favorites list) */  /* errors (storage full, bad import) stay readable over a sheet */
.move .mv-name{display:table}   /* strike line hugs the move name instead of the whole row */
/* N5 no scroll bleed behind sheets */
.sheet{overscroll-behavior:contain}
body.scroll-lock{position:fixed;left:0;right:0;overflow:hidden}
body.on-history .rest{transform:translate(-50%,calc(100% + var(--tabh) + 40px))!important;pointer-events:none}  /* m8 hold pill stays on Today */
/* m14 leftovers: 44pt everywhere a finger lands */
.seg{height:54px}
.grab{height:44px}
.inp.tm{height:56px}
.inp.tm input{height:44px;min-height:44px}
/* m15 leftover: History rule labels at 12px need more than --mute on the hero card */
.msr-t em,.msr-t b{color:var(--ink-2)}
.msr-t.hit em{color:var(--ember-2)}
/* water: last night row and Keep button */
.wn-last{display:flex;align-items:center;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.wn-last span{flex:1;font-size:13px;color:var(--ink-2)}
.wn-last b{font-weight:600;color:var(--ink)}
.wn-keep{display:block;margin:6px auto 0}
/* keypad delete */
.kp-del{display:flex;align-items:center;justify-content:center;gap:6px;margin:8px auto 0;color:var(--ink-2)}
.kp-del .ms{font-size:18px}
.kp-del.arm{color:#ff8a5c}
.rule.plain{margin:14px 0 4px}
.msr .msr-t:not(.start):not(.goal):not(.next):not(.touch):not(.hit) b{color:#a9a294}

/* odometer: wheels sized to their landing digit, digits centred in the wheel */
.odo-s span{display:flex;justify-content:center;text-align:center}
.odo-d{flex:none;transition:width var(--odo-d,780ms) var(--sp-sheet) var(--odo-delay,0ms)}
.odo.is-still .odo-d{transition:none}
/* m15: hit milestone label on the History rule, 5.1:1 on #302d28 (was ember #ff5a1f at 4.4:1) */
.msr .msr-t.hit b{color:#ff7340}

/* ---- sheet navigation: Back top left on every sheet that can be left, Close on nested ones ---- */
.sh-nav{position:sticky;top:32px;z-index:3;display:flex;align-items:center;justify-content:space-between;min-height:48px;margin:0 -10px;padding:2px 0;background:#121110}
.sheet.full .sh-nav{top:0;background:var(--bg)}
.sh-back{display:inline-flex;align-items:center;gap:2px;min-height:44px;min-width:44px;padding:0 14px 0 4px;border-radius:999px;font-size:15px;font-weight:600;color:var(--ink);letter-spacing:.01em}
.sh-back .ms{font-size:26px;margin-right:-1px}
.sh-x{display:grid;place-items:center;width:44px;height:44px;border-radius:999px;color:var(--ink-2)}
.sh-x .ms{font-size:22px}
.sh-back.is-pressed,.sh-x.is-pressed{background:var(--card-2)}
.sh-back:focus-visible,.sh-x:focus-visible,.sh-cbtns button:focus-visible{outline:2px solid var(--ember);outline-offset:2px}
.sh-confirm{position:sticky;top:82px;z-index:3;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px;margin:4px 0 10px;padding:10px 10px 10px 14px;border-radius:18px;background:#1c1a17;border:1px solid var(--line-2);font-size:14px;font-weight:600;animation:shCf var(--sp-pop-d,420ms) var(--sp-pop,cubic-bezier(.34,1.56,.64,1)) both}
.sheet.full .sh-confirm{top:50px}
.sh-confirm[hidden]{display:none}
.sh-cbtns{display:flex;gap:8px;margin-left:auto}
.sh-cbtns button{min-height:44px;padding:0 16px;border-radius:999px;font-size:13px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;background:var(--card-2);border:1px solid var(--line-2);color:var(--ink)}
.sh-cbtns button.on{background:var(--ink);color:#0c0c0b;border-color:var(--ink)}
@keyframes shCf{from{opacity:0;transform:translateY(-6px) scale(.98)}}
@media (prefers-reduced-motion: reduce){.sh-confirm{animation:none}}

/* ---- iPhone Safari: WebKit does not clip composited (animated) children to a parent's rounded corners,
   so the ember layers squared off the pill's right end. Each layer clips itself to the pill shape. ---- */
.pill > .pill-fx,.pill > .pill-grain,.pill > .pill-surge,.pill > .pill-flood{clip-path:inset(0 round 999px);-webkit-clip-path:inset(0 round 999px)}
.pill{-webkit-transform:translateZ(0)}
