/* White Room — cool/technical. Single source of visual truth. */

:root {
  --bg:        #FFFFFF;  /* pure white — seamless with imagery */
  --surface:   #FFFFFF;
  --ink:       #0E0F12;  /* cool near-black — titles, primary */
  --ink-body:  #34383F;  /* softened ink — card body copy */
  --ink-soft:  #898F99;  /* cool grey — labels, meta */
  --line:      #E3E6EA;  /* hairline */
  --mark:      #BFC5CD;  /* technical marks */
  --monolith:  #0E0F12;
  --hatch:     rgba(14,15,18,.035);
  --live:      #B5211B;  /* the one semantic accent: dark live/recording red (header indicator) */
  --r: 0;                /* sharp everywhere */
  --pad: .45rem;         /* shared inner padding — panels and cards alike */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* ---- Panels framed by crop marks (header / body / footer) ---- */
.panel { position: relative; }
.crop { position: absolute; width: 11px; height: 11px; pointer-events: none; }
.crop--tl { top: 0; left: 0;  border-top: 1px solid var(--mark); border-left: 1px solid var(--mark); }
.crop--tr { top: 0; right: 0; border-top: 1px solid var(--mark); border-right: 1px solid var(--mark); }
.crop--bl { bottom: 0; left: 0;  border-bottom: 1px solid var(--mark); border-left: 1px solid var(--mark); }
.crop--br { bottom: 0; right: 0; border-bottom: 1px solid var(--mark); border-right: 1px solid var(--mark); }

/* ---- Shell ---- */
.app {
  position: relative;         /* anchors the drawer to the panel (it rises from the nav bar) */
  width: 100%;                /* definite width — don't let flex centering size it to content */
  max-width: 480px;
  margin: 0 auto;
  height: 100dvh;               /* desktop cap lives in the min-width media rule (#87) */
  display: flex;
  flex-direction: column;
  gap: .42rem;
  padding: max(.5rem, env(safe-area-inset-top)) .85rem max(.5rem, env(safe-area-inset-bottom));
  overflow: hidden;
}
.app__header { padding: var(--pad); }
.hdr { display: grid; grid-template-columns: auto 1fr auto; gap: .7rem; align-items: start; }
.hdr__logo {
  align-self: stretch;       /* match the full height of the title stack */
  aspect-ratio: 1;           /* and stay square */
  position: relative;        /* img is absolute so it can't drive the slot size */
}
.hdr__logo-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
}
.hdr__titles { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
.hdr__eyebrow {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft);
}
.hdr__title {
  font-family: "Cormorant", Georgia, serif;
  font-weight: 600; font-size: 1.42rem; line-height: 1.05; letter-spacing: .005em; margin: 0;
}
.hdr__sub { font-size: .72rem; color: var(--ink-soft); line-height: 1.2; }
/* Live-session indicator — the one semantic accent in the monochrome shell. A
   borderless neutral disc; idle = grey icon, live = dark-red icon with a pulse
   that blooms from the centre out to the disc's edge. No overflow:hidden — the
   pulse (::after, inset 0, radius 50%, scale ≤ 1) is geometrically bound to the
   disc already, and clipping would also clip the ::before hit extension (#64). */
.livedot {
  position: relative; align-self: center;
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; border-radius: 50%;
  background: rgba(14, 15, 18, .055); color: var(--mark);
  text-decoration: none;
  /* usable as a <button> on the canvas too: reset chrome, nudge off the right edge
     for thumb reach on a phone. */
  border: 0; padding: 0; font: inherit; appearance: none; -webkit-appearance: none;
  margin-right: .3rem;
}
.livedot--on { cursor: pointer; }
.livedot__ico { position: relative; z-index: 1; font-size: 1.05rem; line-height: 1; }
/* Live: a transparent-red disc + dark-red icon; the pulse only deepens the red. */
.livedot--on { color: var(--live); background: rgba(181, 33, 27, .12); }
.livedot--on::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--live);
  animation: live-pulse 1.8s ease-out infinite;
}
/* A subtle contrast lift: a soft red blooms from the centre to the disc's edge and fades. */
@keyframes live-pulse {
  0%   { transform: scale(0); opacity: .24; }
  100% { transform: scale(1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .livedot--on::after { display: none; } }
.app__body { position: relative; flex: 1; min-height: 0; }
.app__scroll {
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--pad);
  /* No scrollbar (owner 2026-07-12): the desktop bar stole layout width and shoved
     the crop marks inward. List surfaces hint via .sect__scroll.has-below instead. */
  scrollbar-width: none;
  /* Content resolves to full opacity at the padding edge and dissolves softly
     toward the crop frame, instead of hard-cutting at the panel border. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--pad), #000 calc(100% - var(--pad)), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 var(--pad), #000 calc(100% - var(--pad)), transparent 100%);
}
.app__scroll::-webkit-scrollbar { display: none; }
.app__footer { padding: .28rem; }

/* Auth shell: body hugs its form (crop marks sit tight around it), the cover
   absorbs the slack so the form settles into the lower thumb zone. */
.app--auth .app__body { flex: 0 0 auto; }

/* Desktop / wider viewports: the whole app becomes a height-capped panel,
   centered in the viewport. The cap is page-independent, so the panel never
   jumps when switching pages; the body still absorbs the vertical slack.
   Cap tightened 1080px → 60rem/960px (owner round 2026-07-12, #87): a bit of
   stretch past phone heights stays allowed, absurd proportions don't. */
@media (min-width: 600px) {
  body { min-height: 100dvh; display: flex; align-items: center; justify-content: center; }
  .app { height: min(100dvh, 60rem); }
  .app--auth { justify-content: center; }
}

/* ---- Login cover: one full-bleed archive image, the roman masthead floating
   over its bright top zone (magazine-cover reading). The panel absorbs ALL the
   vertical slack (form hugs below); the image covers whatever height results,
   anchored so head + gaze survive every crop. ---- */
.cover {
  position: relative;
  flex: 1 1 auto; min-height: 16rem;   /* never collapses to a sliver */
  overflow: hidden;
}
.cover__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  /* The motif's core (head → sword hand) lives in the upper-middle band; biasing
     the crop there keeps the figure readable at every panel height. */
  object-position: 50% 22%;
}
.cover__mast {
  position: absolute; top: .55rem; left: 0; right: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: .15rem;
  pointer-events: none;
}
/* The masthead: roman inscriptional capitals in ink — dark print on the blown
   paper-white canvas, not white-on-image (the register is bright, not dark). */
.cover__title {
  margin: 0;
  font-family: "Cormorant", Georgia, serif; font-weight: 600;
  font-size: clamp(3rem, 16vw, 4.4rem);
  line-height: 1;
  letter-spacing: .18em; text-indent: .18em;   /* indent rebalances the tracking */
  color: var(--ink);
}
.cover__sub {
  margin: 0;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .18em; text-indent: .18em;
  text-transform: uppercase; color: var(--ink-body);
  transform: translateY(.4rem);   /* nudged toward the masthead — no layout shift */
}
/* The foot signature: white inscription on the image's lower edge (the floor zone
   is mid-gray in the register, white holds without a shadow crutch). */
.cover__foot {
  position: absolute; left: 0; right: 0; bottom: .5rem; z-index: 1;
  text-align: center; pointer-events: none;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .5rem; letter-spacing: .28em; text-indent: .28em;
  text-transform: uppercase; color: rgba(255, 255, 255, .92);
}

/* The recruiting line under Sign in — quiet mono caps, one underlined link. */
/* Coming-soon cover (#75 placeholder): the announcement sits where the login
   form would — serif statement, mono cohort line, one quiet member door. */
.soon { display: flex; flex-direction: column; align-items: center; gap: .55rem; padding: 1.1rem .8rem 1rem; text-align: center; }
.soon__line { margin: 0; font-family: "Cormorant", Georgia, serif; font-weight: 600; font-size: 1.5rem; line-height: 1.1; color: var(--ink); }
.soon__quiet { margin: 0; font-family: "Space Mono", ui-monospace, monospace; font-size: .62rem; font-weight: 400; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); }
.soon__door { margin: .35rem 0 0; font-family: "Space Mono", ui-monospace, monospace; font-size: .68rem; color: var(--ink-soft); }
.soon__door a { color: var(--live); text-decoration: none; }

.form__aside {
  /* Snugged toward the button (proximity = they belong together; the negative
     margin eats into the form gap) and the section tail kept short. */
  margin: -.3rem 0 .05rem; text-align: center;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-soft);
}
/* The one drop of red on the cover surface: the enlist link. Inside the app the
   accent stays strictly live-semantic — no collision, a logged-out page never
   shows the live pulse; here it marks where something can BEGIN. */
.form__aside a {
  color: var(--live); text-decoration: none;
  border-bottom: 1px solid rgba(181, 33, 27, .35); padding-bottom: .12em;
}
.form__aside a:hover, .form__aside a:active { border-color: var(--live); }

/* The fine print: a bare centered mono line below everything — no panel, no crop
   marks; the cheapest possible height so the cover keeps the room. */
.legal {
  display: flex; align-items: baseline; justify-content: center; gap: .55rem;
  padding: .1rem 0 .05rem;
}
.legal__link {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .5rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-soft); text-decoration: none;
}
.legal__link:hover, .legal__link:active { color: var(--ink); }
.legal__dot { font-size: .5rem; color: var(--mark); }
.legal--drawer { margin-top: .9rem; }

/* Public notice pages (register teaser / legal trio): a letterhead moment —
   wordmark small on top, one serif heading, mono body lines. */
.notice {
  min-height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .5rem; text-align: center; padding: 1rem;
}
.notice__brand {
  font-family: "Cormorant", Georgia, serif; font-weight: 600;
  font-size: 1.1rem; letter-spacing: .22em; text-indent: .22em;
  color: var(--ink);
  margin-bottom: 1.4rem;
}
.notice__heading {
  margin: 0;
  font-family: "Cormorant", Georgia, serif; font-weight: 600;
  font-size: 2rem; line-height: 1.05; color: var(--ink);
}
.notice__line {
  margin: 0;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft); line-height: 1.7;
}
.notice__back {
  margin-top: 1.6rem;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--mark); padding-bottom: .12em;
}
.notice__back:hover, .notice__back:active { border-color: var(--ink); }

/* ---- Mono labels (the technical voice) ---- */
.label {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .62rem;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-soft);
}

/* ---- Feed of journal cards ---- */
.feed { display: flex; flex-direction: column; gap: var(--pad); min-height: 100%; }
/* A live session floats the pulse dot / session ✕ over the body's top-right — the
   first row ducks under its footprint (a label row alone doesn't clear the disc). */
.sect-stack--dot .feed, .sect-stack--dot .exlist { padding-top: 1.05rem; }

/* ---- Card: flat, hairline, sharp — a typed timeline entry ---- */
/* Meta line echoes the header's right-hand data column. */   /* dotted ring, dimmed */
/* Open = the magnifier on the right edge, dead-centred and padded — the same tap
   language as the workout cards' [details] button (the card is a view onto the
   event; changing it is a decision made on the opened page). */   /* workout type marker */
/* Workout card: the name is the title; this is the mono stat line under it. */
.card__text {
  font-size: .78rem;   /* the study's reading text — elegant, quiet */
  line-height: 1.5;
  color: var(--ink-body);
  margin: 0;
  white-space: pre-wrap;
}
.card__text.is-clamped {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card__text.is-open {   /* expanded — overrides the clamp above (must follow it) */
  display: block;
  -webkit-line-clamp: initial;
  overflow: visible;
}
.card__toggle {
  align-self: flex-end;
  margin-top: .3rem;
  padding: 0;
  border: none;
  background: none;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  cursor: pointer;
}
.card__toggle:active { color: var(--ink); }

.feed__more {
  align-self: center;
  padding: .5rem 1rem;
  border: none;
  background: none;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .58rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  cursor: pointer;
}
.feed__more:active { color: var(--ink); }

/* ---- Field ---- */
.field {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: .8rem .95rem;
  font: inherit;
  font-size: 1rem;          /* 16px — also keeps iOS from zooming on focus */
  color: var(--ink);
  resize: none;
}
.field::placeholder { color: var(--ink-soft); }
.field:focus { outline: none; border-color: var(--ink); }

/* ---- Form rhythm: the shared schema for every form ---- */
.form { display: flex; flex-direction: column; gap: .9rem; }
/* Login placeholders wear the label voice (mono caps) — they ARE the labels there. */
.form .field::placeholder {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
}
.form__group { display: flex; flex-direction: column; gap: .4rem; }

/* ---- Monolith button ---- */
.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 52px;
  border: 1px solid var(--monolith);
  border-radius: var(--r);
  background: var(--monolith);
  color: #fff;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .22em;
  text-decoration: none;
  cursor: pointer;
  transition: opacity .2s ease;
}
.btn:active { opacity: .85; }
.btn:disabled { opacity: .3; cursor: default; }
.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:active { opacity: 1; background: var(--hatch); }

/* ---- Action panel: a page's primary action, framed above the footer ---- */
.app__action { padding: var(--pad); }   /* same inset as the body, so columns align */
/* Action bar shares the pebble grid above it: Cancel = Low, Save spans Mid+High. */
.action-bar { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.action-bar > :last-child { grid-column: span 2; }

/* Launch zone (Training): the view switcher over the primary start. The switcher is
   quiet SUB-NAVIGATION (tabs), not buttons: no boxes, soft inactive labels, the active
   one in ink with a short underline. The underline both anchors the unframed labels
   and tells the sub-level apart from the main nav below (which marks without one).
   START WORKOUT stays the only heavy element in the zone. */
.launch { display: flex; flex-direction: column; gap: .35rem; }
.launch__form { display: flex; flex-direction: column; }   /* button keeps full width */
/* One row, however many segments (max four by brand rule; a scroll idea is
   parked for a hypothetical fifth). auto-flow keeps journal (3) and training
   (4) on the same rule. */
.segmented { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
.seg {
  position: relative;
  min-height: 34px; border: none; background: transparent;   /* invisible, full tap target */
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .62rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-soft); cursor: pointer;
  transition: color .15s ease;
}
.seg::after {
  content: ""; position: absolute; left: 50%; bottom: .22rem;
  width: 2.4rem; transform: translateX(-50%);
  border-bottom: 2px solid var(--live);   /* the quiet drop of red under the active view */
  opacity: 0; transition: opacity .15s ease;
}
.seg:active { color: var(--ink); }
.seg:disabled { color: var(--mark); cursor: default; }

.segmented[data-v="today"]     .seg[data-view="today"],
.segmented[data-v="favorites"] .seg[data-view="favorites"],
.segmented[data-v="workouts"]  .seg[data-view="workouts"],
.segmented[data-v="all"]       .seg[data-view="all"],
.segmented[data-v="training"]  .seg[data-view="training"],
.segmented[data-v="notes"]     .seg[data-view="notes"] { color: var(--ink); }
.segmented[data-v="today"]     .seg[data-view="today"]::after,
.segmented[data-v="favorites"] .seg[data-view="favorites"]::after,
.segmented[data-v="workouts"]  .seg[data-view="workouts"]::after,
.segmented[data-v="all"]       .seg[data-view="all"]::after,
.segmented[data-v="training"]  .seg[data-view="training"]::after,
.segmented[data-v="notes"]     .seg[data-view="notes"]::after { opacity: 1; }

/* ---- Today (spec 04, concept locked 2026-07-12): calendar agenda ----
   One card family app-wide (title flush left, meta cluster right, quiet ⋯ always
   visible); the date rail + week stoppers are THIS page's context add-ons. Focus
   (.is-focus, set by the cardFocus component) = dark frame + facts row slides
   open. The ⋯ menu opens over the milk-glass backdrop (white frost — only the
   lifted card and its menu stay sharp); adopted as the app-wide overlay direction. */
.today { display: flex; flex-direction: column; }

.wksep { display: flex; align-items: center; gap: .6rem; margin: 1.05rem 0 .6rem; }
.wksep:first-child { margin-top: .8rem; }  /* air under the "Current Path:" head */
.wksep::before, .wksep::after { content: ""; height: 1px; background: var(--line); flex: 1; }
.wksep span {
  font: 700 .56rem/1 "Space Mono", ui-monospace, monospace;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
}
/* the now-line: the page's one red (together with the today rail date — one marker) */
.wksep--now span { color: var(--live); }
.wksep--now::before, .wksep--now::after { background: var(--live); opacity: .3; }


.tcard {
  min-width: 0; position: relative;
  border: 1px solid var(--line); background: var(--bg);
  cursor: pointer; transition: border-color .18s ease;
}
.tcard.is-focus { border-color: var(--ink); }
.tcard__head { display: flex; align-items: baseline; gap: .55rem; padding: .37rem 2.9rem .62rem .75rem; min-width: 0; position: relative; }
/* one axis: title · mark · kebab. Top padding compensates the ~4px the tall kebab's
   baseline row adds above the title text — measured symmetric, not guessed. */
/* the mirrored right column: position rides the title line, the state mark the
   character line — the head reads as two aligned text rows on BOTH sides */
.tcard__metacol { display: flex; flex-direction: column; align-items: flex-end; gap: .34rem; flex: none; padding-top: .18rem; }
.tcard__title { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .28rem; }
.tcard__sub {
  font: 400 .6rem/1 "Space Mono", ui-monospace, monospace;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tcard--past .tcard__sub { color: var(--mark); }
.tcard__name {
  font-size: .9rem; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tcard__name--soft { font-weight: 500; color: var(--ink-body); }
.tcard--past .tcard__name { color: var(--ink-soft); font-weight: 500; }
.tcard__name--skipped { text-decoration: line-through; text-decoration-color: var(--mark); }
.tcard__num { font: 400 .64rem/1 "Space Mono", ui-monospace, monospace; letter-spacing: .04em; color: var(--ink-soft); flex: none; }
.trow { display: flex; gap: .6rem; }
.trow > .tcard { flex: 1; }  /* fills the row beside the date rail — ONLY there */
.trow + .trow { margin-top: .5rem; }
.trail {
  width: 2.5rem; flex: none; text-align: center; height: 2.62rem;
  display: flex; flex-direction: column; justify-content: center; gap: .12rem;
}
.trail i {
  font: 700 .52rem/1 "Space Mono", ui-monospace, monospace; font-style: normal;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft);
}
.trail b {
  font-family: "Cormorant Garamond", "Cormorant", Georgia, serif;
  font-weight: 600; font-size: 1.3rem; line-height: 1; color: var(--ink-body);
}
.trail--today i, .trail--today b { color: var(--live); }
.trail--past b { color: var(--mark); }
/* overdue: the guilty conscience — the missed day at half voice (frame matches) */
.trail--overdue i, .trail--overdue b { color: rgba(181, 33, 27, .5); }
/* overdue frame: the now-red at half voice; full red under focus */
.tcard--overdue { border-color: rgba(181, 33, 27, .5); }
.tcard--overdue.is-focus { border-color: var(--live); }
.tcard__mark--row { padding-top: .22rem; }  /* sits on the title line beside the kebab */
.tcard__mark {
  transform: translateY(-.1rem);  /* optical: small caps center on the title line */
  font: 700 .56rem/1 "Space Mono", ui-monospace, monospace;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); flex: none;
}
.tcard__mark--now { color: var(--ink); }
/* SIGNAGE(touch-hit): the ⋯ button IS the card's whole top-right corner (owner
   rounds 2026-07-12) — absolutely positioned over the full head height (title +
   sub + the head's own paddings, ~46×47px), so no flex-baseline quirk can shrink
   it. The head's 2.9rem right padding reserves this column — keep in sync. */
.tcard__more {
  position: absolute; top: 0; right: 0; bottom: 0; width: 2.9rem;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer; margin: 0; padding: 0;
  line-height: 1; color: var(--ink-soft);
}
/* The three dots are DRAWN, not a glyph: the phosphor em-box buries its dots in
   internal padding (fill variant = the rejected pill), so we control dot size
   and rhythm directly (owner rounds 2026-07-12). K6 of the kebab study (#82,
   owner pick): a 24px hairline square frames a fine horizontal meatball — the
   frame declares "control", the dots say "more"; the hit area stays the whole
   corner, the square is purely visual. */
.tcard__more .dots3 {
  width: 3.5px; height: 3.5px; border-radius: 50%; background: currentColor;
  box-shadow: -6.5px 0 0 currentColor, 6.5px 0 0 currentColor;
}
.tcard__more::before {
  content: ""; position: absolute; top: 50%; left: 50%; width: 24px; height: 24px;
  transform: translate(-50%, -50%); border: 1px solid var(--line);
  transition: border-color .18s ease;
}
.tcard.is-focus .tcard__more, .tcard.is-lift .tcard__more,
.tcard__more:hover, .tcard__more:active { color: var(--ink); }
.tcard.is-focus .tcard__more::before, .tcard.is-lift .tcard__more::before,
.tcard__more:hover::before, .tcard__more:active::before { border-color: var(--ink); }

.tcard__reveal { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .32s ease; }
.tcard.is-focus .tcard__reveal { grid-template-rows: 1fr; }
.tcard__reveal-in { overflow: hidden; }
/* the perforation (V9): dashed marks the FOLD seam — solid hairlines build the
   card's fixed structure, the dashed line is where it tears open */
.tcard__facts { display: flex; align-items: center; gap: 1.1rem; border-top: 1px dashed var(--mark); padding: .55rem .75rem .6rem; }
.tfact { display: flex; flex-direction: column; gap: .12rem; }
.tfact b { font: 700 .76rem/1 "Space Mono", ui-monospace, monospace; color: var(--ink); }
.tfact span {
  font: 700 .5rem/1 "Space Mono", ui-monospace, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft);
}

/* milk glass + the per-card ⋯ menu */
.milk {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(255,255,255,.45);
  -webkit-backdrop-filter: blur(7px); backdrop-filter: blur(7px);
  transition: opacity .18s ease;
}
/* While a confirm sheet is open from inside a card menu, the sheet brings its
   own full-screen backdrop — the menu's milk yields instead of double-frosting
   the page (#99 review nit). */
.today:has(.drawer--fixed.is-open) .milk { opacity: 0; }
.tcard.is-lift { z-index: 50; }

/* --- Paths library + editorial detail (spec 04 2b.2, studies A2/B3) ------- */
/* Standard family cards; the story frieze crowns ONLY the active path's card
   (owner round 2026-07-12 — plain cards keep the list dense). Frieze focus sits
   slightly above center: heads live, feet may crop — tune object-position per
   motif here until focal metadata earns its keep (one curated path today). */
.pcard__band { display: block; width: 100%; height: 96px; object-fit: cover; object-position: 50% 32%; }
.pcard-ghost {
  border: 1px solid var(--line); padding: .7rem; text-align: center; margin-top: .5rem;
  font: 700 .6rem/1 "Space Mono", ui-monospace, monospace;
  letter-spacing: .14em; text-transform: uppercase; color: var(--mark);
}
.pathfig { margin: 0 0 .75rem; }
.pathfig__img { display: block; width: 100%; height: 140px; object-fit: cover; object-position: 50% 30%; }
.pathlede { font-family: "Cormorant", Georgia, serif; font-style: italic; font-weight: 500; font-size: 1.05rem; line-height: 1.35; color: var(--ink-body); margin: .3rem 0 .7rem; }
.ramp { width: 100%; border-collapse: collapse; font-family: "Space Mono", ui-monospace, monospace; margin-top: .45rem; }
.ramp th { font-size: .5rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); text-align: right; padding: .3rem .2rem; border-bottom: 1px solid var(--line); }
.ramp th:first-child, .ramp td:first-child { text-align: left; }
.ramp td { font-size: .66rem; color: var(--ink); text-align: right; padding: .34rem .2rem; border-bottom: 1px solid var(--line); }
.arc { display: flex; gap: 3px; margin-top: .5rem; flex-wrap: wrap; }
.arc i { width: 6px; height: 12px; border: 1px solid var(--mark); flex: none; }
.arc i.on { background: var(--ink); border-color: var(--ink); }
.arc__label { font: 700 .52rem/1 "Space Mono", ui-monospace, monospace; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); margin-top: .45rem; }
/* Replace/restart confirm: the SESSION SHEET idiom (confirm_sheet macro) —
   fixed because it nests inside the crop-corner-positioned panel, which would
   otherwise trap the drawer shell's inset-0. */
.pathact { flex: 1; display: flex; }
.pathact > .btn { flex: 1; }
.drawer.drawer--fixed { position: fixed; }  /* must outrank .drawer's absolute (defined later in this file) */
.sheet-note { font-size: .82rem; color: var(--ink-body); line-height: 1.5; margin: -.2rem 0 .85rem; }

.tmenu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60; width: 12rem;
  background: var(--bg); border: 1px solid var(--ink); box-shadow: 5px 5px 0 var(--hatch);
  opacity: 0; transform: translateY(4px); pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
}
.tmenu.on { opacity: 1; transform: none; pointer-events: auto; }
.tmenu form { margin: 0; border-top: 1px solid var(--line); }
.tmenu__item {
  display: flex; align-items: center; gap: .6rem; width: 100%;
  border: 0; background: none; text-align: left; cursor: pointer;
  padding: .78rem .8rem; font-size: .8rem; color: var(--ink-body); text-decoration: none;  /* rows ≥44px */
  font-family: inherit;
}
.tmenu__item:active { background: var(--hatch); }
.tmenu__ico { font-size: .95rem; color: var(--ink-soft); width: 1.1rem; text-align: center; }
/* Destructive menu rows wear the danger voice (#92) — same red as the log page's
   trashcan, so "this one bites" reads before the confirm sheet even opens. */
.tmenu__item--danger, .tmenu__item--danger .tmenu__ico { color: var(--live); }

.today-empty { display: flex; flex-direction: column; align-items: center; gap: 1rem; padding-bottom: .8rem; }

/* ---- Sectioned body: scroll area is a stack of self-framed sections ---- */
.app__body--sectioned .app__scroll {
  -webkit-mask-image: none; mask-image: none;
  padding: 0;                  /* sections sit flush to the body edge = header/footer frame */
}
/* The pinned-frame scroll (list surfaces): the section's marks + head stay put,
   only this inner region scrolls. The honest hint: content fades ONLY while more
   waits below (app.js toggles .has-below) — never the frame, never at the end. */
.sect__scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: none; display: flex; flex-direction: column; }
.sect__scroll::-webkit-scrollbar { display: none; }
.sect__scroll.has-below {
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - 56px), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - 56px), transparent 100%);
}
/* The fade mask SUSPENDS while a card menu is open (.is-lift): an ancestor mask
   clips fixed descendants — the viewport-covering .milk pane got cut to the
   scroll region and dissolved through the bottom fade (owner catch on Today,
   #82 round 2). The fade is meaningless behind milk glass anyway. */
.sect__scroll:has(.tcard.is-lift) {
  -webkit-mask-image: none;
          mask-image: none;
}
/* a pinned-frame page constrains its stack to the viewport (no outer scroll) */
.sect-stack:has(.sect__scroll) { height: 100%; }

/* Vertical stack of self-framed sections — the swappable training view lives here. */
.sect-stack { display: flex; flex-direction: column; gap: .55rem; min-height: 100%; }
.sect { padding: var(--pad); }
/* A section that claims the remaining vertical space (frame reaches the action zone),
   regardless of how much content it holds. Its body fills; the placeholder centers. */
.sect--grow { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

/* ---- Run-canvas (live logging): the context zones ---- */
/* Placeholder zones (exercise / set) — filled in the next steps. Same cross
   primitive as the exercise list/figure placeholders. */
.run-ph {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .55rem; padding: 1.1rem 0; text-align: center;
}
.run-ph--lg { flex: 1 1 auto; min-height: 0; }
.run-ph__box {
  width: 3.2rem; height: 3.2rem; border: 1px solid var(--line);
  background:
    linear-gradient(to top right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px)),
    linear-gradient(to bottom right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px));
}
.run-ph__box--lg { width: 4.6rem; height: 4.6rem; }
.run-ph__note {
  margin: 0; font-family: "Space Mono", ui-monospace, monospace;
  font-size: .58rem; letter-spacing: .16em; text-transform: uppercase; color: var(--mark);
}
/* Shared cross placeholder (image not yet generated). */
.ph-cross {
  background:
    linear-gradient(to top right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px)),
    linear-gradient(to bottom right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px));
}
/* Exercise context: a single centred active image (the prev/next carousel + paging
   is parked, optical polish for later). The image fills exactly the air the stage has
   free (largest square fitting the figure), never forces scroll — a viewport guess
   (34vh) overflowed small phones. Mechanism: the figure claims the slack via flex-grow,
   but it CANNOT be the size container itself — Chrome resolves cqh against the
   pre-flexing height (0) when the height comes from flex-grow. So .run-fig sits
   absolutely at inset:0 (its height is the figure's USED height, definite) and is the
   query container; image and rail share the fit expression (square bounded by free
   height and by width minus the rail column, 5rem floor). */
.run-stage { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: .5rem; flex: 1 1 auto; min-height: 0; width: 100%; }
.run-figure {
  /* Full-bleed stage: the image claims the section's whole width (align-self
     beats the stage's centering — all children are absolute, so the box would
     otherwise collapse to zero width; the negative margins then eat the section
     padding). Height: at most the full-width SQUARE — the archive master is 1:1
     and never zooms past full width (owner rule 2026-07-12, #87); short screens
     shrink it (vertical crop, head-band bias, as before). The image anchors TOP,
     the title keeps its fixed gap below, spare height gathers under the title
     (stage justify: flex-start). */
  position: relative;
  align-self: stretch;
  margin: 0 calc(-1 * var(--pad));
  flex: 0 1 auto; aspect-ratio: 1 / 1; min-height: 7rem;
}
.run-cara__img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  /* The archive figures centre in their square masters; biasing the crop toward
     the head band keeps the exercise readable in every (wide or tall) cut. */
  object-position: 50% 30%;
}
/* Workout progress: a red fill in a hinted grey track (a fill needs a vessel), in flow
   inside the section padding (share of sets logged; SET N/M carries the number). */
.runbar {
  display: block; height: 4px; background: rgba(14, 15, 18, .05);
  /* full-bleed like the stage below — bar and image share one width */
  margin: 0 calc(-1 * var(--pad)) .45rem;
}
/* No replay theatrics: the fill renders AT its level. Growth comes from (a) the live
   timer painting (setTimer._paint) and (b) the runBar component easing from the last
   known level to the new one after a log — both ride this width transition. */
.runbar__fill {
  display: block; height: 100%; background: var(--live);
  transition: width .45s ease;
}
/* Logged (past) set: the canvas dims — the set lies in the completed past. The red
   record button is gone too (RESET is monochrome), so the mode reads at a glance. */
.run--past .run-cara__img,
.run--past .duo { opacity: .45; }
/* Fused log axis (#26 step 3): reps × weight on the TRANSPORT's grid (2 : 2.4 : 2,
   same gaps) — the value boxes flucht with Previous/Next below, the unbordered × rides
   the wide middle over LOG SET. Bordered = tappable, the canvas's own language; the
   full pad returns as a popover floating just above the line. */
.duo { position: relative; display: grid; grid-template-columns: 2fr 2.4fr 2fr; gap: .5rem; }
.duo__half { display: flex; }
.duo__half--solo { grid-column: 2; }
.duo { margin-bottom: .5rem; }   /* breathing room to the transport in the shared panel */
.duo__cell {
  flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .2rem; min-height: 3.4rem; padding: .35rem .25rem;
  border: 1px solid var(--line); background: transparent; font: inherit;
  appearance: none; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.duo__cell:active { background: var(--hatch); }
.duo__cell:disabled { cursor: default; }
.duo__line { display: flex; align-items: baseline; gap: .3rem; }
.duo__caps {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .55rem; font-weight: 400;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); white-space: nowrap;
}
/* The session goal in the open pad speaks the live accent (#88 owner round) —
   red is the run's pulse (LOG SET, runbar, live dot); among the pad's many
   numbers the target is the one that must not drown. */
.duo__caps--goal { color: var(--live); }
.duo__num {
  font-family: "Cormorant", Georgia, serif; font-weight: 600; font-size: 2.2rem;
  line-height: 1; font-variant-numeric: lining-nums; color: var(--ink);
}
.duo__unit {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .6rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft);
}
.duo__x {
  grid-column: 2; display: flex; align-items: center; justify-content: center;
  /* 3rem → 2.6rem (#88 owner round): the study's lighter × reads better. */
  font-family: "Cormorant", Georgia, serif; font-size: 2.6rem; line-height: 1;
  color: var(--ink-soft);
}
/* Duration on the session grid: control boxes ride the outer columns, the clock sits
   unbordered in the middle (display, not a control — the × rule). */
.duo__cell--ctl i { font-size: 1.6rem; line-height: 1; }
/* Idle play glyph wears the accent — the nudge to actually start the clock;
   the count-in digit (3·2·1 before the true start) wears live red. */
.duo__ctl--go { color: var(--live); }
.duo__num--countin { color: var(--live); }
.duo__mid {
  grid-column: 2; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .15rem;
}
.duo__half:last-of-type:not(.duo__half--solo) { grid-column: 3; }
/* A2f pad shell (#88): the pad covers the duo row — bottom edge exactly on the
   row's bottom edge, growing upward into the stage. Anchored to .duo (the
   positioned ancestor), so it spans the full row, not one half. */
.duo__pop { position: absolute; left: 0; right: 0; bottom: 0; z-index: 5; }
/* The local frost halo replaces the drop shadow: the surroundings recede without
   disappearing — the exercise stays readable as context, and the halo anchors
   the pad spatially. Owner-tuned 2026-07-13: ring (full-strength zone around the
   pad), fade (soft run-out), blur, veil. The mask ramp is a stepped smoothstep —
   a LINEAR ramp reads as a visible Mach-band edge under this much blur. */
.duo__frost {
  --ring: 1.25rem; --fade: 11.5rem; --blur: 53px; --veil: .8;
  position: absolute; inset: calc(-1 * (var(--ring) + var(--fade))); pointer-events: none;
  -webkit-backdrop-filter: blur(var(--blur)) saturate(.85);
  backdrop-filter: blur(var(--blur)) saturate(.85);
  background: rgba(255, 255, 255, var(--veil));
  -webkit-mask-image:
    linear-gradient(to right,
      transparent, rgba(0,0,0,.04) calc(var(--fade) * .2), rgba(0,0,0,.16) calc(var(--fade) * .4),
      rgba(0,0,0,.42) calc(var(--fade) * .6), rgba(0,0,0,.75) calc(var(--fade) * .8), #000 var(--fade),
      #000 calc(100% - var(--fade)), rgba(0,0,0,.75) calc(100% - var(--fade) * .8),
      rgba(0,0,0,.42) calc(100% - var(--fade) * .6), rgba(0,0,0,.16) calc(100% - var(--fade) * .4),
      rgba(0,0,0,.04) calc(100% - var(--fade) * .2), transparent),
    linear-gradient(to bottom,
      transparent, rgba(0,0,0,.04) calc(var(--fade) * .2), rgba(0,0,0,.16) calc(var(--fade) * .4),
      rgba(0,0,0,.42) calc(var(--fade) * .6), rgba(0,0,0,.75) calc(var(--fade) * .8), #000 var(--fade),
      #000 calc(100% - var(--fade)), rgba(0,0,0,.75) calc(100% - var(--fade) * .8),
      rgba(0,0,0,.42) calc(100% - var(--fade) * .6), rgba(0,0,0,.16) calc(100% - var(--fade) * .4),
      rgba(0,0,0,.04) calc(100% - var(--fade) * .2), transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right,
      transparent, rgba(0,0,0,.04) calc(var(--fade) * .2), rgba(0,0,0,.16) calc(var(--fade) * .4),
      rgba(0,0,0,.42) calc(var(--fade) * .6), rgba(0,0,0,.75) calc(var(--fade) * .8), #000 var(--fade),
      #000 calc(100% - var(--fade)), rgba(0,0,0,.75) calc(100% - var(--fade) * .8),
      rgba(0,0,0,.42) calc(100% - var(--fade) * .6), rgba(0,0,0,.16) calc(100% - var(--fade) * .4),
      rgba(0,0,0,.04) calc(100% - var(--fade) * .2), transparent),
    linear-gradient(to bottom,
      transparent, rgba(0,0,0,.04) calc(var(--fade) * .2), rgba(0,0,0,.16) calc(var(--fade) * .4),
      rgba(0,0,0,.42) calc(var(--fade) * .6), rgba(0,0,0,.75) calc(var(--fade) * .8), #000 var(--fade),
      #000 calc(100% - var(--fade)), rgba(0,0,0,.75) calc(100% - var(--fade) * .8),
      rgba(0,0,0,.42) calc(100% - var(--fade) * .6), rgba(0,0,0,.16) calc(100% - var(--fade) * .4),
      rgba(0,0,0,.04) calc(100% - var(--fade) * .2), transparent);
  mask-composite: intersect;
}
/* The pad body paints AFTER the frost (DOM order), so its border and content
   stay crisp while the halo blurs only the surroundings. */
.duo__pad { position: relative; background: #fff; border: 1px solid var(--ink); }
.duo__padhead { display: flex; align-items: center; justify-content: space-between; padding: .45rem .55rem .35rem; }
/* Close is the shared drawer idiom (drawer__close + ph-x) — same size and glyph
   as the menu sheet and the confirm sheet, one thing to learn. */

/* W3 body (#88): five EQUAL slots on top (anchors + count-up chunks, their own
   even beat), the value row below on the transport grid — minus fluchts with
   Prev, the value with LOG SET, plus with Next. The value cell repeats the duo
   cell's anatomy (Target above, value line, Prev below): the pad IS the cell,
   unfolded. */
.wpad { padding: .45rem; display: grid; gap: .5rem; border-top: 1px solid var(--line); }
.wpad__row { display: grid; gap: .5rem; }
.wpad__row--five { grid-template-columns: repeat(5, 1fr); }
.wpad__row--transport { grid-template-columns: 2fr 2.4fr 2fr; }
.wpad__cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .2rem; min-height: 2.75rem; padding: .2rem;   /* 44px tap floor */
  border: 1px solid var(--line); background: transparent; font: inherit;
  appearance: none; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.wpad__cell:active { background: var(--hatch); }
.wpad__cell--anchor {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .6rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink);
}
.wpad__cell--zero { border-color: var(--ink); }
.wpad__cell--chunk { font-family: "Space Mono", ui-monospace, monospace; font-size: .78rem; font-weight: 400; letter-spacing: .06em; color: var(--ink); }
/* 1.55rem → 2rem (owner round): the glyphs were drowning in their big cells;
   2rem sits just under the 2.2rem value — present, not shouting. */
.wpad__cell--step { font-family: "Cormorant", Georgia, serif; font-weight: 500; font-size: 2rem; line-height: 1; color: var(--ink); }
.wpad__cell--val { cursor: default; gap: .25rem; min-height: 4rem; padding: .35rem .25rem; }
.wpad__cell--val .duo__line { cursor: text; }
.wpad__in { width: 3.4ch; border: 0; background: none; -moz-appearance: textfield; }
.wpad__in:focus { outline: none; }
.wpad__in::-webkit-outer-spin-button, .wpad__in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Section heads that share their top edge with the floating session ✕ (headerless
   pages) clear its footprint on the right. */
.sec-head--clear-exit { padding-right: 2.7rem; }
/* The set rail is retired (#26 step 2): exercise-block progress lives in the set chip
   on the image; the timer's live motion shows in the workout bar alone. */
.run-fig {
  /* The definite-height box the image fills (the figure's used flex height); the
     old square-fit container query (--fit) retired with the full-bleed stage. */
  position: absolute; inset: 0;
}
/* Headerless canvas (#26 steps 1+2): the session ✕ floats at the stage's top-right;
   the EXERCISE-scoped set chip anchors to the image's lower-right corner via
   .run-imgbox — both on translucent hairline-edged backings that hold against a
   bright archive photo. */
.run-imgbox { position: relative; display: block; width: 100%; height: 100%; }
.livedot--float {
  position: absolute; top: .3rem; right: .3rem; z-index: 2; margin-right: 0;
  background: rgba(255, 255, 255, .78);   /* borderless — the disc alone holds */
}
.run-setchip {
  position: absolute; right: .3rem; bottom: .3rem; z-index: 2;
  padding: .18rem .5rem;
  font-family: "Space Mono", monospace; font-size: .82rem; line-height: 1.3;
  color: var(--ink); background: rgba(255, 255, 255, .78);
  border: 1px solid var(--line);
}
.run-titles { display: flex; flex-direction: column; align-items: center; gap: .2rem; }
.run-workout {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .62rem; font-weight: 400;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
}
.run-extitle {
  margin: 0; text-align: center; font-family: "Cormorant", Georgia, serif;
  font-weight: 600; font-size: 1.95rem; line-height: 1.05; color: var(--ink);
}
.run-extitle__look {
  display: inline-flex; align-items: center; margin-left: .4rem; padding: .1rem;
  color: var(--ink-soft); font-size: 1.05rem; text-decoration: none; vertical-align: middle;
}
.run-tags { justify-content: center; flex: 0 1 auto; }
/* Set-logging form — the rudimentary stage; the innovative counter is Slice B. */
/* The set form is a flex column holding both logger sections + the chart buffer. */
.run-setform { flex: 0 0 auto; display: flex; flex-direction: column; gap: .55rem; }
.run-progress {
  margin: 0; text-align: center;
  font-family: "Space Mono", ui-monospace, monospace; font-size: .6rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft);
}
.setlog__fields { display: flex; flex-direction: column; gap: .6rem; }
.setlog__field { display: flex; flex-direction: column; gap: .25rem; }
.setlog__input {
  width: 100%; min-height: 48px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--bg); color: var(--ink); padding: 0 .7rem;
  font-family: "Cormorant", Georgia, serif; font-weight: 600; font-size: 1.6rem;
  text-align: right;   /* values read from the right, like every counter on the canvas */
}

/* ---- Rest interstitial (C2): one oversized clock; the pause IS the page ---- */
/* FIXED height (#26): the clock must not drift lower as the queue empties near the
   workout's end — the NEXT section below is the vertically variable element. */
.rest-stage {
  height: 10.5rem; flex: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .5rem; padding: .5rem 0;
}
.rest-clock {
  font-family: "Cormorant", Georgia, serif; font-weight: 600;
  font-size: clamp(4rem, 22vw, 6.5rem); line-height: 1;
  color: var(--ink); font-variant-numeric: lining-nums;
}
.rest-sub {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
}
/* The lookahead queue behind the next set: same anatomy as the row above it
   (image · name · value, one shared axis); every step back shrinks the image
   inside its fixed slot and fades the row — depth through scale, not offset.
   The rest page has air to spare, so the whole stage runs one size up. */
.rest .exrow { padding: .6rem .55rem; }
.rest .exrow__img { width: 5rem; height: 5rem; }
.rest .exrow__name { font-size: 1.5rem; }
.rest .exrow__sets { font-size: .72rem; }
.restq {
  /* horizontal padding mirrors .exrow's inset, so the image slots and the name
     edge sit on exactly the same axes as the full row above */
  list-style: none; margin: .7rem 0 0; padding: 0 .55rem;
  display: flex; flex-direction: column; gap: .55rem;
}
.restq__row {
  display: flex; align-items: center; gap: .7rem;   /* mirrors .exrow */
  font-size: 1.2rem;   /* per-row base; the steps below scale name + value together */
}
.restq__fig { flex: none; width: 5rem; display: flex; justify-content: center; }
.restq__row .exrow__img { width: var(--qs, 5rem); height: var(--qs, 5rem); }
.restq__name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: "Cormorant", Georgia, serif; font-size: 1em; line-height: 1.1; color: var(--ink);
}
.restq__val {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .6em;
  letter-spacing: .04em; color: var(--ink-soft); white-space: nowrap;
}
.restq__row:nth-child(1) { --qs: 3.9rem; opacity: .65; }
.restq__row:nth-child(2) { --qs: 3.1rem; opacity: .45; font-size: 1.1rem; }
.restq__row:nth-child(3) { --qs: 2.4rem; opacity: .3;  font-size: .95rem; }
.restq__row:nth-child(4) { --qs: 1.8rem; opacity: .18; font-size: .85rem; }
.restq__row:nth-child(5) { --qs: 1.3rem; opacity: .1;  font-size: .78rem; }
/* Session header above the transport: "SESSION" label (left) + the set progress, on the
   same 2 : 2.4 : 2 grid so the progress lands centred over the Log set button. */
.transport-head {
  display: grid; grid-template-columns: 2fr 2.4fr 2fr; gap: .5rem;
  align-items: baseline; margin: 0 0 .4rem;
}
/* Transport (cassette): Previous · Log set · Next. Bordered buttons on the tracker's
   2 : 2.4 : 2 zones with gaps; the gaps come out of Previous/Next, Log set keeps its zone. */
.transport { display: grid; grid-template-columns: 2fr 2.4fr 2fr; gap: .5rem; }
.transport__btn {
  display: flex; align-items: center; justify-content: center; gap: .35rem; min-height: 3.4rem;
  border: 1px solid var(--line); background: transparent; color: var(--ink); cursor: pointer;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .66rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  text-decoration: none;   /* paging slots are anchors */
}
/* The reset form occupies the record slot; its button fills the cell. */
.transport__form { display: flex; }
.transport__form > .transport__btn { flex: 1 1 auto; width: 100%; }
.transport__btn i { font-size: 1.2rem; }
.transport__btn:active { background: var(--hatch); }
/* The record action wears the live/record red (the same --live as the header), not a
   new colour. Red stays strictly "live/record": Finish/Start/Discard stay monochrome. */
.transport__log { background: var(--live); border-color: var(--live); color: #fff; }
.transport__btn--ph { color: var(--mark); }
.transport__btn--ph:disabled { opacity: .6; cursor: default; }
/* Summary action bar on the transport grid: the skip-back (the canvas vocabulary for
   the backward path) fills the Previous zone, Finish spans Log set + Next. */
.run-end { display: grid; grid-template-columns: 2fr 2.4fr 2fr; gap: .5rem; }
.run-end > :last-child { grid-column: 2 / 4; }
.run-end .transport__btn { min-height: 52px; }   /* level with the Finish button */

/* Workout-log detail — headerless (#26): the name + stamp line the header used to
   carry, above the summary's shared exrow reading. Typography mirrors the journal
   card (Cormorant title over a mono caps stamp). */
.loghead { display: flex; flex-direction: column; gap: .12rem; margin: .15rem 0 .8rem; }
.loghead__name { font-family: "Cormorant", Georgia, serif; font-weight: 600; font-size: 1.35rem; line-height: 1.1; color: var(--ink); margin: 0; }
.loghead__stamp { font-family: "Space Mono", ui-monospace, monospace; font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }

/* ---- Training landing ---- */
.sec-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 .35rem; }
.sec-head__action {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .14em; text-transform: uppercase; color: var(--mark);
}
/* Four stats in one row, no dividing lines: a big Cormorant counter over a
   mono label. Week/Month carry a small "/planned" denominator; Total/Overdue
   are single counts. */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: .35rem; }
.stats__cell { display: flex; flex-direction: column; align-items: center; gap: .04rem; text-align: center; }
.stats__v { display: flex; align-items: baseline; justify-content: center; }
.stats__num {
  font-family: "Cormorant", Georgia, serif; font-weight: 600;
  font-size: 1.55rem; line-height: 1; color: var(--ink);
}
.stats__den { font-family: "Cormorant", Georgia, serif; font-size: .95rem; color: var(--ink-soft); }
.stats__k {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .5rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
  white-space: nowrap;
}

/* ---- Workout cards: selectable list (radio drives Start; [details] separate) ---- */
/* Visual radio (a label bound to the input) — a bordered rectangular BUTTON (#26):
   generous padding makes it a real thumb target; the circle rides inside (::before). */
/* Title row: title · origin. The radio and [details] flank the body, centred. */
/* Details = the magnifier as a bordered rectangular BUTTON (#26), the same tap
   language as the radio box beside it. */

/* ---- Workout detail subpage ---- */
/* Badge axis: tags wrap on the left, the favourite toggle stays pinned top-right. */
.tagbar { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .55rem; }
.tagrow { display: flex; flex-wrap: wrap; gap: .3rem; flex: 1 1 auto; }
.favtoggle { flex: none; margin: 0; }
/* Just the star (outline → fill); no label. Padding keeps a real tap target. */
.favtoggle__btn {
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; cursor: pointer; padding: .1rem .25rem;
  color: var(--ink-soft); transition: color .15s ease;
}
.favtoggle__icon { font-size: 1.05rem; line-height: 1; }
.favtoggle--on .favtoggle__btn { color: var(--ink); }
.favtoggle__btn:hover, .favtoggle__btn:active { color: var(--ink); }

/* Description: editorial lead (serif italic) + a schematic mono line. */
.wkabout { display: flex; flex-direction: column; gap: .35rem; }
.wkabout__text {
  margin: 0; font-size: .9rem; line-height: 1.5; color: var(--ink-body);
}
.wkabout__meta {
  margin: 0; font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}

/* View toggle: a square sliding switch (a setting, not a command button). The dark
   knob (::before) slides between the two halves; the active label sits white over it. */
.vtoggle { display: flex; align-items: center; justify-content: flex-end; gap: .5rem; margin-bottom: 1rem; }
.vtoggle__label {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .54rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-soft);
}
.vswitch {
  position: relative;
  display: inline-grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--line);
}
.vswitch::before {
  content: ""; position: absolute; z-index: 0; top: 0; bottom: 0; left: 0; width: 50%;
  background: var(--monolith); transition: transform .18s ease;
}
.vswitch[data-v="sequence"]::before { transform: translateX(100%); }
.vswitch__opt {
  position: relative; z-index: 1; min-height: 26px; padding: 0 .6rem;
  border: none; background: none; cursor: pointer;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .52rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft); transition: color .18s ease;
}
.vswitch[data-v="grouped"]  .vswitch__opt[data-view="grouped"],
.vswitch[data-v="sequence"] .vswitch__opt[data-view="sequence"] { color: #fff; }
/* Light cross-fade on the grouped↔sequence rebuild (htmx swap delay + this class). */
.wklist { flex: 1; min-height: 0; overflow-y: auto; transition: opacity .14s ease; }
.wklist.htmx-swapping { opacity: 0; }
.exlist, .seqlist { list-style: none; margin: 0; padding: 0; }
/* Grouped: one banded row per exercise — image placeholder · name+class · sets. */
.exrow {
  display: flex; align-items: center; gap: .7rem;
  padding: .5rem .55rem;
}
.exrow:nth-child(odd) { background: var(--hatch); }
/* Image + name + class is a link to the exercise page; the sets stay static. */
.exrow__link {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: .7rem;
  text-decoration: none; color: inherit;
}
/* Square image placeholder: a corner-to-corner cross (two diagonals) until real art. */
.exrow__img {
  flex: none; width: 2.9rem; height: 2.9rem; border: 1px solid var(--line);
  background:
    linear-gradient(to top right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px)),
    linear-gradient(to bottom right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px));
}
/* A real exercise image in the slot (summary rows): drop the cross, crop to cover. */
.exrow__img--real { background: none; object-fit: cover; }
.exrow__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .05rem; }
.exrow__meta {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .5rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}
/* Sequence: a schedule — each step (set + its rest) is one banded block. */
.seqlist { counter-reset: seq; }
.seqstep { counter-increment: seq; padding: .65rem .55rem; }
.seqstep:nth-child(odd) { background: var(--hatch); }
.seqrow, .restrow {
  display: flex; align-items: baseline; justify-content: space-between; gap: .9rem;
}
.restrow { padding: .3rem 0 0 1.5rem; }
.seqrow__ex::before {
  content: counter(seq) "   ";
  font-family: "Space Mono", ui-monospace, monospace; font-size: .58rem; color: var(--ink-soft);
}
.exrow__name, .seqrow__ex {
  font-family: "Cormorant", Georgia, serif; font-size: 1.1rem; line-height: 1.1; color: var(--ink);
}
.exrow__sets, .seqrow__val {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .66rem;
  letter-spacing: .04em; color: var(--ink-soft); white-space: nowrap;
}
/* Summary rows list every set's value — stacked, one unbreakable line per set. */
.exrow__sets--stack {
  display: flex; flex-direction: column; align-items: flex-end; gap: .14rem;
  color: var(--ink-body);
}
.exrow__set { white-space: nowrap; }
.exrow__set--open { color: var(--mark); }   /* still-open sets show their target, muted */
.restrow__lbl, .restrow__val {
  font-family: "Space Mono", ui-monospace, monospace; font-size: .56rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}

/* ---- Exercise detail page ---- */
/* 1:1 like the list thumbnail (same crop everywhere). Full panel width by intent:
   this page is where the painting gets seen — scrolling for the prose is fine. */
/* Full-bleed exercise figure: the image runs edge-to-edge over the section's top
   (negative margins eat the padding) at its full square — no crop; the page may
   scroll for prose (a reading page, not a stage — deliberate, 2026-07-09). */
.exfig { margin: calc(-1 * var(--pad)) calc(-1 * var(--pad)) .55rem; }
.exfig__img, .exfig__ph { display: block; width: 100%; aspect-ratio: 1 / 1; }
.exfig__img { object-fit: cover; }
/* The exercise name sits under the image (the header stays generic). */
.extitle {
  margin: 0 0 .4rem; font-family: "Cormorant", Georgia, serif;
  font-weight: 600; font-size: 1.55rem; line-height: 1.1; color: var(--ink);
}
/* Same cross motif as the list thumb until real art lands. */
.exfig__ph {
  border: 1px solid var(--line);
  background:
    linear-gradient(to top right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px)),
    linear-gradient(to bottom right, transparent calc(50% - .5px), var(--mark) calc(50% - .5px), var(--mark) calc(50% + .5px), transparent calc(50% + .5px));
}
.prose { display: flex; flex-direction: column; gap: .9rem; }
.prose__block { display: flex; flex-direction: column; gap: .25rem; }
.prose__label {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft);
}
.prose__text {
  margin: 0; font-family: "Inter", system-ui, sans-serif;
  font-size: .9rem; line-height: 1.55; color: var(--ink-body);
}
.badge {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .52rem; letter-spacing: .1em; text-transform: uppercase;
  padding: .15rem .4rem; border: 1px solid var(--line); color: var(--ink-soft);
}
.tcard__body { border-top: 1px dashed var(--mark); margin-top: 0; padding: .55rem .75rem .6rem; display: flex; flex-direction: column; }  /* perforation, see .tcard__facts */
.tcard__type { font-size: .8rem; }
.tcard__energy { font-size: .72rem; }
.tcard__energy--none { color: var(--mark); opacity: .55; }   /* dotted ring, dimmed */

/* checked = launch-armed; the frame doubles as the selection (no visible radio).
   The radio itself is pure form contract — never painted. */
.wcard__radio { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.tcard:has(.wcard__radio:checked) { border-color: var(--ink); }
/* library list (no date rail): the cards space themselves */
.today > .tcard + .tcard { margin-top: .5rem; }

/* ---- Compose: a full-height capture screen that mirrors the card ---- */
.compose { display: flex; flex-direction: column; gap: .9rem; flex: 1 1 auto; min-height: 0; }  /* fills the grow-section */
.compose__card {
  flex: 1;
  min-height: 8rem;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  padding: .68rem .75rem;      /* the family card's inner rhythm */
}
.compose__card:focus-within { border-color: var(--ink); }
.compose__id { display: flex; flex-direction: column; gap: .28rem; min-width: 0; }
.compose__title {            /* the family card name */
  font-size: .9rem; font-weight: 600; color: var(--ink); line-height: 1.2;
}
.compose__stamp {            /* the family card's second line: mark · stamp */
  font: 400 .6rem/1 "Space Mono", ui-monospace, monospace;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft);
}
.compose__text {
  flex: 1;
  min-height: 4rem;
  border: none;
  border-top: 1px solid var(--line);   /* the writing surface below the head */
  background: none;
  margin-top: .6rem;
  padding: .55rem 0 0;
  resize: none;
  font: inherit;
  font-size: 1rem;           /* 16px — keeps iOS from zooming on focus */
  line-height: 1.45;
  color: var(--ink-body);
}
.compose__text::placeholder { color: var(--ink-soft); }
.compose__text:focus { outline: none; }
.compose__foot {
  border-top: 1px solid var(--line);
  margin-top: .6rem; padding-top: .6rem;
  display: flex; flex-direction: column; gap: .45rem;
}
/* Title row of the capture card: identity left, delete affordance right. */
.compose__head { display: flex; align-items: flex-start; justify-content: space-between; gap: .6rem; }
/* Delete affordance: the idle [delete] and the [keep][confirm] step are stacked in
   ONE grid cell (sized to the wider confirm state) and toggled by visibility, not
   display — so the swap never reflows the title row. */
.compose__danger { display: inline-grid; justify-items: end; align-items: center; }
.compose__danger > * { grid-area: 1 / 1; }
.compose__confirm { display: inline-flex; align-items: center; gap: .5rem; visibility: hidden; }
.compose__danger.is-confirming .compose__ask { visibility: hidden; }
.compose__danger.is-confirming .compose__confirm { visibility: visible; }
.compose__delete, .compose__keep {
  border: none; background: none; cursor: pointer; padding: .15rem;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .56rem; letter-spacing: .14em; text-transform: uppercase;
}
/* The first step is a trashcan icon; the confirm/keep step stays textual. */
.compose__ask { display: inline-flex; align-items: center; padding: .05rem .2rem; font-size: .95rem; }
.compose__delete { color: #B23A2E; }
.compose__keep { color: var(--ink-soft); }

/* ---- Wellbeing pebbles ---- */
.pebbles { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.pebble {
  min-height: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink-soft);
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .64rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.pebble__ico { font-size: 1.05rem; line-height: 1; letter-spacing: 0; }
.pebbles[data-v="low"]    .pebble[data-opt="low"],
.pebbles[data-v="medium"] .pebble[data-opt="medium"],
.pebbles[data-v="high"]   .pebble[data-opt="high"] {
  /* selected = the focused-card reading (dark frame, ink type) — a filled slab
     would masquerade as a button (owner 2026-07-12) */
  border-color: var(--ink); color: var(--ink);
}

/* (journal cards live in the Card section above) */

/* ---- Empty state: hatched + registration mark ---- */
.empty {
  flex: 1;   /* fill the available space when the parent is a flex column (feed / grown section) */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: 3rem 1rem;
  border: 1px solid var(--line);
  background-image: repeating-linear-gradient(
    45deg, transparent, transparent 7px, var(--hatch) 7px, var(--hatch) 8px
  );
}
.empty .regmark { color: var(--mark); display: block; margin: 0 auto .9rem; }
.empty__line {
  font-family: "Cormorant", Georgia, serif;
  font-style: italic;
  font-size: 1.4rem;
  color: var(--ink-soft);
  margin: 0;
}

/* ---- Bottom nav ---- */
.nav { display: flex; }
.nav__item--btn { background: none; border: none; font: inherit; cursor: pointer; }

/* ---- Drawer (bottom sheet — continuation of the footer menu) ---- */
[x-cloak] { display: none !important; }
.drawer {
  position: absolute;          /* scoped to the .app panel — rises from the nav bar on every viewport */
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  pointer-events: none;              /* transparent shell; only backdrop + sheet are interactive */
}
/* Lens-defocus: the backdrop blurs UP like a camera losing focus (from sss-platform),
   driven by transitions so it can LEAD the sheet in and TRAIL it out. */
.drawer__backdrop {
  position: fixed;             /* full-viewport blur (the sheet stays panel-anchored) */
  inset: 0;
  pointer-events: auto;
  /* milk glass (spec 04, app-wide overlay standard): white frost — only the
     sheet stays sharp; the page dissolves upward instead of dimming down */
  background: rgba(255,255,255,.45);
  -webkit-backdrop-filter: blur(7px);
          backdrop-filter: blur(7px);
}
.gb-enter { transition: backdrop-filter .22s ease, -webkit-backdrop-filter .22s ease, background .22s ease; }
.gb-leave { transition: backdrop-filter .2s ease .12s, -webkit-backdrop-filter .2s ease .12s, background .2s ease .12s; }
.gb--clear { -webkit-backdrop-filter: blur(0); backdrop-filter: blur(0); background: rgba(255,255,255,0); }
.gb--blur  { -webkit-backdrop-filter: blur(7px); backdrop-filter: blur(7px); background: rgba(255,255,255,.45); }

.drawer__sheet {
  position: relative;
  pointer-events: auto;
  width: 100%;
  max-width: 480px;
  background: var(--surface);
  margin: 0 .85rem max(.7rem, env(safe-area-inset-bottom));
  padding: 1rem 1.05rem 1.1rem;
  border: 1px solid var(--line);
}
/* Slide-up; on enter it waits for the blur to lead (delay), on leave it goes first. */
.sheet-enter { transition: transform .26s cubic-bezier(.22,.61,.36,1) .12s, opacity .22s ease .12s; }
.sheet-leave { transition: transform .22s cubic-bezier(.4,0,.7,.3), opacity .2s ease; }
.sheet--down { transform: translateY(100%); opacity: 0; }
.sheet--up   { transform: translateY(0);    opacity: 1; }

.drawer__top { display: flex; align-items: flex-start; justify-content: space-between; gap: .6rem; margin-bottom: .9rem; }
/* brand header: the wordmark holds the true center; the ✕ floats right */
.drawer__top--brand { position: relative; align-items: center; justify-content: center; min-height: 2.3rem; }
.drawer__top--brand .drawer__close { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
.drawer__brand {
  font-family: "Cormorant Garamond", "Cormorant", Georgia, serif;
  font-weight: 600; font-size: 1.15rem; letter-spacing: .22em; color: var(--ink);
  padding-left: .22em;  /* optical centering: offsets the trailing letter-space */
}
/* identity beside the door: account block + Sign out, above the legal trio */
.drawer__exit { border-top: 1px solid var(--line); margin-top: .55rem; padding-top: .35rem; }
.drawer__meta { color: var(--ink-soft); font-weight: 400; }
.drawer__close {
  flex: none; display: inline-flex; align-items: center;
  border: none; background: none; cursor: pointer; padding: .1rem .2rem;
  font-size: 1.1rem; color: var(--ink-soft); line-height: 1;
}
.drawer__close:hover, .drawer__close:active { color: var(--ink); }

.drawer__list { display: flex; flex-direction: column; }
.drawer__item {
  display: flex;
  align-items: center;
  gap: .7rem;
  width: 100%;
  text-align: left;
  padding: .9rem .15rem;
  border: none;
  background: none;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
}
.drawer__item--disabled { color: var(--ink-soft); opacity: .55; pointer-events: none; }
.drawer__ico { font-size: 1rem; line-height: 1; color: var(--ink-soft); }

/* Session sheet — two full-width exits stacked: a ghost "to summary" (safe) over a
   solid "discard" (the prominent intent). Discard reads destructive by icon + label,
   not colour: red stays reserved for live/record. */
/* Per-button centred content (icons across the two buttons deliberately do NOT
   align — a shared-column variant read as off-centre; tried + reverted 2026-06-11). */
.sheet-acts { display: flex; flex-direction: column; gap: .5rem; }
.sheet-act {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  width: 100%; min-height: 52px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: transparent; color: var(--ink);
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  text-decoration: none; cursor: pointer;
}
.sheet-act:active { background: var(--hatch); }
.sheet-act--disabled { opacity: .35; pointer-events: none; }   /* "you are already here" */
.sheet-act--solid { background: var(--monolith); border-color: var(--monolith); color: #fff; }
.sheet-act--solid:active { opacity: .85; background: var(--monolith); }
.nav__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .22rem;
  padding: .32rem 0;
  color: var(--ink-soft);
  text-decoration: none;
}
.nav__item .ico { font-size: 1.25rem; line-height: 1; }
.nav__item span {
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: .54rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.nav__item--active { color: var(--ink); background: var(--surface); }
.nav__item--disabled { opacity: .35; pointer-events: none; }

/* ---- Inline notice ---- */
.text-error {
  font-family: "Space Mono", ui-monospace, monospace;
  color: #B23A2E;
  font-size: .72rem;
  letter-spacing: .04em;
  margin: 0;
}

/* ---- Touch-target sweep (#64): ≥44px effective hit areas, zero visual change ----
   Every rule here grows a control's HIT box only: a transparent, absolutely-positioned
   pseudo-element hit-tests as its originating element — no layout, no paint, and the
   :active/hover paints keep their original box, so rendering stays pixel-identical.
   (min-height/padding are deliberately avoided: they reflow or repaint.) Insets are
   px-tuned against border boxes measured headless at 390×844; the arithmetic per rule
   is `visual size + extensions = hit size`.
   SIGNAGE(touch-hit): extensions must stay DIRECTIONAL — dead whitespace only, never
   over a neighbouring control's visual box. Hit-testing follows paint order (later DOM
   paints on top), so an overlapping extension silently STEALS the neighbour's taps.
   Corollary for DESTRUCTIVE controls: they must not gain hit area over a neighbouring
   INPUT either — where dead space runs out, an under-44 destructive control is the
   accepted trade (see the [confirm] carve-out below). When a control moves or gains
   neighbours, re-check its insets here. */

/* Controls that were still statically positioned get a positioning context for their
   hit pseudo. All are leaf controls; relative changes nothing visually for them. */
.card__toggle, .feed__more, .drawer__close, .legal__link, .compose__ask,
.compose__keep, .compose__delete,
.favtoggle__btn, .run-extitle__look { position: relative; }

/* Header/canvas live dot (33.6×33.6 → 44×44): a centred fixed square — the disc is
   sized in rem, so a symmetric inset can't hit 44 exactly. Requires the un-clipped
   .livedot above. Covers the idle span (inert), the header link, and the ✕ variants. */
.livedot::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}
/* Segmented tabs (116×34 → 116×44): vertical only — below sits Start Workout at a
   5.6px gap (training) resp. the panel padding (journal). */
.seg::before { content: ""; position: absolute; inset: -5px 0; }
/* Card [more]/[less] toggle (53.9×13 → 53.9×44): biased upward into the card's own
   text; below is card padding + feed gap (14.4px dead). */
.card__toggle::before { content: ""; position: absolute; inset: -21px 0 -10px; }
/* [load more] pager (122.7×29 → 122.7×45): card gap above, scroll tail below. */
.feed__more::before { content: ""; position: absolute; inset: -8px 0; }
/* Sheet close ✕, nav drawer + session sheet (24×20.8 → 44×44.8): sheet padding above/
   right, the .9rem head margin below, identity text far left. */
.drawer__close::before { content: ""; position: absolute; inset: -12px -10px; }
/* Legal trio inside the drawer sheet (narrowest: Terms ~31×12 → ~47×44): the .9rem
   margin above (Sign out ends there — do not extend further) and the sheet's bottom
   padding below. Horizontally ±8px into the dot separators (inert): adjacent link
   boxes sit ~21.6px apart (two .55rem gaps + the dot), so two 8px zones never meet,
   let alone reach a neighbour's box. Elsewhere (login) the same sideways bump but
   only a token vertical one: the row sits directly under the Sign in slab, and a
   full vertical extension would steal its taps — login is outside #64's scope. */
.legal__link::before { content: ""; position: absolute; inset: -6px -8px; }
.legal--drawer .legal__link::before { inset: -14px -8px -18px; }
/* Compose trashcan on the journal edit card (21.6×16.8 → ~46×44.5): biased upward
   past the card edge (the scrollport clips ~17px up — hit-testing stops at the
   .app__scroll edge, which is why every upward figure here is measured, not ideal);
   below, ~4px grazes the textarea's top-right corner — ask is safe/reversible. */
.compose__ask::before { content: ""; position: absolute; inset: -18px -12px -10px; }
/* Delete confirm step, [keep] 58.7 / [confirm] 78.9 wide × 17.8 tall: vertical
   only — NO horizontal extension (the pair sits .5rem apart; a sideways hit zone
   would let [confirm] swallow [keep], a destructive mis-tap). Upward both run to
   the scrollport ceiling (~17px). Downward they differ: [keep] (harmless) grazes
   the textarea ~4px to reach ~45 tall; the DESTRUCTIVE [confirm] stops at the
   textarea's edge (-5px = head-row slack + card gap only) and lands at ~40px —
   a deliberate carve-out (#71 review): a destructive control must never gain hit
   area over a neighbouring input; under-size beats overlap. */
.compose__keep::before { content: ""; position: absolute; inset: -20px 0 -10px; }
.compose__confirm .compose__delete::before { content: ""; position: absolute; inset: -20px 0 -5px; }
/* The same cluster on the workout-log page lives in a .sec-head at the very TOP of a
   sectioned scroll area: only 7px exist above before the scrollport clips, so these
   flip the bias downward — over the sec-head margin and the inert loghead text line
   (no input lives below there, so [confirm] keeps the full zone and reaches 44.8). */
.sec-head .compose__ask::before { inset: -7px -7px -21px -16px; }
.sec-head .compose__keep::before,
.sec-head .compose__confirm .compose__delete::before { inset: -7px 0 -20px; }
/* Favourite star, workout detail (24.8×20 → 44.8×44): pinned to the section's
   top-right corner, so up/right stop at the 7px section padding (scrollport clip);
   the mass goes left over the inert badges and down over the tagbar margin + prose. */
.favtoggle__btn::before { content: ""; position: absolute; inset: -7px -7px -17px -13px; }
/* Grouped/sequence switch (66.6×26 → 66.6×44): vertical only — prose above, the
   switch's own 1rem bottom margin below. */
.vswitch__opt::before { content: ""; position: absolute; inset: -9px 0; }
/* Exercise magnifier beside the run-canvas title (20×20 → 44×44): title glyphs and
   stage air on all sides; the duo boxes start well below. */
.run-extitle__look::before { content: ""; position: absolute; inset: -12px; }
