:root {
  --bg: #15171a;
  --surface: #1d2024;
  --surface2: #24282d;
  --border: #34393f;
  --border-soft: #2a2e33;
  --text: #eceef0;
  --text-dim: #8a9099;
  --text-faint: #565c64;
  --accent: #ff5a1f;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --good: #3ddc84;
  --good-soft: #10261a;
  --bad: #ff5468;
  --bad-soft: #2c1418;
  --warn: #f2c94c;
  --reset-border: #ffffff;
  --reset-bg: #3a3f45;
  --reset-text: #eceef0;
  --myo: #cc00cc;
  --goldenrod: #daa520;
  --font-head: 'Barlow Condensed', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 3px;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f2f1ee;
    --surface: #ffffff;
    --surface2: #f7f6f3;
    --border: #dcdad4;
    --border-soft: #e8e6e1;
    --text: #1a1a1a;
    --text-dim: #6b6b68;
    --text-faint: #a3a19b;
    --accent: #e0501a;
    --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
    --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
    --good: #1a9c56;
    --good-soft: #e3f5ea;
    --bad: #d63a4d;
    --bad-soft: #fbe6e8;
    --reset-border: #4a4a4a;
    --reset-bg: #e3e2df;
    --reset-text: #1a1a1a;
  }
}
:root[data-theme="light"] {
  --bg: #f2f1ee;
  --surface: #ffffff;
  --surface2: #f7f6f3;
  --border: #dcdad4;
  --border-soft: #e8e6e1;
  --text: #1a1a1a;
  --text-dim: #6b6b68;
  --text-faint: #a3a19b;
  --accent: #e0501a;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --good: #1a9c56;
  --good-soft: #e3f5ea;
  --bad: #d63a4d;
  --bad-soft: #fbe6e8;
  --reset-border: #4a4a4a;
  --reset-bg: #e3e2df;
  --reset-text: #1a1a1a;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; scrollbar-width: none; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.45;
  padding-bottom: 76px;
  min-height: 100vh;
  overscroll-behavior-y: contain;
}
h1, h2, h3, .head { font-family: var(--font-head); font-weight: 700; letter-spacing: 0.01em; margin: 0; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
button { font-family: var(--font-body); cursor: pointer; }
input, select, textarea { font-family: var(--font-body); }

/* ---------- Top bar ---------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--bg);
  border-bottom: 1px solid var(--border-soft);
  padding: 14px 16px 10px;
  display: flex; align-items: center; justify-content: center;
}
.topbar .brand { font-family: var(--font-head); font-size: 19px; font-weight: 800; letter-spacing: 0.01em; white-space: nowrap; }
.topbar .brand span { color: var(--accent); }
.topbar .settings-btn { }
.topbar .nav-btns-right { position: absolute; right: 16px; top: 50%; transform: translateY(-50%); display: flex; gap: 6px; }
.topbar .nav-btns { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); display: flex; gap: 6px; }
.unit-toggle {
  display: flex; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
}
.unit-toggle button {
  background: var(--surface); color: var(--text-dim); border: none; padding: 5px 10px; font-size: 12px;
  font-weight: 600; font-family: var(--font-mono);
}
.unit-toggle button.active { background: var(--accent); color: #16171a; }

/* ---------- Screen container ---------- */
.screen { padding: 14px 16px 24px; max-width: 640px; margin: 0 auto; }
.hidden { display: none !important; }

/* ---------- Section headers ---------- */
.section-title {
  font-family: var(--font-head); font-size: 20px; font-weight: 700; margin: 22px 0 10px;
  color: var(--text);
}
.section-title:first-child { margin-top: 4px; }
.subtle-label {
  font-size: 11px; color: var(--text-faint); font-weight: 600; letter-spacing: 0.04em; margin-bottom: 6px;
}

/* ---------- Cards / panels ---------- */
.panel {
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius);
  padding: 14px; margin-bottom: 10px;
}
.panel + .panel { margin-top: 10px; }
.panel-done { border-color: var(--good); background: var(--good-soft); }
.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.row + .row { margin-top: 8px; }
.stack { display: flex; flex-direction: column; gap: 8px; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--surface2); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 14px; font-size: 14px; font-weight: 600;
}
.btn:active { transform: scale(0.98); }
.btn-primary { background: var(--accent); color: #17181b; border-color: var(--accent); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-dim); }
.btn-danger { background: var(--bad-soft); border-color: var(--bad); color: var(--bad); }
.btn-good { background: var(--good-soft); border-color: var(--good); color: var(--good); }
.btn-block { width: 100%; }
.btn-sm { padding: 6px 10px; font-size: 12px; }

/* ---------- Inputs ---------- */
label.field { display: block; margin-bottom: 10px; }
label.field .lbl { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 4px; font-weight: 500; }
input[type="text"], input[type="number"], select, textarea {
  width: 100%; background: var(--surface2); border: 1px solid var(--border); color: var(--text);
  border-radius: var(--radius); padding: 10px 11px; font-size: 15px;
}
input[type="number"] { font-family: var(--font-mono); }
/* No spin-button up/down control on number inputs — mobile never shows one, so the web version
   shouldn't either (matched by the wheel-scroll guard in JS, which stops scrolling over a
   focused number field from silently changing it too). */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
textarea { resize: vertical; min-height: 50px; }
.field-row { display: flex; gap: 8px; }
.field-row > label.field { flex: 1; }
select { -webkit-appearance: none; appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--text-dim) 50%), linear-gradient(135deg, var(--text-dim) 50%, transparent 50%); background-position: calc(100% - 16px) center, calc(100% - 11px) center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 30px; }

/* ---------- Bottom nav ---------- */
.tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  background: var(--surface); border-top: 1px solid var(--border);
  display: flex; padding: 6px 6px calc(6px + env(safe-area-inset-bottom));
  /* flex-basis:auto (not the flex:1 shorthand's 0%) lets buttons keep their natural
     content width: with room to spare they still stretch evenly (flex-grow:1), but once a
     section has enough sub-tabs to exceed the viewport (e.g. Health & Diet's 7), they hold
     their min-content size instead of being crushed illegibly — the row overflows and
     overflow-x:auto below turns that overflow into a scrollable strip instead of tabs
     silently clipping off-screen and becoming unreachable. */
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
}
.tabbar button {
  flex: 1 1 auto; min-width: 58px; white-space: nowrap;
  background: none; border: none; color: var(--text-faint); padding: 8px 4px;
  display: flex; flex-direction: column; align-items: center; gap: 3px; font-size: 11px; font-weight: 600;
  font-family: var(--font-head); letter-spacing: 0.03em;
}
.tabbar button .ic { font-size: 18px; line-height: 1; }
.tabbar button.active { color: var(--accent); }
/* CLOSE always reads as a dismiss/exit action, so it stays on --bad (red-family in every
   aesthetic by design convention) instead of the tabbar's usual faint/accent coloring — a fixed
   signal regardless of which aesthetic or accent color is active. */
.tabbar button.tabbar-close, .tabbar button.tabbar-close.active { color: var(--bad); }

/* ---------- Mouse-hover FX (desktop/trackpad only — @media(hover:hover) never matches a touch
   screen, so none of this changes anything on mobile) ---------- */
@media (hover: hover) and (pointer: fine) {
  /* Neutral-bordered buttons animate their border to full accent on hover — buttons that are
     already accent by default (btn-primary) or carry their own semantic color (btn-danger/
     btn-good/tabbar-close) are left alone so hovering doesn't muddy their meaning. */
  .btn, .icon-btn, .subnav button, .unit-toggle button, .cycle-btns button, .tabbar button, .home-tile {
    transition: border-color .15s ease, color .15s ease;
  }
  .btn:not(.btn-primary):not(.btn-danger):not(.btn-good):hover,
  .icon-btn:hover,
  .subnav button:not(.active):hover,
  .unit-toggle button:not(.active):hover,
  .cycle-btns button:hover,
  .home-tile:hover {
    border-color: var(--accent);
  }
  /* Any clickable row holding a hit-mark toggle (Today's Schedule anchors, Today's Workouts
     cards, etc.) — hovering the row previews the toggle by animating its border to accent,
     regardless of which render function built that particular row. */
  [onclick]:hover .hit-mark { border-color: var(--accent); transition: border-color .15s ease; }
  /* Tabbar: inactive icons desaturate to match the already-desaturated inactive text, then both
     animate back to full accent together on hover — CLOSE keeps its fixed --bad color throughout. */
  .tabbar button:not(.active):not(.tabbar-close) .ic { filter: grayscale(0.9) opacity(0.6); transition: filter .15s ease; }
  .tabbar button:not(.active):not(.tabbar-close):hover .ic { filter: none; }
  .tabbar button:not(.active):not(.tabbar-close):hover { color: var(--accent); }
}
.btn-close { color: var(--bad); border-color: var(--bad); }

/* ---------- Training grid (144 cells) ---------- */
.week-selector { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.week-selector .cycle-label { font-family: var(--font-head); font-size: 24px; font-weight: 800; }
.week-selector .cycle-btns { display: flex; gap: 6px; }
.week-selector .cycle-btns button { width: 36px; height: 36px; border-radius: 50%; background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-size: 18px; font-weight: 700; }

.workout-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.workout-cell {
  aspect-ratio: 1; background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  position: relative;
}
.workout-cell .wnum { font-family: var(--font-head); font-size: 26px; font-weight: 800; }
.workout-cell .wname { font-size: 10px; color: var(--text-dim); text-align: center; padding: 0 4px; line-height: 1.2; }
.workout-cell .cell-icon { display: flex; align-items: center; justify-content: center; margin-top: 2px; }
.workout-cell .cell-icon svg { width: 26px !important; height: 26px !important; }
.workout-cell.done { border-color: var(--good); background: var(--good-soft); }
.workout-cell.done .wnum { color: var(--good); }
.workout-cell.partial { border-color: var(--warn); }
.workout-cell.empty-slot { opacity: 0.35; }
.workout-cell.unfinished-weight { border-color: var(--accent); }
.workout-cell.unfinished-cardio { border-color: var(--accent); }
/* Home's 6 section tiles reuse .workout-cell/.wname but read bigger — this is the first thing
   tapped on the app's landing screen, not a dense data grid like Train's, so it can afford it. */
.workout-cell.home-tile .wname { font-size: 13px; font-weight: 700; }
/* Each section's color (HOME_SECTION_META) shows as a vignette tracing the tile's own edges,
   fading to a neutral center (homeTileGlowStyle() in app.js) — set inline per tile since it's a
   literal radial-gradient(color), not expressible as a CSS var alone. Third revision of this same
   day: flat tile tint, then a glow behind the icon, now this — still enough to track a tile by
   color through a drag-reorder, without a flat recolor or concentrating the color mid-tile. */

/* ---------- Stage plate indicator ---------- */
.plate-row { display: flex; gap: 6px; }
.plate {
  width: 30px; height: 30px; border-radius: 50%; border: 2px solid var(--border);
  display: flex; align-items: center; justify-content: center; font-family: var(--font-mono);
  font-size: 12px; font-weight: 700; color: var(--text-dim); background: var(--surface2);
}
.plate.active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* ---------- Tier block (in workout log) ---------- */
.tier-block { border: 1px solid var(--border-soft); border-radius: var(--radius); margin-bottom: 10px; overflow: hidden; background: var(--bg); }
.superset-group {
  background: var(--accent-soft); border: 2px solid var(--accent); border-radius: var(--radius);
  padding: 10px; margin-bottom: 10px;
}
.superset-label { font-size: 10px; font-weight: 800; letter-spacing: 0.06em; color: var(--accent); margin-bottom: 8px; }
.superset-group.superset-complete { background: var(--surface2); border-color: var(--border); }
.superset-group.superset-complete .superset-label { color: var(--text-faint); }
.superset-done-tag { color: var(--good); }
.superset-member .tier-block { margin-bottom: 10px; }
.superset-member:last-child .tier-block { margin-bottom: 0; }
.superset-primary-tag { font-size: 9px; font-weight: 800; letter-spacing: 0.06em; color: var(--accent); margin-bottom: 4px; }
.order-line {
  display: flex; flex-wrap: wrap; gap: 6px; padding: 6px; min-height: 24px;
  border-radius: 6px; border: 1px dashed transparent;
}
.order-dropzone { height: 10px; border-radius: 4px; }
.order-dropzone.drop-target-active {
  background: var(--accent-soft); border: 1px dashed var(--accent); height: 22px;
}
.exercise-chip {
  display: inline-flex; align-items: center; gap: 7px; background: var(--surface2); border: 1px solid var(--border);
  border-radius: 5px; padding: 9px 11px; cursor: grab;
  touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.exercise-chip:active { cursor: grabbing; }
.exercise-chip.drop-target-active { background: var(--accent-soft); border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); }
.exercise-chip-dragging { opacity: 0.3; }
.exercise-chip-ghost { position: fixed; pointer-events: none; z-index: 999; box-shadow: 0 6px 18px rgba(0,0,0,0.35); opacity: 0.96; }

/* ---------- Home Edit mode: reorder/hide sections & boxes ---------- */
/* var(--warn), not --accent: the pencil sat in the topbar right next to the accent-colored
   Settings gear and the accent-colored brand wordmark, so tinting it accent too made "you're in
   edit mode" nearly invisible — same button, same color as everything around it. --warn reads as
   "careful, this changes your layout" and stands apart from the rest of the accent-colored chrome. */
/* .icon-btn.home-edit-toggle-active, not just .home-edit-toggle-active: .icon-btn (styles.css,
   further down) has equal specificity and sets its own background/border/color, so on source
   order alone it would silently win and this rule would never actually show — the CSS gotcha
   CLAUDE.md already warns about. Compounding the class onto .icon-btn's own selector wins on
   specificity instead, regardless of where either rule sits in the file. */
.icon-btn.home-edit-toggle-active { background: color-mix(in srgb, var(--warn) 14%, var(--surface)); border-color: var(--warn); color: var(--warn); }
.home-edit-item { position: relative; border: 1px dashed var(--border); touch-action: none; }
.home-edit-box { border-radius: var(--radius); padding: 6px 6px 2px; margin-bottom: 4px; }
.home-drag-source { opacity: 0.3; }
.home-drag-ghost { position: fixed; pointer-events: none; z-index: 999; box-shadow: 0 6px 18px rgba(0,0,0,0.35); opacity: 0.96; }
[data-home-drag-id].drop-target-active { background: var(--accent-soft); border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); }
.home-edit-x {
  position: absolute; top: -8px; right: -8px; width: 22px; height: 22px; border-radius: 50%;
  background: var(--bad); color: #fff; border: 2px solid var(--bg); display: flex; align-items: center;
  justify-content: center; font-size: 10px; z-index: 5; padding: 0;
}
.home-edit-x .icon-svg { width: 11px !important; height: 11px !important; }
.home-edit-x path { stroke: #fff !important; fill: #fff !important; }
.home-add-btn {
  position: absolute; bottom: -14px; right: 6px; width: 34px; height: 34px; border-radius: 50%;
  background: var(--good); color: #17181b; border: 2px solid var(--bg); font-size: 20px; font-weight: 800;
  display: flex; align-items: center; justify-content: center; line-height: 1; z-index: 4;
}
.home-popup-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 100; display: flex; align-items: flex-end; justify-content: center; }
.home-popup { width: 100%; max-width: 640px; max-height: 70vh; overflow-y: auto; border-radius: var(--radius) var(--radius) 0 0; margin: 0; }
.home-popup-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; font-size: 13px; border-bottom: 1px solid var(--border-soft); cursor: pointer; }
.home-popup-row:last-child { border-bottom: none; }
.chip-tier { font-family: var(--font-mono); font-size: 10px; font-weight: 700; color: var(--text-dim); }
.chip-name { font-size: 12px; font-weight: 600; color: var(--text); }
.chip-primary-bar { color: var(--accent); font-weight: 900; font-size: 15px; margin-left: 2px; line-height: 1; }
.tier-head {
  background: var(--surface2); padding: 10px 12px; display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--border-soft);
}
.tier-head .tname { font-family: var(--font-head); font-weight: 700; font-size: 16px; }
.tier-head .tmove { font-size: 12px; color: var(--text-dim); }
.tier-body { padding: 10px 12px; }
.target-line { font-size: 13px; color: var(--text-dim); margin-bottom: 10px; }
.target-line .tv { color: var(--text); font-weight: 700; font-family: var(--font-mono); }

.set-row { display: grid; grid-template-columns: 28px 1fr 1fr 34px; gap: 8px; align-items: center; margin-bottom: 6px; }
.set-row-rp { display: grid; grid-template-columns: 24px 1fr 1fr 0.7fr 30px; gap: 6px; align-items: center; margin-bottom: 6px; }
.set-row-rp input { text-align: center; }
.set-row .setnum { font-family: var(--font-mono); font-size: 12px; color: var(--text-faint); text-align: center; }
.set-row input { text-align: center; }
.set-row .amrap-tag { font-size: 9px; color: var(--accent); font-weight: 700; text-align: center; }
.hit-mark { width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 1px solid var(--border); flex-shrink: 0; }
.hit-mark.hit { border-color: var(--accent); background: var(--accent-soft); }
/* .hit-mark is a <div> everywhere it only displays state, and a <button> where it's also the
   control (a reminder's done toggle). A button brings UA padding, background and font with it --
   and since .hit-mark is display:flex inside a fixed 20px circle, that padding leaves ~6px of
   content box and squashes the 14px check into an invisible sliver. It renders the whole time;
   you just can't see it. Resetting the button-specific defaults is all that's needed: the
   .hit-mark.hit background above still wins on specificity (0,2,0 beats this rule's 0,1,1). */
button.hit-mark { padding: 0; background: none; font: inherit; color: inherit; cursor: pointer; }
.set-row .hit-mark { width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800; border: 1px solid var(--border); color: var(--text-faint); background: none; }
.set-row .hit-mark.hit { background: var(--good-soft); border-color: var(--good); color: var(--good); }
.set-row .hit-mark.miss { background: var(--bad-soft); border-color: var(--bad); color: var(--bad); }
.set-row .hit-mark.tilde { background: rgba(242,201,76,0.15); border-color: var(--warn); color: var(--warn); }
.icon-svg { display: inline-block; vertical-align: -0.15em; }
.set-row.set-greyed { opacity: 0.4; }

.suggestion-box {
  margin-top: 10px; padding: 10px; border-radius: var(--radius); background: var(--accent-soft);
  border: 1px solid var(--accent-dim); display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.suggestion-box .sugtext { font-size: 12px; color: var(--text-dim); }
.suggestion-box .sugval { font-family: var(--font-mono); font-size: 16px; font-weight: 700; color: var(--accent); }
.suggestion-box input { width: 74px; text-align: center; }
.suggestion-box.applied { background: var(--good-soft); border-color: var(--good); }
.suggestion-box.applied .sugval { color: var(--good); }

/* ---------- Measurement / weight tables ---------- */
.entry-list { display: flex; flex-direction: column; gap: 8px; }
.entry-card { background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 12px; }
.entry-card .ehead { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.entry-card .edate { font-family: var(--font-mono); font-weight: 700; font-size: 13px; }
.entry-card .estats { display: flex; flex-wrap: wrap; gap: 10px 16px; font-size: 12px; color: var(--text-dim); }
.entry-card .estats b { color: var(--text); font-family: var(--font-mono); }

/* ---------- Budgeting: income bar, recurring-reserved line, incidentals fill ---------- */
.budget-bar { position: relative; height: 34px; border-radius: 10px; background: var(--surface2); border: 1px solid var(--border-soft); overflow: hidden; }
.budget-bar-recurring { position: absolute; top: 0; bottom: 0; left: 0; background: var(--bad); opacity: 0.38; }
.budget-bar-savings { position: absolute; top: 0; bottom: 0; background: repeating-linear-gradient(135deg, var(--savings) 0, var(--savings) 4px, transparent 4px, transparent 8px); opacity: 0.4; }
/* Solid fill drawn on top of .budget-bar-savings' hatched "planned" outline, growing as
   isSavings recurring charges get checked off contributed for the month — see
   toggleSavingsCompletion() / budgetRecurringSavingsCompletedTotal(). */
.budget-bar-savings-fill { position: absolute; top: 0; bottom: 0; background: var(--savings); box-shadow: 0 0 6px 0 var(--savings); transition: width 0.3s ease; }
.budget-bar-incidentals { position: absolute; top: 0; bottom: 0; background: var(--accent); }
.budget-bar-line { position: absolute; top: -2px; bottom: -2px; width: 3px; background: var(--bad); }
.budget-bar-over { box-shadow: 0 0 0 2px var(--bad) inset; }
.budget-bar-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 12px; color: var(--text-dim); margin-top: 10px; }
.budget-bar-legend b { color: var(--text); font-family: var(--font-mono); }
.budget-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; vertical-align: middle; }
.savings-badge {
  display: inline-flex; align-items: center; gap: 3px; background: var(--savings-soft); color: var(--savings);
  border: 1px solid var(--savings); padding: 1px 7px; border-radius: 10px; font-size: 10px; font-weight: 700;
  margin-right: 6px; white-space: nowrap;
}

/* ---------- Budget: Savings Goals (Budget -> Goals) ---------- */
.goal-bar { position: relative; height: 10px; border-radius: 20px; background: var(--surface2); border: 1px solid var(--border-soft); overflow: hidden; }
.goal-bar-fill { height: 100%; background: var(--savings); border-radius: 20px; transition: width 0.3s ease; }
.goal-bar-fill-complete { background: var(--good); }

/* ---------- Notes (tag-colored, styled after the exercise-order drag chips) ---------- */
.note-card { background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 12px; }
.note-tag-label { font-family: var(--font-mono); font-size: 9px; font-weight: 800; letter-spacing: 0.04em; border: 1px solid; border-radius: 20px; padding: 2px 8px; flex-shrink: 0; }
.tag-pill-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px; }
.tag-pill { background: var(--surface2); border: 1px solid var(--tc, var(--border)); color: var(--tc, var(--text-dim)); padding: 5px 11px; border-radius: 20px; font-size: 11px; font-weight: 700; flex-shrink: 0; }
.tag-pill.active { background: var(--tc, var(--surface2)); color: #17181b; border-color: var(--tc, var(--border)); }

.delta-pos { color: var(--good); }
.delta-neg { color: var(--bad); }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }

/* ---------- Misc ---------- */
.divider { height: 1px; background: var(--border-soft); margin: 14px 0; }
.pill { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: 10px; font-weight: 700; letter-spacing: 0.03em; }
.pill-lower { background: #2a2416; color: #e6b64a; }
.pill-upper { background: #182533; color: #5db0e6; }
.empty-state { text-align: center; padding: 40px 20px; color: var(--text-faint); }
.empty-state .big { font-size: 40px; margin-bottom: 8px; }
.empty-state .big svg.icon-svg { width: 40px; height: 40px; }
.chart-wrap { background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 12px; margin-bottom: 12px; }
.toast {
  position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%);
  background: var(--text); color: var(--bg); padding: 10px 18px; border-radius: 20px; font-size: 13px; font-weight: 600;
  z-index: 50; opacity: 0; pointer-events: none; transition: opacity 0.2s, transform 0.2s;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(-6px); }
/* A horizontally-scrolling row of sub-tab buttons. Always built by subNav() in JS, which wraps
   it in .subnav-wrap and adds the scroll affordances below — never hand-write `<div class="subnav">`.
   The 14px bottom gap now lives on the wrap so the overlay children can sit flush to the row. */
.subnav { display: flex; gap: 6px; overflow-x: auto; }
.subnav button { flex-shrink: 0; background: var(--surface2); border: 1px solid var(--border); color: var(--text-dim); padding: 7px 13px; border-radius: 20px; font-size: 12px; font-weight: 600; }
.subnav button.active { background: var(--accent); border-color: var(--accent); color: #17181b; }
/* ---- Sub-nav scroll affordances (see attachSubnavScrollAffordances() in JS) ----
   Native scrollbars are hidden app-wide, and a strip of 5-6 sub-tabs overflows a phone. Two
   cues: a thin bar along the bottom edge that fades in while the strip is being scrolled and
   out ~800ms after (matching the page/tabbar indicators), plus an accent chevron pinned to
   whichever end still has more strip that way, over a short fade so buttons dissolve instead
   of being sliced. The chevron is decorative on touch (you swipe) and, on a fine pointer,
   clickable to page the strip — gated purely here via pointer-events. */
.subnav-wrap { position: relative; margin-bottom: 14px; }
.subnav-more {
  position: absolute; top: 0; bottom: 4px; width: 52px;
  display: flex; align-items: center;
  border: none; background: transparent; padding: 0; margin: 0;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 2;
}
/* opaque bg for the first half so the chevron always sits on the page colour, not on a
   half-sliced (often accent-coloured) button, then a quick fade to nothing */
.subnav-more .icon-svg { width: 22px; height: 22px; filter: drop-shadow(0 0 2px var(--bg)) drop-shadow(0 0 5px var(--bg)); }
.subnav-more.visible { opacity: 1; }
.subnav-more-l { left: 0; justify-content: flex-start;
  background: linear-gradient(to right, var(--bg) 0 48%, color-mix(in srgb, var(--bg) 35%, transparent) 74%, transparent); }
.subnav-more-r { right: 0; justify-content: flex-end;
  background: linear-gradient(to left, var(--bg) 0 48%, color-mix(in srgb, var(--bg) 35%, transparent) 74%, transparent); }
@media (hover: hover) and (pointer: fine) {
  .subnav-more.visible { pointer-events: auto; cursor: pointer; }
}
.subnav-scrollbar {
  position: absolute; left: 0; bottom: 0; height: 3px; min-width: 24px;
  border-radius: 2px; background: var(--text-faint);
  opacity: 0; pointer-events: none;
  transition: opacity 0.25s ease; z-index: 1;
}
.subnav-scrollbar.visible { opacity: 0.6; }
@media (prefers-reduced-motion: reduce) {
  .subnav-more, .subnav-scrollbar { transition: none; }
}
.icon-btn { background: none; border: 1px solid var(--border); color: var(--text-dim); width: 30px; height: 30px; border-radius: var(--radius); display: flex; align-items: center; justify-content: center; font-size: 14px; }
.icon-btn.disabled { opacity: 0.3; pointer-events: none; }

/* ---------- Rest timer: floating launcher + active-countdown widget ---------- */
.rest-timer-fab {
  position: fixed; right: 16px; bottom: 84px; z-index: 35;
  width: 52px; height: 52px; font-size: 22px; background: var(--surface);
  box-shadow: 0 4px 14px rgba(0,0,0,0.28);
}
.rest-timer-widget {
  position: fixed; left: 12px; right: 12px; bottom: 78px; z-index: 35;
  max-width: 420px; margin: 0 auto; padding: 0; overflow: hidden;
  box-shadow: 0 6px 20px rgba(0,0,0,0.28);
}
.rest-timer-bar { height: 4px; background: var(--accent); transition: width 1s linear; }
.rest-timer-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; }
.rest-timer-label { font-size: 11px; font-weight: 700; color: var(--text-dim); letter-spacing: 0.05em; }
.rest-timer-time { font-family: var(--font-mono); font-size: 22px; font-weight: 700; line-height: 1.2; }
.rest-timer-controls { display: flex; gap: 6px; flex-shrink: 0; }
.rest-timer-controls .btn { padding: 8px 10px; }
::-webkit-scrollbar { display: none; }

/* ---------- Scroll indicators: native scrollbars are hidden above (::-webkit-scrollbar{display:
   none}), so this is the only visible scroll affordance. A thin bar that fades in while the page
   (or an inner scrollable box, e.g. Meal Builder's food list) is actively scrolling, then fades
   back out ~700ms after motion stops — see attachScrollIndicators()/updatePageScrollIndicator()
   in JS, which size and position these from real scrollTop/scrollHeight on every scroll event. */
.scroll-indicator {
  position: absolute; top: 0; right: 2px; width: 4px; min-height: 24px; border-radius: 2px;
  background: var(--text-faint); opacity: 0; pointer-events: none;
  transition: opacity 0.25s ease; z-index: 40;
}
.scroll-indicator.visible { opacity: 0.6; }
.scroll-indicator-page { position: fixed !important; right: 3px; }
/* Horizontal variant for the bottom tabbar: same fade-in-while-scrolling behavior, rotated —
   a thin bar along the tabbar's top edge instead of the page's right edge. Positioned/sized
   from real scrollLeft/scrollWidth by updateTabbarScrollIndicator() in JS. */
.scroll-indicator-h {
  position: fixed !important; top: auto; right: auto; bottom: auto; left: 0;
  width: auto; min-width: 26px; height: 3px; min-height: 0;
}
.scroll-box { position: relative; }

/* ---------- In-app confirm modal (native confirm() is blocked in sandboxed artifact iframes) ---------- */
.modal-overlay {
  position: fixed; inset: 0; z-index: 100; background: rgba(0,0,0,0.55);
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.modal-overlay.hidden { display: none; }
.modal-box {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 20px; max-width: 340px; width: 100%;
}
.modal-msg { font-size: 14px; line-height: 1.5; margin-bottom: 18px; color: var(--text); }
.modal-actions { display: flex; gap: 8px; }

.accent-swatch-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.accent-swatch {
  width: 42px; height: 42px; border-radius: 50%; border: 2px solid var(--border-soft);
  background: var(--sw); cursor: pointer; padding: 0; position: relative; flex-shrink: 0;
  transition: transform 0.1s;
}
.accent-swatch:active { transform: scale(0.92); }
.accent-swatch.active { border-color: var(--text); box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--sw); }
.accent-swatch.active::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,0.35);
}
.accent-swatch-label { font-size: 9px; text-align: center; margin-top: 4px; color: var(--text-faint); }
.accent-swatch-item { display: flex; flex-direction: column; align-items: center; }

/* ---------- Notes: rich-text editor + rendered note body ---------- */
.rt-toolbar { display: flex; gap: 6px; margin-bottom: 8px; flex-wrap: wrap; }
.rt-btn { background: var(--surface2); border: 1px solid var(--border); color: var(--text); min-width: 32px; height: 30px; padding: 0 9px; border-radius: var(--radius); font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
.rt-btn:active { transform: scale(0.95); }
.note-editor { min-height: 140px; border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 11px; background: var(--surface2); color: var(--text); font-size: 14px; outline: none; }
.note-editor:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.note-editor:empty:before { content: attr(data-placeholder); color: var(--text-faint); }
.rich-text { line-height: 1.5; }
.rich-text ul, .rich-text ol { margin: 4px 0 4px 20px; padding: 0; }
.rich-text b, .rich-text strong { font-weight: 700; }
.note-body:empty:before { content: '(empty)'; color: var(--text-faint); }

/* ---------- Photo attachments (Notes + Measurements) ---------- */
.photo-thumb-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.photo-thumb { position: relative; width: 64px; height: 64px; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--border); flex-shrink: 0; }
.photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; cursor: pointer; }
.photo-thumb-remove { position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; border-radius: 50%; background: rgba(0,0,0,0.6); border: none; color: #fff; display: flex; align-items: center; justify-content: center; padding: 0; }
.photo-thumb-remove svg { width: 9px; height: 9px; }
.card-photo-row .photo-thumb { width: 52px; height: 52px; }

/* ---------- Calendar (Schedule -> Calendar) ---------- */
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 4px; }
.cal-weekday { text-align: center; font-size: 10px; font-weight: 700; color: var(--text-faint); padding: 4px 0; }
.cal-cell { position: relative; aspect-ratio: 1; background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; color: var(--text); font-size: 12px; }
.cal-cell-blank { background: transparent; border-color: transparent; }
/* An inset box-shadow (not border-color/background) so today's ring survives being combined with
   .cal-cell-selected below, which sets both of those — the two used to collide and today's own
   marker vanished whenever today was also the selected day (the default state on every visit).
   --good rather than another accent shade: it's a distinct hue per aesthetic, so "today" reads
   as its own signal instead of a dimmer version of the selection color. */
.cal-cell-today { box-shadow: inset 0 0 0 2px var(--good); }
.cal-cell-today .cal-daynum { color: var(--good); font-weight: 800; }
.cal-cell-selected { border-color: var(--accent); background: var(--accent-soft); }
.cal-daynum { font-family: var(--font-mono); font-weight: 700; }
.cal-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); }
/* Per-schedule indicator — a day with an assigned schedule (see scheduleForDate()), colored via
   inline style (scheduleColorFor()) since the palette is categorical, not an aesthetic token.
   Opposite corner from the reminder dot above so both signals are visible on the same cell at
   once; pointer-events:none so it never steals the cell button's own click. */
.cal-anchor-icon { position: absolute; top: 2px; right: 2px; width: 10px; height: 10px; pointer-events: none; }
/* Opposite corner from .cal-anchor-icon on purpose, so a scheduled day that's also a due day shows
   both without collision. A plain colour swatch rather than an icon -- matches the day-extra rows'
   own convention for a section identity that isn't tied to one specific glyph. */
.cal-charge-mark { position: absolute; top: 3px; left: 3px; width: 6px; height: 6px; border-radius: 2px; pointer-events: none; }
.cal-anchor-icon svg { width: 100%; height: 100%; display: block; }

/* Zoom toggle (YEAR/MONTH/WEEK/DAY) — reuses .unit-toggle's pill styling, just stretched full
   width with evenly-flexed buttons instead of content-sized ones. */
.cal-zoom-toggle { width: 100%; margin-bottom: 12px; }
.cal-zoom-toggle button { flex: 1; text-align: center; }

/* Schedule color legend, above the grid on Year/Month/Week (Day already names its schedule in
   text, see renderDailySchedule()) — decodes the .cal-anchor-icon / .cal-mini-anchor-dot colors
   into the actual schedule names they stand for. */
.cal-schedule-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 11px; color: var(--text-dim); margin-bottom: 10px; }
.cal-legend-swatch { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 4px; vertical-align: middle; }

/* ---------- Schedule Builder: week-at-a-glance strip (renderWeekOverviewStrip()) ---------- */
.week-overview-strip { display: flex; gap: 5px; }
.week-overview-day { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; }
.week-overview-label { font-size: 10px; font-weight: 700; color: var(--text-faint); }
/* No aspect-ratio here (unlike .cal-cell) — these badges only ever share a row with 6 others, so
   the extra horizontal-only room goes toward fitting more of a schedule's name (5 chars, not 3 —
   "Weekday"/"Weekend" only actually diverge at their 5th letter, the single most likely real
   collision this abbreviation has to survive). */
.week-overview-badge { width: 100%; height: 30px; border: 1.5px solid; border-radius: 6px; background: var(--surface2); display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 800; letter-spacing: 0.01em; overflow: hidden; padding: 0 2px; }
.week-overview-empty { width: 100%; height: 30px; border: 1.5px dashed var(--border); border-radius: 6px; background: transparent; color: var(--text-faint); display: flex; align-items: center; justify-content: center; font-size: 12px; }
.week-overview-conflict { position: absolute; top: -5px; right: -5px; width: 14px; height: 14px; border-radius: 50%; background: var(--warn); color: #1a1a1a; font-size: 9px; font-weight: 900; display: flex; align-items: center; justify-content: center; }

/* Year zoom: a compact 12-up grid of mini-months, 2 across (was 3 — that went off-screen on a
   real phone despite fitting the 390px viewport this was designed/tested against, so 2 is the
   safer default). No weekday header row — no room for it at this size — and day numbers alone
   (no dot) mark a day with a reminder, since a 5px dot doesn't read at this cell size;
   .cal-mini-today's ring takes priority visually over .cal-mini-has's color when a day is both
   (rare, but consistent with the full .cal-cell-today/-selected coexistence rule above). */
.cal-year-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.cal-mini-month { background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 6px; }
.cal-mini-month-label { font-size: 11px; font-weight: 800; letter-spacing: 0.05em; color: var(--text-dim); text-align: center; margin-bottom: 4px; cursor: pointer; }
.cal-mini-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.cal-mini-cell { position: relative; aspect-ratio: 1; background: transparent; border: none; color: var(--text-faint); font-size: 9px; font-family: var(--font-mono); display: flex; align-items: center; justify-content: center; border-radius: 2px; }
.cal-mini-blank { visibility: hidden; }
.cal-mini-has { color: var(--accent); font-weight: 800; }
.cal-mini-today { box-shadow: inset 0 0 0 1px var(--good); color: var(--good); font-weight: 800; }
.cal-mini-selected { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
/* Same per-schedule signal as .cal-anchor-icon, shrunk to a flat dot — a detailed icon doesn't
   read at ~14px, a solid color square still does. */
.cal-mini-anchor-dot { position: absolute; top: 1px; right: 1px; width: 4px; height: 4px; border-radius: 1px; pointer-events: none; }

/* ---------- Habits: multi-habit "success calendar" (Schedule -> Setup -> Habits) ---------- */
.habit-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 4px; }
.habit-cal-cell { aspect-ratio: 1; background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 2px; overflow: hidden; }
.habit-cal-blank { background: transparent; border-color: transparent; }
.habit-cal-daynum { font-family: var(--font-mono); font-weight: 700; font-size: 11px; }
.habit-cal-marks { display: flex; flex-wrap: wrap; gap: 2px; justify-content: center; }
/* Color = status (kept/broke), shape = which habit — see habitShapeFor()/habitStatusOn(). An
   unmarked day always renders as a plain hollow circle regardless of the habit's real shape
   (shapeClass forces this in renderHabitCalendar()) — distinguishing *which* habit hasn't been
   logged yet isn't worth the visual noise; distinguishing *that* it's unmarked is what matters. */
.habit-mark { display: inline-block; width: 6px; height: 6px; flex-shrink: 0; }
.habit-shape-circle { border-radius: 50%; }
.habit-shape-square { border-radius: 1px; }
.habit-shape-triangle { clip-path: polygon(50% 0%, 0% 100%, 100% 100%); }
.habit-shape-diamond { transform: rotate(45deg); border-radius: 1px; }
.habit-mark-kept { background: var(--good); }
.habit-mark-broken { background: var(--bad); }
.habit-mark-unmarked { background: transparent; border: 1px solid var(--text-faint); box-sizing: border-box; }
.habit-cal-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 11px; color: var(--text-dim); margin-bottom: 10px; }
.habit-legend-swatch { width: 8px; height: 8px; margin-right: 4px; background: var(--text-dim); vertical-align: middle; }

/* ---------- Calendar Day zoom: the day timeline (renderDailySchedule) ----------
   A list rather than a to-scale hour grid, chosen deliberately: the whole day fits one phone
   screen, and anchor check-off keeps a full-size tap target that a 22px-tall 30-minute block in
   a time grid couldn't offer. Duration is carried by the bar height plus explicit text, and
   unscheduled stretches get named outright (.day-gap) instead of being empty space. */
.day-row { display: flex; gap: 10px; align-items: stretch; }
.day-row + .day-row, .day-gap + .day-row { border-top: 1px solid var(--border-soft); }
.day-row-now { background: var(--accent-soft); border-radius: var(--radius); }
.day-bar-col { width: 4px; flex-shrink: 0; display: flex; align-items: center; }
.day-bar { width: 4px; border-radius: 2px; }
.day-body { flex: 1; padding: 10px 0; min-width: 0; }
.day-gap { display: flex; gap: 8px; align-items: center; padding: 4px 0 4px 14px; font-size: 10px; color: var(--text-faint); font-style: italic; }
.day-gap-line { flex: 1; border-top: 1px dashed var(--border-soft); }
.day-chip { display: inline-block; font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 3px; margin-left: 5px; vertical-align: middle; letter-spacing: 0.04em; }
.day-chip-now { background: var(--bad); color: #fff; }
/* ---------- Week zoom: the time rollup (renderWeekTimeRollup()) ---------- */
.rollup-row + .rollup-row { margin-top: 10px; }
.rollup-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; }
.rollup-label { font-size: 12px; font-weight: 700; letter-spacing: 0.03em; }
.rollup-swatch { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 6px; }
.rollup-mins { font-size: 12px; color: var(--text-dim); }
.rollup-track { height: 6px; border-radius: 3px; background: var(--surface2); overflow: hidden; }
.rollup-fill { height: 100%; border-radius: 3px; }

/* ---------- Agenda: the next 7 days (renderAgenda()) ---------- */
.agenda-day { cursor: pointer; }
.agenda-today { border-color: var(--accent); }
.agenda-daylabel { font-size: 13px; font-weight: 800; letter-spacing: 0.04em; }
.agenda-date { font-size: 11px; color: var(--text-faint); }
.agenda-items { margin-top: 8px; border-top: 1px solid var(--border-soft); }
.agenda-item { display: flex; gap: 10px; align-items: baseline; padding: 6px 0; }
.agenda-item + .agenda-item { border-top: 1px solid var(--border-soft); }
.agenda-time { font-size: 10px; color: var(--text-faint); flex: 0 0 58px; }
.agenda-label { font-size: 13px; font-weight: 600; flex: 1; min-width: 0; }
.agenda-repeat { margin-left: 5px; color: var(--text-faint); font-size: 11px; vertical-align: middle; }

/* A genuine collision between two blocks neither of which is marked "open" (see
   dayOverlapWarnings()). --warn, matching the app's other "pay attention" signals. */
.day-chip-clash { background: color-mix(in srgb, var(--warn) 20%, transparent); color: var(--warn); }

/* Calendar Day: the untimed day-level band (renderDayUntimedItems()) — planned workouts, planned
   meals and habits, none of which carry clock times and so can't live in the timeline above. */
.day-extra-group + .day-extra-group { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-soft); }
.day-extra-label { display: flex; align-items: center; font-size: 10px; font-weight: 700; letter-spacing: 0.08em; color: var(--text-dim); margin-bottom: 8px; }
.day-extra-swatch { width: 8px; height: 8px; border-radius: 2px; margin-right: 6px; flex-shrink: 0; }
.day-extra-row { display: flex; align-items: center; gap: 8px; padding: 7px 0; }
.day-extra-row + .day-extra-row { border-top: 1px solid var(--border-soft); }
.day-extra-name { flex: 1; min-width: 0; font-size: 13px; font-weight: 600; }
.day-extra-meta { font-size: 11px; color: var(--text-faint); flex-shrink: 0; }

/* Past-due reminder mark (pastDueMark()). Takes the active aesthetic's own --warn so it belongs to
   whatever theme is on, rather than one fixed hue sitting outside the palette. The glow is built
   from currentColor for the same reason — it tracks --warn automatically with no per-theme rule. */
.past-due-mark {
  color: var(--warn); font-weight: 900; font-size: 15px; line-height: 1;
  flex-shrink: 0; margin-right: 6px;
  text-shadow: 0 0 4px currentColor, 0 0 10px currentColor;
  animation: past-due-pulse 1.6s ease-in-out infinite;
}
@keyframes past-due-pulse {
  0%, 100% { text-shadow: 0 0 3px currentColor, 0 0 6px currentColor; opacity: 0.8; }
  50%      { text-shadow: 0 0 6px currentColor, 0 0 15px currentColor; opacity: 1; }
}
/* Someone who's asked their OS for less motion still gets the mark, just held at a steady glow. */
@media (prefers-reduced-motion: reduce) {
  .past-due-mark { animation: none; text-shadow: 0 0 4px currentColor, 0 0 10px currentColor; }
}


/* ---------- Cross-entity links (renderLinkChips / renderLinkPicker) ----------
   A chip carries its target's home-section colour (HOME_SECTION_META), so which part of your life
   a connection points into is legible at a glance rather than needing to be read. */
.link-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
.link-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 4px 3px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--lc) 14%, var(--surface)); border: 1px solid color-mix(in srgb, var(--lc) 45%, transparent);
  font-size: 11px; font-weight: 600; max-width: 100%; }
.link-chip-label { cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
.link-chip-dead .link-chip-label { opacity: .5; text-decoration: line-through; cursor: default; }
.link-chip-x { background: none; border: none; padding: 0 2px; color: var(--text-faint); display: flex; align-items: center; font-size: 10px; }
.link-swatch { width: 7px; height: 7px; border-radius: 2px; flex-shrink: 0; }
.link-add { background: none; border: 1px dashed var(--border); color: var(--text-faint);
  border-radius: 999px; padding: 3px 9px; font-size: 10px; font-weight: 700; letter-spacing: .04em; }

.link-picker-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 60; }
.link-picker { position: fixed; left: 50%; transform: translateX(-50%); top: 8vh; width: min(92vw, 460px);
  max-height: 80vh; overflow-y: auto; z-index: 61; background: var(--surface); border: 1px solid var(--accent);
  border-radius: var(--radius); padding: 14px; box-shadow: 0 12px 40px rgba(0,0,0,.5); }
.link-results { max-height: 52vh; overflow-y: auto; }
.link-result { display: flex; align-items: baseline; gap: 8px; padding: 8px 0; cursor: pointer; }
.link-result + .link-result { border-top: 1px solid var(--border-soft); }
.link-result-title { font-size: 13px; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-result-meta { font-size: 10px; color: var(--text-faint); flex-shrink: 0; }



/* Brief highlight on the thing you just navigated to (flashEntity()). Several destinations are
   lists where the entity is one row among many, so landing on the right screen isn't the same as
   finding the row. Uses --accent so it reads as "here" in every aesthetic. */
@keyframes entity-flash-fade {
  0%   { box-shadow: 0 0 0 2px var(--accent), 0 0 18px color-mix(in srgb, var(--accent) 60%, transparent); }
  100% { box-shadow: 0 0 0 2px transparent, 0 0 0 transparent; }
}
.entity-flash { animation: entity-flash-fade 1.4s ease-out; border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) {
  /* Still mark it, just without the fade -- the point is finding the row, not the animation. */
  .entity-flash { animation: none; box-shadow: 0 0 0 2px var(--accent); }
}




/* ---------- Weight goal: pace and projection ---------- */
/* The bar is the only "big number" here on purpose. Required / actual / projected are a plain
   label-value-context list rather than three tiles, because two of the three rows routinely say
   "not yet" -- a goal's first fortnight has no trend to read, and a tile that renders as a dash
   looks broken in a way a list row doesn't. */
.goal-bar {
  height: 8px; border-radius: 4px; background: var(--surface2);
  border: 1px solid var(--border-soft); overflow: hidden; margin-top: 14px;
}
.goal-bar-fill { height: 100%; background: var(--accent); border-radius: 3px; }

.goal-rows { margin-top: 14px; border-top: 1px solid var(--border-soft); }
.goal-row {
  display: grid; grid-template-columns: 74px auto 1fr; gap: 10px; align-items: baseline;
  padding: 9px 0; border-bottom: 1px solid var(--border-soft);
}
.goal-row:last-child { border-bottom: none; }
.goal-row-k { font-size: 11px; color: var(--text-faint); letter-spacing: 0.04em; text-transform: uppercase; }
.goal-row-v { font-size: 14px; font-weight: 700; white-space: nowrap; }
.goal-row-x { font-size: 11px; color: var(--text-faint); text-align: right; min-width: 0; }

/* Advisory only -- the band names where a rate sits, it never prevents one. Same posture as the
   water target being "a target, not a cap". */
.goal-band { font-weight: 700; }
.goal-band-conservative { color: var(--good); }
.goal-band-typical      { color: var(--good); }
.goal-band-aggressive   { color: var(--warn); }
.goal-band-beyond       { color: var(--bad); }

.goal-pace { font-weight: 700; }
.goal-pace-ok     { color: var(--good); }
.goal-pace-behind { color: var(--warn); }

.goal-note-late {
  font-size: 11px; color: var(--warn); line-height: 1.5;
  padding: 9px 0 0; border-top: 1px solid var(--border-soft);
}

/* ---------- Phases ---------- */
/* Each phase is a card because it's an editable object with its own controls, not a row in a
   readout -- the opposite call from .goal-row above, and for the opposite reason. The left edge
   carries the state instead of a second badge colour: the whole strip of cards should read
   done / done / NOW / upcoming at a glance without any of them shouting. */
.phase-list { display: grid; gap: 10px; }
.phase-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  border-left: 3px solid var(--border); padding: 12px 13px;
}
.phase-state-past    { border-left-color: var(--border); }
.phase-state-current { border-left-color: var(--accent); }
.phase-state-future  { border-left-color: var(--border-soft); }

/* .ehead's flex layout is scoped to `.entry-card .ehead` in the entry-card block above, so a
   phase card has to declare its own -- without this the chip drops onto its own line under a
   full-width input, which is exactly what it did. */
.phase-card .ehead { display: flex; justify-content: space-between; align-items: center; gap: 8px; }

/* Borderless so the label reads as a heading, not a form field, until you're in it. Same treatment
   as the goal name above it.

   Written as input[type="text"].phase-label (0,2,1) on purpose: the base input rule is (0,1,1) and
   a bare `.phase-label` (0,1,0) loses to it, so the "border: none" silently did nothing. Same trap
   as .log-water-target further down. */
input[type="text"].phase-label {
  font-weight: 700; font-size: 14px; border: none; background: transparent;
  padding: 0; color: var(--text); font-family: var(--font-body);
  flex: 1 1 auto; width: auto; min-width: 0;
}
.phase-chip {
  font-size: 9px; font-weight: 700; letter-spacing: 0.08em; padding: 2px 6px;
  border-radius: 3px; white-space: nowrap; flex: none;
}
.phase-chip-past    { color: var(--text-faint); background: var(--surface2); }
.phase-chip-current { color: var(--bg); background: var(--accent); }
.phase-chip-future  { color: var(--text-dim); background: var(--surface2); }

.phase-when { font-size: 11px; color: var(--text-dim); margin-top: 3px; }

/* Three tracks that collapse to two then one. `minmax(0, 1fr)` rather than `1fr`: a number input
   has a min-content width wider than its track, which pushes the grid past the card otherwise. */
.phase-controls {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px; margin-top: 11px;
}
.phase-controls .field { margin-bottom: 0; }
/* An exercise block has one control, not three -- a 3-up grid would stretch a two-digit week count
   across a third of the card. */
.phase-controls-1 { grid-template-columns: minmax(0, 120px); }
/* font-size only -- `select` sets padding-right: 30px to clear its own arrow, and a padding
   shorthand here would win on specificity and put the text under the arrow. */
.phase-controls input, .phase-controls select { font-size: 14px; }
@media (max-width: 360px) { .phase-controls { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

.phase-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }

/* The calorie target sits on its own row rather than as a fourth control column -- it's the one
   number here you'll act on every day, and a 4-up grid on a phone would squeeze all four. */
.phase-cal { display: flex; gap: 8px; align-items: flex-end; margin-top: 11px; }
.phase-cal .field { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.phase-cal input { font-size: 14px; }
.phase-cal .btn { flex: none; }
.phase-cal-note { font-size: 11px; color: var(--text-faint); line-height: 1.5; margin-top: 5px; }

/* Stacked, not .suggestion-box's side-by-side: two buttons in a column beside the text wrapped
   "USE THIS" and "KEEP MINE" onto two lines each at phone width. Same offer, room to read it. */
.phase-drift {
  margin-top: 9px; padding: 10px 11px;
  background: var(--accent-soft); border: 1px solid var(--accent); border-radius: var(--radius);
}
.phase-drift-actions { display: flex; gap: 8px; margin-top: 9px; }
.phase-drift-actions .btn { flex: 1 1 0; }

/* Says WHICH target a figure came from. Per-phase targets mean the number you eat against lives in
   two places depending on the day, so every screen showing one names its source. */
.cal-source { font-size: 11px; color: var(--text-faint); line-height: 1.5; margin-top: 4px; }

/* Names which week the Planner is editing. Renders at all only once training blocks exist -- with
   one plan there is nothing to disambiguate and a banner would be pure noise. */
.planner-scope {
  padding: 10px 12px; margin-bottom: 14px;
  background: var(--surface2); border: 1px solid var(--border-soft); border-radius: var(--radius);
  font-size: 11px; color: var(--text-dim); line-height: 1.5;
}
.planner-scope-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

/* ---------- Deload ---------- */
/* Everything a deload does is display-time: the saved workout is never edited, so the flag exists to
   make that visible. It sits beside the target it changed, with the original alongside it, because
   a halved number with no explanation reads as a bug. */
.deload-flag {
  font-size: 9px; font-weight: 700; letter-spacing: 0.08em;
  padding: 1px 5px; border-radius: 3px; background: var(--warn); color: var(--bg);
}
.deload-bar {
  margin-top: 10px; padding: 10px 12px;
  background: var(--surface2); border: 1px solid var(--border-soft); border-radius: var(--radius);
  font-size: 12px; color: var(--text-dim);
}
.deload-bar-on { border-color: var(--warn); }
.deload-bar-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.deload-levers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 10px; }
.deload-levers .field { margin-bottom: 0; }
.deload-levers input { font-size: 14px; }

/* ---------- Lift library ---------- */
/* A lift is pure identity and outlives any plan using it, so the link is shown as a distinct row
   rather than another form field -- it's a reference, not a value you type. */
.lift-link {
  display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left;
  padding: 7px 9px; margin-bottom: 8px;
  background: var(--surface2); border: 1px dashed var(--border); border-radius: var(--radius);
  color: var(--text-dim); font-family: var(--font-body); font-size: 12px; cursor: pointer;
}
.lift-link-set { border-style: solid; border-color: var(--accent); color: var(--text); }
.lift-link-k { font-size: 9px; font-weight: 700; letter-spacing: 0.08em; color: var(--text-faint); flex: none; }
.lift-link-v { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.lift-link-short { font-size: 10px; color: var(--text-faint); font-family: var(--font-mono); flex: none; }

.lift-picker {
  padding: 11px 12px; margin-bottom: 8px;
  background: var(--surface2); border: 1px solid var(--accent); border-radius: var(--radius);
}
/* Muscle first, then the lift: a dozen options beats scrolling two hundred, and muscle alone makes
   the list short enough that a second filter isn't worth the tap. */
.lift-muscles { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 10px; }
.lift-list { display: grid; gap: 4px; max-height: 260px; overflow-y: auto; }
.lift-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  width: 100%; text-align: left; padding: 8px 10px;
  background: var(--surface); border: 1px solid var(--border-soft); border-radius: var(--radius);
  color: var(--text); font-family: var(--font-body); font-size: 13px; cursor: pointer;
}
.lift-row-current { border-color: var(--accent); }
.lift-row-name { font-weight: 600; min-width: 0; }
.lift-row-short { font-size: 10px; color: var(--text-faint); font-family: var(--font-mono); flex: none; }
.lift-add { border-top: 1px solid var(--border-soft); margin-top: 10px; }
/* `.ehead`'s flex layout is scoped to `.entry-card .ehead`, so anything else using that class has to
   declare its own -- without this the delete button drops onto its own line under a full-width
   select. Third time this has bitten (see .phase-card .ehead above). */
.ex-target .ehead { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.ex-target .ehead select { min-width: 0; }

/* A reached target turns green and stays -- monotonic by nature, and nothing un-achieves it. */
.ex-target-hit { border-color: var(--good); }
.ex-target-hit-text { color: var(--good); }
.ex-target-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-bottom: 4px; }
.ex-target-fields .field { margin-bottom: 0; }
.ex-target-fields input { font-size: 14px; }

/* The PR log: one cell per rep threshold, because "225 on the bar" and "225 for five" are different
   achievements and a single number can't show both. */
.pr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 6px; }
.pr-cell {
  padding: 7px 8px; border-radius: var(--radius);
  background: var(--surface2); border: 1px solid var(--border-soft); text-align: center;
}
.pr-reps { font-size: 9px; font-weight: 700; letter-spacing: 0.06em; color: var(--text-faint); }
.pr-weight { font-size: 16px; font-weight: 700; margin: 2px 0; font-variant-numeric: tabular-nums; }
.pr-date { font-size: 9px; color: var(--text-faint); }

.lift-muscle-chip {
  font-size: 9px; font-weight: 700; color: #1a1a1a;
  padding: 2px 7px; border-radius: 10px; flex: none;
}

.phase-sum {
  margin-top: 10px; padding: 10px 13px;
  background: var(--surface2); border: 1px solid var(--border-soft); border-radius: var(--radius);
}
.phase-sum-note { font-size: 11px; color: var(--text-faint); margin-top: 3px; }

/* ---------- Skills ---------- */
/* The five rungs never get their own hex. Each maps onto a token every aesthetic already
   redefines, so a skill badge retints with the theme for free and can never be the one element
   left painted in the default palette:

     new        border-soft + text-faint   nothing has happened yet, so it claims nothing
     learning   --warn                     in progress, and the colour the app already uses for it
     proficient --accent                    the theme's own voice, for the rung most items sit at
     expert     --good                      outlined
     mastered   --good, filled              retired: the same hue, but unmistakably terminal

   Four hues, five appearances -- expert and mastered share a colour because they ARE the same
   direction, and filling one of them separates "still practising it" from "done with it" without
   spending a fifth hue the palettes don't reliably have. */
.skill-list { display: flex; flex-direction: column; gap: 8px; }
.skill-row {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; padding: 12px 13px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text); font-family: inherit; cursor: pointer;
}
.skill-row-main { flex: 1 1 auto; min-width: 0; }
.skill-row-name { font-weight: 650; font-size: 15px; }
.skill-row-sub { font-size: 11px; color: var(--text-faint); margin-top: 2px; }
.skill-row-time { flex: none; text-align: right; font-variant-numeric: tabular-nums; }
.skill-row-time .mono { font-size: 14px; font-weight: 700; }
.skill-row-when { font-size: 9px; letter-spacing: 0.06em; color: var(--text-faint); }
.skill-row-chev { flex: none; display: flex; font-size: 16px; color: var(--text-faint); }
/* Dashed, because it isn't a skill yet -- it's a skill-shaped thing waiting to be converted. The
   border says "temporary" before the LEGACY pill has to. */
/* Archived: still listed, visibly not in play. Dimmed rather than hidden, because the reason it
   is still here at all is that logged hours out in the schedule still point at it. */
.skill-row-archived { opacity: 0.6; cursor: default; }
.skill-row-archived .skill-row-name { font-weight: 600; }

/* Editable in place, like every other name in the app -- typed over, not opened in a dialog. */
/* All four inline-edit fields here are written as `input[type="text"].x` (0,2,1) rather than a
   bare class (0,1,0), because the base input rule at the top of this file is (0,1,1) and would
   otherwise keep its border and box background -- turning every editable name back into a form
   field. Exactly the trap documented on .phase-label above; this is the third and fourth time. */
input[type="text"].skill-title {
  width: 100%; padding: 4px 0; margin-bottom: 4px;
  background: none; border: none; border-bottom: 1px solid transparent;
  color: var(--text); font-family: var(--font-head); font-size: 22px; font-weight: 700;
}
input[type="text"].skill-title:focus { outline: none; border-bottom-color: var(--accent); }
input[type="text"].skill-list-name {
  flex: 1 1 auto; min-width: 0; width: auto; padding: 3px 0;
  background: none; border: none; border-bottom: 1px solid transparent;
  color: var(--text); font-family: var(--font-head); font-size: 14px;
  font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
}
input[type="text"].skill-list-name:focus { outline: none; border-bottom-color: var(--accent); }

.skill-add-row { display: flex; gap: 6px; align-items: center; padding: 9px 10px; }
.skill-add-row input { flex: 1 1 auto; min-width: 0; margin: 0; }
.skill-add-row .btn { flex: none; }

/* Fourth time: `.ehead`'s flex layout is scoped to `.entry-card .ehead`, so every other user of
   the class declares its own (see .phase-card and .ex-target above). */
.skill-item .ehead { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
input[type="text"].skill-item-name {
  flex: 1 1 auto; min-width: 0; width: auto; padding: 2px 0;
  background: none; border: none; border-bottom: 1px solid transparent;
  color: var(--text); font-family: var(--font-body); font-size: 14px; font-weight: 650;
}
input[type="text"].skill-item-name:focus { outline: none; border-bottom-color: var(--accent); }
/* A textarea rather than an input, because the descriptions that came across with the guitar
   migration run to a full sentence and an <input> silently clips them -- the old guitar screen
   wrapped this text, so an input here would have been a regression on migrated content. Two rows
   holds almost all of them; anything longer scrolls in place. `textarea.x` is (0,1,1), which
   beats the base `textarea` rule's min-height. */
textarea.skill-item-detail {
  width: 100%; padding: 2px 0; background: none; border: none; resize: none;
  min-height: 0; line-height: 1.35; overflow-y: auto;
  color: var(--text-dim); font-family: var(--font-body); font-size: 12px;
}
textarea.skill-item-detail:focus { outline: none; color: var(--text); }
/* Where it's supported this sizes to the actual text, so a one-line note stops reserving a
   second row. Progressive enhancement on purpose -- the alternative is measuring scrollHeight
   per item after every render, which is 39 forced reflows on a migrated guitar list. Everywhere
   else the two rows stand and the rule below keeps the leftover line from compounding. */
textarea.skill-item-detail { field-sizing: content; }
textarea.skill-item-detail + .skill-sched { margin-top: 0; }

/* The rung is ONE custom property, not a colour repeated per component. The stripe down the card's
   edge and the badge in its header both read `--rung`, so adding a rung means one line here rather
   than an edit in every place a rung is drawn -- and the badge inside a card inherits it for free.
   It's a stripe drawn as a ::before rather than a left border because several aesthetics restate
   `.panel`'s border-color at (0,2,0) and would repaint a border out from under us; nothing anywhere
   sets --rung, so this survives every theme. */
.skill-item, .skill-band { --rung: var(--border-soft); }
.skill-item-new,        .skill-band-new        { --rung: var(--border); }
.skill-item-learning,   .skill-band-learning   { --rung: var(--warn); }
.skill-item-proficient, .skill-band-proficient { --rung: var(--accent); }
.skill-item-expert,     .skill-band-expert,
.skill-item-mastered,   .skill-band-mastered   { --rung: var(--good); }

.skill-item { position: relative; padding-left: 17px; overflow: hidden; }
.skill-item::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--rung);
}
.skill-item-mastered input[type="text"].skill-item-name { color: var(--text-dim); }

.skill-band {
  flex: none; font-size: 9px; font-weight: 700; letter-spacing: 0.07em;
  padding: 2px 7px; border-radius: 10px; white-space: nowrap;
  border: 1px solid var(--rung); color: var(--rung);
}
/* Mastered is EXPERT's hue filled in: same direction, unmistakably terminal, and no fifth colour
   the palettes can't all supply. */
.skill-band-mastered { background: var(--rung); color: var(--bg); }
.skill-band-new { color: var(--text-faint); }

/* One bar for the whole skill: how many items sit at each rung, in order. Segments are widths of
   a flex row rather than a stacked gradient, so a rung with no items simply isn't emitted and the
   bar stays honest at any mix. */
.skill-bar {
  display: flex; height: 10px; border-radius: 5px; overflow: hidden;
  background: var(--surface2); border: 1px solid var(--border-soft);
}
.skill-bar-seg { height: 100%; }
.skill-bar-new { background: var(--border); }
.skill-bar-learning { background: var(--warn); }
.skill-bar-proficient { background: var(--accent); }
.skill-bar-expert { background: color-mix(in srgb, var(--good) 55%, var(--surface)); }
.skill-bar-mastered { background: var(--good); }
.skill-legend { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 10px; color: var(--text-faint); }
.skill-legend-item { display: inline-flex; align-items: center; gap: 4px; text-transform: uppercase; letter-spacing: 0.05em; }
.skill-legend-item i { width: 8px; height: 8px; border-radius: 2px; flex: none; }

/* ---------- A practice session ---------- */
/* STALE is the one badge that isn't a rung: it says WHY an item is in the block, not where it sits
   on the ladder. Its own hue keeps that distinction visible -- a stale expert is still an expert. */
.skill-band-stale { --rung: var(--warn); background: none; }
.skill-start { padding: 12px 13px; }
.skill-start-title { font-family: var(--font-head); font-size: 12px; font-weight: 700; letter-spacing: 0.08em; }
.skill-start-note { font-size: 11px; color: var(--text-faint); line-height: 1.5; }
/* "4 of 5 learning slots" -- produced by arithmetic, not by asking a model. It goes quiet until it
   matters, and only turns red once you're genuinely past the cap. */
.skill-wip {
  flex: none; font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
  color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.skill-wip-over { color: var(--bad); }
/* The standing cost sits under the one-off one and only raises its voice when the ceiling is
   actually exceeded -- at which point it stops being background information and becomes the
   explanation for the red number above it. */
.skill-standing b { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.skill-standing-over { color: var(--text-dim); }
.skill-standing-over b { color: var(--bad); }

.skill-run-head { padding: 11px 13px; margin-bottom: 0; }
.skill-run-name { flex: 1 1 auto; min-width: 0; font-size: 15px; font-weight: 650; }
/* The allocated minutes, as the control that runs them. It always looked tappable; now it is.
   Deliberately quiet at rest -- it's an offer to focus, not an instruction, and four rating
   buttons below it already have first claim on the card's attention. */
.skill-run-timer {
  flex: none; min-width: 52px; padding: 4px 9px; cursor: pointer;
  background: var(--surface2); border: 1px solid var(--border-soft); border-radius: var(--radius);
  color: var(--text-dim); font-family: var(--font-mono); font-size: 13px; font-weight: 700;
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
/* Running is the one state that earns the accent: it's the only thing on the screen that's
   changing by itself, so it should be the thing your eye returns to. */
.skill-run-timer-on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.skill-run-timer-done { border-color: var(--good); color: var(--good); }
.skill-run-detail { font-size: 12px; color: var(--text-dim); margin: -2px 0 8px; }
/* A rated card recedes rather than vanishing: the block is also the record of what you just did,
   and a list that empties as you work gives you nothing to look back at mid-session. */
.skill-run-done > *:not(.skill-rate) { opacity: 0.5; }

/* Four equal targets, because the rating IS the interaction and a mis-tap costs an interval.
   `1fr` each rather than auto widths keeps them the same size whatever the label. */
.skill-rate { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; margin-top: 10px; }
.skill-rate-btn {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 2px; min-height: 44px; cursor: pointer;
  background: var(--surface2); border: 1px solid var(--border-soft); border-radius: var(--radius);
  color: var(--text-dim); font-family: inherit;
}
.skill-rate-label { font-size: 10px; font-weight: 700; letter-spacing: 0.05em; }
.skill-rate-hint { font-size: 8px; color: var(--text-faint); letter-spacing: 0.02em; }
/* Unselected buttons stay neutral: colouring all four at rest turns the row into a traffic light
   you have to decode, when the only thing that needs a colour is the answer you gave. */
.skill-rate-btn.active { color: var(--bg); border-color: transparent; font-weight: 700; }
.skill-rate-btn.active .skill-rate-hint { color: var(--bg); opacity: 0.72; }
.skill-rate-again.active { background: var(--bad); }
.skill-rate-hard.active { background: var(--warn); }
.skill-rate-good.active { background: var(--good); }
.skill-rate-easy.active { background: var(--accent); }

.skill-overflow { border-color: var(--warn); margin-top: 10px; }
.skill-overflow-title { font-size: 12px; font-weight: 700; color: var(--warn); margin-bottom: 3px; }

/* ---------- The pre-commit summary ---------- */
/* Direction is the whole point of this screen, so it gets the one signal that reads without being
   read: a stripe down the edge, green for a rung gained, red for one lost, neutral for a rung
   repeated. Same --rung mechanism as the item cards -- a ::before rather than a border, because
   several aesthetics restate `.panel`'s border-color and would repaint one out from under it. */
.skill-move { --rung: var(--border); position: relative; padding-left: 17px; overflow: hidden; }
.skill-move::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--rung); }
.skill-move-up { --rung: var(--good); }
.skill-move-down { --rung: var(--bad); }
.skill-move-line { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; }
.skill-move-arrow { color: var(--text-faint); font-size: 13px; }
.skill-move-due { font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
/* FIFTH time: `.ehead`'s flex layout is scoped to `.entry-card .ehead`, so every other user of
   the class declares its own (see .phase-card, .ex-target and .skill-item above). Without it the
   verdict chip drops onto its own line under the item name. */
.skill-move .ehead { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
/* The verdict is coloured by the RATING, not by where the item landed. Taking the band's colour
   meant two GOOD chips rendering differently depending on the rung underneath them, which reads
   as a distinction that isn't there. These match the four rating buttons in the runner. */
.skill-verdict-again { --rung: var(--bad); }
.skill-verdict-hard  { --rung: var(--warn); }
.skill-verdict-good  { --rung: var(--good); }
.skill-verdict-easy  { --rung: var(--accent); }

/* ---------- The ladder, made legible ---------- */
/* STUCK is not a rung -- it says an item is fighting you, which is orthogonal to how far along it
   is. Its own hue keeps that separate: a stuck EXPERT is still an expert. */
.skill-band-stuck { --rung: var(--bad); }
/* The plain-English half leads, because "due in 3" is the question people actually have; the raw
   numbers follow it for when the answer isn't enough. Wraps rather than truncating -- four short
   facts on a 390px screen is two lines, and two legible lines beat one clipped one. */
.skill-sched {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 10px; margin-top: 7px;
  font-family: var(--font-mono); font-size: 10px; color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.skill-sched-due { color: var(--text-dim); font-weight: 700; }
.skill-sched-bad { color: var(--bad); }

.skill-stuck { border-color: var(--bad); }
.skill-stuck-row { display: flex; align-items: center; gap: 9px; }
.skill-stuck-name { flex: 1 1 auto; min-width: 0; font-weight: 650; font-size: 13px; }
.skill-stuck-list { flex: none; font-size: 10px; color: var(--text-faint); }

/* What's WAITING, beside the name, on the list you see before opening anything. One flag, not
   three: a row is a glance, and three competing counts on it is a dashboard nobody reads. */
.skill-row-flag {
  display: inline-block; vertical-align: 1px; margin-left: 5px;
  font-family: var(--font-mono); font-size: 9px; font-weight: 700; letter-spacing: 0.05em;
  padding: 1px 6px; border-radius: 3px;
  background: var(--accent-soft); color: var(--accent);
}
/* Stale is the loudest because it means real time has passed rather than sessions -- the one thing
   the session counter can't notice on its own. */
.skill-row-flag-stale { background: color-mix(in srgb, var(--warn) 16%, var(--surface)); color: var(--warn); }
.skill-row-flag-stuck { background: color-mix(in srgb, var(--bad) 14%, var(--surface)); color: var(--bad); }

/* ---------- Skill targets ---------- */
/* Green on reach and it stays, like .ex-target-hit -- but a skill target CAN slip afterwards, so the
   card keeps its tick and the live count appears underneath in the warning colour rather than the
   tick quietly going away. */
.skill-target { --rung: var(--border); position: relative; padding-left: 17px; overflow: hidden; }
.skill-target::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--rung); }
.skill-target-hit { --rung: var(--good); border-color: var(--good); }
.skill-target-late { --rung: var(--warn); }
.skill-target .ehead { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.skill-target-name { flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: 650; }
.skill-target-bar { height: 6px; border-radius: 3px; background: var(--surface2); overflow: hidden; margin-bottom: 6px; }
.skill-target-fill { height: 100%; background: var(--accent); }

/* ---------- Lab panels ---------- */
/* A reading is a name, a number and -- only when it falls outside the reference interval you set --
   a flag. Deliberately three states and not a colour scale: the app records results, it does not
   grade them, and a gradient from green to red would be a verdict in everything but words. */
.lab-readings { display: grid; gap: 4px; margin-top: 2px; }
.lab-reading {
  display: flex; align-items: baseline; gap: 8px;
  padding: 5px 8px; border-radius: var(--radius);
  background: var(--surface2); border-left: 3px solid var(--border);
}
.lab-reading-name { flex: 1 1 auto; min-width: 0; font-size: 12px; }
.lab-reading-val { flex: none; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.lab-unit { font-style: normal; font-size: 9px; color: var(--text-faint); margin-left: 3px; }
/* Meeting your own stricter target takes the accent, not a green tick -- it's worth seeing, not
   congratulating. Outside the reference interval is the only thing that raises its voice. */
.lab-on-target { border-left-color: var(--good); }
.lab-out { border-left-color: var(--bad); }
.lab-out .lab-reading-val { color: var(--bad); }
.lab-flag {
  flex: none; font-family: var(--font-mono); font-size: 8px; font-weight: 700;
  letter-spacing: 0.06em; color: var(--bad);
  background: color-mix(in srgb, var(--bad) 14%, var(--surface)); padding: 1px 5px; border-radius: 3px;
}
/* A reading whose marker was deleted: shown, unranged, visibly lesser. It stays because the delete
   confirm said it would, not because it is still meaningful. */
.lab-orphan { opacity: 0.55; border-left-style: dashed; }

/* The position bar, built the same way the volume landmarks build theirs: zones painted as a
   linear-gradient across the track, a marker line for the reading. Soft fills throughout -- the
   bands are context, and a saturated red zone would be the verdict this screen refuses to give. */
.lab-bar { margin-top: 6px; }
.lab-bar-track {
  position: relative; height: 11px; border-radius: 20px;
  border: 1px solid var(--border-soft); overflow: hidden;
}
/* The current reading is the same shape as its own history, just solid and a size up. It used to be
   a tall bold rule, which was right when it was the only thing on the track and wrong the moment
   the trail arrived: one series rendered as two different objects reads as two different things.
   The dark halo is what separates it from the band behind it now that it no longer spans the bar. */
.lab-bar-mark {
  position: absolute; top: 50%; width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px; border-radius: 50%;
  background: var(--text); box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.45);
}
.lab-bar-key {
  display: flex; gap: 10px; margin-top: 3px;
  font-size: 9px; color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.lab-bar-key-target { color: var(--good); }

.lab-stand { padding: 9px 0; border-bottom: 1px solid var(--border-soft); }
.lab-stand:last-child { border-bottom: 0; }
.lab-stand-group {
  font-family: var(--font-head); font-size: 9px; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-faint); margin: 12px 0 2px;
}
.lab-stand-group:first-child { margin-top: 0; }
.lab-stand-head { display: flex; align-items: baseline; gap: 8px; }
.lab-stand-name { flex: 1 1 auto; min-width: 0; font-size: 12px; font-weight: 650; }
.lab-stand-val { flex: none; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.lab-stand-when { flex: none; font-size: 9px; color: var(--text-faint); }
.lab-out-text { color: var(--bad); }
.lab-count { flex: none; font-size: 10px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.lab-note { font-size: 11px; color: var(--text-dim); margin-top: 7px; line-height: 1.5; }
/* Said once, at the foot of the screen, and never repeated on a card. */
.lab-disclaimer {
  margin-top: 18px; padding: 12px 14px; border-radius: var(--radius);
  background: var(--surface2); border: 1px solid var(--border-soft);
  font-size: 10px; line-height: 1.6; color: var(--text-faint);
}
/* .ehead's flex layout is scoped to `.entry-card .ehead`, and a lab card IS an entry card, so this
   one needs nothing of its own -- noted because the last five components each did. */

/* The ranges editor. Two rows of two bounds per marker, and an empty box means "no bound on that
   side" -- which is also how you clear an override and fall back to the shipped default. */
.lab-range-row { padding: 9px 0; border-bottom: 1px solid var(--border-soft); }
.lab-range-row:last-of-type { border-bottom: 0; }
.lab-range-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.lab-range-name { flex: 1 1 auto; min-width: 0; font-size: 12px; font-weight: 650; }
/* Says a number is yours rather than mine, so a shipped default you deliberately kept and one you
   changed are never confused. */
.lab-range-edited {
  flex: none; font-family: var(--font-mono); font-size: 8px; font-weight: 700; letter-spacing: 0.06em;
  color: var(--accent); background: var(--accent-soft); padding: 1px 5px; border-radius: 3px;
}
.lab-range-grid { display: grid; grid-template-columns: auto 1fr auto 1fr; gap: 5px 7px; align-items: center; }
.lab-range-k { font-size: 10px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; }
.lab-range-dash { font-size: 10px; color: var(--text-faint); }
input[type="number"].lab-range-in {
  width: 100%; padding: 5px 7px; font-size: 12px; font-variant-numeric: tabular-nums;
}

/* A template is a starting point, so it reads as a choice rather than a submit: two lines, left
   aligned, and never the primary button next to CREATE. */
.skill-template { display: block; text-align: left; padding: 10px 12px; margin-bottom: 6px; }
.skill-template-name { display: block; font-weight: 700; font-size: 13px; }
.skill-template-blurb { display: block; font-size: 11px; color: var(--text-faint); font-weight: 400; margin-top: 2px; }
/* detail2 held a category from day one (a chord's "Open minor", a song's genre) and nothing ever
   rendered it. It's a subtitle, not a note, so it sits above the editable detail line. */
.skill-item-kind { font-size: 10px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-faint); margin: -2px 0 4px; }



/* ---------- A checked-off reminder ---------- */
/* It stays exactly where it was rather than disappearing -- the point is being able to look back
   at a day and see that you did the thing, instead of wondering because the row vanished. So this
   only lowers its claim on your attention; it never hides anything.

   Dimming each CHILD rather than the container is deliberate. opacity on a parent compounds onto
   everything inside it and cannot be raised back by a child, so `.reminder-done { opacity: .5 }`
   plus `.hit-mark { opacity: 1 }` renders the tick at 0.5 anyway -- making the one control you
   need in order to UNDO this the hardest thing on the card to see. Excluding the tick from the
   dimmed set is the only way it stays at full strength. .ehead is excluded from the first
   selector so its children aren't dimmed twice (0.55 * 0.55). */
.reminder-done > *:not(.hit-mark):not(.ehead),
.reminder-done > .ehead > *:not(.hit-mark) { opacity: 0.55; }
.reminder-done .reminder-title,
.reminder-done > .ehead input[type="text"] { text-decoration: line-through; }

/* ---------- Quick-log strips (LOG · AM / LOG · PM) ---------- */
/* Replaced two panels of always-visible number inputs -- ~360px of Home for four fields that sat
   empty most of the day and told you nothing about what you'd already logged. A chip shows today's
   value; tapping opens the sheet. */
.log-strip { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px; }
.log-chip {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 54px; padding: 8px 4px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text); font-family: inherit; cursor: pointer;
}
.log-chip-label {
  font-family: var(--font-head); font-size: 9px; font-weight: 700;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-faint);
}
.log-chip-value {
  font-size: 15px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--text-dim);
}
/* A logged value is the point of the chip, so it takes the accent; an unlogged dash stays quiet. */
.log-chip-set { border-color: var(--accent-dim); }
.log-chip-set .log-chip-value { color: var(--accent); }
.log-chip-plus {
  font-style: normal; font-size: 11px; font-weight: 700; margin-left: 3px;
  color: var(--text-faint); vertical-align: 1px;
}
/* Water's numbers mean nothing without their unit, and the chip is too narrow to spell it out on
   the value line, so it rides along in the label. */
.log-chip-unit { font-style: normal; opacity: 0.75; }
/* Water is the only chip carrying a fraction of four-digit numbers plus a + and a dot, so it gets
   a smaller value type rather than crowding its neighbours out of alignment. */
.log-chip-wide .log-chip-value { font-size: 13px; }
/* The hydration colour marker, on the chip itself — a marker that "lasts until changed" is only
   useful if it's visible without opening anything. Fixed swatch colours by design: this is a
   physical reference scale, so it must NOT follow the aesthetic's palette. The ring is what adapts. */
.log-chip-dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-left: 5px; vertical-align: 1px;
  border: 1px solid rgba(0, 0, 0, 0.35);
}

.log-color-row { align-items: center; }
.log-color-scale { flex: 1; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 4px; }
.log-color-sw {
  height: 30px; border-radius: 5px; border: 1px solid rgba(0, 0, 0, 0.35);
  padding: 0; cursor: pointer;
}
/* Outline rather than a fill change, so the swatch still shows its own true colour while selected —
   the whole point is comparing against it. */
.log-color-sw-on { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }
.log-color-sw:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.log-color-note { font-size: 10px; color: var(--text-faint); margin: -2px 0 8px; line-height: 1.45; }
/* A marker older than half a day is describing a body you no longer have. It stays visible -- it's
   the only reading you've got -- but stops presenting itself as current. */
.log-color-stale { color: var(--warn); font-weight: 700; }
.log-chip-dot-stale { opacity: 0.4; border-style: dashed; }

/* Ten readings, oldest to newest. One dark reading is a moment; four in a row is a direction, and
   that's what a single current swatch can never show. */
.log-trend { display: flex; align-items: center; gap: 8px; margin: 2px 0 6px; }
.log-trend-cap { font-size: 9px; color: var(--text-faint); letter-spacing: 0.06em; text-transform: uppercase; flex: 0 0 auto; }
.log-trend-dots { flex: 1; display: flex; align-items: center; gap: 4px; }
/* Fixed swatch hex again, same reason as the scale itself: a physical reference must not follow
   the aesthetic's palette. */
.log-trend-dot {
  flex: 1; height: 10px; border-radius: 2px; border: 1px solid rgba(0, 0, 0, 0.3);
}

.log-insight {
  font-size: 10px; line-height: 1.5; color: var(--text-dim);
  border-left: 2px solid var(--border); padding: 4px 0 4px 8px; margin: 0 0 10px;
}
.log-insight b { color: var(--text); }
.log-insight-n { color: var(--text-faint); }
.log-color-note b { color: var(--text-dim); }

/* The sheet itself reuses .home-popup / .home-popup-backdrop (the bottom-sheet pattern Home's
   "add back a box" popup already uses), so only its rows need styling. */
.log-sheet-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--border-soft);
}
.log-sheet-row:last-of-type { border-bottom: none; }
.log-sheet-label { flex: 0 0 70px; font-size: 12px; color: var(--text-dim); }
.log-sheet-row input[type="number"], .log-sheet-row select { flex: 1; min-width: 0; }
.log-sheet-unit { flex: 0 0 auto; font-size: 11px; color: var(--text-faint); }
.log-water-ctl { flex: 1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.log-water-count { font-size: 16px; font-weight: 700; min-width: 2ch; text-align: center; }
/* Beats input[type="number"] (0,1,1) -- a bare class would lose to it. Same trap as the recipe
   ingredient rows; see CLAUDE.md. */
.log-sheet-row input[type="number"].log-water-target { flex: 0 0 56px; }

/* ---------- Home's section tiles: one compact row ---------- */
/* Five sections, not six -- SCHEDULE retired when Home started rendering the schedule itself.
   Five doesn't fill a 3-column grid, and full-size tiles pushed the day off the first screen,
   so they drop to a single row of five. Overrides .workout-grid's 3 columns and .workout-cell's
   square ratio: those size the Exercise grid, where a big square tile is the point. Here the
   tiles are navigation sitting above the thing you actually came to read.

   Note .workout-cell sizes its icon wrapper with an inline font-size from app.js, so the wrapper
   (not the SVG) is what sets the row's height -- see CLAUDE.md's note on re-shaped tiles. */
.workout-grid.home-tile-row { grid-template-columns: repeat(5, 1fr); gap: 6px; }
.home-tile-row .workout-cell { aspect-ratio: auto; min-height: 62px; padding: 8px 2px; gap: 5px; }
.home-tile-row .workout-cell > div:first-of-type { font-size: 20px !important; line-height: 1; }
.home-tile-row .workout-cell .cell-icon svg,
.home-tile-row .workout-cell svg { width: 20px !important; height: 20px !important; }
.workout-cell.home-tile .wname, .home-tile-row .workout-cell.home-tile .wname {
  font-size: 8px; font-weight: 700; line-height: 1.15; letter-spacing: 0.02em;
}
/* Edit mode adds a hide (X) button per tile; at this size it needs to not swamp the tile. */
.home-tile-row .home-edit-x { transform: scale(0.8); }

/* ---------- Collapsed day timeline ---------- */
/* The day folds around the moment you're in: what's behind you and what's ahead collapse to a
   count you can open, with the block(s) actually underway left expanded between them. Only used
   on today (see renderDailySchedule) -- another date has no "now" to fold around. */
.day-band {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 10px 0; background: none; border: 0; text-align: left;
  color: var(--text-dim); font-family: inherit; font-size: 12px; font-weight: 500;
  cursor: pointer;
}
.day-band + .day-row, .day-band-list + .day-band, .day-row + .day-band, .day-nownothing + .day-band {
  border-top: 1px solid var(--border-soft);
}
.day-band:hover { color: var(--text); }
.day-band-caret { display: inline-flex; font-size: 11px; color: var(--text-faint); transition: transform 160ms ease; }
.day-band-open .day-band-caret { transform: rotate(90deg); }
.day-band-count { font-weight: 700; color: var(--text); font-size: 13px; }
.day-band-label { margin-left: -3px; }
/* Pushed to the far end so the count and the hint read as two separate facts, not one phrase. */
.day-band-hint { margin-left: auto; font-size: 10px; color: var(--text-faint); text-align: right; }
.day-band-list { padding-bottom: 2px; }
/* A folded day with nothing underway still has to say so — otherwise the two bands sit flush
   against each other and read as a rendering fault rather than a genuine gap in the day. */
.day-nownothing { padding: 12px 0 12px 14px; font-size: 12px; color: var(--text-faint); font-style: italic; }
/* A completed anchor inside a fold reads as finished rather than just listed. */
.day-row-done .day-body { opacity: 0.55; }
@media (prefers-reduced-motion: reduce) { .day-band-caret { transition: none; } }

/* ---------- Recipe notes ---------- */
.recipe-meta { font-size: 11px; color: var(--text-dim); margin-bottom: 8px; font-weight: 600; }
.recipe-ing-list { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 10px; }
.recipe-ing-pill { font-size: 11px; padding: 2px 7px; border-radius: 999px; background: var(--surface2); border: 1px solid var(--border-soft); }
.recipe-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
/* Compose-form ingredient rows: name flexes, qty stays narrow, unit fixed -- so a long food name
   truncates rather than squeezing the number it belongs to. */
.recipe-ing-row { display: flex; align-items: center; gap: 6px; padding: 6px 0; }
.recipe-ing-row + .recipe-ing-row { border-top: 1px solid var(--border-soft); }
.recipe-ing-name { flex: 1; min-width: 0; font-size: 12px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Descendant-scoped (0,2,0) on purpose: styles.css's own `input[type="number"], select { width:
   100% }` is (0,1,1) and BEATS a bare class, which shoved the qty box to full width, pushed the
   row past the panel edge and squeezed the ingredient name to nothing. Same specificity trap
   CLAUDE.md documents for .btn/.btn-primary. */
.recipe-ing-row .recipe-ing-qty { width: 68px; flex-shrink: 0; }
.recipe-ing-row .recipe-ing-unit { width: 96px; flex-shrink: 0; font-size: 12px; color: var(--text-dim); }
.recipe-ing-total { font-size: 11px; color: var(--text-dim); font-weight: 700; padding-top: 8px; margin-top: 4px; border-top: 1px solid var(--border-soft); }

/* ---------- Settings: aesthetic picker ---------- */
.aesthetic-card { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px; position: relative; color: var(--text); }
.aesthetic-card.active { border-color: var(--accent); }
.aesthetic-card-preview { width: 42px; height: 42px; border-radius: 5px; flex-shrink: 0; border: 1px solid rgba(0,0,0,0.25); }
.aesthetic-preview-terminal { background: linear-gradient(135deg, #0a0f0a 55%, #39ff6a 55%); }
.aesthetic-preview-editorial { background: linear-gradient(135deg, #f4ecdd 55%, #b8863a 55%); }
.aesthetic-preview-brutalist { background: linear-gradient(135deg, #7c8791 55%, #f5e400 55%); border-color: #0a0a0a; }
.aesthetic-preview-soft { background: linear-gradient(135deg, #faf8f5 55%, #8fa888 55%); }
.aesthetic-preview-cyberpunk { background: linear-gradient(135deg, #0d0221 55%, #ff2bd6 55%); }
.aesthetic-preview-academia { background: linear-gradient(135deg, #e6d9ba 55%, #8a5a28 55%); }
.aesthetic-preview-sakura { background: linear-gradient(135deg, #fdf1f4 55%, #e8749a 55%); }
.aesthetic-preview-honey { background: linear-gradient(135deg, #fdf3dd 55%, #e8940c 55%); }
.aesthetic-preview-gutterslime { background: linear-gradient(135deg, #0c130b 55%, #c6ff1a 55%); }
.aesthetic-preview-spookyscary { background: linear-gradient(100deg, #0a0612 35%, #ffb627 58%, #b06aff 76%, #8aff6a 92%); }
.aesthetic-preview-thrash { background: linear-gradient(100deg, #0a0a0a 40%, #ffd400 62%, #ff1f3d 82%, #c0c0c0 96%); }
.aesthetic-preview-sixiang { background: linear-gradient(100deg, #050f14 25%, #22d3ee 42%, #ff3b30 58%, #b8ccdb 76%, #14b8a6 94%); }
/* Picker swatches must live here, NOT in an external aesthetic's own theme.css — the picker
   renders while some *other* aesthetic is active, so a lazily-loaded theme file isn't there. */
.aesthetic-preview-frutigeraero { background: linear-gradient(160deg, #2ea8e0 8%, #7fd0f0 34%, #d8f2fb 56%, #9fd47a 78%, #5aa844 96%); }
.aesthetic-preview-y2k { background: linear-gradient(180deg, #0a0d11 4%, #8fb9c9 26%, #ffffff 45%, #1a1620 53%, #8f5f9e 78%, #e2b6ee 96%); }
.aesthetic-preview-draconic { background: linear-gradient(160deg, #0a0405 6%, #7d0d0e 30%, #e02617 52%, #ff6b1a 74%, #ffd24a 96%); }
.aesthetic-preview-cream { background: linear-gradient(100deg, #2a0d44 18%, #562a8c 36%, #f5c518 58%, #1fbf6b 78%, #a855f7 96%); }
.aesthetic-preview-metalheart { background: linear-gradient(160deg, #04080a 6%, #35454e 26%, #eaf6fa 46%, #131d23 54%, #1fa8e0 76%, #18d6a8 96%); }
/* Hard stops, not a blend — this one's whole motif is stepped bands of solid colour. */
.aesthetic-preview-millennium { background: linear-gradient(135deg, #1a0511 0 18%, #ff2d95 18% 38%, #ff7a18 38% 55%, #ffe23d 55% 68%, #2f9dff 68% 84%, #a855f7 84% 100%); }
.aesthetic-preview-runic { background: linear-gradient(150deg, #14181a 6%, #34251a 30%, #7a5c39 48%, #c8d2d8 62%, #4fe08c 84%, #12331f 98%); }
/* Nested bands, not a blend — the swatch is the corridor seen end-on. */
.aesthetic-preview-liminal { background: linear-gradient(180deg, #fffdf0 0 10%, #d9cf9c 10% 30%, #d3c88a 30% 48%, #cdc079 48% 66%, #c6b96b 66% 86%, #86793f 86% 100%); }
/* The view out of the windshield, top to bottom: deep space, nebula, the lit horizon, neon
   asphalt, then the black dash. Retinted per SECTOR at runtime; this is the default. */
.aesthetic-preview-spacehighway { background: linear-gradient(180deg, #05070f 0 20%, #a875f0 20% 34%, #3fb4e6 34% 44%, #cfe8ff 44% 50%, #262e3a 50% 74%, #05070b 74% 100%); }
/* The Sanic default, read as a run left to right: night sky, the blue itself, his white
   glove, a ring, then the red of the shoes. Retinted per CHARACTER at runtime; this is the default. */
.aesthetic-preview-hedge { background: linear-gradient(150deg, #050d22 6%, #3d82ff 32%, #eef4ff 50%, #ffce3a 68%, #ef4b23 88%, #150404 100%); }
.aesthetic-preview-cartomancer { background: linear-gradient(135deg, #f0dfa0 0 20%, #4aa8e8 20% 40%, #8452b8 40% 60%, #e8562a 60% 80%, #4a9450 80% 100%); }
.aesthetic-card-name { font-weight: 700; font-size: 13px; }
.aesthetic-card-desc { font-size: 11px; color: var(--text-faint); margin-top: 2px; line-height: 1.4; }
.aesthetic-card-check { position: absolute; right: 10px; top: 10px; }
.aesthetic-group { margin-top: 4px; }
.aesthetic-group-header {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  background: none; border: none; padding: 8px 2px; cursor: pointer;
  font: inherit; font-weight: 700; font-size: 12px; letter-spacing: 0.04em; color: var(--text-dim);
  text-transform: uppercase;
}
.aesthetic-group-header svg { width: 14px; height: 14px; }
.aesthetic-group-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
/* The accent/palette picker, inlined right after the active card in its group instead of one
   fixed spot at the bottom of Settings — see aestheticGroupCardsHtml() in app.js. Indented +
   accent-bordered so it reads as attached to the card above it, not just another list item. */
.accent-picker-inline { background: var(--surface); border: 1px solid var(--accent); border-radius: var(--radius); padding: 12px; margin-left: 14px; }
.disabled-block { opacity: 0.35; pointer-events: none; }

/* ---------- Aesthetic: Retro Terminal — phosphor-green CLI on near-black, scanlines, sharp corners ---------- */
:root[data-aesthetic="terminal"] {
  --bg: #060a06;
  --surface: #0d140d;
  --surface2: #121b12;
  --border: #234226;
  --border-soft: #17281a;
  --text: #4dff7a;
  --text-dim: #2fae55;
  --text-faint: #1f6b38;
  --accent: #39ff6a;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #39ff6a;
  --good-soft: #0f2415;
  --bad: #ff5b5b;
  --bad-soft: #2a1010;
  --warn: #e8ff5b;
  --reset-border: #4dff7a;
  --reset-bg: #16261a;
  --reset-text: #4dff7a;
  --myo: #ff5bd6;
  --goldenrod: #e8ff5b;
  --savings: #5bd6ff;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'JetBrains Mono', monospace;
  --font-body: 'JetBrains Mono', monospace;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 0px;
}
[data-aesthetic="terminal"] .brand span,
[data-aesthetic="terminal"] .section-title,
[data-aesthetic="terminal"] .cycle-label,
[data-aesthetic="terminal"] .wnum,
[data-aesthetic="terminal"] .btn-primary { text-shadow: 0 0 6px currentColor; }
[data-aesthetic="terminal"] body::after {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 999;
  background: repeating-linear-gradient(0deg, rgba(0,0,0,0.24) 0px, rgba(0,0,0,0.24) 1px, transparent 1px, transparent 3px);
  mix-blend-mode: multiply; opacity: 0.55;
}

/* ---------- Aesthetic: Luxury Editorial — serif headlines on warm cream, single gold accent ---------- */
:root[data-aesthetic="editorial"] {
  --bg: #f4ecdd;
  --surface: #fbf6ec;
  --surface2: #f0e6d2;
  --border: #d8c8a8;
  --border-soft: #e6dac0;
  --text: #241d13;
  --text-dim: #6b5f47;
  --text-faint: #a3936f;
  --accent: #b8863a;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #4c6b3a;
  --good-soft: #e6ecd9;
  --bad: #97392f;
  --bad-soft: #f3e0dc;
  --warn: #b8863a;
  --reset-border: #241d13;
  --reset-bg: #ece0c8;
  --reset-text: #241d13;
  --myo: #7a3a6b;
  --goldenrod: #b8863a;
  --savings: #2f6b6b;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Playfair Display', serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 0px;
}
[data-aesthetic="editorial"] .topbar { border-bottom: 2px solid var(--accent); }
[data-aesthetic="editorial"] .tabbar { border-top: 2px solid var(--accent); }
[data-aesthetic="editorial"] .btn-primary { letter-spacing: 0.04em; }

/* ---------- Aesthetic: Neo-Brutalist — raw concrete, thick black borders, hard offset shadows ---------- */
:root[data-aesthetic="brutalist"] {
  --bg: #7c8791;
  --surface: #ffffff;
  --surface2: #a7b1b9;
  --border: #0a0a0a;
  --border-soft: #0a0a0a;
  --text: #0a0a0a;
  --text-dim: #3a3a3a;
  --text-faint: #6b6b6b;
  --accent: #f5e400;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 22%, var(--surface));
  --good: #1a7a1a;
  --good-soft: #d4ecd4;
  --bad: #d61f1f;
  --bad-soft: #f5d4d4;
  --warn: #f5e400;
  --reset-border: #0a0a0a;
  --reset-bg: #a7b1b9;
  --reset-text: #0a0a0a;
  --myo: #ff1a8c;
  --goldenrod: #f5e400;
  --savings: #1a4fd6;
  --savings-soft: color-mix(in srgb, var(--savings) 22%, var(--surface));
  --font-head: 'Archivo Black', sans-serif;
  --font-body: 'Space Grotesk', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 0px;
}
[data-aesthetic="brutalist"] .panel,
[data-aesthetic="brutalist"] .entry-card,
[data-aesthetic="brutalist"] .modal-box,
[data-aesthetic="brutalist"] .workout-cell,
[data-aesthetic="brutalist"] .aesthetic-card { border: 3px solid var(--border); box-shadow: 5px 5px 0 var(--border); }
[data-aesthetic="brutalist"] .btn { border: 3px solid var(--border); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; box-shadow: 3px 3px 0 var(--border); }
[data-aesthetic="brutalist"] .btn:active { box-shadow: 1px 1px 0 var(--border); transform: translate(2px, 2px); }
[data-aesthetic="brutalist"] .btn-primary { color: #0a0a0a; }
[data-aesthetic="brutalist"] .section-title, [data-aesthetic="brutalist"] .brand { text-transform: uppercase; }
[data-aesthetic="brutalist"] .topbar { border-bottom: 3px solid var(--border); }
[data-aesthetic="brutalist"] .tabbar { border-top: 3px solid var(--border); }

/* ---------- Aesthetic: Soft Minimal — airy off-white, pastel accent, rounded hairlines ---------- */
:root[data-aesthetic="soft"] {
  --bg: #faf8f5;
  --surface: #ffffff;
  --surface2: #f3f0ea;
  --border: #e6e1d8;
  --border-soft: #eeebe4;
  --text: #3a362f;
  --text-dim: #8a8378;
  --text-faint: #bab3a6;
  --accent: #8fa888;
  --accent-dim: color-mix(in srgb, var(--accent) 45%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --good: #7a9e6e;
  --good-soft: #eaf1e4;
  --bad: #c98a8a;
  --bad-soft: #f6e6e4;
  --warn: #d9b46a;
  --reset-border: #d8d2c4;
  --reset-bg: #f3f0ea;
  --reset-text: #3a362f;
  --myo: #b09bc9;
  --goldenrod: #d9b46a;
  --savings: #7ea3c9;
  --savings-soft: color-mix(in srgb, var(--savings) 14%, var(--surface));
  --font-head: 'Quicksand', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 16px;
}
[data-aesthetic="soft"] .panel, [data-aesthetic="soft"] .entry-card, [data-aesthetic="soft"] .modal-box, [data-aesthetic="soft"] .workout-cell { border-width: 1px; box-shadow: 0 1px 3px rgba(58,54,47,0.06); }
[data-aesthetic="soft"] .btn { border-radius: var(--radius); }
[data-aesthetic="soft"] .btn-primary { font-weight: 600; letter-spacing: 0.01em; }
[data-aesthetic="soft"] .section-title { font-weight: 600; }

/* ---------- Aesthetic: Cyberpunk Neon — glowing UI on deep purple-navy, angular accents ---------- */
:root[data-aesthetic="cyberpunk"] {
  --bg: #0d0221;
  --surface: #170a35;
  --surface2: #1f1146;
  --border: #4a2b7d;
  --border-soft: #33195c;
  --text: #e6dcff;
  --text-dim: #9d8bcf;
  --text-faint: #63508f;
  --accent: #ff2bd6;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 18%, var(--surface));
  --good: #00ff9f;
  --good-soft: #0a2e28;
  --bad: #ff2b5e;
  --bad-soft: #300a18;
  --warn: #f5f500;
  --reset-border: #00f0ff;
  --reset-bg: #1f1146;
  --reset-text: #e6dcff;
  --myo: #ff2bd6;
  --goldenrod: #f5f500;
  --savings: #00d4ff;
  --savings-soft: color-mix(in srgb, var(--savings) 18%, var(--surface));
  --font-head: 'Orbitron', sans-serif;
  --font-body: 'Rajdhani', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 2px;
}
[data-aesthetic="cyberpunk"] .brand span,
[data-aesthetic="cyberpunk"] .section-title,
[data-aesthetic="cyberpunk"] .cycle-label,
[data-aesthetic="cyberpunk"] .wnum { text-shadow: 0 0 8px var(--accent), 0 0 18px var(--accent); }
[data-aesthetic="cyberpunk"] .panel, [data-aesthetic="cyberpunk"] .entry-card, [data-aesthetic="cyberpunk"] .modal-box { border-color: var(--border); box-shadow: 0 0 0 1px var(--accent-dim) inset; }
[data-aesthetic="cyberpunk"] .btn-primary { box-shadow: 0 0 12px var(--accent); text-shadow: 0 0 6px rgba(0,0,0,0.4); }
[data-aesthetic="cyberpunk"] .topbar { border-bottom: 1px solid var(--accent); box-shadow: 0 1px 12px -2px var(--accent); }
[data-aesthetic="cyberpunk"] .tabbar { border-top: 1px solid var(--accent); box-shadow: 0 -1px 12px -2px var(--accent); }

/* ---------- Aesthetic: Dark Academia — ornate serif on aged parchment, brass and oxblood ---------- */
:root[data-aesthetic="academia"] {
  --bg: #e6d9ba;
  --surface: #f1e6c9;
  --surface2: #ddceac;
  --border: #a68f5c;
  --border-soft: #c4b384;
  --text: #2b1f14;
  --text-dim: #5c4a34;
  --text-faint: #8a7550;
  --accent: #8a5a28;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --good: #33472e;
  --good-soft: #dbe4d2;
  --bad: #7a3020;
  --bad-soft: #e8d2ce;
  --warn: #a8813c;
  --reset-border: #2b1f14;
  --reset-bg: #ddceac;
  --reset-text: #2b1f14;
  --myo: #4a2b5c;
  --goldenrod: #a8813c;
  --savings: #1f3a5c;
  --savings-soft: color-mix(in srgb, var(--savings) 14%, var(--surface));
  --font-head: 'Cormorant Garamond', serif;
  --font-body: 'EB Garamond', serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 2px;
}
[data-aesthetic="academia"] body::after {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 999;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(43,31,20,0.16) 100%);
}
/* A medieval apothecary's worktable instead of a bare parchment ground — flasks, a mortar and
   pestle, a balance scale and a sprig of dried herb, faint and low-contrast, tiled large so it
   reads as texture rather than decoration. Mask+tint over --text-faint so it always matches the
   palette; body is a positioning context so this ::before layer sits behind the real content
   (which, as ordinary in-flow markup after it in tree order, always paints on top). */
[data-aesthetic="academia"] body { position: relative; }
[data-aesthetic="academia"] body::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: -1;
  background-color: var(--text-faint); opacity: 0.22;
  -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSIwIDAgMjAwIDIwMCI+Cgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0Niw1MikiPgogIDxwYXRoIGQ9Ik0gLTIsLTMwIEwgLTIsLTMwIEwgLTIsLTEwIEwgLTEzLDEwIEMgLTE2LDE2IC0xMywyMiAtNiwyMiBMIDYsMjIgQyAxMywyMiAxNiwxNiAxMywxMAogICAgICAgICAgIEwgMiwtMTAgTCAyLC0zMCBaIiBmaWxsPSIjZmZmIi8+CiAgPHJlY3QgeD0iLTQiIHk9Ii0zNCIgd2lkdGg9IjgiIGhlaWdodD0iNiIgcng9IjEuNSIgZmlsbD0iI2ZmZiIvPgogIDxyZWN0IHg9Ii0xMSIgeT0iNyIgd2lkdGg9IjIyIiBoZWlnaHQ9IjQiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuNzUiLz4KPC9nPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUwLDE0MCkiPgogIDxwYXRoIGQ9Ik0gLTIwLDAgQyAtMjAsMTQgLTExLDIyIDAsMjIgQyAxMSwyMiAyMCwxNCAyMCwwIFoiIGZpbGw9IiNmZmYiLz4KICA8ZWxsaXBzZSBjeD0iMCIgY3k9IjAiIHJ4PSIyMCIgcnk9IjUiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuNiIvPgogIDxyZWN0IHg9Ii0zIiB5PSItMjQiIHdpZHRoPSI2IiBoZWlnaHQ9IjI2IiByeD0iMyIgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJyb3RhdGUoMjggMCAtOCkiLz4KICA8ZWxsaXBzZSBjeD0iMTMiIGN5PSItMjciIHJ4PSI1IiByeT0iNCIgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJyb3RhdGUoMjggMCAtOCkiLz4KPC9nPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUwLDQ0KSI+CiAgPHJlY3QgeD0iLTEuNiIgeT0iLTI2IiB3aWR0aD0iMy4yIiBoZWlnaHQ9IjQwIiBmaWxsPSIjZmZmIi8+CiAgPHBvbHlnb24gcG9pbnRzPSIwLC0yNiAtNSwtMTkgNSwtMTkiIGZpbGw9IiNmZmYiLz4KICA8bGluZSB4MT0iLTMyIiB5MT0iLTE4IiB4Mj0iMzIiIHkyPSItMTgiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgogIDxsaW5lIHgxPSItMzIiIHkxPSItMTgiIHgyPSItMzIiIHkyPSItNCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIvPgogIDxsaW5lIHgxPSIzMiIgeTE9Ii0xOCIgeDI9IjMyIiB5Mj0iLTQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiLz4KICA8cGF0aCBkPSJNIC00MCwtNCBBIDgsNiAwIDAgMCAtMjQsLTQgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIvPgogIDxwYXRoIGQ9Ik0gNDAsLTQgQSA4LDYgMCAwIDAgMjQsLTQgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIvPgogIDxyZWN0IHg9Ii0xNCIgeT0iMTMiIHdpZHRoPSIyOCIgaGVpZ2h0PSI1IiByeD0iMS41IiBmaWxsPSIjZmZmIiBvcGFjaXR5PSIwLjciLz4KPC9nPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTYsMTUwKSByb3RhdGUoLTEyKSI+CiAgPGxpbmUgeDE9IjAiIHkxPSIwIiB4Mj0iMCIgeTI9IjI2IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS44Ii8+CiAgPHBhdGggZD0iTSAwLDQgQyAtOCwwIC0xMywtOCAtMTIsLTE2IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KICA8cGF0aCBkPSJNIDAsMTAgQyA4LDYgMTMsLTIgMTIsLTExIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KICA8cGF0aCBkPSJNIDAsMTYgQyAtNywxMyAtMTEsNyAtMTAsMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CiAgPHJlY3QgeD0iLTQiIHk9IjIyIiB3aWR0aD0iOCIgaGVpZ2h0PSI1IiByeD0iMSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iMC43NSIvPgo8L2c+Cgo8L3N2Zz4=");
  mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSIwIDAgMjAwIDIwMCI+Cgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0Niw1MikiPgogIDxwYXRoIGQ9Ik0gLTIsLTMwIEwgLTIsLTMwIEwgLTIsLTEwIEwgLTEzLDEwIEMgLTE2LDE2IC0xMywyMiAtNiwyMiBMIDYsMjIgQyAxMywyMiAxNiwxNiAxMywxMAogICAgICAgICAgIEwgMiwtMTAgTCAyLC0zMCBaIiBmaWxsPSIjZmZmIi8+CiAgPHJlY3QgeD0iLTQiIHk9Ii0zNCIgd2lkdGg9IjgiIGhlaWdodD0iNiIgcng9IjEuNSIgZmlsbD0iI2ZmZiIvPgogIDxyZWN0IHg9Ii0xMSIgeT0iNyIgd2lkdGg9IjIyIiBoZWlnaHQ9IjQiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuNzUiLz4KPC9nPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUwLDE0MCkiPgogIDxwYXRoIGQ9Ik0gLTIwLDAgQyAtMjAsMTQgLTExLDIyIDAsMjIgQyAxMSwyMiAyMCwxNCAyMCwwIFoiIGZpbGw9IiNmZmYiLz4KICA8ZWxsaXBzZSBjeD0iMCIgY3k9IjAiIHJ4PSIyMCIgcnk9IjUiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuNiIvPgogIDxyZWN0IHg9Ii0zIiB5PSItMjQiIHdpZHRoPSI2IiBoZWlnaHQ9IjI2IiByeD0iMyIgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJyb3RhdGUoMjggMCAtOCkiLz4KICA8ZWxsaXBzZSBjeD0iMTMiIGN5PSItMjciIHJ4PSI1IiByeT0iNCIgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJyb3RhdGUoMjggMCAtOCkiLz4KPC9nPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUwLDQ0KSI+CiAgPHJlY3QgeD0iLTEuNiIgeT0iLTI2IiB3aWR0aD0iMy4yIiBoZWlnaHQ9IjQwIiBmaWxsPSIjZmZmIi8+CiAgPHBvbHlnb24gcG9pbnRzPSIwLC0yNiAtNSwtMTkgNSwtMTkiIGZpbGw9IiNmZmYiLz4KICA8bGluZSB4MT0iLTMyIiB5MT0iLTE4IiB4Mj0iMzIiIHkyPSItMTgiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgogIDxsaW5lIHgxPSItMzIiIHkxPSItMTgiIHgyPSItMzIiIHkyPSItNCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIvPgogIDxsaW5lIHgxPSIzMiIgeTE9Ii0xOCIgeDI9IjMyIiB5Mj0iLTQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiLz4KICA8cGF0aCBkPSJNIC00MCwtNCBBIDgsNiAwIDAgMCAtMjQsLTQgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIvPgogIDxwYXRoIGQ9Ik0gNDAsLTQgQSA4LDYgMCAwIDAgMjQsLTQgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIvPgogIDxyZWN0IHg9Ii0xNCIgeT0iMTMiIHdpZHRoPSIyOCIgaGVpZ2h0PSI1IiByeD0iMS41IiBmaWxsPSIjZmZmIiBvcGFjaXR5PSIwLjciLz4KPC9nPgoKPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTYsMTUwKSByb3RhdGUoLTEyKSI+CiAgPGxpbmUgeDE9IjAiIHkxPSIwIiB4Mj0iMCIgeTI9IjI2IiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS44Ii8+CiAgPHBhdGggZD0iTSAwLDQgQyAtOCwwIC0xMywtOCAtMTIsLTE2IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KICA8cGF0aCBkPSJNIDAsMTAgQyA4LDYgMTMsLTIgMTIsLTExIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KICA8cGF0aCBkPSJNIDAsMTYgQyAtNywxMyAtMTEsNyAtMTAsMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CiAgPHJlY3QgeD0iLTQiIHk9IjIyIiB3aWR0aD0iOCIgaGVpZ2h0PSI1IiByeD0iMSIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iMC43NSIvPgo8L2c+Cgo8L3N2Zz4=");
  -webkit-mask-repeat: repeat; mask-repeat: repeat;
  -webkit-mask-size: 200px 200px; mask-size: 200px 200px;
}
[data-aesthetic="academia"] .topbar { border-bottom: 3px double var(--accent); }
[data-aesthetic="academia"] .tabbar { border-top: 3px double var(--accent); }
[data-aesthetic="academia"] .section-title { font-weight: 700; letter-spacing: 0.01em; }
[data-aesthetic="academia"] .panel, [data-aesthetic="academia"] .entry-card, [data-aesthetic="academia"] .modal-box { border-width: 2px; }

/* ---------- Aesthetic: Sakura (Soft Blossom) — pale pink daytime cherry blossom ---------- */
:root[data-aesthetic="sakura"] {
  --bg: #fdf1f4;
  --surface: #ffffff;
  --surface2: #fce4ea;
  --border: #f3c6d3;
  --border-soft: #f9dde5;
  --text: #4a2e35;
  --text-dim: #9c7078;
  --text-faint: #c9a3ac;
  --accent: #e8749a;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #7fa87a;
  --good-soft: #e6f0e2;
  --bad: #d15a6e;
  --bad-soft: #f9dde2;
  --warn: #e0a95c;
  --reset-border: #4a2e35;
  --reset-bg: #fce4ea;
  --reset-text: #4a2e35;
  --myo: #b98fc9;
  --goldenrod: #e0a95c;
  --savings: #7ab8d9;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Comfortaa', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 18px;
}
[data-aesthetic="sakura"] .workout-cell { border-radius: 18px; }
[data-aesthetic="sakura"] .btn-primary { border-radius: 999px; color: #5a2740; }
[data-aesthetic="sakura"] .btn { border-radius: 999px; }
[data-aesthetic="sakura"] .icon-btn { border-radius: 999px; }
[data-aesthetic="sakura"] body {
  background-color: var(--bg);
  background-image: radial-gradient(circle, var(--accent-soft) 3px, transparent 3.5px);
  background-size: 32px 32px;
}
[data-aesthetic="sakura"] .section-title { font-weight: 700; }

/* ---------- Aesthetic: Hunny (Honeycomb Gold) — warm amber & cream, hex pattern ---------- */
:root[data-aesthetic="honey"] {
  --bg: #f6dfa0;
  --surface: #fff8e6;
  --surface2: #f2cf7a;
  --border: #b8790a;
  --border-soft: #e0bd6a;
  --text: #3a2205;
  --text-dim: #6b4a12;
  --text-faint: #a67f2e;
  --accent: #d9900a;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #5e7a1a;
  --good-soft: #e4ecc8;
  --bad: #a8330a;
  --bad-soft: #f0d2c0;
  --warn: #b8790a;
  --reset-border: #3a2205;
  --reset-bg: #f2cf7a;
  --reset-text: #3a2205;
  --myo: #8a4a1a;
  --goldenrod: #b8790a;
  --savings: #2e7d6b;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Fredoka', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 10px;
}
[data-aesthetic="honey"] .workout-cell, [data-aesthetic="honey"] .panel, [data-aesthetic="honey"] .entry-card, [data-aesthetic="honey"] .modal-box {
  border-radius: 10px;
}
[data-aesthetic="honey"] .btn-primary { color: #3a2405; font-weight: 700; }
[data-aesthetic="honey"] .accent-swatch { border-radius: 0; clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); }
[data-aesthetic="honey"] .topbar { border-bottom: 2px solid var(--accent); }
[data-aesthetic="honey"] .tabbar { border-top: 2px solid var(--accent); }
[data-aesthetic="honey"] .section-title { font-weight: 700; }
/* Section headers/labels often sit directly on the honeycomb-patterned body (no panel behind
   them) — the embossed pattern is intentionally bold now, so give bare text a soft light halo to
   keep it legible against it instead of blending into the gold. */
[data-aesthetic="honey"] .section-title,
[data-aesthetic="honey"] .home-date-line {
  text-shadow: 0 1px 3px rgba(255,250,235,0.85), 0 1px 1px rgba(255,250,235,0.85);
}
[data-aesthetic="honey"] .subtle-label {
  color: var(--text-dim);
  display: inline-block;
  background: rgba(255,250,235,0.82);
  padding: 3px 9px;
  border-radius: 6px;
}
[data-aesthetic="honey"] body {
  position: relative;
  /* A true tessellating pointy-top hexagon grid, flat glossy amber cell fills with a small white
     sheen baked into each cell — only the honeycomb's outline network carries color, via the two
     ::before/::after mask+gradient layers below (lighter "roof" edges, darker "base" edges), each
     masked by its own tiled stroke pattern (true hex-boundary edges only, no interior diagonal)
     and tinted by a symmetric deep-amber -> bright saturated-orange -> deep-amber gradient with a
     wide bright plateau through the middle, so the cell fills stay flat while the grid lines glow
     brightest across the panel's center. */
  background-color: var(--bg);
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MiIgaGVpZ2h0PSI0NSIgdmlld0JveD0iMCAwIDUyIDQ1Ij4KPHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0iI2M2N2ExMiIvPgo8cGF0aCBkPSJNIC04LjAsLTIuNSBRIC01LjAsLTExLjAgMS4wLC05LjUgUSAtMy4wLC02LjAgLTguMCwtMi41IFoiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuMzIiLz4KPHBhdGggZD0iTSAxOC4wLC0yLjUgUSAyMS4wLC0xMS4wIDI3LjAsLTkuNSBRIDIzLjAsLTYuMCAxOC4wLC0yLjUgWiIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iMC4zMiIvPgo8cGF0aCBkPSJNIDQ0LjAsLTIuNSBRIDQ3LjAsLTExLjAgNTMuMCwtOS41IFEgNDkuMCwtNi4wIDQ0LjAsLTIuNSBaIiBmaWxsPSIjZmZmIiBvcGFjaXR5PSIwLjMyIi8+CjxwYXRoIGQ9Ik0gNS4wLDIwLjAgUSA4LjAsMTEuNSAxNC4wLDEzLjAgUSAxMC4wLDE2LjUgNS4wLDIwLjAgWiIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iMC4zMiIvPgo8cGF0aCBkPSJNIDMxLjAsMjAuMCBRIDM0LjAsMTEuNSA0MC4wLDEzLjAgUSAzNi4wLDE2LjUgMzEuMCwyMC4wIFoiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuMzIiLz4KPHBhdGggZD0iTSA1Ny4wLDIwLjAgUSA2MC4wLDExLjUgNjYuMCwxMy4wIFEgNjIuMCwxNi41IDU3LjAsMjAuMCBaIiBmaWxsPSIjZmZmIiBvcGFjaXR5PSIwLjMyIi8+CjxwYXRoIGQ9Ik0gLTguMCw0Mi41IFEgLTUuMCwzNC4wIDEuMCwzNS41IFEgLTMuMCwzOS4wIC04LjAsNDIuNSBaIiBmaWxsPSIjZmZmIiBvcGFjaXR5PSIwLjMyIi8+CjxwYXRoIGQ9Ik0gMTguMCw0Mi41IFEgMjEuMCwzNC4wIDI3LjAsMzUuNSBRIDIzLjAsMzkuMCAxOC4wLDQyLjUgWiIgZmlsbD0iI2ZmZiIgb3BhY2l0eT0iMC4zMiIvPgo8cGF0aCBkPSJNIDQ0LjAsNDIuNSBRIDQ3LjAsMzQuMCA1My4wLDM1LjUgUSA0OS4wLDM5LjAgNDQuMCw0Mi41IFoiIGZpbGw9IiNmZmYiIG9wYWNpdHk9IjAuMzIiLz4KPC9zdmc+");
  background-repeat: repeat; background-size: 52px 45px;
}
[data-aesthetic="honey"] body::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: -1;
  -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MiIgaGVpZ2h0PSI0NSIgdmlld0JveD0iMCAwIDUyIDQ1Ij4KPHBvbHlsaW5lIHBvaW50cz0iLTEzLjAsLTcuNSAwLjAsLTE1LjAgMTMuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMTMuMCwtNy41IDI2LjAsLTE1LjAgMzkuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMzkuMCwtNy41IDUyLjAsLTE1LjAgNjUuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMC4wLDE1LjAgMTMuMCw3LjUgMjYuMCwxNS4wIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMjYuMCwxNS4wIDM5LjAsNy41IDUyLjAsMTUuMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjUyLjAsMTUuMCA2NS4wLDcuNSA3OC4wLDE1LjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSItMTMuMCwzNy41IDAuMCwzMC4wIDEzLjAsMzcuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjEzLjAsMzcuNSAyNi4wLDMwLjAgMzkuMCwzNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMzkuMCwzNy41IDUyLjAsMzAuMCA2NS4wLDM3LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4=");
  mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MiIgaGVpZ2h0PSI0NSIgdmlld0JveD0iMCAwIDUyIDQ1Ij4KPHBvbHlsaW5lIHBvaW50cz0iLTEzLjAsLTcuNSAwLjAsLTE1LjAgMTMuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMTMuMCwtNy41IDI2LjAsLTE1LjAgMzkuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMzkuMCwtNy41IDUyLjAsLTE1LjAgNjUuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMC4wLDE1LjAgMTMuMCw3LjUgMjYuMCwxNS4wIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMjYuMCwxNS4wIDM5LjAsNy41IDUyLjAsMTUuMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjUyLjAsMTUuMCA2NS4wLDcuNSA3OC4wLDE1LjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSItMTMuMCwzNy41IDAuMCwzMC4wIDEzLjAsMzcuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjEzLjAsMzcuNSAyNi4wLDMwLjAgMzkuMCwzNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMzkuMCwzNy41IDUyLjAsMzAuMCA2NS4wLDM3LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4=");
  -webkit-mask-repeat: repeat; mask-repeat: repeat;
  -webkit-mask-size: 52px 45px; mask-size: 52px 45px;
  background: linear-gradient(to bottom, #8a4d18 0%, #ff9a29 32%, #ff9a29 68%, #8a4d18 100%);
}
[data-aesthetic="honey"] body::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: -1;
  -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MiIgaGVpZ2h0PSI0NSIgdmlld0JveD0iMCAwIDUyIDQ1Ij4KPHBvbHlsaW5lIHBvaW50cz0iMTMuMCwtNy41IDEzLjAsNy41IDAuMCwxNS4wIC0xMy4wLDcuNSAtMTMuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMzkuMCwtNy41IDM5LjAsNy41IDI2LjAsMTUuMCAxMy4wLDcuNSAxMy4wLC03LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSI2NS4wLC03LjUgNjUuMCw3LjUgNTIuMCwxNS4wIDM5LjAsNy41IDM5LjAsLTcuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjI2LjAsMTUuMCAyNi4wLDMwLjAgMTMuMCwzNy41IDAuMCwzMC4wIDAuMCwxNS4wIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iNTIuMCwxNS4wIDUyLjAsMzAuMCAzOS4wLDM3LjUgMjYuMCwzMC4wIDI2LjAsMTUuMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9Ijc4LjAsMTUuMCA3OC4wLDMwLjAgNjUuMCwzNy41IDUyLjAsMzAuMCA1Mi4wLDE1LjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSIxMy4wLDM3LjUgMTMuMCw1Mi41IDAuMCw2MC4wIC0xMy4wLDUyLjUgLTEzLjAsMzcuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjM5LjAsMzcuNSAzOS4wLDUyLjUgMjYuMCw2MC4wIDEzLjAsNTIuNSAxMy4wLDM3LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSI2NS4wLDM3LjUgNjUuMCw1Mi41IDUyLjAsNjAuMCAzOS4wLDUyLjUgMzkuMCwzNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+");
  mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MiIgaGVpZ2h0PSI0NSIgdmlld0JveD0iMCAwIDUyIDQ1Ij4KPHBvbHlsaW5lIHBvaW50cz0iMTMuMCwtNy41IDEzLjAsNy41IDAuMCwxNS4wIC0xMy4wLDcuNSAtMTMuMCwtNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iMzkuMCwtNy41IDM5LjAsNy41IDI2LjAsMTUuMCAxMy4wLDcuNSAxMy4wLC03LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSI2NS4wLC03LjUgNjUuMCw3LjUgNTIuMCwxNS4wIDM5LjAsNy41IDM5LjAsLTcuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjI2LjAsMTUuMCAyNi4wLDMwLjAgMTMuMCwzNy41IDAuMCwzMC4wIDAuMCwxNS4wIiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBvbHlsaW5lIHBvaW50cz0iNTIuMCwxNS4wIDUyLjAsMzAuMCAzOS4wLDM3LjUgMjYuMCwzMC4wIDI2LjAsMTUuMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9Ijc4LjAsMTUuMCA3OC4wLDMwLjAgNjUuMCwzNy41IDUyLjAsMzAuMCA1Mi4wLDE1LjAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSIxMy4wLDM3LjUgMTMuMCw1Mi41IDAuMCw2MC4wIC0xMy4wLDUyLjUgLTEzLjAsMzcuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjEuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjM5LjAsMzcuNSAzOS4wLDUyLjUgMjYuMCw2MC4wIDEzLjAsNTIuNSAxMy4wLDM3LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cG9seWxpbmUgcG9pbnRzPSI2NS4wLDM3LjUgNjUuMCw1Mi41IDUyLjAsNjAuMCAzOS4wLDUyLjUgMzkuMCwzNy41IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS42IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+");
  -webkit-mask-repeat: repeat; mask-repeat: repeat;
  -webkit-mask-size: 52px 45px; mask-size: 52px 45px;
  background: linear-gradient(to bottom, #6b3a12 0%, #ff7e00 32%, #ff7e00 68%, #6b3a12 100%);
}
/* A small striped bee, confined to the topbar wordmark (not roaming the panel) — idle bob plus a
   quick wing-flutter, baked as one small SVG (accent-matched stripes + white wing dots) rather
   than mask+tint, since it needs two distinct fixed colors at once. */
[data-aesthetic="honey"] .brand { display: inline-flex; align-items: center; }
[data-aesthetic="honey"] .brand::after {
  content: ''; display: inline-block; margin-left: 7px; width: 15px; height: 11px; flex-shrink: 0;
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMCIgaGVpZ2h0PSIyNiIgdmlld0JveD0iMCAwIDMwIDI2Ij48ZGVmcz48Y2xpcFBhdGggaWQ9ImIiPjxlbGxpcHNlIGN4PSIxNSIgY3k9IjE3IiByeD0iMTMiIHJ5PSI5Ii8+PC9jbGlwUGF0aD48L2RlZnM+PGcgY2xpcC1wYXRoPSJ1cmwoI2IpIj48cmVjdCB4PSIyIiB5PSI4IiB3aWR0aD0iMjYiIGhlaWdodD0iMTgiIGZpbGw9IiMxYTFhMWEiLz48cmVjdCB4PSI2LjYiIHk9IjgiIHdpZHRoPSI0LjYiIGhlaWdodD0iMTgiIGZpbGw9IiNkOTkwMGEiLz48cmVjdCB4PSIxNS44IiB5PSI4IiB3aWR0aD0iNC42IiBoZWlnaHQ9IjE4IiBmaWxsPSIjZDk5MDBhIi8+PC9nPjxjaXJjbGUgY3g9IjgiIGN5PSI2IiByPSI0LjUiIGZpbGw9InJnYmEoMjU1LDI1NSwyNTUsMC42KSIvPjxjaXJjbGUgY3g9IjE5IiBjeT0iNiIgcj0iNC41IiBmaWxsPSJyZ2JhKDI1NSwyNTUsMjU1LDAuNikiLz48L3N2Zz4=");
  background-size: contain; background-repeat: no-repeat;
  animation: honey-bee-idle 2.6s ease-in-out infinite;
}
@keyframes honey-bee-idle {
  0%, 100% { transform: translateY(0) rotate(-4deg); }
  50% { transform: translateY(-3px) rotate(4deg); }
}

/* ---------- Aesthetic: Gutterslime — matte dark-green industrial shell, glowing toxic neon ---------- */
:root[data-aesthetic="gutterslime"] {
  --bg: #0c130b;
  --surface: #131c10;
  --surface2: #1a2616;
  --border: #3a4a30;
  --border-soft: #223019;
  --text: #dfe8d0;
  --text-dim: #8fa07a;
  --text-faint: #576b45;
  --accent: #c6ff1a;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #c6ff1a;
  --good-soft: #1c2a12;
  --bad: #ff4d4d;
  --bad-soft: #2a1212;
  --warn: #ffcc33;
  --reset-border: #c6ff1a;
  --reset-bg: #1a2616;
  --reset-text: #dfe8d0;
  --myo: #ff4dd8;
  --goldenrod: #ffcc33;
  --savings: #1ae0ff;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Wallpoet', sans-serif;
  --font-body: 'Rajdhani', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 3px;
}
[data-aesthetic="gutterslime"] .brand span,
[data-aesthetic="gutterslime"] .section-title,
[data-aesthetic="gutterslime"] .cycle-label,
[data-aesthetic="gutterslime"] .wnum { text-shadow: 0 0 8px var(--accent), 0 0 16px var(--accent); }
[data-aesthetic="gutterslime"] .btn-primary { box-shadow: 0 0 12px var(--accent); }
[data-aesthetic="gutterslime"] .topbar { border-bottom: 1px solid var(--accent); box-shadow: 0 1px 10px -2px var(--accent); }
[data-aesthetic="gutterslime"] .tabbar { border-top: 1px solid var(--accent); box-shadow: 0 -1px 10px -2px var(--accent); }
[data-aesthetic="gutterslime"] .panel, [data-aesthetic="gutterslime"] .entry-card, [data-aesthetic="gutterslime"] .modal-box { box-shadow: 0 0 0 1px var(--accent-dim) inset; }
[data-aesthetic="gutterslime"] body {
  /* Diamond-plate tread texture — matte embossed lenses on a dark grout base, tuned low-contrast
     so it reads as an industrial floor plate without fighting with foreground text. */
  background-color: var(--bg);
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMi4wIiBoZWlnaHQ9IjU0LjAiIHZpZXdCb3g9IjAgMCAzMi4wIDU0LjAiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iZyIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzU5NzA0YSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzE0MWQxMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiMwYzEzMGIiLz48ZWxsaXBzZSBjeD0iLTMyLjAwIiBjeT0iMTMuNTAiIHJ4PSIxMS41IiByeT0iNC44IiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgLTMyLjAwIDEzLjUwKSIgZmlsbD0idXJsKCNnKSIvPjxlbGxpcHNlIGN4PSItMzIuMDAiIGN5PSIxMy41MCIgcng9IjcuMTMiIHJ5PSIxLjYzIiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgLTMyLjAwIDEzLjUwKSB0cmFuc2xhdGUoMCAtMS41NCkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzlhYjU3ZSIgc3Ryb2tlLXdpZHRoPSIxIiBvcGFjaXR5PSIwLjciLz48ZWxsaXBzZSBjeD0iMC4wMCIgY3k9IjEzLjUwIiByeD0iMTEuNSIgcnk9IjQuOCIgdHJhbnNmb3JtPSJyb3RhdGUoLTI1IDAuMDAgMTMuNTApIiBmaWxsPSJ1cmwoI2cpIi8+PGVsbGlwc2UgY3g9IjAuMDAiIGN5PSIxMy41MCIgcng9IjcuMTMiIHJ5PSIxLjYzIiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgMC4wMCAxMy41MCkgdHJhbnNsYXRlKDAgLTEuNTQpIiBmaWxsPSJub25lIiBzdHJva2U9IiM5YWI1N2UiIHN0cm9rZS13aWR0aD0iMSIgb3BhY2l0eT0iMC43Ii8+PGVsbGlwc2UgY3g9IjMyLjAwIiBjeT0iMTMuNTAiIHJ4PSIxMS41IiByeT0iNC44IiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgMzIuMDAgMTMuNTApIiBmaWxsPSJ1cmwoI2cpIi8+PGVsbGlwc2UgY3g9IjMyLjAwIiBjeT0iMTMuNTAiIHJ4PSI3LjEzIiByeT0iMS42MyIgdHJhbnNmb3JtPSJyb3RhdGUoLTI1IDMyLjAwIDEzLjUwKSB0cmFuc2xhdGUoMCAtMS41NCkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzlhYjU3ZSIgc3Ryb2tlLXdpZHRoPSIxIiBvcGFjaXR5PSIwLjciLz48ZWxsaXBzZSBjeD0iLTE2LjAwIiBjeT0iNDAuNTAiIHJ4PSIxMS41IiByeT0iNC44IiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgLTE2LjAwIDQwLjUwKSIgZmlsbD0idXJsKCNnKSIvPjxlbGxpcHNlIGN4PSItMTYuMDAiIGN5PSI0MC41MCIgcng9IjcuMTMiIHJ5PSIxLjYzIiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgLTE2LjAwIDQwLjUwKSB0cmFuc2xhdGUoMCAtMS41NCkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzlhYjU3ZSIgc3Ryb2tlLXdpZHRoPSIxIiBvcGFjaXR5PSIwLjciLz48ZWxsaXBzZSBjeD0iMTYuMDAiIGN5PSI0MC41MCIgcng9IjExLjUiIHJ5PSI0LjgiIHRyYW5zZm9ybT0icm90YXRlKC0yNSAxNi4wMCA0MC41MCkiIGZpbGw9InVybCgjZykiLz48ZWxsaXBzZSBjeD0iMTYuMDAiIGN5PSI0MC41MCIgcng9IjcuMTMiIHJ5PSIxLjYzIiB0cmFuc2Zvcm09InJvdGF0ZSgtMjUgMTYuMDAgNDAuNTApIHRyYW5zbGF0ZSgwIC0xLjU0KSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOWFiNTdlIiBzdHJva2Utd2lkdGg9IjEiIG9wYWNpdHk9IjAuNyIvPjxlbGxpcHNlIGN4PSI0OC4wMCIgY3k9IjQwLjUwIiByeD0iMTEuNSIgcnk9IjQuOCIgdHJhbnNmb3JtPSJyb3RhdGUoLTI1IDQ4LjAwIDQwLjUwKSIgZmlsbD0idXJsKCNnKSIvPjxlbGxpcHNlIGN4PSI0OC4wMCIgY3k9IjQwLjUwIiByeD0iNy4xMyIgcnk9IjEuNjMiIHRyYW5zZm9ybT0icm90YXRlKC0yNSA0OC4wMCA0MC41MCkgdHJhbnNsYXRlKDAgLTEuNTQpIiBmaWxsPSJub25lIiBzdHJva2U9IiM5YWI1N2UiIHN0cm9rZS13aWR0aD0iMSIgb3BhY2l0eT0iMC43Ii8+PC9zdmc+");
  background-size: 32px 54px;
  background-repeat: repeat;
}

/* ---------- Aesthetic: Jack-o'-Lantern — carved pumpkin glow on a violet Halloween night ---------- */
:root[data-aesthetic="spookyscary"] {
  --bg: #0a0612;
  --surface: #150d24;
  --surface2: #1c1230;
  --border: #3d2a52;
  --border-soft: #281c38;
  --text: #f5e6c8;
  --text-dim: #b89a68;
  --text-faint: #6b5940;
  --accent: #ffb627;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #8aff6a;
  --good-soft: #16281a;
  --bad: #ff3b3b;
  --bad-soft: #331416;
  --warn: #ffe066;
  --reset-border: #ffb627;
  --reset-bg: #1c1230;
  --reset-text: #f5e6c8;
  --myo: #b06aff;
  --goldenrod: #d98c1f;
  --savings: #4fd1c5;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Creepster', cursive;
  --font-body: 'IM Fell English', serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 6px;
}
/* Signature: warm multi-layer pumpkin glow behind the headline, plus a witch-green neon outline
   traced around the letterforms via -webkit-text-stroke (paint-order puts the stroke behind the
   fill so it reads as an outline, not a color shift). Replaces the old vignette-overlay signature. */
[data-aesthetic="spookyscary"] .brand span {
  text-shadow: 0 0 8px var(--accent), 0 0 20px var(--accent), 0 0 42px rgba(255,182,39,0.45);
  -webkit-text-stroke: 1.1px var(--good); paint-order: stroke fill; letter-spacing: 0.03em;
}
[data-aesthetic="spookyscary"] .section-title {
  text-shadow: 0 0 8px var(--accent), 0 0 22px var(--accent);
  -webkit-text-stroke: 1.3px var(--good); paint-order: stroke fill; letter-spacing: 0.03em;
}
[data-aesthetic="spookyscary"] body {
  background-color: var(--bg);
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MCIgaGVpZ2h0PSI3MCIgdmlld0JveD0iMCAwIDcwIDcwIj4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTYsMTgpIHNjYWxlKDEuMCkiPgo8Zz4KICA8ZWxsaXBzZSBjeD0iMCIgY3k9IjIiIHJ4PSIxMiIgcnk9IjEwIiBmaWxsPSIjM2ExZDA4Ii8+CiAgPHBhdGggZD0iTSAtMTIsMiBDIC0xMiwtNiAtNiwtOSAwLC05IEMgNiwtOSAxMiwtNiAxMiwyIiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC44IiBvcGFjaXR5PSIwLjciLz4KICA8cGF0aCBkPSJNIC02LC04IEMgLTYsMCAtNiw2IC02LDExIiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC45IiBvcGFjaXR5PSIwLjYiLz4KICA8cGF0aCBkPSJNIDAsLTkgQyAwLDAgMCw3IDAsMTEuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjUxMjBhIiBzdHJva2Utd2lkdGg9IjAuOSIgb3BhY2l0eT0iMC42Ii8+CiAgPHBhdGggZD0iTSA2LC04IEMgNiwwIDYsNiA2LDExIiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC45IiBvcGFjaXR5PSIwLjYiLz4KICA8cmVjdCB4PSItMS40IiB5PSItMTMuNSIgd2lkdGg9IjIuOCIgaGVpZ2h0PSI1IiByeD0iMSIgZmlsbD0iIzJmNWExYSIvPgogIDxwb2x5Z29uIHBvaW50cz0iLTQuNSwtMSAtMS41LC0xIC0zLDIiIGZpbGw9IiNmZmI2MjciIG9wYWNpdHk9IjAuODUiLz4KICA8cG9seWdvbiBwb2ludHM9IjQuNSwtMSAxLjUsLTEgMywyIiBmaWxsPSIjZmZiNjI3IiBvcGFjaXR5PSIwLjg1Ii8+CiAgPHBhdGggZD0iTSAtNCw1LjUgUSAwLDguNSA0LDUuNSBMIDMsNy41IFEgMCw5LjUgLTMsNy41IFoiIGZpbGw9IiNmZmI2MjciIG9wYWNpdHk9IjAuODUiLz4KPC9nPgo8L2c+CjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUyLDUwKSBzY2FsZSgxLjApIj4KPGc+CiAgPGVsbGlwc2UgY3g9IjAiIGN5PSIyIiByeD0iMTIiIHJ5PSIxMCIgZmlsbD0iIzNhMWQwOCIvPgogIDxwYXRoIGQ9Ik0gLTEyLDIgQyAtMTIsLTYgLTYsLTkgMCwtOSBDIDYsLTkgMTIsLTYgMTIsMiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjUxMjBhIiBzdHJva2Utd2lkdGg9IjAuOCIgb3BhY2l0eT0iMC43Ii8+CiAgPHBhdGggZD0iTSAtNiwtOCBDIC02LDAgLTYsNiAtNiwxMSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjUxMjBhIiBzdHJva2Utd2lkdGg9IjAuOSIgb3BhY2l0eT0iMC42Ii8+CiAgPHBhdGggZD0iTSAwLC05IEMgMCwwIDAsNyAwLDExLjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzI1MTIwYSIgc3Ryb2tlLXdpZHRoPSIwLjkiIG9wYWNpdHk9IjAuNiIvPgogIDxwYXRoIGQ9Ik0gNiwtOCBDIDYsMCA2LDYgNiwxMSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjUxMjBhIiBzdHJva2Utd2lkdGg9IjAuOSIgb3BhY2l0eT0iMC42Ii8+CiAgPHJlY3QgeD0iLTEuNCIgeT0iLTEzLjUiIHdpZHRoPSIyLjgiIGhlaWdodD0iNSIgcng9IjEiIGZpbGw9IiMyZjVhMWEiLz4KICA8cG9seWdvbiBwb2ludHM9Ii00LjUsLTEgLTEuNSwtMSAtMywyIiBmaWxsPSIjZmZiNjI3IiBvcGFjaXR5PSIwLjg1Ii8+CiAgPHBvbHlnb24gcG9pbnRzPSI0LjUsLTEgMS41LC0xIDMsMiIgZmlsbD0iI2ZmYjYyNyIgb3BhY2l0eT0iMC44NSIvPgogIDxwYXRoIGQ9Ik0gLTQsNS41IFEgMCw4LjUgNCw1LjUgTCAzLDcuNSBRIDAsOS41IC0zLDcuNSBaIiBmaWxsPSIjZmZiNjI3IiBvcGFjaXR5PSIwLjg1Ii8+CjwvZz4KPC9nPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NSwxMikgc2NhbGUoMC41NSkiPgo8Zz4KICA8ZWxsaXBzZSBjeD0iMCIgY3k9IjIiIHJ4PSIxMiIgcnk9IjEwIiBmaWxsPSIjM2ExZDA4Ii8+CiAgPHBhdGggZD0iTSAtMTIsMiBDIC0xMiwtNiAtNiwtOSAwLC05IEMgNiwtOSAxMiwtNiAxMiwyIiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC44IiBvcGFjaXR5PSIwLjciLz4KICA8cGF0aCBkPSJNIC02LC04IEMgLTYsMCAtNiw2IC02LDExIiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC45IiBvcGFjaXR5PSIwLjYiLz4KICA8cGF0aCBkPSJNIDAsLTkgQyAwLDAgMCw3IDAsMTEuNSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMjUxMjBhIiBzdHJva2Utd2lkdGg9IjAuOSIgb3BhY2l0eT0iMC42Ii8+CiAgPHBhdGggZD0iTSA2LC04IEMgNiwwIDYsNiA2LDExIiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC45IiBvcGFjaXR5PSIwLjYiLz4KICA8cmVjdCB4PSItMS40IiB5PSItMTMuNSIgd2lkdGg9IjIuOCIgaGVpZ2h0PSI1IiByeD0iMSIgZmlsbD0iIzJmNWExYSIvPgogIDxwb2x5Z29uIHBvaW50cz0iLTQuNSwtMSAtMS41LC0xIC0zLDIiIGZpbGw9IiNmZmI2MjciIG9wYWNpdHk9IjAuODUiLz4KICA8cG9seWdvbiBwb2ludHM9IjQuNSwtMSAxLjUsLTEgMywyIiBmaWxsPSIjZmZiNjI3IiBvcGFjaXR5PSIwLjg1Ii8+CiAgPHBhdGggZD0iTSAtNCw1LjUgUSAwLDguNSA0LDUuNSBMIDMsNy41IFEgMCw5LjUgLTMsNy41IFoiIGZpbGw9IiNmZmI2MjciIG9wYWNpdHk9IjAuODUiLz4KPC9nPgo8L2c+CjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwLDU4KSBzY2FsZSgwLjU1KSI+CjxnPgogIDxlbGxpcHNlIGN4PSIwIiBjeT0iMiIgcng9IjEyIiByeT0iMTAiIGZpbGw9IiMzYTFkMDgiLz4KICA8cGF0aCBkPSJNIC0xMiwyIEMgLTEyLC02IC02LC05IDAsLTkgQyA2LC05IDEyLC02IDEyLDIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzI1MTIwYSIgc3Ryb2tlLXdpZHRoPSIwLjgiIG9wYWNpdHk9IjAuNyIvPgogIDxwYXRoIGQ9Ik0gLTYsLTggQyAtNiwwIC02LDYgLTYsMTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzI1MTIwYSIgc3Ryb2tlLXdpZHRoPSIwLjkiIG9wYWNpdHk9IjAuNiIvPgogIDxwYXRoIGQ9Ik0gMCwtOSBDIDAsMCAwLDcgMCwxMS41IiBmaWxsPSJub25lIiBzdHJva2U9IiMyNTEyMGEiIHN0cm9rZS13aWR0aD0iMC45IiBvcGFjaXR5PSIwLjYiLz4KICA8cGF0aCBkPSJNIDYsLTggQyA2LDAgNiw2IDYsMTEiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzI1MTIwYSIgc3Ryb2tlLXdpZHRoPSIwLjkiIG9wYWNpdHk9IjAuNiIvPgogIDxyZWN0IHg9Ii0xLjQiIHk9Ii0xMy41IiB3aWR0aD0iMi44IiBoZWlnaHQ9IjUiIHJ4PSIxIiBmaWxsPSIjMmY1YTFhIi8+CiAgPHBvbHlnb24gcG9pbnRzPSItNC41LC0xIC0xLjUsLTEgLTMsMiIgZmlsbD0iI2ZmYjYyNyIgb3BhY2l0eT0iMC44NSIvPgogIDxwb2x5Z29uIHBvaW50cz0iNC41LC0xIDEuNSwtMSAzLDIiIGZpbGw9IiNmZmI2MjciIG9wYWNpdHk9IjAuODUiLz4KICA8cGF0aCBkPSJNIC00LDUuNSBRIDAsOC41IDQsNS41IEwgMyw3LjUgUSAwLDkuNSAtMyw3LjUgWiIgZmlsbD0iI2ZmYjYyNyIgb3BhY2l0eT0iMC44NSIvPgo8L2c+CjwvZz4KPC9zdmc+");
  background-size: 70px 70px;
}
[data-aesthetic="spookyscary"] .btn-primary { color: #2a1608; font-weight: 700; }

/* ---------- Aesthetic: Thrash Metal — amp-stack black, yellow/chrome/blood-red under stage light ---------- */
:root[data-aesthetic="thrash"] {
  --bg: #0a0a0a;
  --surface: #141414;
  --surface2: #1c1c1c;
  --border: #3a3a3a;
  --border-soft: #262626;
  --text: #f2f2f2;
  --text-dim: #b8b8b8;
  --text-faint: #6e6e6e;
  --accent: #ffd400;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --good: #7dffb0;
  --good-soft: #12291d;
  --bad: #ff1f3d;
  --bad-soft: #2a0f14;
  --warn: #ffb000;
  --reset-border: #ffd400;
  --reset-bg: #1c1c1c;
  --reset-text: #f2f2f2;
  --myo: #29b6ff;
  --goldenrod: #ff6a00;
  --savings: #4fc3f7;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Metal Mania', cursive;
  --font-body: 'Oswald', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 2px;
}
/* Signature: a hard-edged --bad drop-shadow behind every headline (no glow — this aesthetic reads
   as printed, not lit) plus cut-corner panels via clip-path standing in for the usual border-radius. */
[data-aesthetic="thrash"] .brand span,
[data-aesthetic="thrash"] .section-title { text-shadow: 2px 2px 0 var(--bad); text-transform: uppercase; letter-spacing: 0.02em; }
[data-aesthetic="thrash"] .panel,
[data-aesthetic="thrash"] .entry-card,
[data-aesthetic="thrash"] .modal-box,
[data-aesthetic="thrash"] .workout-cell,
[data-aesthetic="thrash"] .aesthetic-card {
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
}
[data-aesthetic="thrash"] .btn-primary { color: #1a1400; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; box-shadow: 0 0 14px -2px var(--accent); }
[data-aesthetic="thrash"] body {
  background-color: var(--bg);
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NiIgaGVpZ2h0PSI0NiIgdmlld0JveD0iMCAwIDQ2IDQ2Ij4KPHBvbHlnb24gcG9pbnRzPSIyNi4wMCwzLjAwIDI2LjAwLDMuMDAgMTIuMDAsMjIuMDAgMjAuMDAsMjIuMDAgMTYuMDAsNDMuMDAgMTYuMDAsNDMuMDAiIGZpbGw9IiMzMzJjMTYiLz48cG9seWdvbiBwb2ludHM9IjI2LjAwLDMuMDAgMjYuMDAsMy4wMCAxNi4wMCw0My4wMCAxNi4wMCw0My4wMCAzNC4wMCwyMC4wMCAyNC4wMCwyMC4wMCAzMC4wMCwzLjAwIiBmaWxsPSIjMTYxMzEwIi8+Cjxwb2x5Z29uIHBvaW50cz0iMjYuMDAsMy4wMCAxMi4wMCwyMi4wMCAyMC4wMCwyMi4wMCAxNi4wMCw0My4wMCAzNC4wMCwyMC4wMCAyNC4wMCwyMC4wMCAzMC4wMCwzLjAwIiBmaWxsPSJub25lIiBzdHJva2U9IiM4YzhjOGMiIHN0cm9rZS13aWR0aD0iMS4xIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIwLjgiLz4KPC9zdmc+");
  background-size: 46px 46px;
}

/* ---------- Aesthetic: Four Symbols (Sì Xiàng) — guardian-picker re-themes the whole app ---------- */
/* Base block below is the fallback backdrop (matches the Azure Dragon guardian, the default pick)
   used before JS applies a chosen guardian's mini-palette via applyAccentColor(). --bad, --warn,
   --myo, --goldenrod, --savings, fonts and --radius are fixed across all four guardians — see
   AESTHETIC_ACCENTS.sixiang / FULL_PALETTE_AESTHETICS for the properties that do swap per guardian. */
:root[data-aesthetic="sixiang"] {
  --bg: #050f14;
  --surface: #0a1d24;
  --surface2: #0f2830;
  --border: #1c4d55;
  --border-soft: #123138;
  --text: #d8f5f0;
  --text-dim: #7fb8c0;
  --text-faint: #4a7d85;
  --accent: #22d3ee;
  --accent-dim: color-mix(in srgb, var(--accent) 55%, var(--border));
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--surface));
  --good: #4ade80;
  --good-soft: #0f2a1c;
  --bad: #f43f5e;
  --bad-soft: #2a1016;
  --warn: #eab308;
  --reset-border: #22d3ee;
  --reset-bg: #123138;
  --reset-text: #22d3ee;
  --myo: #a855f7;
  --goldenrod: #c9a227;
  --savings: #38bdf8;
  --savings-soft: color-mix(in srgb, var(--savings) 16%, var(--surface));
  --font-head: 'Ma Shan Zheng', cursive;
  --font-body: 'ZCOOL XiaoWei', serif;
  --font-mono: 'JetBrains Mono', monospace;
  --radius: 10px;
}
[data-aesthetic="sixiang"] .panel,
[data-aesthetic="sixiang"] .entry-card,
[data-aesthetic="sixiang"] .modal-box { box-shadow: 0 0 0 1px var(--accent-dim) inset; }
[data-aesthetic="sixiang"] .btn-primary { font-weight: 600; }
/* Woodblock-style cloud tile, single non-overlapping outline, laid down as a mask over an
   --accent-soft fill so it retints with the chosen guardian — kept off `body` directly (mask
   would hide body's real content too) and instead on a ::before layer painted behind it. Body
   is made a positioning context so the layer can sit absolutely under the real content, which
   (as ordinary in-flow markup after this pseudo-element in tree order) always paints on top. */
[data-aesthetic="sixiang"] body { position: relative; }
[data-aesthetic="sixiang"] body::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-color: var(--accent-soft); opacity: 0.65;
  -webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTIwIDEyMCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTIsNDApIHNjYWxlKDEuMCkiPjxwYXRoIGQ9Ik0gLTM0LDYgQyAtMzQsLTIgLTMwLC02IC0yNSwtNiBDIC0yNSwtMTMgLTE4LC0xNyAtMTEsLTEzIEMgLTgsLTE5IDIsLTE5IDUsLTEzIEMgOCwtMTkgMTgsLTE5IDIxLC0xMyBDIDI4LC0xNyAzNSwtMTMgMzUsLTYgQyA0MCwtNiA0NCwtMiA0NCw0IEMgNDQsOSA0MCwxMSAzNiw5IEMgMzMsNy41IDMzLDQgMzYsMyBDIDI4LDkgMTYsMTEgNiw4IEMgLTQsMTEgLTE2LDkgLTI0LDMgQyAtMjEsNCAtMjEsNy41IC0yNCw5IEMgLTI4LDExIC0zMiw5IC0zNCw2IFoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvZz4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNiw5Mikgc2NhbGUoMC41KSI+PHBhdGggZD0iTSAtMzQsNiBDIC0zNCwtMiAtMzAsLTYgLTI1LC02IEMgLTI1LC0xMyAtMTgsLTE3IC0xMSwtMTMgQyAtOCwtMTkgMiwtMTkgNSwtMTMgQyA4LC0xOSAxOCwtMTkgMjEsLTEzIEMgMjgsLTE3IDM1LC0xMyAzNSwtNiBDIDQwLC02IDQ0LC0yIDQ0LDQgQyA0NCw5IDQwLDExIDM2LDkgQyAzMyw3LjUgMzMsNCAzNiwzIEMgMjgsOSAxNiwxMSA2LDggQyAtNCwxMSAtMTYsOSAtMjQsMyBDIC0yMSw0IC0yMSw3LjUgLTI0LDkgQyAtMjgsMTEgLTMyLDkgLTM0LDYgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIuNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9nPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDQsOTIpIHNjYWxlKDAuNSkiPjxwYXRoIGQ9Ik0gLTM0LDYgQyAtMzQsLTIgLTMwLC02IC0yNSwtNiBDIC0yNSwtMTMgLTE4LC0xNyAtMTEsLTEzIEMgLTgsLTE5IDIsLTE5IDUsLTEzIEMgOCwtMTkgMTgsLTE5IDIxLC0xMyBDIDI4LC0xNyAzNSwtMTMgMzUsLTYgQyA0MCwtNiA0NCwtMiA0NCw0IEMgNDQsOSA0MCwxMSAzNiw5IEMgMzMsNy41IDMzLDQgMzYsMyBDIDI4LDkgMTYsMTEgNiw4IEMgLTQsMTEgLTE2LDkgLTI0LDMgQyAtMjEsNCAtMjEsNy41IC0yNCw5IEMgLTI4LDExIC0zMiw5IC0zNCw2IFoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvZz48L3N2Zz4=");
  mask-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTIwIDEyMCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTIsNDApIHNjYWxlKDEuMCkiPjxwYXRoIGQ9Ik0gLTM0LDYgQyAtMzQsLTIgLTMwLC02IC0yNSwtNiBDIC0yNSwtMTMgLTE4LC0xNyAtMTEsLTEzIEMgLTgsLTE5IDIsLTE5IDUsLTEzIEMgOCwtMTkgMTgsLTE5IDIxLC0xMyBDIDI4LC0xNyAzNSwtMTMgMzUsLTYgQyA0MCwtNiA0NCwtMiA0NCw0IEMgNDQsOSA0MCwxMSAzNiw5IEMgMzMsNy41IDMzLDQgMzYsMyBDIDI4LDkgMTYsMTEgNiw4IEMgLTQsMTEgLTE2LDkgLTI0LDMgQyAtMjEsNCAtMjEsNy41IC0yNCw5IEMgLTI4LDExIC0zMiw5IC0zNCw2IFoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvZz4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNiw5Mikgc2NhbGUoMC41KSI+PHBhdGggZD0iTSAtMzQsNiBDIC0zNCwtMiAtMzAsLTYgLTI1LC02IEMgLTI1LC0xMyAtMTgsLTE3IC0xMSwtMTMgQyAtOCwtMTkgMiwtMTkgNSwtMTMgQyA4LC0xOSAxOCwtMTkgMjEsLTEzIEMgMjgsLTE3IDM1LC0xMyAzNSwtNiBDIDQwLC02IDQ0LC0yIDQ0LDQgQyA0NCw5IDQwLDExIDM2LDkgQyAzMyw3LjUgMzMsNCAzNiwzIEMgMjgsOSAxNiwxMSA2LDggQyAtNCwxMSAtMTYsOSAtMjQsMyBDIC0yMSw0IC0yMSw3LjUgLTI0LDkgQyAtMjgsMTEgLTMyLDkgLTM0LDYgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjIuNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PC9nPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDQsOTIpIHNjYWxlKDAuNSkiPjxwYXRoIGQ9Ik0gLTM0LDYgQyAtMzQsLTIgLTMwLC02IC0yNSwtNiBDIC0yNSwtMTMgLTE4LC0xNyAtMTEsLTEzIEMgLTgsLTE5IDIsLTE5IDUsLTEzIEMgOCwtMTkgMTgsLTE5IDIxLC0xMyBDIDI4LC0xNyAzNSwtMTMgMzUsLTYgQyA0MCwtNiA0NCwtMiA0NCw0IEMgNDQsOSA0MCwxMSAzNiw5IEMgMzMsNy41IDMzLDQgMzYsMyBDIDI4LDkgMTYsMTEgNiw4IEMgLTQsMTEgLTE2LDkgLTI0LDMgQyAtMjEsNCAtMjEsNy41IC0yNCw5IEMgLTI4LDExIC0zMiw5IC0zNCw2IFoiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIyLjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvZz48L3N2Zz4=");
  -webkit-mask-repeat: repeat; mask-repeat: repeat;
  -webkit-mask-size: 120px 120px; mask-size: 120px 120px;
}

/* ---- Pasting a lab report ---- */
.lab-paste {
  background: var(--surface2); border: 1px solid var(--border-soft);
  border-radius: var(--radius); padding: 12px; margin-bottom: 14px;
}
.lab-paste-help { font-size: 11px; color: var(--text-dim); line-height: 1.5; margin: 6px 0 10px; }
/* Monospace and unwrapped on purpose: pasted report text arrives column-aligned, and either a
   proportional face or a soft wrap scrambles that into something you can't check against the paper
   in your hand. Report lines are wider than a phone, so the box scrolls sideways instead. */
.lab-paste textarea {
  font-family: var(--font-mono); font-size: 12px;
  white-space: pre; overflow-x: auto;
}
.lab-paste-report {
  display: flex; align-items: center; gap: 10px;
  font-size: 11px; line-height: 1.45; color: var(--text-dim);
  background: var(--surface2); border-left: 3px solid var(--accent);
  border-radius: var(--radius); padding: 8px 10px; margin-bottom: 14px;
}
.lab-paste-report span { flex: 1 1 auto; min-width: 0; }
.lab-paste-report .btn { flex: none; }
/* A field the paste filled reads differently from one you typed, so the numbers still needing a
   check against the report are obvious at a glance. Element+class, because a bare class loses to
   the (0,1,1) of `input[type="number"]`. */
input[type="number"].lab-filled {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
}

/* ---- Editing a saved panel ---- */
.lab-editing-head {
  font-size: 11px; line-height: 1.45; color: var(--text-dim);
  border-left: 3px solid var(--accent); background: var(--surface2);
  border-radius: var(--radius); padding: 8px 10px; margin-bottom: 12px;
}
/* The form is at the top of the screen and its card can be several scrolls down, so the card says
   which one is under the knife. A ::before stripe rather than a border, for the reason the --rung
   tinting learned: aesthetics restate .entry-card's border-color at a specificity this can't beat. */
.lab-card-editing { position: relative; }
.lab-card-editing::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--accent); border-radius: var(--radius) 0 0 var(--radius);
}
.lab-card-editing .lab-count { color: var(--accent); font-weight: 700; }

/* ---- The trail: earlier readings on the same track ---- */
/* Dots, not lines, so the current reading's mark stays the one thing that reads as "now". They sit
   under it in the DOM, so an old reading that landed on today's value never hides it. */
.lab-bar-ghost {
  position: absolute; top: 50%; width: 5px; height: 5px;
  margin: -2.5px 0 0 -2.5px; border-radius: 50%;
  background: var(--text); pointer-events: none;
}
.lab-delta {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 10px; margin-top: 4px; color: var(--text-dim);
}
.lab-delta-since { color: var(--text-faint); }
/* Coloured by movement relative to the band YOU set, never by the raw sign — down 14 on ApoB is
   progress and down 14 on HDL is not, and this screen doesn't get to hold that opinion itself.
   A marker with no bounds stated keeps the inherited dim colour. */
.lab-move-toward .mono { color: var(--good); }
.lab-move-away .mono { color: var(--bad); }
