:root {
  --bg: #0e1116; --panel: #171c24; --panel2: #1f2630; --line: #2a323d;
  --fg: #e8edf3; --muted: #93a1b0; --accent: #28c76f; --accent2: #1f9bcf;
  --warn: #ff9f43; --bad: #ea5455;
}
* { box-sizing: border-box; }
/* The `hidden` attribute ALWAYS wins. Without this, any class that sets `display`
   (.hud is display:flex) silently overrides the UA's [hidden]{display:none} — which is
   how `hud.hidden = true` in mirror mode used to leave the rep HUD on screen. The
   session screen's opt-in widgets rely on `hidden` meaning hidden. */
[hidden] { display: none !important; }
html { height: 100%; }
/* min-height (not height:100%) so the page grows with its content and the document
   itself scrolls — a height:100% body is only one viewport tall, which collapses the
   sticky header's containing block and lets it scroll away after one screenful. */
body { margin: 0; min-height: 100%; background: var(--bg); color: var(--fg);
  font: 15px/1.45 -apple-system, system-ui, "Segoe UI", Roboto, sans-serif; }
a { color: inherit; text-decoration: none; }

.wrap { max-width: 760px; margin: 0 auto; padding: 16px; }
header.top { position: sticky; top: 0; background: var(--bg); padding: 14px 16px 8px;
  border-bottom: 1px solid var(--line); z-index: 5; }
h1 { font-size: 18px; margin: 0 0 10px; }
.sub { color: var(--muted); font-size: 13px; }
/* One-line pool summary under the title ("141 of 248 camera-tracked · 40 timed"). */
.pool-summary { color: var(--muted); font-size: 12.5px; margin: -6px 0 10px; }

input.search { width: 100%; padding: 11px 13px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--panel); color: var(--fg); font-size: 15px; }
/* Unified multi-select filter bar (toggle tags; no redundant "All"). */
.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
.chip { padding: 5px 11px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--panel); color: var(--muted); font-size: 12px; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s; }
.chip:hover { border-color: var(--accent); color: var(--fg); }
.chip.on { background: var(--accent); color: #06210f; border-color: var(--accent); font-weight: 600; }
.chip.clear { background: transparent; color: var(--bad); border-color: var(--bad); }
.chip.clear:hover { background: rgba(234,84,85,.12); color: var(--bad); }
.chip-sep { width: 1px; align-self: stretch; min-height: 18px; background: var(--line); margin: 0 4px; }

.list { display: grid; gap: 10px; padding: 12px 0 48px; }
/* Section headers splitting camera-tracked from self-view cards (list.js). */
.list-section { color: var(--muted); font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; margin: 10px 2px 0; }
.list-section:first-child { margin-top: 0; }
/* "How you'll run it" hint leading an untracked card's subtitle. */
.card .run-hint { color: var(--fg); font-weight: 600; }
.card { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 14px;
  background: var(--panel); border: 1px solid var(--line); cursor: pointer;
  transition: border-color .12s, background .12s, transform .06s; }
.card:hover { border-color: var(--accent); }
.card:active { background: var(--panel2); transform: scale(.995); }
.card .name { font-weight: 700; font-size: 15.5px; }
.card .meta { color: var(--muted); font-size: 12px; margin-top: 3px; }
.card .chev { flex: 0 0 auto; color: var(--muted); font-size: 22px; line-height: 1; }
/* Non-trackable exercises: launchable into a camera MIRROR (no rep counting), but kept
   visually distinct from auto-counted ones — dashed border + slightly dimmed. */
.card.untracked { opacity: .85; border-style: dashed; }
.card.untracked:hover { border-color: var(--accent2); }

/* Badge row lives INSIDE the card body (left), wrapping under the name. */
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.badge { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
  display: inline-flex; align-items: center; gap: 3px; }
.badge.cat { color: var(--fg); background: var(--panel2); border-color: var(--line); }
.badge.track { color: var(--accent); border-color: var(--accent); background: rgba(40,199,111,.10); }
.badge.side { color: var(--accent2); border-color: var(--accent2); }
.badge.front { color: var(--accent2); border-color: var(--accent2); }
/* format badges — distinct class names so they don't collide with the session
   HUD's `.reps` rep-counter (which sets font-size:44px). */
.badge.fmt-reps { color: var(--accent); border-color: var(--accent); }
.badge.fmt-timed { color: var(--accent2); border-color: var(--accent2); }
.badge.warmup { color: var(--warn); border-color: var(--warn); background: rgba(255,159,67,.10); }
.badge.untracked { color: var(--muted); border-style: dashed; }
.grow { flex: 1; min-width: 0; }
.rel { font-size: 12px; color: var(--muted); }
.rel.reliable { color: var(--accent); }
.rel.experimental, .rel.marginal { color: var(--warn); }
.empty { color: var(--muted); text-align: center; padding: 40px 0; }

/* session page */
.stage { position: fixed; inset: 0; background: #000; overflow: hidden; }
/* cover so the camera fills the screen (natural full-bleed camera look). The
   stream's aspect is requested to match the screen orientation (see session.js),
   so cover fills without big letterbox bars on phone or desktop. The overlay
   canvas uses the same object-fit + intrinsic size, so the skeleton stays aligned.
   (Display mode is cosmetic — MediaPipe always processes the full video frame.) */
video#cam { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
canvas#overlay { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Mirror only the front (selfie) camera — toggled by session.js per facingMode.
   The rear camera must NOT be mirrored. */
video#cam.mirrored, canvas#overlay.mirrored { transform: scaleX(-1); }

/* Session-page top-right rail: exercise-name card + form-verdict chip, with the camera-flip
   FAB stacked directly beneath them. A sibling of (not inside) the .hud so it survives mirror
   mode and owns its own pointer events (only the flip re-enables them). Anchored below the
   notch on the upper-right, clear of the bottom-right tutorial PIP even when the PIP is
   enlarged. NOTE: NOT named `.chips` — that class is the home page's filter row; keep separate. */
.info-rail { position: absolute; right: 14px; top: calc(16px + env(safe-area-inset-top)); z-index: 7;
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
  pointer-events: none; max-width: 62%; }
/* Rail chip typography — compact, right-aligned, and wrap-safe. Pills size to their content
   up to the rail's max-width, then wrap cleanly; a long name/reason stays readable across
   lines and an over-long token breaks instead of spilling past the pill. The exercise name
   leads; the camera-angle hint sits under it as a smaller, subordinate line. */
.info-rail .pill { max-width: 100%; text-align: right; font-size: 13px; line-height: 1.3;
  overflow-wrap: anywhere; }
.info-rail #exName { font-size: 14px; font-weight: 700; }
.info-rail .angle-hint { margin-top: 3px; font-size: 12px; font-weight: 600; }
.info-rail #form { font-weight: 700; }

/* Front/back camera toggle — a translucent circular FAB flowing at the bottom of the rail,
   directly beneath the chips. Its own pointer events are enabled. The SVG icon is sized to
   fill most of the FAB so it doesn't read as a small mark floating in empty space. */
.cam-flip { width: 46px; height: 46px; border-radius: 50%; border: 1px solid rgba(255,255,255,.28);
  background: rgba(0,0,0,.55); color: #fff; line-height: 1; cursor: pointer;
  pointer-events: auto; backdrop-filter: blur(4px); display: flex; align-items: center;
  justify-content: center; }
.cam-flip svg { width: 26px; height: 26px; display: block; }
.cam-flip:active { background: rgba(0,0,0,.75); }
.cam-flip[hidden] { display: none; }
/* Phone-angle guide — a slim plumb "spirit level" pinned to the left edge (display-only).
   The bubble rides a vertical vial and turns green inside the center band when the phone
   is upright (~90°). Small edge footprint instead of a center dial. */
.level { position: absolute; left: calc(10px + env(safe-area-inset-left)); top: 50%; z-index: 6;
  transform: translateY(-50%); width: 16px; height: 150px; border-radius: 999px;
  background: rgba(0,0,0,.5); border: 1px solid rgba(255,255,255,.22);
  backdrop-filter: blur(4px); pointer-events: none; }
.level[hidden] { display: none; }
/* center "you're level" band — its half-height matches the ±tolerance in level.js */
.level-zone { position: absolute; left: 0; right: 0; top: 50%; height: 48px; transform: translateY(-50%);
  border-top: 1px solid rgba(255,255,255,.35); border-bottom: 1px solid rgba(255,255,255,.35); }
.level.ok .level-zone { background: rgba(40,199,111,.28); border-color: var(--accent); }
.level-bubble { position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%; background: var(--warn); box-shadow: 0 0 8px rgba(0,0,0,.55);
  transition: transform .08s linear, background .15s; }
.level-bubble.ok { background: var(--accent); }

.hud { position: absolute; inset: 0; pointer-events: none; display: flex; flex-direction: column;
  justify-content: space-between; padding: 16px; }
.hud .top { display: flex; gap: 10px; align-items: flex-start; }
/* Pill box styling — self-contained on the class itself (NOT ancestor-scoped), so the box
   chrome travels with any .pill wherever it sits: the top-left rep counter, the top-right
   rail (exercise-name card + form-verdict chip), and the mirror-mode name pill. */
.pill { background: rgba(0,0,0,.55); border: 1px solid rgba(255,255,255,.14);
  border-radius: 12px; padding: 8px 12px; backdrop-filter: blur(4px); }
.reps { font-size: 44px; font-weight: 800; line-height: 1; }
.reps small { font-size: 16px; color: var(--muted); font-weight: 600; }
/* Countdown for a timed run, sits above the rep count in the same pill. */
.countdown { font-size: 22px; font-weight: 800; line-height: 1; color: var(--accent2);
  font-variant-numeric: tabular-nums; margin-bottom: 6px; }
.countdown[hidden] { display: none; }
.angle-hint { color: var(--accent2); font-weight: 700; }
.form-ok { color: var(--accent); } .form-bad { color: var(--bad); }
.state-bar { height: 6px; border-radius: 3px; background: rgba(255,255,255,.15); overflow: hidden; margin-top: 8px; }
.state-bar > i { display: block; height: 100%; background: var(--accent); width: 0%; }

/* Mirror mode (non-trackable exercises): camera as a plain self-view — name pill on
   top, a big centered timer, a Done button at the bottom. Sits above the (hidden) rep
   HUD; only the button takes pointer events so the flip/level FABs stay reachable. */
.mirror-hud { position: absolute; inset: 0; z-index: 5; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 16px; padding-top: calc(16px + env(safe-area-inset-top));
  padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
.mirror-hud[hidden] { display: none; }
.mirror-name { text-align: center; max-width: 70%; }
.mirror-mid { flex: 1; display: flex; align-items: center; justify-content: center; }
.mirror-timer { font-size: 56px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums;
  padding: 12px 24px; border-radius: 16px; background: rgba(0,0,0,.5);
  border: 1px solid rgba(255,255,255,.14); backdrop-filter: blur(4px); }
.mirror-done { pointer-events: auto; min-width: 160px; padding: 16px 28px; font-size: 17px; }

.center { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  flex-direction: column; gap: 14px; text-align: center; padding: 24px; pointer-events: auto; }
/* The intro doubles as the "tap to start" gesture target (see session.js boot()). */
.center.tap { cursor: pointer; }
button.go { pointer-events: auto; background: var(--accent); color: #06210f; border: 0; font-weight: 700;
  padding: 14px 22px; border-radius: 12px; font-size: 16px; }
button.ghost { background: transparent; color: var(--fg); border: 1px solid var(--line);
  padding: 10px 16px; border-radius: 10px; }
.banner { position: absolute; left: 0; right: 0; top: 0; text-align: center; padding: 10px;
  background: var(--accent); color: #06210f; font-weight: 700; transform: translateY(-100%);
  transition: transform .3s; }
.banner.show { transform: translateY(0); }
/* Transient notification — centered on screen so a conditional, occasional message (a
   bad-posture "Rep not counted", a camera-switch confirmation, a connection error) lands
   where the eye already is rather than at the bottom edge. Shows for ~2.5s, then fades. */
.toast { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: rgba(0,0,0,.7); border: 1px solid var(--line); padding: 8px 14px; border-radius: 10px;
  color: var(--fg); font-size: 13px; text-align: center; max-width: 76%; z-index: 8; }

/* tutorial picture-in-picture (bottom-right, over the camera) */
.pip { position: absolute; right: 12px; bottom: 12px; width: 34%; max-width: 200px;
  aspect-ratio: 4 / 3; border-radius: 10px; overflow: hidden; z-index: 6;
  border: 1px solid rgba(255,255,255,.28); background: #000;
  box-shadow: 0 4px 16px rgba(0,0,0,.55); }
.pip.big { width: 62%; max-width: 360px; }
.pip video { width: 100%; height: 100%; object-fit: cover; display: block; }
.pip-label { position: absolute; left: 6px; top: 5px; font-size: 10px; color: #fff;
  background: rgba(0,0,0,.5); padding: 1px 6px; border-radius: 6px; pointer-events: none; }
.pip-close { position: absolute; right: 3px; top: 3px; width: 22px; height: 22px; border: 0;
  border-radius: 50%; background: rgba(0,0,0,.6); color: #fff; font-size: 15px; line-height: 1;
  cursor: pointer; padding: 0; }
.pip-fallback { position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 4px; background: #11161d;
  color: var(--muted); font-size: 12px; }
/* The `hidden` attribute must beat `display:flex` above (equal specificity would
   otherwise keep this opaque panel covering the playing tutorial video). */
.pip-fallback[hidden] { display: none; }
.pip-fallback-icon { font-size: 22px; color: var(--accent); }

/* completion page */
.done-screen { position: fixed; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px; text-align: center;
  padding: 24px; background: var(--bg); }
.spinner { width: 54px; height: 54px; border-radius: 50%;
  border: 5px solid var(--line); border-top-color: var(--accent);
  animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.done-title { font-size: 22px; font-weight: 800; }
.done-sub { font-size: 32px; font-weight: 800; color: var(--accent); }

/* ── dev/test parameter panel (off-production only; see js/devpanel.js) ───────── */
/* Lives on the exercise list only: a per-card gear opens this right-side drawer. */
.dp-backdrop { position: fixed; inset: 0; z-index: 90; background: rgba(0,0,0,.45); }
.dp-drawer { position: fixed; top: 0; bottom: 0; right: 0; z-index: 91; width: min(360px, 88vw);
  display: flex; flex-direction: column; background: var(--panel); border-left: 1px solid var(--line);
  box-shadow: 0 0 32px rgba(0,0,0,.6); transform: translateX(102%); transition: transform .2s ease;
  padding-top: env(safe-area-inset-top); }
.dp-drawer.open { transform: translateX(0); }
.dp-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px;
  border-bottom: 1px solid var(--line); font-size: 15px; }
/* per-card gear on the exercise list (off-production only) */
.dp-card-gear { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 9px; padding: 0;
  border: 1px solid var(--line); background: var(--panel2); color: var(--muted); font-size: 16px;
  line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.dp-card-gear:hover, .dp-card-gear:active { color: var(--accent); border-color: var(--accent); }
/* read-only "what am I configuring" banner shown when opened from a card */
.dp-ctx { padding: 11px 16px; border-bottom: 1px solid var(--line); background: var(--panel2); }
.dp-ctx[hidden] { display: none; }
.dp-ctx-name { font-weight: 700; font-size: 14px; }
.dp-ctx-meta { color: var(--muted); font-size: 12px; margin-top: 2px; }
.dp-x { background: transparent; border: 0; color: var(--muted); font-size: 24px; line-height: 1;
  cursor: pointer; padding: 0 4px; }
.dp-body { flex: 1; overflow-y: auto; padding: 8px 16px 16px; -webkit-overflow-scrolling: touch; }
.dp-group { margin-top: 16px; }
.dp-group-title { font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin-bottom: 8px; }
.dp-field { display: flex; flex-direction: column; gap: 3px; margin-bottom: 12px; }
.dp-key { font-size: 12px; font-weight: 600; color: var(--fg); font-family: ui-monospace, Menlo, monospace; }
.dp-input { width: 100%; padding: 9px 11px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--panel2); color: var(--fg); font-size: 14px; }
select.dp-input { appearance: none; }
.dp-help { font-size: 11px; color: var(--muted); line-height: 1.35; }
/* A goal field that doesn't apply to the chosen exercise (reps for a timed exercise,
   or seconds for a rep one) is disabled + dimmed. */
.dp-field.dp-disabled { opacity: .4; }
.dp-input:disabled { cursor: not-allowed; }
.dp-foot { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 12px 16px;
  border-top: 1px solid var(--line); padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
.dp-btn { flex: 1; min-width: 88px; padding: 10px 12px; border-radius: 9px; border: 1px solid var(--line);
  background: var(--panel2); color: var(--fg); font-size: 13px; font-weight: 600; cursor: pointer; }
.dp-apply { background: var(--accent); color: #06210f; border-color: var(--accent); }
.dp-ghost { flex: 0 0 auto; min-width: 0; }
.dp-off { flex-basis: 100%; text-align: center; color: var(--muted); font-size: 12px;
  text-decoration: underline; margin-top: 2px; }
.dp-toast { position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 12px); z-index: 95;
  background: rgba(0,0,0,.82); border: 1px solid var(--line); color: var(--fg); font-size: 13px;
  padding: 8px 14px; border-radius: 10px; opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s; }
.dp-toast.show { opacity: 1; transform: translate(-50%, 0); }
