:root {
  --cobalt: #0047AB;
  --ink: #1A1A1A;
  --gray-100: #F5F5F5;
  --gray-300: #DDD;
  --gray-500: #888;
  --white: #FFFFFF;
  /* --surface is the "elevated background" color (cards, inputs, page
     background, outline-button fill) — kept separate from --white
     because --white is also used as *text* color on top of solid-cobalt
     buttons/navlinks, which must stay light in both themes. Overloading
     one variable for both roles would break button text in dark mode. */
  --surface: #FFFFFF;
  --green: #1E7B34;
  --red: #B3261E;
  --amber: #B8860B;
}

[data-theme="dark"] {
  --cobalt: #5B9BFF;
  --ink: #EDEDED;
  --gray-100: #262626;
  --gray-300: #3A3A3A;
  --gray-500: #A0A0A0;
  --surface: #1C1C1C;
  --green: #4ADE80;
  --red: #F87171;
  --amber: #FBBF24;
}
body { background: var(--surface); }
[data-theme="dark"] body { background: #121212; }
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  background: var(--surface);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}
a { color: var(--cobalt); text-decoration: none; }
/* Comfortable density pass (approved 2026-08-14) — more breathing room
   across the whole app: wrap padding, headings, cards, stat cards, inputs
   and a few other spacing-heavy elements all bumped up from the previous
   tighter defaults. Bottom padding keeps enough clearance for the fixed
   rest-timer/bottom-tab bars, just a touch more than before. */
.wrap { max-width: 480px; margin: 0 auto; padding: 26px 24px 96px; }
/* The coach dashboard is used mostly on desktop, so this used to leave a
   lot of empty space on anything wider than ~960px. Matches .wrap-full
   now (kept as a separate class in case a page ever needs a narrower
   "wide" tier again, but both render identically today). */
.wrap-wide { max-width: 1320px; margin: 0 auto; padding: 36px 32px 68px; }
.wrap-full { max-width: 1320px; margin: 0 auto; padding: 36px 32px 68px; }

/* Thin bar fired on link-click/form-submit to show a real navigation is in
   flight (see the page-progress IIFE in views.js's page() script). Sits
   above everything, including the topbar. Width jumps most of the way
   immediately then creeps the rest of the way over a few seconds via a
   slow transition — it's never meant to visually "complete", since the
   real page swap (not JS) is what removes it. */
#page-progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  background: var(--cobalt); z-index: 9999; opacity: 0;
  transition: width 0.35s ease-out, opacity 0.2s ease;
}
#page-progress.loading {
  opacity: 1; width: 78%;
  transition: width 5s cubic-bezier(0.1, 0.6, 0.2, 1), opacity 0.15s ease;
}

header.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px 28px; border-bottom: 1px solid var(--gray-300);
  flex-wrap: wrap; row-gap: 10px;
}
/* Global search box (coach only — see topbar() in views.js). Sits between
   the page nav and the who/logout block, and is allowed to shrink or wrap
   to its own row on narrow viewports rather than squeezing the nav links,
   since search is a nice-to-have next to actually getting to a page. */
.topbar-search { flex: 1 1 220px; max-width: 360px; margin: 0 4px; position: relative; }
.topbar-search input[type="search"] {
  padding: 7px 14px 7px 34px; border-radius: 999px; font-size: 13.5px;
}
.topbar-search .search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--gray-500); pointer-events: none; display: flex; }
header.topbar .brand { font-weight: 800; letter-spacing: 0.02em; font-size: 18px; display: flex; align-items: center; gap: 8px; }
header.topbar .brand span { color: var(--cobalt); }
header.topbar .brand-mark { display: inline-flex; line-height: 0; }
header.topbar .brand-mark img { display: block; border-radius: 6px; }
header.topbar .who { font-size: 13px; color: var(--gray-500); text-align: right; }
header.topbar .who a { color: var(--ink); font-size: 12px; text-decoration: underline; }
/* Log out is a POST form now (was a plain GET link — a real, if minor,
   CSRF gap since SameSite=Lax only blocks cross-site POST, not top-level
   GET navigation), styled to look identical to the plain link it replaced. */
.who-logout-btn {
  background: none; border: none; padding: 0; margin: 0;
  color: var(--ink); font-size: 12px; text-decoration: underline;
  cursor: pointer; font-family: inherit;
}
.theme-toggle {
  background: none; border: 1.5px solid var(--gray-300); border-radius: 999px;
  width: 30px; height: 30px; line-height: 1; cursor: pointer;
  color: var(--ink); margin-bottom: 4px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.theme-toggle:active { opacity: 0.7; }
/* Two icons live inside the button at all times; CSS (not JS) decides
   which one shows, based on the current data-theme attribute — the icon
   shown is the action ("tap to switch to this"), not the current state. */
.theme-toggle .theme-icon-dark { display: none; }
[data-theme="dark"] .theme-toggle .theme-icon-dark { display: flex; }
[data-theme="dark"] .theme-toggle .theme-icon-light { display: none; }

/* Icon set (see the `icon()` helper in views.js) — inherits color so it
   follows text color / dark mode automatically without separate rules. */
.icon { flex-shrink: 0; vertical-align: -3px; }
.navlink { display: inline-flex; align-items: center; gap: 6px; }
button.navlink .icon { display: none; } /* muscle-group filter buttons reuse .navlink but have no icon markup — nothing to show, this just guards against layout shift if that ever changes */

/* Small avatar-initials circle for client rows — real initials from the
   client's actual name, not a photo (no avatar upload feature exists). */
.avatar-circle {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  background: rgba(0, 71, 171, 0.1); color: var(--cobalt);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700;
}
[data-theme="dark"] .avatar-circle { background: rgba(91, 155, 255, 0.18); }

/* Client-app bottom tab bar (see clientBottomNav in views.js) — real
   destinations only (Home / Progress / Nutrition), fixed to the bottom of
   the viewport like the rest timer bar, but at a lower z-index so the rest
   bar (only shown mid-set, on the session-log page, which doesn't render
   this nav at all) would still win if they ever somehow overlapped. .wrap
   already carries 90px of bottom padding for the rest bar, which is also
   enough to clear this shorter bar. */
.bottom-tabs {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; background: var(--surface); border-top: 1px solid var(--gray-300);
}
.bottom-tab {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 8px 0 10px; font-size: 10.5px; font-weight: 600; color: var(--gray-500);
}
.bottom-tab.active { color: var(--cobalt); }

.wrap-wide header.topbar, .wrap header.topbar {}

h1 { font-size: 22px; margin: 24px 0 8px; }
h2 { font-size: 17px; margin: 38px 0 14px; border-bottom: 1px solid var(--gray-300); padding-bottom: 8px; }
.sub { color: var(--gray-500); font-size: 14px; margin-bottom: 28px; }

.card {
  border: 1px solid var(--gray-300);
  border-radius: 16px;
  padding: 26px;
  margin-bottom: 20px;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  /* Lets a browser's native #fragment scroll (e.g. jumping to a
     newly-added session/exercise card — see the redirect targets in
     server.js) leave a little breathing room above the card instead of
     landing it flush against the top edge of the viewport. Has no effect
     on normal in-page scrolling. */
  scroll-margin-top: 14px;
}
[data-theme="dark"] .card { box-shadow: none; }
.card.accent { border-left: 4px solid var(--cobalt); }

/* Habit checklist rows — a light cobalt wash on the checked state gives
   immediate visual feedback (checkbox alone reads as flat/static on a
   dark background) without relying on color contrast tricks that would
   need separate light/dark tuning everywhere. */
.habit-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px; margin: 2px -10px; border-radius: 8px;
  font-weight: 400; text-transform: none; font-size: 14.5px; color: var(--ink);
  transition: background 0.15s ease;
}
.habit-row input[type="checkbox"] { width: 22px; height: 22px; flex-shrink: 0; accent-color: var(--cobalt); }
.habit-row:has(input:checked) { background: rgba(0, 71, 171, 0.07); }
[data-theme="dark"] .habit-row:has(input:checked) { background: rgba(91, 155, 255, 0.14); }

.btn {
  display: inline-block; background: var(--cobalt); color: var(--white);
  border: none; padding: 13px 20px; border-radius: 12px; font-size: 15px;
  font-weight: 700; cursor: pointer; text-align: center; width: 100%;
  transition: opacity 0.15s ease;
}
.btn:active { opacity: 0.85; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn.secondary { background: var(--surface); color: var(--ink); border: 1.5px solid var(--ink); }
.btn.small { width: auto; padding: 11px 18px; font-size: 14.5px; }

input, select {
  font-size: 16px; padding: 13px 16px; border: 1.5px solid var(--gray-300);
  border-radius: 10px; width: 100%; background: var(--surface); color: var(--ink);
}
input:focus, select:focus { outline: none; border-color: var(--cobalt); }
label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--gray-500); display: block; margin-bottom: 4px; }

.login-box { max-width: 360px; margin: 12vh auto 0; padding: 0 20px; }
.login-box .card { padding: 28px 24px; }
.error-msg { background: #FDECEA; color: var(--red); border: 1px solid #F5C6C3; padding: 10px 12px; border-radius: 7px; font-size: 14px; margin-bottom: 14px; }

.empty-state { text-align: center; padding: 60px 20px; }
.empty-state .icon { font-size: 40px; margin-bottom: 12px; }
.empty-state h2 { border: none; }
.empty-state p { color: var(--gray-500); font-size: 14px; }

.exercise-row {
  border: 1px solid var(--gray-300); border-radius: 14px; padding: 18px 22px; margin-bottom: 18px;
}
.exercise-row .ex-name { font-weight: 700; font-size: 15.5px; margin-bottom: 2px; }
.exercise-row .ex-target { color: var(--gray-500); font-size: 13px; margin-bottom: 10px; }
.ex-inputs { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
.ex-inputs div label { margin-bottom: 3px; }

table.data-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.data-table th, table.data-table td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--gray-300); }
table.data-table th { color: var(--gray-500); font-weight: 600; text-transform: uppercase; font-size: 11px; letter-spacing: 0.03em; }
table.data-table tr:hover td { background: var(--gray-100); }

.badge { display: inline-block; padding: 3px 9px; border-radius: 20px; font-size: 11.5px; font-weight: 700; }
.badge.up { background: #E7F4EA; color: var(--green); }
.badge.hold { background: #FDF2DC; color: var(--amber); }
.badge.wait { background: var(--gray-100); color: var(--gray-500); }

.client-list a.client-link { display: block; }
.client-row { display: flex; justify-content: space-between; align-items: center; padding: 18px 6px; border-bottom: 1px solid var(--gray-300); }
.client-row:last-child { border-bottom: none; }
.client-row .name { font-weight: 700; }
.client-row .meta { font-size: 12.5px; color: var(--gray-500); }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 640px) { .grid-2 { grid-template-columns: 1fr; } }

.kpi { border: 1px solid var(--gray-300); border-radius: 14px; padding: 18px; text-align: center; }
.kpi .num { font-size: 24px; font-weight: 800; color: var(--cobalt); }
.kpi .lbl { font-size: 11.5px; color: var(--gray-500); text-transform: uppercase; letter-spacing: 0.03em; }

footer.note { text-align: center; color: var(--gray-500); font-size: 11.5px; margin-top: 40px; }

/* --- Added for full feature parity with the FitCoach prototype --- */
.badge.down { background: #FDEAEA; color: var(--red); }

.topnav { display: flex; gap: 10px; flex-wrap: wrap; }
/* Pill-shaped nav/filter chips — same shape whether it's a page-nav <a>
   (topnav) or a client-side filter <button> (exercise library's
   muscle-group buttons reuse this class on real buttons below). */
.navlink { padding: 9px 18px; border-radius: 999px; font-size: 13px; font-weight: 600; color: var(--gray-500); transition: background 0.15s ease, border-color 0.15s ease; }
.navlink.active { background: var(--cobalt); color: var(--white); }
/* .navlink started out as an <a>-only style (the top nav); the exercise
   library's muscle-group filter buttons reuse it on real <button>
   elements instead (no page navigation, just client-side show/hide), so
   strip the native button chrome that would otherwise show through. */
button.navlink { background: none; border: 1px solid var(--gray-300); cursor: pointer; font-family: inherit; }
button.navlink.active { border-color: var(--cobalt); }

.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin: 20px 0 26px; }
@media (max-width: 640px) { .stat-grid { grid-template-columns: repeat(2, 1fr); } }
.stat-card { border: 1px solid var(--gray-300); border-radius: 14px; padding: 18px; text-align: center; background: var(--surface); }
.stat-card.warn { border-color: #E8C976; background: #FDF6E3; }
.stat-icon { color: var(--cobalt); margin-bottom: 4px; display: flex; justify-content: center; }
.stat-card.warn .stat-icon { color: var(--amber); }
.stat-value { font-size: 19px; font-weight: 800; color: var(--cobalt); }
.stat-card.warn .stat-value { color: var(--amber); }
.stat-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--gray-500); margin-top: 2px; }
/* One stat per page gets the solid-fill treatment (see the `hero` flag on
   statCards() items in views.js) — the single number that deserves the
   most visual weight, e.g. sessions due today, rather than every card
   competing equally for attention. */
.stat-card.hero { background: var(--cobalt); border-color: var(--cobalt); }
.stat-card.hero .stat-value, .stat-card.hero .stat-label, .stat-card.hero .stat-icon { color: var(--white); }
.stat-card.hero .stat-label { opacity: 0.85; }

.label-sm { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--gray-500); margin-bottom: 8px; }

.activity-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 15px 6px; border-bottom: 1px solid var(--gray-300); font-size: 14px; color: var(--ink); }
.activity-row:last-child { border-bottom: none; }
.ok-text { color: var(--green); font-weight: 700; font-size: 12.5px; }

.card.attn { border-color: #E8C976; background: #FDF8EC; padding: 10px 16px; }
/* Today page's "Needs attention" rows — like .activity-row, but the link
   and the quick-capture note/task toggle underneath it can't share one
   <a> (forms can't nest inside anchors), so this is its own block-level
   row rather than an anchor. */
.attn-row { padding: 14px 6px; border-bottom: 1px solid var(--gray-300); }
.attn-row:last-child { border-bottom: none; }
/* In-progress add-form variant — same amber pairing as .card.attn, kept as
   its own class (rather than reusing .card.add-new's cobalt) specifically
   for the nutrition "add a variant" form, which otherwise sat directly
   next to a saved Main variant card in the same cobalt color and read as
   the same thing. Blue now means "this is the saved Main option"; amber
   means "you're currently filling this in". Scoped narrowly on purpose —
   .card.add-new (Add exercise, Add meal, Add task, etc.) has no such
   neighboring blue card to collide with, so it's left untouched. */
.card.add-variant { border-color: #E8C976; background: #FDF8EC; }
/* Success variant (e.g. the one-time new-password reveal) — same light-
   pastel/dark-tint pairing as .card.attn above, just green instead of
   amber, so it actually adapts to the theme instead of being hardcoded
   to look "dark" (which used to render wrong on the light theme). */
.card.success { border-color: #8FCB9F; background: #EEF8F0; }
/* Nested card — an exercise block sitting inside its parent session card
   needs a shade that reads as "recessed" in both themes, not just a
   light-mode-only white that goes stark against a dark page. */
.card.nested { background: var(--gray-100); }
/* "Add new" affordance card — a light cobalt wash instead of plain
   var(--surface), so create-actions read as distinct and inviting in
   both themes. */
.card.add-new { background: rgba(0, 71, 171, 0.05); border-color: var(--cobalt); }
[data-theme="dark"] .card.add-new { background: rgba(91, 155, 255, 0.10); }

.filter-bar { display: flex; gap: 8px; margin: 10px 0 14px; flex-wrap: wrap; }
.filter-bar input[type=text] { flex: 1; min-width: 200px; }
.filter-bar select { width: auto; }

.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 640px) { .two-col { grid-template-columns: 1fr; } }

/* Three independent panels side by side on desktop (client-detail page's
   notes / communication log / tasks) instead of stacking full-width —
   each is its own scrollable list, so there's no reason they can't share
   a row on anything wide enough. Collapses earlier than .two-col (900px,
   not 640px) since three narrow columns get cramped sooner than two. */
.three-col { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; align-items: start; }
@media (max-width: 900px) { .three-col { grid-template-columns: 1fr; } }

/* Client-detail page's sticky jump-nav (see subNav in coachClientDetail)
   — every section it links to already renders on the same page load, so
   this is a pure in-page anchor row. Sticks to the top of the viewport
   once scrolled there (the main topbar above it is not itself sticky, so
   there's no double-sticky-bar stacking to account for). Horizontal
   scroll on a narrow viewport rather than wrapping, so it never grows
   past a single row and pushes content down. */
.subnav {
  position: sticky; top: 0; z-index: 25;
  display: flex; gap: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch;
  background: var(--surface); border-bottom: 1px solid var(--gray-300);
  padding: 10px 2px; margin: 4px 0 18px;
}
.subnav a { flex-shrink: 0; }

.tag { display: inline-block; font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 10px; background: var(--gray-100); color: var(--gray-500); text-transform: uppercase; letter-spacing: 0.02em; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.client-tag { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; padding: 3px 6px 3px 9px; border-radius: 12px; background: rgba(0,71,171,0.08); color: var(--cobalt); }
[data-theme="dark"] .client-tag { background: rgba(91,155,255,0.16); }
.client-tag-remove { background: none; border: none; padding: 0; margin: 0; color: inherit; opacity: 0.6; cursor: pointer; font-size: 13px; line-height: 1; font-family: inherit; }
.client-tag-remove:hover { opacity: 1; }

.note-row { padding: 8px 0; border-bottom: 1px solid var(--gray-300); font-size: 13.5px; }
.note-row:last-child { border-bottom: none; }

.comm-log-list { max-height: 360px; overflow-y: auto; }
.comm-log-row { display: flex; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--gray-300); font-size: 13.5px; }
.comm-log-row:last-child { border-bottom: none; }
.comm-log-icon { flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%; background: var(--gray-100); display: flex; align-items: center; justify-content: center; font-size: 13px; }

.heatmap { display: flex; gap: 6px; flex-wrap: wrap; }
.hm-cell-wrap { flex: 1; min-width: 46px; text-align: center; }
.hm-cell { border-radius: 7px; padding: 8px 2px; font-size: 11px; font-weight: 700; }
.hm-full { background: #E7F4EA; color: var(--green); }
.hm-partial { background: #FDF2DC; color: var(--amber); }
.hm-none { background: #FDEAEA; color: var(--red); }
.hm-excluded { background: var(--gray-100); color: var(--gray-500); }
.hm-label { font-size: 9px; color: var(--gray-500); margin-top: 3px; }

.btn.outline { background: var(--surface); color: var(--ink); border: 1.5px solid var(--ink); width: auto; padding: 11px 18px; font-size: 14.5px; }
.btn.outline.danger { color: var(--red); border-color: var(--red); }
/* Reference-video link on exercise cards — deliberately a bit larger than
   a standard small button (client taps this mid-set on a phone, often
   between reps, so it needs to be easy to hit without careful aiming). */
.watch-demo-btn { padding: 13px 20px; font-size: 15px; margin: 6px 0 4px; color: var(--cobalt); border-color: var(--cobalt); }
/* Solid variant — reserved for the one truly irreversible action (permanent
   client deletion), so it reads as meaningfully higher-stakes than the
   outline danger buttons used for reversible deletes (a task, a tag, a
   session, a template). */
.btn.danger:not(.outline) { background: var(--red); color: #fff; border-color: var(--red); }
.btn.danger:not(.outline):disabled { opacity: 0.45; cursor: not-allowed; }

.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; margin-top: 8px; }
.photo-tile { text-align: center; }
.photo-tile img { width: 100%; height: 140px; object-fit: cover; border-radius: 14px; border: 1px solid var(--gray-300); }

/* Groups photos into dated check-ins, separated by a visible divider so
   scrolling through a client's history reads as a timeline rather than
   one undifferentiated wall of thumbnails. */
.checkin-group { padding-top: 14px; margin-top: 14px; border-top: 1px solid var(--gray-300); }
.checkin-group:first-child { padding-top: 0; margin-top: 0; border-top: none; }
.checkin-date { font-weight: 700; font-size: 13.5px; margin-bottom: 4px; }

.set-row { border-top: 1px solid var(--gray-300); padding-top: 10px; margin-top: 10px; }
.set-row:first-of-type { border-top: none; margin-top: 0; }
.set-row-label { font-size: 12.5px; font-weight: 700; margin-bottom: 6px; }

/* Per-set action row (rest timer + drop-set toggle) — sized to match the
   app's other small buttons rather than the old plain-text summary link,
   so they're comfortable to tap mid-set on a phone. */
.set-row-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.set-action-btn {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 9px 14px; border-radius: 10px; border: 1.5px solid var(--gray-300);
  background: var(--surface); color: var(--ink); font-size: 13.5px; font-weight: 700;
  cursor: pointer; list-style: none;
}
.set-action-btn::-webkit-details-marker { display: none; }
/* "+ Add exercise" toggle — same idea as the drop-set details/summary
   above, styled as a plain outline button instead of a small pill: the
   form only renders open once the coach actually wants to add something,
   instead of always taking up a full card at the bottom of every session. */
.add-exercise-toggle summary { cursor: pointer; list-style: none; }
.add-exercise-toggle summary::-webkit-details-marker { display: none; }
/* Inline edit-in-place toggle — coach notes, communication log entries and
   tasks all use this: the <summary> is the normal read view, opening it
   reveals a small edit form in place, no separate edit page/modal. */
.edit-toggle summary { cursor: pointer; list-style: none; }
.edit-toggle summary::-webkit-details-marker { display: none; }
.edit-toggle[open] summary { margin-bottom: 4px; }
/* Workout history rows expand in place instead of navigating to a
   separate page — .activity-row already gives the right spacing/border,
   this just makes the <summary> behave like the rest of that row (no
   default triangle, whole row clickable). */
.workout-history-toggle summary { cursor: pointer; list-style: none; }
.workout-history-toggle summary::-webkit-details-marker { display: none; }
.workout-history-toggle[open] summary { border-bottom: none; }

/* Client dashboard's Body weight / Habits cards — collapsed by default,
   summary line + tap to open. The <details> element itself carries the
   .card/.card.accent styling so the collapsed and expanded states are the
   same box, just taller when open. */
.collapsible-card summary {
  cursor: pointer; list-style: none;
  display: flex; justify-content: space-between; align-items: flex-start; gap: 10px;
}
.collapsible-card summary::-webkit-details-marker { display: none; }
.collapsible-card summary .chevron {
  color: var(--gray-500); font-size: 12px; flex-shrink: 0; margin-top: 4px;
  transition: transform 0.15s ease;
}
.collapsible-card[open] summary .chevron { transform: rotate(180deg); }
.set-action-btn:active { opacity: 0.7; }
.rest-btn.active { border-color: var(--cobalt); color: var(--cobalt); }

/* Drag-and-drop reorder handle (sessions, meals, recipes) — see
   dragHandle()/initDragReorder() in views.js. touch-action:none is the
   part that actually matters for mobile: without it, the browser starts
   its own page-scroll gesture on the first touchmove and the drag never
   gets a chance to take over. */
.drag-handle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0;
  color: var(--gray-500); cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none;
  border-radius: 8px;
}
.drag-handle:hover { color: var(--ink); background: var(--gray-100); }
[data-reorder-id].dragging { opacity: 0.5; cursor: grabbing; }
[data-reorder-id].dragging .drag-handle { cursor: grabbing; }

/* Food database search dropdown — see initFoodSearch() in views.js.
   .food-search-wrap is the positioning context; the results list is
   absolutely positioned so it overlays whatever comes after the input
   instead of pushing the form's layout around while open. */
.food-search-wrap, .composer-search-wrap { position: relative; }
.food-search-results, .composer-search-results {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 20;
  background: var(--surface); border: 1px solid var(--gray-300); border-radius: 10px;
  margin-top: 4px; max-height: 220px; overflow-y: auto;
  box-shadow: 0 6px 20px rgba(0,0,0,0.12);
}
.food-search-results:empty, .composer-search-results:empty { display: none; }
.food-search-result, .composer-search-result { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--gray-100); }
.food-search-result:last-child, .composer-search-result:last-child { border-bottom: none; }
.food-search-result:hover, .composer-search-result:hover { background: var(--gray-100); }
.food-search-empty, .composer-search-empty { padding: 8px 12px; }
.food-macro-preview:empty { display: none; }

/* An already-added ingredient's editable row (name/grams/Save/Remove) —
   a plain div now, not its own <form>, since it lives inline inside the
   recipe/variant's own Save-everything form (two <form> elements can't
   nest). See initFoodSearch()/initIngredientRowActions() in views.js. */
.ingredient-edit-row { margin-bottom: 10px; }

/* Ingredient composer — see initIngredientComposer() in views.js. Type a
   name, pick from the dropdown (or press Enter to keep whatever was
   typed), confirm a gram amount, and it becomes a removable chip; several
   chips can be staged before one "Add ingredients" submit. Nothing here
   is ever auto-matched — every food link is a dropdown result the coach
   actually clicked. */
.ingredient-composer { margin-top: 8px; }
.composer-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.composer-chips:empty { margin-bottom: 0; }
.composer-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--gray-100); border: 1px solid var(--gray-300); border-radius: 999px;
  padding: 4px 6px 4px 12px; font-size: 13px;
}
.composer-chip-remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%; border: none; background: transparent;
  color: var(--gray-500); cursor: pointer; font-size: 15px; line-height: 1; padding: 0;
}
.composer-chip-remove:hover { background: var(--gray-300); color: var(--ink); }
.composer-pending {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  padding: 8px 10px; background: var(--gray-100); border-radius: 10px;
}
.composer-pending-name { font-weight: 600; flex: 1; }
.composer-grams-input { max-width: 90px; }

.drop-details { font-size: 12.5px; }
.drop-details[open] .set-action-btn { border-color: var(--cobalt); color: var(--cobalt); }
.drop-details textarea {
  margin-top: 8px; width: 140px; max-width: 100%; min-height: 34px;
  height: 34px; padding: 6px 8px; font-size: 13px; resize: vertical;
}

/* Rest timer — sticky bar pinned to the bottom of the viewport, hidden
   until a set's "Rest" button is tapped. .wrap already has 90px of bottom
   padding, which happens to be exactly what keeps the last card clear of
   this bar when it's showing. */
.rest-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  background: var(--cobalt); color: var(--white);
  padding: 14px 18px; display: flex; align-items: center; justify-content: space-between;
  gap: 14px; box-shadow: 0 -2px 14px rgba(0,0,0,0.2);
  transform: translateY(110%); transition: transform 0.25s ease;
}
.rest-bar.show { transform: translateY(0); }
.rest-bar .rest-time { font-size: 24px; font-weight: 800; font-variant-numeric: tabular-nums; line-height: 1; }
.rest-bar .sub { margin-bottom: 0; }
.rest-bar .rest-controls { display: flex; gap: 8px; align-items: center; }
.rest-bar button {
  background: rgba(255,255,255,0.18); border: none; color: var(--white);
  border-radius: 8px; padding: 9px 13px; font-weight: 700; font-size: 13px; cursor: pointer;
}
.rest-bar button:active { opacity: 0.7; }

/* Sticky save bar (client session-log page) — appears once the client has
   entered at least one value (see the inline script in clientSessionLog),
   so logging a long session doesn't require scrolling all the way back
   down past every exercise just to submit. z-index sits below the rest
   bar (which occupies the same viewport edge while a set's timer is
   running) so the rest bar visually wins without either fighting for the
   same space — .wrap's existing bottom padding for the rest bar is also
   tall enough to clear this bar underneath it. */
.session-save-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  background: var(--surface); border-top: 1px solid var(--gray-300);
  padding: 10px 16px; box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.1);
  transform: translateY(110%); transition: transform 0.2s ease;
}
.session-save-bar.show { transform: translateY(0); }
.session-save-bar .btn { margin: 0; }

/* Overflow ("more actions") menu — client-detail page's rare/destructive
   actions (reset password, export data, delete client) live here instead
   of sitting at equal visual weight next to Edit program/Nutrition plan/
   Food log, which a coach opens constantly. <details>/<summary>, same
   no-JS-required toggle pattern as every other collapsible element in
   this app; a small script (see closeOverflowMenus in page()'s script)
   closes it on an outside click or Escape, since <details> has no native
   "close on outside click" behavior. */
.overflow-menu { position: relative; display: inline-block; }
.overflow-menu-btn {
  list-style: none; cursor: pointer; padding: 11px 14px !important;
  display: inline-flex; align-items: center; justify-content: center;
}
.overflow-menu-btn::-webkit-details-marker { display: none; }
.overflow-menu-list {
  position: absolute; right: 0; top: 100%; z-index: 30; margin-top: 6px;
  background: var(--surface); border: 1px solid var(--gray-300); border-radius: 12px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14); min-width: 180px; padding: 6px; overflow: hidden;
}
.overflow-menu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 10px 12px; border-radius: 8px; font-size: 14px; font-weight: 600;
  color: var(--ink); cursor: pointer; font-family: inherit; text-decoration: none;
}
.overflow-menu-item:hover { background: var(--gray-100); }
.overflow-menu-item.danger { color: var(--red); }

/* background/color were missing here (input/select set them, textarea
   didn't) — in dark mode every textarea rendered with the browser's
   default white background and black text instead of following the
   theme. Existing textareas (program builder's set-list, lead notes) had
   this bug too; fixing it here fixes all of them, not just the new
   nutrition ones. */
textarea { font-size: 14px; padding: 13px 16px; border: 1.5px solid var(--gray-300); border-radius: 10px; width: 100%; background: var(--surface); color: var(--ink); font-family: inherit; }
textarea:focus { outline: none; border-color: var(--cobalt); }

@media print {
  header.topbar, .no-print { display: none !important; }
  .wrap-wide, .wrap { max-width: 100%; padding: 0; }
}


/* ---- Dark theme overrides for hardcoded pastel status colors ----
   These badge/warning/heatmap backgrounds were tuned as light pastels for
   a white page and don't work as-is on a dark background — text contrast
   and "sticky note on a dark screen" glare both suffer without this. */
[data-theme="dark"] .error-msg { background: #3A1F1F; border-color: #5A2E2E; }
[data-theme="dark"] .badge.up { background: #16321F; }
[data-theme="dark"] .badge.hold { background: #3A2E10; }
[data-theme="dark"] .badge.down { background: #3A1414; }
[data-theme="dark"] .stat-card.warn { border-color: #6B5518; background: #2E2612; }
[data-theme="dark"] .card.attn { border-color: #6B5518; background: #2E2612; }
[data-theme="dark"] .card.add-variant { border-color: #6B5518; background: #2E2612; }
[data-theme="dark"] .card.success { border-color: #2E7D46; background: #10281A; }
[data-theme="dark"] .hm-full { background: #16321F; }
[data-theme="dark"] .hm-partial { background: #3A2E10; }
[data-theme="dark"] .hm-none { background: #3A1414; }

/* ---- CRM: tasks & follow-up reminders ---- */
.task-list { max-height: 420px; overflow-y: auto; }
.task-row { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--gray-300); }
.task-row:last-child { border-bottom: none; }
.task-check { flex-shrink: 0; background: none; border: none; padding: 0; margin-top: 1px; font-size: 18px; line-height: 1; cursor: pointer; color: var(--cobalt); }
.task-text-wrap { flex: 1; min-width: 0; }
.task-text { font-size: 14px; }
.task-text.done { text-decoration: line-through; color: var(--gray-500); }
.task-delete { flex-shrink: 0; background: none; border: none; padding: 0 2px; margin: 0; color: inherit; opacity: 0.5; cursor: pointer; font-size: 15px; line-height: 1.4; font-family: inherit; }
.task-delete:hover { opacity: 1; color: var(--red); }
.task-row.task-overdue .task-text { color: var(--red); font-weight: 600; }
.task-row.task-overdue .sub { color: var(--red); }
.task-row.task-today .sub { color: var(--amber); font-weight: 600; }

/* ---- CRM: lead/prospect pipeline ---- */
.lead-board { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px; }
.lead-column { flex: 0 0 220px; background: var(--gray-100); border-radius: 10px; padding: 10px; }
.lead-column-header { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--gray-500); margin-bottom: 8px; }
.lead-card { background: var(--surface); border: 1px solid var(--gray-300); border-radius: 12px; padding: 10px; margin-bottom: 8px; }
.lead-card-name { display: block; font-weight: 700; font-size: 13.5px; margin-bottom: 2px; color: inherit; text-decoration: none; }
.lead-card-name:hover { text-decoration: underline; }

/* ---- Nutrition plan ---- */
/* auto-fit + minmax instead of a fixed 2-column grid: a card never gets
   squeezed below ~320px just because its sibling exists — with 1 variant
   it takes the full row, with 2+ it gives each as much room as the
   container actually has, wrapping to a new row only when it must. This
   directly addresses "boxes feel cramped with more than one row". */
.variant-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 18px; }
.variant-card { border: 1.5px solid var(--gray-300); border-radius: 14px; padding: 20px; background: var(--surface); }
/* Protein/Carbs/Fat as their own row, separate from Portions above it —
   previously all four shared one flex-wrap row and wrapped mid-label on
   anything narrower than ~500px. Always exactly 3 columns since it's
   always exactly 3 fields, so it never needs to wrap. */
.macro-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
/* Main variant gets the same cobalt-accent treatment as .card.add-new,
   so it reads as "the selected default" in both themes without a
   hardcoded light-only tint. */
.variant-card.main { border-color: var(--cobalt); background: rgba(0, 71, 171, 0.05); }
[data-theme="dark"] .variant-card.main { background: rgba(91, 155, 255, 0.10); }
.variant-card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; gap: 8px; }
.variant-food { font-weight: 700; font-size: 14px; margin-bottom: 2px; }
/* Client-facing card sections: a small uppercase label before Portions and
   before Cooking notes, plus a divider ahead of the notes block, so the
   card reads as distinct labeled fields instead of one run-on gray/italic
   paragraph (the actual issue reported on mobile). */
.variant-section-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--gray-500); margin: 10px 0 3px; }
.variant-portions { font-size: 13.5px; color: var(--ink); }
.variant-divider { border-top: 1px solid var(--gray-300); margin-top: 12px; }
.variant-notes { font-size: 12.5px; font-style: italic; color: var(--gray-500); margin-top: 0; }

/* ---- iOS-language pass (approved 2026-08-14) ----
   Capsule buttons, toggle switches, frosted tab bar, segmented control,
   large-title headers, grouped-inset rows and bigger touch targets —
   CSS-only where possible, ported from the approved preview. No new JS,
   no new dependency. */

/* Capsule (fully-rounded) primary/outline buttons — .btn.outline doesn't
   redeclare border-radius, so it inherits this automatically since outline
   buttons always also carry the base .btn class. */
.btn { border-radius: 999px; }

/* iOS-style toggle switch — replaces a plain checkbox visually while still
   being a real <input type="checkbox"> underneath (same name/value
   semantics, same form submission, no JS). Markup:
   <label class="toggle-switch"><input type="checkbox" ...><span class="track"></span></label> */
.toggle-switch { position: relative; display: inline-flex; align-items: center; flex-shrink: 0; cursor: pointer; }
.toggle-switch input[type="checkbox"] {
  position: absolute; opacity: 0; width: 42px; height: 25px; margin: 0; cursor: pointer; z-index: 1;
}
.toggle-switch .track {
  width: 42px; height: 25px; border-radius: 13px; background: var(--gray-300);
  position: relative; display: inline-block; transition: background 0.15s ease; flex-shrink: 0;
}
.toggle-switch .track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 21px; height: 21px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25);
  transition: transform 0.15s ease;
}
.toggle-switch input:checked + .track { background: var(--green); }
.toggle-switch input:checked + .track::after { transform: translateX(17px); }
.toggle-switch input:focus-visible + .track { outline: 2px solid var(--cobalt); outline-offset: 2px; }
/* Habit rows now use the toggle switch at the trailing edge instead of a
   leading checkbox square — row becomes a label/switch pair. */
.habit-row { justify-content: space-between; }
.habit-row .habit-row-label { flex: 1; }

/* Frosted/translucent bottom tab bar — content peeks through slightly,
   matching iOS's own tab bar / Control Center material. */
.bottom-tabs {
  background: rgba(255,255,255,.72);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid rgba(221,221,221,.6);
}
[data-theme="dark"] .bottom-tabs {
  background: rgba(28,28,28,.72);
  border-top-color: rgba(58,58,58,.6);
}

/* Segmented control — radio inputs + labels, CSS-only "pill" selection
   state via :checked + label sibling selector. Markup:
   <div class="segmented"><input type="radio" id="x" name="status" value="active" checked><label for="x">Active</label>...</div> */
.segmented { display: flex; background: var(--gray-100); border-radius: 10px; padding: 3px; gap: 2px; }
.segmented input[type="radio"] { position: absolute; opacity: 0; width: 0; height: 0; }
.segmented label {
  flex: 1; text-align: center; padding: 8px 0; border-radius: 8px;
  font-size: 12.5px; font-weight: 600; letter-spacing: normal; text-transform: none;
  color: var(--gray-500); cursor: pointer; margin: 0; transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.segmented input[type="radio"]:checked + label {
  background: var(--surface); color: var(--ink); font-weight: 700; box-shadow: 0 1px 3px rgba(0,0,0,.12);
}
.segmented input[type="radio"]:focus-visible + label { outline: 2px solid var(--cobalt); outline-offset: 1px; }

/* iOS "large title" header pattern — small colored back-link above a big,
   bold page title, instead of a uniform-size h1 everywhere. */
h1 { font-size: 29px; font-weight: 800; letter-spacing: -0.02em; margin: 18px 0 6px; }
.back-link { display: inline-block; font-size: 13px; font-weight: 600; color: var(--cobalt); margin-bottom: 2px; }

/* Grouped-inset list rows (client list, similar flat lists) — rows sit
   inside one rounded card with dividers between them instead of a flat
   list, and get a trailing chevron like an iOS settings/table row. */
.inset-list { background: var(--surface); border: 1px solid var(--gray-300); border-radius: 14px; overflow: hidden; }
.client-row .row-chevron { color: var(--gray-500); flex-shrink: 0; margin-left: 6px; }

/* Small round pinned "+" action next to a page title — fixes the
   "buried at the bottom of a long page" findability problem for
   Add session / Add exercise style actions. */
.title-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.title-row h1 { margin: 18px 0 0; }
.round-add-btn {
  width: 40px; height: 40px; border-radius: 50%; background: var(--cobalt); color: var(--white);
  border: none; font-size: 22px; line-height: 1; flex-shrink: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; margin-top: 16px;
  text-decoration: none;
}
.round-add-btn:active { opacity: 0.85; }

/* ---- Bigger touch targets (Apple HIG 44x44pt minimum) ---- */
.task-row { align-items: center; padding: 11px 4px; min-height: 44px; }
.task-check {
  width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 21px; margin-top: 0;
}
.task-delete {
  width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 17px; border-radius: 10px;
}
.task-delete:hover { background: rgba(179, 38, 30, 0.08); }
/* Small icon-only delete buttons (nutrition ingredient rows etc.) — was a
   bare 22x22px glyph with no visible boundary; now a real 40x40px button
   with a clear edge, easier to aim at without careful precision. */
.icon-delete-btn {
  width: 40px; height: 40px; padding: 0; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 17px; flex-shrink: 0;
}
.comm-log-icon { width: 28px; height: 28px; }

/* ---- Food log: photo capture (client food-log page) ----
   Replaces a bare <input type=file> with a clickable card that has real
   visual weight — icon, title, sub-copy — and swaps to a small preview
   thumbnail once a photo is picked, instead of the browser's own
   filename-only file-input chrome. */
.photo-dropzone {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px;
  border: 1.5px dashed var(--gray-300); border-radius: 12px; cursor: pointer;
  background: var(--gray-100); transition: border-color 0.15s ease, background 0.15s ease;
  margin-top: 6px;
}
.photo-dropzone:hover { border-color: var(--cobalt); background: rgba(0, 71, 171, 0.05); }
.photo-dropzone .dz-icon { color: var(--cobalt); flex-shrink: 0; display: flex; }
.photo-dropzone .dz-text { flex: 1; min-width: 0; }
.photo-dropzone .dz-title { font-weight: 700; font-size: 14px; }
.photo-dropzone .dz-sub { font-size: 12px; color: var(--gray-500); margin-top: 2px; display: block; }
.photo-dropzone input[type="file"] { display: none; }
.photo-preview-wrap { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
/* Recent-foods shortlist (client food-log page) — one tap re-logs the
   exact numbers saved last time for that food, so the most common case
   (the same coffee, the same shake, most mornings) doesn't need a type-
   and-search round trip. See getRecentDistinctFoods in queries.js and
   the recentChips click handler in clientFoodLog's script. */
.recent-food-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.recent-food-chip {
  background: var(--gray-100); border: 1px solid var(--gray-300); border-radius: 999px;
  padding: 8px 15px; font-size: 13px; font-weight: 600; color: var(--ink);
  cursor: pointer; font-family: inherit; max-width: 220px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recent-food-chip:active { opacity: 0.7; }

.photo-preview-wrap img {
  width: 56px; height: 56px; border-radius: 10px; object-fit: cover; border: 1px solid var(--gray-300); flex-shrink: 0;
}

/* ---- Nutrition: "swap this meal" (client food-log in swap mode, and the
   swap badge shown back on the plan page for both client and coach) ---- */
.swap-context-banner {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: rgba(0, 71, 171, 0.06); border: 1px solid rgba(0, 71, 171, 0.25);
  border-radius: 10px; padding: 10px 12px; margin-top: 12px; font-size: 13px; color: var(--ink);
}
.swap-context-banner .icon { color: var(--cobalt); flex-shrink: 0; }
.swap-cancel-link { margin-left: auto; font-size: 12.5px; font-weight: 600; flex-shrink: 0; }
.swap-badge {
  display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 700;
  padding: 4px 10px; border-radius: 20px; background: #E7F4EA; color: var(--green); margin-top: 10px;
}
[data-theme="dark"] .swap-badge { background: #16321F; }
.swap-link {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 600;
  color: var(--cobalt); margin-top: 10px;
}

/* ---- Daily kcal/macro counter (client nutrition page) — MyFitnessPal-
   style consumed-vs-target bars, fed by today's food log against the
   plan's targets (see nutrition.computeDeltas, reused as-is). A macro
   with no target set just shows the consumed number, no bar. ---- */
.macro-counter { display: flex; flex-direction: column; gap: 12px; }
.macro-counter-row { display: flex; align-items: center; gap: 10px; }
.macro-counter-label {
  width: 56px; flex-shrink: 0; font-size: 11px; font-weight: 700; color: var(--gray-500);
  text-transform: uppercase; letter-spacing: 0.03em;
}
.macro-counter-track { flex: 1; height: 8px; border-radius: 6px; background: var(--gray-100); overflow: hidden; }
.macro-counter-fill { height: 100%; border-radius: 6px; background: var(--cobalt); transition: width 0.3s ease; }
.macro-counter-fill.over { background: var(--red); }
.macro-counter-value {
  width: 104px; flex-shrink: 0; text-align: right; font-size: 12.5px; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.macro-counter-value .over-label { color: var(--red); font-weight: 700; }

/* ---- Superset/tri-set/giant-set grouping (client session-log page) — a
   contiguous run of exercises the coach linked via "Link with next
   exercise" renders as one bordered wrapper with a shared label, instead
   of unrelated back-to-back .card.accent blocks. Each member keeps its own
   inner spacing so the individual exercise cards (name, sets, inputs)
   still read cleanly, just visually grouped under one roof. ---- */
.exercise-group { padding-top: 12px; }
.exercise-group-label {
  font-size: 12px; font-weight: 800; color: var(--cobalt); text-transform: uppercase;
  letter-spacing: 0.03em; margin-bottom: 10px;
}
.exercise-group-member { padding: 10px 0; border-top: 1px dashed var(--gray-100); }
.exercise-group-member:first-child { border-top: none; padding-top: 0; }

/* Small pill marking a set's prescribed intensity technique (myo-reps,
   cluster set, rest-pause, AMRAP) or an exercise's superset/tri-set/giant-
   set membership — reuses .tag's sizing but in cobalt to stand out from
   the plain gray body-part tags. */
.technique-badge { background: rgba(0, 71, 171, 0.1); color: var(--cobalt); }
