/* Inter, self-hosted rather than pulled from Google Fonts. Two reasons: the
   app promises nobody sees your training, and a Google Fonts link would hand
   Google a request from every device on every launch; and a basement gym has
   no signal, so a font that lives on a CDN is a font that doesn't render.
   These files are cached by the service worker with the rest of the shell.

   One variable file covers 100-900, which is what lets the weight-650 rules
   below mean 650 instead of rounding to 700.

   'swap' so text is readable immediately in the fallback and reflows when the
   font lands — never invisible. The unicode-range split means latin-ext is
   only fetched if a page actually uses those characters. */
@font-face{
  font-family:'Inter';
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url('fonts/inter-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Inter';
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url('fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* Dark is the default; :root[data-theme="light"] swaps the palette.
   Every colour in this file goes through a token — no raw hex in the rules
   below — so the two themes can't drift apart. */
:root{
  color-scheme:dark;

  /* A plum ramp built out from the brand purple #311432 rather than a neutral
     black. The brand colour sits at surface-2 — the raised card — because at
     14% lightness it is a background, not a fill: on near-black it measured
     1.19:1, which is invisible. Everything above it in the stack is the same
     hue lifted, so the theme reads as one colour rather than purple on grey. */
  --bg:#1A0A1B;                /* deep plum, the floor */
  --surface:#251126;
  --surface-2:#311432;         /* the brand purple, exactly */
  --line:#4A2249;
  --text:#ffffff;
  --muted:#BCA6BD;

  /* Lifted well clear of the surfaces so a button still reads as a button. */
  --accent:#9C4A9E;
  --accent-ink:#ffffff;        /* text sitting on --accent */
  --accent-fg:#E3ABE5;         /* plum as text, readable on --bg */

  --good:#3ddc84;
  --good-ink:#04240f;
  --warn:#ffb020;
  --danger:#ff6b6b;
  --danger-ink:#2b0708;      /* white on this red is only 2.8:1 */
  --tip-ink:#ffffff;

  --chip-ink:#1A0A1B;
  --scrim:rgba(0,0,0,.6);
  --tour-scrim:rgba(8,3,9,.74);   /* deeper than a sheet backdrop: it has to
                                     read as "look here", not "something is
                                     open in front" */
  --tabbar-bg:rgba(37,17,38,.93);
  --day-active:#3D1B3E;

  --done-bg:#0f2418;
  --done-line:#1f5137;

  --pill-lift-line:#5A2A5C;
  --pill-cardio-line:#4a3a1a;

  --warnbox-bg:#241d0e;
  --warnbox-line:#4a3a1a;
  --warnbox-text:#f0d9a8;

  --shadow:0 8px 28px rgba(0,0,0,.5);

  --radius:14px;
  --tabbar-h:64px;

  /* Typography is a token like everything else, so the stack is stated once.
     The system fonts stay behind Inter as the fallback that renders during the
     swap and if the file ever fails to load — not as a device-dependent
     choice. */
  --font:'Inter',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
}

:root[data-theme="light"]{
  color-scheme:light;

  --bg:#f5f3ed;
  --surface:#ffffff;
  --surface-2:#eeebe2;
  --line:#dcd7c9;
  --text:#111111;
  --muted:#5f5a50;

  /* Light mode is where the brand purple works untouched: 14.8:1 against the
     cream, and white on it is 16.5:1. */
  --accent:#311432;
  --accent-ink:#ffffff;
  /* Purple-as-text is lifted off #311432, which at this size would be
     indistinguishable from the near-black body text. */
  --accent-fg:#6B2E6D;

  --good:#15803d;
  --good-ink:#ffffff;
  --warn:#b45309;
  --danger:#b91c1c;
  --danger-ink:#ffffff;
  --tip-ink:#ffffff;

  --chip-ink:#ffffff;
  --scrim:rgba(35,15,36,.38);
  --tour-scrim:rgba(35,15,36,.58);
  --tabbar-bg:rgba(255,255,255,.94);
  --day-active:#EFE2EF;        /* a plum tint so trained days still pop */

  --done-bg:#e8f5ec;
  --done-line:#8fc7a5;

  --pill-lift-line:#D5BDD6;
  --pill-cardio-line:#e2c9a3;

  --warnbox-bg:#fdf3e0;
  --warnbox-line:#e2c9a3;
  --warnbox-text:#5c4218;

  --shadow:0 8px 28px rgba(60,50,30,.16);
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--bg);
  color:var(--text);
  font:16px/1.45 var(--font);
  -webkit-font-smoothing:antialiased;
  padding-bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  overscroll-behavior-y:contain;
}
h1,h2,h3{margin:0;font-weight:650;letter-spacing:-0.01em}
button{font:inherit;color:inherit;cursor:pointer}

/* The hidden attribute must beat the layout rules below (.sheet uses flex). */
[hidden]{display:none !important}
input,select{font:inherit}

/* The brand mark, carried as an alpha silhouette and painted by CSS rather
   than baked into a coloured image. One file then serves purple-on-black,
   purple-on-cream and white-on-purple, and it follows the theme token instead
   of needing a copy per background. */
.mark{
  display:inline-block;
  background:var(--accent);
  -webkit-mask:url(logo-mark-v2.png) center/contain no-repeat;
  mask:url(logo-mark-v2.png) center/contain no-repeat;
}
.mark.on-accent{background:var(--accent-ink)}

/* ---------- top bar ---------- */
.topbar{
  position:sticky;top:0;z-index:20;
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:calc(12px + env(safe-area-inset-top)) 16px 12px;
  background:linear-gradient(var(--bg) 70%, transparent);
  backdrop-filter:blur(8px);
}
.topbar h1{font-size:26px}

/* ---------- layout ---------- */
#main{padding:0 16px 24px}
.view{display:block}
.stack{display:flex;flex-direction:column;gap:12px}
.row{display:flex;align-items:center;gap:10px}
.row.wrap{flex-wrap:wrap}
.spacer{flex:1}
.muted{color:var(--muted)}
.small{font-size:13px}
.center{text-align:center}

/* ---------- cards ---------- */
.card{
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius);
  padding:14px;
}
.card + .card{margin-top:12px}
.card-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:10px}
.card-title{font-size:17px;font-weight:650}
.card-sub{color:var(--muted);font-size:13px;margin-top:2px}

/* ---------- buttons ---------- */
.btn{
  border:1px solid transparent;
  background:var(--accent);color:var(--accent-ink);
  border-radius:12px;padding:12px 16px;font-weight:600;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
}
.btn:active{transform:translateY(1px)}
.btn.block{width:100%}
.btn.secondary{background:var(--surface-2);color:var(--text);border-color:var(--line)}
.btn.danger{background:transparent;color:var(--danger);border-color:var(--line)}
.ghost{background:transparent;border:1px solid var(--line);border-radius:10px;padding:8px 12px;color:var(--text)}
.ghost.small{padding:6px 10px;font-size:13px}
.linkish{background:none;border:0;color:var(--accent-fg);padding:4px;font-weight:600}
.icon-btn{background:transparent;border:0;color:var(--muted);font-size:20px;line-height:1;padding:6px 8px;border-radius:8px}
.icon-btn:hover{color:var(--text);background:var(--surface-2)}

/* ---------- empty state ---------- */
.empty{
  text-align:center;padding:44px 20px;color:var(--muted);
  border:1px dashed var(--line);border-radius:var(--radius);
}
.empty h3{color:var(--text);margin-bottom:6px}
.empty p{margin:0 0 16px}

/* ---------- exercise / set rows ---------- */
.ex{margin-top:0}
.ex-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.ex-name{font-weight:650}
.pill{
  font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  padding:3px 8px;border-radius:999px;border:1px solid var(--line);color:var(--muted);
}
.pill.cardio{color:var(--warn);border-color:var(--pill-cardio-line)}
.pill.lifting{color:var(--accent-fg);border-color:var(--pill-lift-line)}

.set-grid{display:grid;gap:8px}
.set-head,.set-row{display:grid;gap:8px;align-items:center}
.set-head{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.lifting .set-head,.lifting .set-row{grid-template-columns:34px 1fr 1fr 44px}
/* Cardio carries a fifth column for the hour box. It is narrower than the
   others because it holds one or two digits and nothing else, which keeps
   distance and minutes readable at 375px. */
.cardio  .set-head,.cardio  .set-row{grid-template-columns:34px 1fr .62fr .78fr 44px}
.practice .set-head,.practice .set-row{grid-template-columns:34px 1fr 1fr 44px}
.timed .set-head,.timed .set-row{grid-template-columns:34px 1fr 1fr 44px}
.set-n{color:var(--muted);text-align:center;font-variant-numeric:tabular-nums}

input.cell{
  width:100%;background:var(--surface-2);border:1px solid var(--line);
  border-radius:10px;padding:11px 10px;color:var(--text);text-align:center;
  font-variant-numeric:tabular-nums;
}
input.cell:focus{outline:2px solid var(--accent);outline-offset:-1px}
.set-row.done input.cell{border-color:var(--done-line);background:var(--done-bg)}

.check{
  width:44px;height:42px;border-radius:10px;border:1px solid var(--line);
  background:var(--surface-2);color:var(--muted);font-size:17px;
}
.check.on{background:var(--good);border-color:var(--good);color:var(--good-ink)}

/* ---------- forms ---------- */
label.field{display:block;margin-bottom:12px}
label.field span{display:block;font-size:13px;color:var(--muted);margin-bottom:6px}
input.text,select.text{
  width:100%;background:var(--surface-2);border:1px solid var(--line);
  border-radius:10px;padding:12px;color:var(--text);
}
input.text:focus,select.text:focus{outline:2px solid var(--accent);outline-offset:-1px}

/* ---------- tabbar ---------- */
.tabbar{
  position:fixed;left:0;right:0;bottom:0;z-index:30;
  height:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom:env(safe-area-inset-bottom);
  display:grid;grid-template-columns:repeat(5,1fr);
  background:var(--tabbar-bg);backdrop-filter:blur(12px);
  border-top:1px solid var(--line);
}
.tab{
  background:none;border:0;color:var(--muted);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  font-size:11px;font-weight:600;
}
.tab-ico{font-size:17px;line-height:1}
.tab.is-active{color:var(--accent-fg)}

/* ---------- rest bar ---------- */
.timer-bar{
  position:fixed;left:0;right:0;z-index:29;
  bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  background:var(--surface-2);border-top:1px solid var(--line);
}
.timer-progress{height:3px;background:var(--line)}
#timer-fill{height:100%;width:100%;background:var(--accent);transition:width .25s linear}
.timer-row{display:flex;align-items:center;gap:10px;padding:10px 16px}
.timer-label{color:var(--muted);font-size:13px;text-transform:uppercase;letter-spacing:.06em}
.timer-time{font-size:20px;font-weight:700;font-variant-numeric:tabular-nums;margin-right:auto}

/* ---------- sheet ---------- */
.sheet{position:fixed;inset:0;z-index:70;display:flex;align-items:flex-end}
body.sheet-open .tabbar,
body.sheet-open .timer-bar{visibility:hidden}
.sheet-backdrop{position:absolute;inset:0;background:var(--scrim)}
.sheet-panel{
  position:relative;width:100%;max-height:86vh;display:flex;flex-direction:column;
  background:var(--surface);border-radius:18px 18px 0 0;border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
  animation:rise .18s ease-out;
}
@keyframes rise{from{transform:translateY(14px);opacity:.6}to{transform:none;opacity:1}}
.sheet-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px}
.sheet-body{overflow:auto;padding:0 16px 20px}

.pick{
  display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  background:var(--surface-2);border:1px solid var(--line);border-radius:12px;
  padding:12px;margin-bottom:8px;
}
.pick:active{border-color:var(--accent)}
/* Ticked on a multi-select step. Border and tick rather than a filled
   background, so several selected rows don't turn the sheet into a block of
   colour. */
.pick.on{border-color:var(--accent-fg);box-shadow:inset 0 0 0 1px var(--accent-fg)}
.pick.on > span{color:var(--accent-fg);font-weight:700}
.grow{flex:1;min-width:0}
.pick .nm{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ---------- toast ---------- */
.toast{
  position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 18px);
  z-index:80;background:var(--surface-2);border:1px solid var(--line);
  padding:11px 16px;border-radius:999px;font-size:14px;box-shadow:var(--shadow);
}

@media (min-width:640px){
  #main,.topbar{max-width:640px;margin-inline:auto}
  .tabbar{max-width:640px;margin-inline:auto;border-radius:16px 16px 0 0}
  .timer-bar{max-width:640px;margin-inline:auto}
}

/* ---------- paste-in from notes ---------- */
textarea.text{
  width:100%;background:var(--surface-2);border:1px solid var(--line);
  border-radius:10px;padding:12px;color:var(--text);
  font:14px/1.5 var(--font);
  resize:vertical;min-height:180px;
}
textarea.text:focus{outline:2px solid var(--accent);outline-offset:-1px}

.warnbox{
  background:var(--warnbox-bg);border:1px solid var(--warnbox-line);color:var(--warnbox-text);
  border-radius:12px;padding:12px;margin-top:12px;font-size:14px;
}
.warnbox strong{display:block;margin-bottom:6px;color:var(--warn)}
.warnbox ul{margin:0;padding-left:18px}
.warnbox li{margin:2px 0;font-size:13px}
.warnbox p{margin:8px 0 0}

.sheet-body code{
  background:var(--surface-2);border:1px solid var(--line);border-radius:5px;
  padding:1px 5px;font-size:12.5px;
}

/* ---------- calendar ---------- */
.cal-head{display:flex;align-items:center;gap:6px;margin-bottom:10px}
.cal-title{flex:1;text-align:center;font-size:18px;font-weight:650}
.cal-head .icon-btn{font-size:26px;line-height:1;padding:2px 12px}

.seg{display:inline-flex;border:1px solid var(--line);border-radius:10px;overflow:hidden}
.seg button{background:transparent;border:0;padding:7px 18px;font-size:13px;font-weight:600;color:var(--muted)}
.seg button.on{background:var(--accent);color:var(--accent-ink)}

.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}
.cal-dow{
  font-size:11px;color:var(--muted);text-align:center;padding:2px 0 6px;
  text-transform:uppercase;letter-spacing:.04em;
}

.cal-cell{
  background:var(--surface);border:1px solid var(--line);border-radius:10px;
  min-height:48px;padding:5px 3px 4px;
  display:flex;flex-direction:column;align-items:center;gap:3px;
  overflow:hidden;
}
.cal-cell.out{opacity:.3}
.cal-cell.has{background:var(--day-active)}
.cal-cell.today .cal-num{
  background:var(--accent);color:var(--accent-ink);
  border-radius:50%;width:22px;height:22px;display:grid;place-items:center;
}
.cal-cell.sel{outline:2px solid var(--accent);outline-offset:-2px}
.cal-num{font-size:13px;font-variant-numeric:tabular-nums;line-height:22px;height:22px}
.cal-more{font-size:9px;color:var(--muted)}

.cal-dots{display:flex;gap:3px;justify-content:center}
.cal-dot{width:6px;height:6px;border-radius:50%;display:inline-block}
/* The .k-* colours live with the chart series further down — one meaning, one
   colour, and only one place to change it. */

/* week view: taller cells that show the workout name */
.cal-cell.week{min-height:110px;justify-content:flex-start;gap:3px;padding:5px 3px}
.cal-chip{
  width:100%;font-size:9.5px;line-height:1.35;padding:2px 3px;border-radius:4px;
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
  color:var(--chip-ink);font-weight:600;
}

.cal-legend{
  display:flex;gap:14px;justify-content:center;flex-wrap:wrap;
  margin-top:14px;font-size:12px;color:var(--muted);
}
.cal-legend span{display:inline-flex;align-items:center;gap:5px}
.cal-legend i{width:8px;height:8px;border-radius:50%;display:inline-block}

.sess{border-left:3px solid var(--line);padding-left:11px;margin-top:12px}
.sess.k-lifting{border-left-color:var(--accent);background:none}
.sess.k-cardio{border-left-color:var(--warn);background:none}
.sess.k-mixed{border-left-color:var(--good);background:none}

@media (max-width:400px){
  .tab{font-size:10px}
  .cal-chip{font-size:9px}
}

/* ---------- data tab / charts ----------
   Series colours are validated as a set — see viz.js. Marks wear the series
   colour; text always wears a text token, never a series hue. */
:root{
  --series-lift:#C169C3;
  --series-cardio:#E8A020;   /* brightened: the plum lift and the old amber merged under tritanopia */
  --series-practice:#64B4AE;
  --series-mixed:#199e70;
  --viz-grid:#3A1C3B;
  --viz-dim:#543054;
  --viz-area:rgba(193,105,195,.14);
  --viz-tip-bg:#3D1B3E;
}
:root[data-theme="light"]{
  --series-lift:#6B2E6D;
  --series-cardio:#A45A06;   /* darkened: white chip labels were 3.2:1 on the old amber */
  --series-practice:#1B7E76;
  --series-mixed:#166534;
  --viz-grid:#e5e1d6;
  --viz-dim:#cbc5b6;
  --viz-area:rgba(107,46,109,.10);
  --viz-tip-bg:#241c3c;
}

/* the semantic dot/chip colours across the app follow the same validated set */
.k-lifting{background:var(--series-lift)}
.k-cardio{background:var(--series-cardio)}
.k-practice{background:var(--series-practice)}
.k-mixed{background:var(--series-mixed)}
.sess.k-lifting{border-left-color:var(--series-lift);background:none}
.sess.k-cardio{border-left-color:var(--series-cardio);background:none}
.sess.k-practice{border-left-color:var(--series-practice);background:none}
.sess.k-mixed{border-left-color:var(--series-mixed);background:none}

.seg.wrap{flex-wrap:wrap}
.seg button{white-space:nowrap}
select.text.slim{width:auto;flex:1;min-width:150px;padding:8px 10px;font-size:13px}

/* stat tiles + hero figure */
.hero{
  font-size:52px;font-weight:700;line-height:1.05;margin:2px 0 4px;
  letter-spacing:-0.02em;   /* proportional figures: never tabular at this size */
}
.stat-label{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
.stat-value{font-size:26px;font-weight:700;margin-top:3px}
.stat-unit{font-size:13px;font-weight:600;color:var(--muted);margin-left:3px}
.delta{font-size:13px;color:var(--muted)}
.delta.up{color:var(--good)}
.delta.down{color:var(--muted)}

.tiles{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-top:12px}
.tiles .card{margin-top:0;padding:12px}
.tile{text-align:center}

/* The tiles are buttons now — they open the working behind the number. Styled
   to stay looking like cards, because a row of four obvious buttons would
   shout louder than the figures they carry. */
button.tile{
  width:100%;font:inherit;color:inherit;cursor:pointer;
  -webkit-appearance:none;appearance:none;
}
button.tile:active{transform:scale(.985)}
button.tile:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.tiles-hint{text-align:center;margin:8px 0 0}

/* What the time-of-day bands actually mean. Nowhere in the UI said, so
   "Midday" was left to the reader to guess at. */
.band-key{
  display:flex;flex-wrap:wrap;gap:4px 12px;justify-content:center;
  margin-top:8px;font-size:11px;color:var(--muted);
}
.band-key b{color:var(--text);font-weight:600}

/* The app saying the thing outright, rather than leaving it in the chart. */
.verdict{
  margin-top:12px;padding:10px 12px;border-radius:10px;
  background:var(--surface-2);border-left:2px solid var(--accent);
  font-size:13px;line-height:1.5;
}
.verdict strong{color:var(--text)}

.detail-row{
  display:flex;align-items:baseline;gap:10px;padding:7px 0;
  border-bottom:1px solid var(--line);font-size:13px;
}
.detail-row:last-child{border-bottom:0}
.detail-row .grow{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}
.detail-row b{font-variant-numeric:tabular-nums}

/* chart marks */
.viz{width:100%;height:auto;display:block;margin-top:10px;overflow:visible;touch-action:pan-y}
.viz-grid{stroke:var(--viz-grid);stroke-width:1}
.viz-tick{fill:var(--muted);font-size:9px;font-variant-numeric:tabular-nums}
.viz-bar{fill:var(--series-lift)}
.viz-bar-dim{fill:var(--viz-dim)}
.viz-line{fill:none;stroke:var(--series-lift);stroke-width:2;stroke-linejoin:round;stroke-linecap:round}
.viz-area{fill:var(--viz-area);stroke:none}
.viz-dot{fill:var(--series-lift);stroke:var(--surface);stroke-width:2}
.viz-endlabel{fill:var(--text);font-size:11px;font-weight:700}

/* The week still running. Same job as .viz-bar-dim on the column charts: the
   number is real but incomplete, and a solid line into it reads as a fall. */
.viz-line-partial{stroke-dasharray:4 4;opacity:.5}
.viz-dot-partial{fill:var(--viz-dim)}
.viz-endlabel-partial{fill:var(--muted);font-weight:600}
.viz-rowlabel{fill:var(--text);font-size:11px}
.viz-rowvalue{fill:var(--muted);font-size:11px;font-variant-numeric:tabular-nums}
/* SVG marks need fill, not background — .k-* alone leaves them black. */
.viz-seg{stroke:none}
.viz-seg.k-lifting{fill:var(--series-lift)}
.viz-seg.k-cardio{fill:var(--series-cardio)}
.viz-seg.k-practice{fill:var(--series-practice)}
.viz-seg.k-mixed{fill:var(--series-mixed)}
.viz-hit,.viz-hit-dot{fill:transparent;cursor:pointer}

.viz-legend{
  display:flex;gap:14px;flex-wrap:wrap;justify-content:center;
  margin-top:12px;font-size:12px;color:var(--muted);
}
.viz-legend span{display:inline-flex;align-items:center;gap:5px}
.viz-legend i{width:9px;height:9px;border-radius:3px;display:inline-block}
.viz-legend b{color:var(--text)}

.viz-tip{
  position:fixed;z-index:90;pointer-events:none;
  background:var(--viz-tip-bg);color:var(--tip-ink);border:1px solid var(--line);
  border-radius:9px;padding:7px 10px;font-size:12px;line-height:1.35;
  box-shadow:var(--shadow);max-width:200px;
}
.viz-tip .tip-head{display:block;font-weight:700}
.viz-tip .tip-sub{display:block;opacity:.82}

/* ---------- last time, PR, goal, backup, plates ---------- */
.lastline{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  font-size:13px;margin:-2px 0 10px;
}
.lastline b{font-variant-numeric:tabular-nums;font-weight:600}
.lastline .linkish{padding:2px 4px;font-size:13px}

.pill.pr{
  color:var(--good);border-color:var(--good);
  font-weight:700;letter-spacing:.08em;
}

.goal-card{display:flex;align-items:center;gap:16px}
.goal-card.at-risk{border-color:var(--warn)}
.goal-card.hit{border-color:var(--good)}

.ring{width:84px;height:84px;flex:none}
.ring-track{fill:none;stroke:var(--surface-2);stroke-width:9}
.ring-fill{
  fill:none;stroke:var(--series-lift);stroke-width:9;stroke-linecap:round;
  transition:stroke-dasharray .3s ease;
}
.at-risk .ring-fill{stroke:var(--warn)}
.hit .ring-fill{stroke:var(--good)}
.ring-num{fill:var(--text);font-size:26px;font-weight:700;text-anchor:middle}
.ring-den{fill:var(--muted);font-size:11px;text-anchor:middle}

.banner{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  background:var(--warnbox-bg);border:1px solid var(--warnbox-line);
  color:var(--warnbox-text);border-radius:var(--radius);
  padding:14px;margin-bottom:12px;
}
.banner strong{display:block;margin-bottom:2px;color:var(--warn)}
.banner .btn{padding:8px 14px}
.banner .ghost{padding:8px 12px;color:inherit;border-color:var(--warnbox-line)}

.plates{display:flex;flex-wrap:wrap;gap:8px}
.plate{
  background:var(--series-lift);color:var(--accent-ink);
  border-radius:999px;padding:8px 14px;font-weight:700;
  font-variant-numeric:tabular-nums;
}

/* ---------- backdated logging ---------- */
.backdate-bar{
  background:var(--surface-2);border:1px solid var(--accent);
  border-radius:var(--radius);padding:10px 14px;margin-bottom:12px;
  font-size:14px;color:var(--text);
}
.backdate-bar strong{color:var(--accent-fg)}

/* ---------- timed exercises + timer ---------- */
.timed .set-head,.timed .set-row{grid-template-columns:34px 1fr 44px 44px}
.pill.timed{color:var(--good);border-color:var(--done-line)}
.pill.practice{color:var(--series-practice);border-color:var(--series-practice)}
.timer-btn{font-size:14px}
.timer-btn.on{background:var(--danger);border-color:var(--danger);color:var(--danger-ink)}
.timer-bar.counting-up #timer-fill{background:var(--good)}
.timer-label{
  max-width:38%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
  text-transform:none;letter-spacing:0;
}

/* ---------- editable routine / import items ---------- */
.item-edit{
  background:var(--surface-2);border:1px solid var(--line);
  border-radius:12px;padding:10px;margin-bottom:8px;
}
.item-edit > input.text{background:var(--surface);margin-bottom:8px}
.item-edit-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.item-edit-row select.text.slim{flex:0 0 auto;min-width:0;width:auto;font-size:12px;padding:6px 8px}
.item-edit-row .grow{text-align:right;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Sets and reps side by side. auto-fit rather than a fixed column count so a
   lift (sets/reps/weight) and a class (minutes alone) both read properly, and
   so two fields don't end up 40px wide on a small phone. */
.tfields{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(88px,1fr));
  gap:8px;margin-top:10px;
}
.tfield{display:block;min-width:0}
.tfield > span{
  display:block;font-size:11px;letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);margin-bottom:4px;
}
.tfield input.text,.tfield textarea.text{background:var(--surface);margin:0}
.note-field{margin-top:10px}
.note-field textarea.text{min-height:52px;font-size:14px;line-height:1.45}

.note-dot{opacity:.55;font-size:11px}

/* A suggestion, not a warning — nothing is wrong yet, the log is just split
   across two spellings. Uses the accent rather than the warning colour. */
.card.dupe{border-color:var(--accent);margin-bottom:10px}
.card.dupe .card-title{font-size:15px}

/* A cue you want to read mid-set, so it keeps the newlines it was written
   with rather than running the lines together. */
.ex-note{
  margin:10px 0 0;padding:8px 10px;border-left:2px solid var(--accent);
  background:var(--surface-2);border-radius:0 8px 8px 0;
  font-size:13px;line-height:1.5;color:var(--muted);white-space:pre-wrap;
}

/* ---------- topbar actions ---------- */
.topbar-actions{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.home-btn{
  display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:38px;padding:0;color:var(--muted);text-decoration:none;
}
.home-btn:hover{color:var(--accent-fg);border-color:var(--accent)}

/* ---------- plan preview ---------- */
.plan-row{
  display:flex;justify-content:space-between;gap:12px;margin-top:6px;font-size:13px;
}
.plan-row .muted{white-space:nowrap;font-variant-numeric:tabular-nums}

/* Raw weigh-ins are context behind the average line, so they're de-emphasised.
   Needs `fill` — an SVG circle ignores `background` and would default to black. */
.viz-raw-dot{fill:var(--viz-dim)}

/* A loss is the one thing on these charts that is not good news, so it is the
   one thing that gets the warning colour. */
.viz-bar-down{fill:var(--danger)}
.viz-rowvalue-down{fill:var(--danger)}

/* ---------- swipe left to delete ----------
   Used for both an exercise card and a single set row. The Delete button sits
   underneath and is a real button in the DOM at all times, so keyboard and
   screen-reader users reach it by tabbing — focusing it slides the face open so
   it's visible before it's pressed. */
/* Red underneath, so a swipe longer than the button reveals the delete colour
   rather than a hole. The face covers it completely at rest. */
.swipe-wrap{position:relative;overflow:hidden;background:var(--danger)}
.swipe-del{
  position:absolute;top:1px;right:1px;bottom:1px;width:96px;
  border-radius:inherit;
  /* Inset by a pixel: at identical radii the two curves antialias against each
     other and a red hairline shows along the face's rounded corner. */
  border:0;background:var(--danger);color:var(--danger-ink);
  font-weight:700;font-size:14px;
}
.swipe-wrap > .swipe-face{
  position:relative;
  transition:transform .18s ease;
  touch-action:pan-y;         /* vertical scrolling stays with the page */
  will-change:transform;
}
.swipe-wrap.dragging > .swipe-face{transition:none;user-select:none}
.swipe-wrap.open > .swipe-face{transform:translateX(-96px)}

/* Past the point where letting go deletes. The button takes the whole row so
   the change is unmissable while the finger is still down. */
.swipe-wrap.will-delete > .swipe-del{width:auto;left:1px;transition:none}

.ex-swipe{margin-top:12px;border-radius:var(--radius)}

/* A set row is transparent by default, which would let the red panel show
   through the gaps between inputs. It needs its own opaque background. */
.set-swipe{border-radius:10px}
.set-swipe > .set-row{background:var(--surface)}

.swipe-hint{
  display:block;text-align:center;color:var(--muted);
  font-size:12px;margin-top:12px;
}

/* A value in a line of text that happens to be editable. Underlined so it
   reads as a control at 13px, where colour alone is too quiet. */
.linkish.inline-edit{
  padding:0;font-size:inherit;font-weight:600;
  text-decoration:underline;text-underline-offset:2px;
}

/* ---------- alerts, notes, undo ---------- */
.slider{flex:1;accent-color:var(--accent)}

.session-note{
  margin:8px 0 0;padding:8px 10px;font-size:13px;
  background:var(--surface-2);border-left:3px solid var(--accent);
  border-radius:0 8px 8px 0;color:var(--text);
}

.toast{display:flex;align-items:center;gap:12px}
.toast-action{
  background:none;border:0;padding:2px 4px;
  color:var(--accent-fg);font-weight:700;font-size:14px;
}

/* Sign-off at the foot of Settings — the one place the mark and the tagline
   appear inside the app itself, where they read as a signature rather than
   as chrome competing with the workout in front of you. */
.brand-footer{
  display:flex;flex-direction:column;align-items:center;gap:6px;
  margin-top:26px;padding-top:22px;border-top:1px solid var(--line);
}
.brand-footer .mark{width:30px;height:30px}
.brand-footer .brand-name{
  font-size:14px;font-weight:800;letter-spacing:.10em;text-transform:uppercase;line-height:1;
}
.brand-footer .brand-tag{
  font-size:9px;font-weight:600;letter-spacing:.24em;text-transform:uppercase;
  color:var(--muted);line-height:1;
}
.brand-footer .small{margin-top:6px}

/* The workout time doubles as its own edit control — it looks like the text it
   replaces until you touch it, so the day panel doesn't turn into a form. */
.sess-meta{display:flex;align-items:center;gap:5px;flex-wrap:wrap}
.time-edit{
  background:transparent;border:0;padding:0;margin:0;
  color:var(--muted);font:inherit;font-size:13px;
  border-bottom:1px dashed var(--line);cursor:pointer;
}
.time-edit:hover,.time-edit:focus{color:var(--text);border-bottom-color:var(--accent-fg)}
/* Parked at midday because it was logged after the fact — flagged so it reads
   as "nobody has said yet", not as "you trained at noon". */
.time-edit.unset{color:var(--accent-fg);border-bottom-style:solid}
.time-edit::-webkit-calendar-picker-indicator{opacity:.5;cursor:pointer}

/* ---------- first-run walkthrough ---------- */

/* The dimming is one element: a small box with an enormous spread shadow, so
   the "hole" is exact and needs no four-div frame. pointer-events:none
   throughout — the tour points, it does not trap. */
.tour-spot{
  position:fixed;z-index:60;pointer-events:none;
  border-radius:12px;
  box-shadow:0 0 0 9999px var(--tour-scrim);
  outline:2px solid var(--accent-fg);
  outline-offset:0;
  transition:top .18s ease, left .18s ease, width .18s ease, height .18s ease;
}
/* Raised above the dimming so the thing being pointed at stays lit. Applied to
   the tab bar rather than the tab, which has no stacking context of its own. */
.tour-above{z-index:61 !important}

.tour-bubble{
  position:fixed;z-index:62;
  width:min(320px, calc(100vw - 20px));
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow);
  padding:14px;
  transition:top .18s ease, left .18s ease;
}
.tour-bubble:focus{outline:none}
.tour-bubble.centered{
  top:50% !important;left:50% !important;
  transform:translate(-50%,-50%);
}
.tour-bubble h3{font-size:17px;margin:0 0 6px}
.tour-bubble p{margin:0;font-size:14px;color:var(--muted);line-height:1.5}
.tour-count{
  font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--accent-fg);font-weight:600;margin-bottom:6px;
}
.tour-actions{display:flex;align-items:center;gap:8px;margin-top:14px}
.btn.small{padding:8px 14px;font-size:14px;border-radius:10px}


/* Sub-headings inside a settings card, so a long list of buttons reads as a
   few short groups instead of one undifferentiated column. */
.settings-group{
  font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);margin:20px 0 8px;
}

/* The next-set suggestion. Sits under "last time" as an offer, not an
   instruction — quiet enough to ignore, tappable when it's right. */
.suggest{
  display:flex;align-items:baseline;gap:8px;width:100%;text-align:left;
  background:transparent;border:1px dashed var(--pill-lift-line);border-radius:10px;
  padding:7px 10px;margin:2px 0 10px;color:var(--accent-fg);
}
.suggest:active{background:var(--surface-2)}
.suggest-label{font-weight:650;font-size:13px}
.suggest-why{font-size:12px;color:var(--muted)}
/* Holding is not a lesser outcome, so it is not styled as a warning — just
   without the forward-looking accent. */
.suggest.hold{color:var(--muted);border-color:var(--line)}
.suggest.hold .suggest-label{color:var(--text)}

/* The exercise name doubles as the way into its history, so it has to look
   tappable without turning the card header into a button bar. */
button.ex-name{
  background:none;border:0;padding:0;margin:0;text-align:left;
  font:inherit;font-weight:650;color:var(--text);
  border-bottom:1px dotted var(--pill-lift-line);
}
button.ex-name:active{color:var(--accent-fg)}

.hist-list{margin-top:12px;border-top:1px solid var(--line)}
.hist-row{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:8px 0;border-bottom:1px solid var(--line);
}
.hist-row b{font-variant-numeric:tabular-nums;font-weight:600;text-align:right}

/* The text version of a routine, shown before you send it. */
.share-preview{
  margin:0;white-space:pre-wrap;font:13px/1.6 var(--font);
  background:var(--surface-2);border:1px solid var(--line);border-radius:10px;
  padding:12px;color:var(--text);max-height:220px;overflow:auto;
}

/* Something the app noticed. Quiet by design — it is an observation, not an
   alert, and it appears at most one at a time. */
.notice{
  display:flex;align-items:flex-start;gap:10px;
  background:var(--surface);border:1px solid var(--pill-lift-line);
  border-left:3px solid var(--accent-fg);
  border-radius:var(--radius);padding:12px 12px 12px 14px;margin-top:12px;
}
.notice p{margin:0;font-size:14px;line-height:1.5}
.notice-mark{color:var(--accent-fg);font-size:10px;line-height:20px}
