/* ============================================================
   session-shared.css
   css/components/session-shared.css
   04 Oct 2026 v13

   v13 - D-3 ARC-HOME. The arc screen in the LOOK cards: .sa-title,
   .sa-meta, .sa-card (head, label), .sa-know, and the Change your arc
   rows (.sa-change: tile, title and line, chevron; 56px tall, focus
   ring, a divider between rows).

   v12 - STRETCH-WHY. .ys-pose-why, one line under a pose name saying
   why it is in today's session. Notes at the foot of this file.

   v11

   v11 - STRETCH-FOCUS. .cs-target-grid, .cs-target-card and
   .cs-focus-question: the "what's this for today?" row above the
   practice-style cards. Notes at the foot of this file.

   16 Sep 2026 v11

   v11 - SAVE-ALL. .save-block and its parts: "Keep this one?" on the
   reflection screen, the one place every session ends. Notes at the foot
   of this file.

   15 Sep 2026 v10

   v10 - SAFETY-GATE. .safety-gate and its parts: the before-you-start
   dialog, read once per occasion and acknowledged deliberately. Same
   rose grammar as .xcard-block--hazard. Notes at the foot of this file.

   12 Sep 2026 v9

   v9 - SEARCH-1b. .sb-swap-search and its label, input and status line.
        The status is a role="status" region and is ALWAYS in the DOM,
        empty when there is nothing to say -- a live region inserted at
        the moment it gains text is frequently not announced at all.

   08 Sep 2026 v8

   v8 - A11Y-HEADER. .workout-header-title is a real heading now, in all
        11 views that use it -- it was a <span> in every one and a heading
        in none, while the rest of the app uses 48 h1 and 67 h2. It is the
        screen's title, styled as one and read as one, and it existed in
        presentation only. WCAG 2.2 AA 1.3.1.

        margin: 0 is load-bearing: an h1 carries a UA default of 0.67em
        top and bottom and a span carries none, so the element change
        alone would have pushed all 42 of these headers out of shape
        inside their flex rows.

   05 Sep 2026 v7

   v7 - SWAP-1. The swap sheet, and the sore mark inside it.

        CONTRAST, MEASURED NOT ASSUMED. --color-danger #FB7185 on
        --color-bg-card #334155 is 3.85:1. That PASSES WCAG 2.2 AA
        1.4.11 for a non-text boundary (3:1) and FAILS 1.4.3 for text
        (4.5:1). So the ring is rose and the words underneath are
        --color-text, never rose. Red text on this card would have been
        an accessibility failure dressed as emphasis.

        Nothing depends on colour alone: every marked option carries its
        reason in words, wired with aria-describedby.

        --color-bg-card is used deliberately rather than the
        --color-surface these files reach for elsewhere. THAT TOKEN IS
        NOT DEFINED ANYWHERE -- 34 references with no fallback across
        nine files, the same fault --color-bg-elevated had in August, so
        every one of those surfaces is currently rendering transparent.
        Logged as SURFACE-TOKEN and NOT fixed here; touch-once. But a
        contrast claim measured against an undefined colour would be a
        claim about nothing, so this block does not make one.

   31 Aug 2026 v6

   v6 - BYPASS-RED. .sb-severe-banner. The Gentle Care card already
        explained itself in the coach's prose, and prose is what people
        skim once they have read a few of them. Somebody who asked for
        mostly-mobility, skimmed the paragraph and got breathing exercises
        concludes the coach got it wrong rather than that it was
        listening.

        Rose, matching .xcard-block--hazard, so there is ONE visual
        grammar for "this is not ordinary text" rather than a new
        treatment per surface. The body text keeps the normal colour for
        the same reason it does there: legibility must not depend on the
        accent being readable. --color-danger carries a light-scheme
        override, added 31 Aug.

        role="note", not role="alert". Nothing has gone wrong and nothing
        is interrupting -- the session was changed deliberately and the
        person is reading about it in their own time. An alert would
        announce over whatever they were doing and would frame a careful
        decision as an emergency.

   15 Aug 2026 v5

   v5 - SHARED-1. .sm-* replaces .cs-first-session / .cs-baseline /
        .cs-pacing. The moments are no longer core-session's.

   v4 - ASSESS-1. .cs-baseline, the calibration block. Must not read as a
        form: no progress dots, no numbering, no required markers.

   v3 - PACE-1. .cs-pacing, the daily pacing block.
   v2 - DELIGHT-1. .cs-first-session, the recognition block on a first
        completed session. (The v2 change was made without bumping this
        header; recorded here rather than silently.)
   v1 - 12 Aug 2026.

   The .cs-*, .gym-* and .workout-header-title families — used across
   core-session, yoga-session, gym-programme and others, and never
   defined anywhere.

   Found in the device pass. Graeme, part 4: "I also found lots of bad
   styling." His screenshots show the Yoga & Pilates practice picker, the
   Core Session picker and both duration pickers rendering as plain
   centred text — no cards, no structure, just sentences run together.

   Same fault as the .ws-* family fixed earlier: a class with no rule is
   valid markup that renders as a draft. `.workout-header-title` alone is
   used 41 times, the largest single gap in the codebase, and is why
   several screens show the title colliding with the home icon.

   Styled to patterns already in the product rather than inventing
   another: choice cards follow .ci-choice's recessed treatment, exercise
   cards follow .card. Contrast verified in all three schemes by
   tools/contrast-check.mjs.
   ============================================================ */

/* ── Session header ──────────────────────────────────────────────────
   Used by nearly every session view. Its absence is why the title and
   the home icon overlap on several screens: the title had no width
   constraint and ran under the fixed icon. */

.workout-header-title {
  /* A11Y-HEADER, 08 Sep 2026. This was a <span> in 11 views and a heading
     in none, while the rest of the app uses 48 h1 and 67 h2. It is the
     screen's title, styled as one and read as one -- and it existed in
     presentation only, so nobody navigating by heading could find it.
     WCAG 2.2 AA 1.3.1.

     margin: 0 is load-bearing. An h1 carries a UA default of 0.67em top
     and bottom; a span carries none, so the element change alone would
     have pushed every one of these headers out of shape inside its flex
     row. Nothing moves visually. */
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;                    /* lets it shrink rather than overflow */
  padding-right: var(--space-6);   /* clears the fixed home icon */
  font-size: var(--text-lg);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  text-align: right;
  line-height: var(--leading-normal);
  overflow-wrap: break-word;
}

/* ── Focus and duration pickers (.cs-*) ──────────────────────────────
   Recessed, not elevated: text on --color-bg clears AA in all three
   schemes where an elevated surface does not. Same reasoning as
   .ci-choice. */

.cs-focus-grid,
.cs-duration-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.cs-focus-card,
.cs-duration-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
  padding: var(--space-4);
  min-height: 64px;
  background: var(--color-bg);
  border: 1px solid var(--color-text-muted);
  border-radius: var(--radius-md);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition-fast);
}

.cs-focus-card:hover,
.cs-duration-card:hover { border-color: var(--color-primary); }

.cs-focus-card:focus-visible,
.cs-duration-card:focus-visible {
  outline: 3px solid var(--color-border-focus);
  outline-offset: 2px;
}

.cs-focus-card.is-selected,
.cs-duration-card.is-selected {
  border: 2px solid var(--color-primary);
  padding: calc(var(--space-4) - 1px);
}

.cs-focus-icon {
  font-size: var(--text-2xl);
  line-height: 1;
  margin-bottom: var(--space-1);
}

.cs-focus-label,
.cs-duration-label {
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  line-height: var(--leading-normal);
}

.cs-focus-desc,
.cs-duration-desc {
  font-size: var(--text-sm);
  font-weight: var(--font-normal);
  color: var(--color-text-secondary);
  line-height: var(--leading-relaxed);
}

/* The "5 poses" / "4 exercises" count. Secondary to the label it sits
   beside, never a headline — this is a fact, not a target. */
.cs-duration-count {
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.cs-why-details {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  line-height: var(--leading-relaxed);
}

/* ── Exercise list on session overviews (.gym-*) ─────────────────── */

.gym-exercises-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.gym-exercise-card {
  padding: var(--space-4);
  background: var(--color-bg-card);
  border-radius: var(--radius-md);
}

.gym-exercise-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}

.gym-exercise-header-left {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.gym-exercise-name {
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-text);
  line-height: var(--leading-normal);
}

.gym-card-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.gym-card-chevron {
  flex-shrink: 0;
  color: var(--color-primary);
  font-size: var(--text-lg);
  line-height: 1;
}

.gym-exercise-detail {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  line-height: var(--leading-relaxed);
}

.core-overview-badge {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  background: var(--color-bg);
  border: 1px solid var(--color-text-muted);
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}

/* ── Coaching cues ──────────────────────────────────────────────────
   .exercise-cues (the list) already exists; the individual item did
   not, so cues rendered as unstyled list items. */

.exercise-cue {
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  line-height: var(--leading-relaxed);
  margin-bottom: var(--space-1);
}

.exercise-why-label {
  display: block;
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--space-1);
}

/* ── View containers ────────────────────────────────────────────── */

.yoga-session-view,
.walk-session-view,
.session-builder-view {
  padding: var(--space-4);
  padding-bottom: var(--space-8);
}

@media (prefers-reduced-motion: reduce) {
  .cs-focus-card,
  .cs-duration-card { transition: none; }
}

/* ── First-session recognition (DELIGHT-1, 15 Aug 2026) ───────────────────
   Sits ABOVE the usual done card, as its own quiet block. Deliberately
   not decorated: no badge, no confetti, no colour change. Somebody who
   told us that every previous attempt escalated until it broke is exactly
   the person who reads celebration as the start of pressure.

   The separation between the two cards is doing the work — the
   recognition is a different kind of statement from "8 exercises, 20
   minutes", and it should not look like a subheading of it. */

.cs-first-session {
  text-align: left;
}

.cs-first-session__heading {
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}

/* ── Proactive pacing (PACE-1, 15 Aug 2026) ──────────────────────────────
   Same undecorated treatment as the first-session block, and for the same
   reason in reverse: this must not read as a warning. No amber, no icon,
   no border colour. It is the coach saying one thing, in the same voice
   it says everything else. */

.cs-pacing {
  text-align: left;
}

.cs-pacing__heading {
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}

/* ── Baseline calibration (ASSESS-1, 15 Aug 2026) ─────────────────────────
   Same undecorated card as the first-session and pacing blocks. This one
   especially must not look like a form or a quiz: no progress dots, no
   numbering, no required markers. Three questions in the coach's own
   card, in the coach's own voice.

   Skip sits beside Done at the same weight, because a skip that looks
   like the lesser option is not really an option. */

.cs-baseline {
  text-align: left;
}

.cs-baseline__heading {
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}

.cs-baseline__group {
  border: 0;
  margin: var(--space-4) 0 0;
  padding: 0;
  min-inline-size: 0;
}

.cs-baseline__legend {
  padding: 0;
  margin-bottom: var(--space-2);
}

.cs-baseline__chips,
.cs-baseline__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.cs-baseline__actions {
  margin-top: var(--space-4);
}

/* ── Shared session moments (SHARED-1, 15 Aug 2026) ───────────────────────
   Renamed from .cs-* because these no longer belong to core-session — they
   render in reflect.js for every session view, and in the two wellbeing
   views that do not route there. Same undecorated treatment as before. */

.sm-first, .sm-baseline, .sm-pacing { text-align: left; }

.sm-first__heading,
.sm-baseline__heading,
.sm-pacing__heading {
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}

.sm-baseline__group {
  border: 0;
  margin: var(--space-4) 0 0;
  padding: 0;
  min-inline-size: 0;
}

.sm-baseline__legend { padding: 0; margin-bottom: var(--space-2); }

.sm-baseline__chips,
.sm-baseline__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.sm-baseline__actions { margin-top: var(--space-4); }

/* ── BYPASS-RED ───────────────────────────────────────────────────────
   The severe-pain banner on the Gentle Care card. See the v6 note. */
.sb-severe-banner {
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  border-left: 4px solid var(--color-danger);
  border-radius: var(--radius-md, 8px);
  background: var(--color-danger-light);
}

.sb-severe-banner__head {
  margin: 0 0 var(--space-2) 0;
  font-weight: var(--font-bold, 700);
  font-size: var(--text-base);
  color: var(--color-danger);
}

.sb-severe-banner__body {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed, 1.6);
}

@media (prefers-contrast: more) {
  .sb-severe-banner { border-left-width: 6px; }
}

/* ── PICKER-GROUP ─────────────────────────────────────────────────────
   Section headings in the session-type picker. Quiet on purpose: they
   are orientation, not content, and must not compete with the tiles
   they introduce. */
.sb-group-heading {
  margin: 0 0 var(--space-2) 0;
  font-size: var(--text-xs, 0.75rem);
  font-weight: var(--font-bold, 700);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary, inherit);
  opacity: 0.75;
}

/* ── ZONE-1 ───────────────────────────────────────────────────────────
   Body-zone chips. Multi-select, so they must read as toggles rather
   than as a list you pick one from -- hence the filled selected state
   rather than a border change alone. */
.sb-zone-chip {
  min-height: 48px;                /* WCAG 2.2 target size */
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-xl, 999px);
  border: 2px solid var(--color-border, currentColor);
  background: var(--color-surface);
  color: inherit;
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}

.sb-zone-chip--on {
  border-color: var(--color-primary);
  background: var(--color-primary);
  color: var(--color-bg, #0b1622);
  font-weight: var(--font-bold, 700);
}

.sb-zone-chip:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ARC-1. What the arc has not reached yet. Quiet, and phrased about the
   plan rather than the person — so it must not look like a warning. */
.sb-zone-note {
  margin: 0 0 var(--space-4) 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed, 1.6);
  opacity: 0.75;
}

/* The coach speaking, not a card. Graeme, 2 Sep: teal writing, a little
   bigger, no border. A bordered box reads as a component; this is a
   voice, and it should sit in the page like one. */
.sb-coach-line {
  margin: 0 0 var(--space-5) 0;
  font-size: var(--text-lg, 1.125rem);
  line-height: var(--leading-relaxed, 1.6);
  color: var(--color-primary);
}

/* SORE-ZONE. Marked, never disabled — stretching a sore area is often
   the right call, and the exercises are condition-filtered underneath.
   The dot is decorative; the note below the chips carries the meaning,
   so nothing depends on colour alone. */
.sb-zone-chip--sore {
  border-color: var(--color-danger);
}

.sb-zone-chip--sore.sb-zone-chip--on {
  border-color: var(--color-danger);
  background: var(--color-primary);
}

/* ── ARC-2 ────────────────────────────────────────────────────────────
   The stretch arc screen. Deliberately plain: an arc is a direction, and
   a screen that looked like a dashboard would invite reading it as a
   scoreboard. */
.sa-body {
  margin: 0 0 var(--space-4) 0;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed, 1.6);
}

.sa-panel {
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  border-radius: var(--radius-md, 8px);
  background: var(--color-surface-raised, rgba(255, 255, 255, 0.04));
}

.sa-goal {
  margin: var(--space-1) 0 0 0;
  font-size: var(--text-lg, 1.125rem);
  font-weight: var(--font-bold, 700);
}

.sa-zones {
  margin: var(--space-1) 0 0 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed, 1.6);
}

.sa-note {
  margin: var(--space-2) 0 var(--space-4) 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed, 1.6);
  opacity: 0.75;
}

.sa-list {
  list-style: none;
  margin: var(--space-2) 0 0 0;
  padding: 0;
  font-size: var(--text-sm);
}

.sa-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) 0;
}

/* A date, never a count. See stretch-arc.js's header. */
.sa-when {
  opacity: 0.7;
  white-space: nowrap;
}

/* ── ARC-3-SETUP ──────────────────────────────────────────────────────
   One question per screen. The heading IS the question, so it carries
   the weight rather than a label above a form. */
.as-question {
  margin: 0 0 var(--space-2) 0;
  font-size: var(--text-2xl, 1.5rem);
  line-height: var(--leading-tight, 1.25);
}

.as-question:focus-visible {
  outline: none;   /* programmatic focus for screen readers, not a click target */
}

.as-help {
  margin: 0 0 var(--space-5) 0;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed, 1.6);
  opacity: 0.85;
}

.as-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.as-option {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.as-option--on {
  border-color: var(--color-primary);
}

/* Disabled here means "you have chosen your three", not "you may not
   have this". The cap is the reason, so it must not read as a lock. */
.as-option[disabled] {
  opacity: 0.55;
  cursor: default;
}

/* A mind strand says so plainly. Somebody choosing three things to work
   on should know one of them is not a movement before they pick it. */
.as-kind {
  display: block;
  margin-top: 2px;
  font-size: var(--text-sm);
  opacity: 0.7;
}

.as-count {
  margin: 0 0 var(--space-4) 0;
  font-size: var(--text-sm);
  opacity: 0.75;
}

.as-label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
}

.as-input {
  width: 100%;
  padding: var(--space-3);
  margin-bottom: var(--space-4);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  background: none;
  color: inherit;
  font: inherit;
  line-height: var(--leading-relaxed, 1.6);
}

.as-input:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
}

.as-summary {
  padding: var(--space-4);
  margin-bottom: var(--space-5);
  border: 2px solid var(--color-primary);
  border-radius: var(--radius-md, 8px);
}

.as-summary-aim {
  margin: 0;
  font-size: var(--text-lg, 1.125rem);
  font-weight: var(--font-bold, 700);
}

.as-summary-strands {
  margin: var(--space-2) 0 0 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed, 1.6);
}

.as-summary-marker {
  margin: var(--space-3) 0 0 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed, 1.6);
  opacity: 0.85;
}

/* ── SWAP-1 ───────────────────────────────────────────────────────────
   The preview row as an affordance, and the sheet behind it. */

/* A swappable row is a button. It must not look like one -- the preview
   is a list you read, not a form -- so the only change from the static
   row is the cue on the right and a border that appears on focus. */
.sb-exercise-item--swappable {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  min-height: 48px;                /* WCAG 2.2 AA 2.5.8 target size */
  text-align: left;
  font: inherit;
  color: inherit;
  background: none;
  border: 2px solid transparent;
  cursor: pointer;
}

.sb-exercise-item--swappable:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.sb-swap-cue {
  flex-shrink: 0;
  color: var(--color-primary);
  font-weight: var(--font-semibold, 600);
}

/* Prescribed work says why it cannot be changed, rather than simply
   lacking the affordance and leaving somebody to wonder. */
.sb-exercise-note {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-text-secondary);
}

.sb-swap-groups {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.sb-swap-count {
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}

.sb-swap-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.sb-swap-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  width: 100%;
  min-height: 48px;                /* WCAG 2.2 AA 2.5.8 -- kept even when unselectable */
  padding: var(--space-3) var(--space-4);
  text-align: left;
  font: inherit;
  color: inherit;
  background: var(--color-bg-card);
  border: 2px solid transparent;
  border-radius: var(--radius-md, 8px);
  cursor: pointer;
}

.sb-swap-option:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.sb-swap-option-name { font-weight: var(--font-semibold, 600); }

/* SORE-ZONE's grammar, carried across: a rose boundary means "the coach
   knows something about this", never "you may not". 3.85:1 on
   --color-bg-card, measured. */
.sb-swap-option--sore {
  border-color: var(--color-danger);
}

/* Unselectable at 7, where getActiveConditionIds already switches to
   acute-safe variants.

   aria-disabled, NEVER the disabled attribute -- so this styles the
   ARIA state, which is the thing that is actually true. Reduced opacity
   is applied to the NAME AND META ONLY, never to the reason: the one
   sentence explaining why it is unavailable is the last thing that
   should be hard to read. cursor stays default rather than
   not-allowed, which reads as a scolding. */
.sb-swap-option--blocked {
  cursor: default;
}

.sb-swap-option--blocked .sb-swap-option-name,
.sb-swap-option--blocked .sb-swap-option-meta {
  opacity: 0.6;
}

/* --color-text, not --color-danger. Rose text here would be 3.85:1 and
   would fail 1.4.3. The ring carries the colour; the words carry the
   meaning. */
.sb-swap-option-note {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-text);
  line-height: var(--leading-relaxed, 1.6);
  opacity: 1;
}

/* ============================================================
   SEARCH-1b - SEARCHING THE SWAP SHEET
   ============================================================ */

.sb-swap-search {
  margin-bottom: var(--space-3);
}

/* Visible, not a placeholder standing in for one. A placeholder vanishes
   the moment somebody types, which is when they most need to know what
   the box does. */
.sb-swap-search-label {
  display: block;
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.sb-swap-search-input {
  width: 100%;
  min-height: 48px;                /* WCAG 2.2 target size, as the chips */
  padding: var(--space-2) var(--space-3);
  border: 2px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  background: var(--color-bg-card);
  color: var(--color-text);
  font: inherit;
}

.sb-swap-search-input:focus-visible {
  outline: 3px solid var(--color-border-focus);
  outline-offset: 2px;
}

/* Empty most of the time, and that is deliberate: the element stays put
   so the region exists before it has anything to announce. */
.sb-swap-search-status:empty {
  display: none;
}

.sb-swap-search-status {
  margin: var(--space-2) 0;
  font-size: var(--text-sm);
  color: var(--color-text);
}

/* ============================================================
   SAFETY-GATE — the before-you-start dialog
   15 Sep 2026

   Read once per occasion rather than skimmed forty times, and
   acknowledged deliberately. The acknowledgement is evidence of
   notice and nothing more: under the Consumer Rights Act 2015
   s.65 no notice excludes liability for personal injury caused
   by negligence, which is why the label reads "I have read
   this" and never "I accept the risk".

   Same rose grammar as .xcard-block--hazard. One visual
   language for "this is not ordinary text", not a new
   treatment per surface.
   ============================================================ */

.safety-gate {
  max-width: 34rem;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3) var(--space-6);
}

.gate-kicker {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* tabindex="-1" for programmatic focus on mount. The outline is
   suppressed only for that case — a heading is not a control
   and a ring on it reads as an error. Keyboard focus of real
   controls below is unaffected. */
.gate-title {
  margin: 0 0 var(--space-4);
  font-size: var(--text-2xl);
}
.gate-title:focus { outline: none; }

.gate-block--hazard {
  padding: var(--space-3);
  border-left: 4px solid var(--color-danger);
  border-radius: var(--radius-md, 8px);
  background: var(--color-danger-light);
}

.gate-block-label {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--font-semibold);
  letter-spacing: 0.04em;
  color: var(--color-danger);
}

.gate-lines { margin: 0; padding-left: var(--space-4); }
.gate-lines li + li { margin-top: var(--space-3); }

.gate-guidance {
  margin: var(--space-4) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* The label is the target, not the 22px box — 2.5.8. */
.gate-ack {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  margin: var(--space-5) 0 var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  cursor: pointer;
}

.gate-ack input[type="checkbox"] {
  flex: none;
  width: 24px;
  height: 24px;
  margin: 0;
  accent-color: var(--color-primary);

  /* GATE-BOX, 16 Sep 2026. The cause was in css/base/reset.css, which
     stripped `appearance` from every input and so deleted the box
     itself. Fixed there, for all four checkboxes in the app. This
     declaration is kept as a local guarantee for the one control where
     an invisible box is a safety failure rather than an annoyance. */
  -webkit-appearance: checkbox;
  appearance: checkbox;
  border-radius: 4px;
}

/* Belt and braces: if a browser still refuses to paint the native box,
   the label row shows the state in a second channel. Not decoration --
   1.4.1, the state must not be carried by one mechanism alone. */
.gate-ack::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-md, 8px);
  pointer-events: none;
}

.gate-ack:has(input:checked) {
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 12%, transparent);
}

.gate-ack:has(input:checked) span::after {
  content: " \2713";
  color: var(--color-primary);
  font-weight: var(--font-semibold);
}

.gate-ack:focus-within {
  outline: 3px solid var(--color-focus, var(--color-primary));
  outline-offset: 2px;
}

/* aria-live region. Always in the DOM so the announcement is
   the text changing, not the element appearing — a region
   inserted at the same moment it is populated is unreliably
   announced. min-height reserves the space so nothing shifts
   under a thumb when the message arrives. */
.gate-error {
  min-height: 1.5rem;
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-danger);
}

/* NOT a fixed bottom bar. 2.4.11 Focus Not Obscured: the
   session views' own sticky action bars are exactly the hazard
   this avoids — a focused checkbox must not sit behind one. */
.gate-start { width: 100%; }

.gate-leave {
  display: block;
  width: 100%;
  min-height: 44px;
  margin-top: var(--space-3);
  background: none;
  border: none;
  font-family: inherit;
  font-size: var(--text-base);
  color: var(--color-primary);
  cursor: pointer;
}

.gate-leave:focus-visible {
  outline: 3px solid var(--color-focus, var(--color-primary));
  outline-offset: 2px;
  border-radius: var(--radius-sm, 4px);
}

@media (prefers-contrast: more) {
  .gate-block--hazard { border-left-width: 6px; }
  .gate-ack { border-width: 2px; }
}

/* ============================================================
   STRETCH-FOCUS — the target row
   16 Sep 2026

   "What am I doing stretching for? Is there an area of the body
   I'm doing stretching for?" The style cards below this row ask
   what KIND of practice; this asks WHERE, which is the question
   people actually arrive with.

   A horizontal row, not another stacked column. It sits above
   four full-width style cards and must read as a different
   question rather than as four more of the same — and four
   short labels fit a phone across.
   ============================================================ */

.cs-focus-question {
  margin: 0 0 var(--space-2);
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-text);
}

.cs-target-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
}

.cs-target-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  /* 2.5.8 Target Size (Minimum). */
  min-height: 64px;
  padding: var(--space-3) var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--color-text);
  text-align: center;
  cursor: pointer;
}

/* 1.4.1 Use of Colour. Selected is carried by border WEIGHT and
   text weight as well as colour, and aria-pressed carries it to
   anyone not looking at the screen at all. */
.cs-target-card.is-selected {
  border-width: 2px;
  border-color: var(--color-primary);
  font-weight: var(--font-semibold);
  color: var(--color-primary);
}

.cs-target-card:focus-visible {
  outline: 3px solid var(--color-focus, var(--color-primary));
  outline-offset: 2px;
}

@media (prefers-contrast: more) {
  .cs-target-card.is-selected { border-width: 3px; }
}

/* SAVE-IN-MOMENT, 16 Sep 2026. Offered at the END of a session, never
   before it: the ones worth keeping are the ones that turned out well. */
.ys-save-block {
  margin-top: var(--space-6);
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
}

.ys-save-name {
  width: 100%;
  min-height: 48px;
  padding: var(--space-3);
  margin-bottom: var(--space-2);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 4px);
  font-family: inherit;
  font-size: var(--text-base);
  color: var(--color-text);
}

.ys-save-name:focus-visible {
  outline: 3px solid var(--color-focus, var(--color-primary));
  outline-offset: 2px;
}

/* Always in the DOM so the live region announces a CHANGE rather than
   an insertion, and the space is reserved so nothing shifts on save. */
.ys-save-status {
  min-height: 1.5rem;
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* ============================================================
   SAVE-ALL — "Keep this one?" on the reflection screen
   16 Sep 2026

   One block, on the one screen every session ends on. Quiet by
   design: it sits between the reflection and Done, and must not
   compete with either. Somebody who ignores it loses nothing.
   ============================================================ */

.save-block {
  margin-top: var(--space-6);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  text-align: left;
}

.save-block-title {
  margin: 0 0 var(--space-1);
  font-size: var(--text-base);
  font-weight: var(--font-semibold);
  color: var(--color-text);
}

.save-block-sub { margin: 0 0 var(--space-3); }

.save-block-name {
  width: 100%;
  min-height: 48px;
  margin-bottom: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: inherit;
  font-size: var(--text-base);
}

.save-block-name:focus-visible {
  outline: 3px solid var(--color-focus, var(--color-primary));
  outline-offset: 2px;
}

/* aria-live region. Always present so the announcement is the text
   changing rather than the element appearing, and space is reserved
   so nothing shifts under a thumb when the message arrives. */
.save-block-status {
  min-height: 1.5rem;
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* Disabled here means "already saved", which is a success state and
   should not read as a refusal. */
.save-block button[disabled] {
  opacity: 1;
  border-color: var(--color-primary);
  color: var(--color-primary);
  cursor: default;
}

@media (prefers-contrast: more) {
  .save-block,
  .save-block-name { border-width: 2px; }
}

/* STRETCH-WHY, 16 Sep 2026. One line, under the pose name, saying why
   this one is here today. Quiet: it explains, it does not instruct, and
   it is absent far more often than present. */
.ys-pose-why {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-text-muted);
  border-left: 3px solid var(--color-primary);
  padding-left: var(--space-3);
  text-align: left;
}

/* D-3 ARC-HOME, 04 Oct 2026 -- the arc screen in the LOOK cards. */
.sa-title { margin: var(--space-2) 0 0; font-size: var(--text-2xl); font-weight: 700; color: var(--color-text); }
.sa-title:focus { outline: none; }
.sa-title:focus-visible { outline: 3px solid var(--color-border-focus); outline-offset: 2px; }
.sa-meta { margin: var(--space-1) 0 var(--space-4); font-size: var(--text-sm); color: var(--color-text-secondary); }
.sa-card { padding: var(--space-4); margin-bottom: var(--space-3); }
.sa-card__head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.sa-card__label { margin: 0 0 var(--space-2); font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-text-secondary); }
.sa-card__head .sa-card__label { margin: 0; }
.sa-card .sa-goal { margin: 0; font-size: var(--text-lg); font-weight: 600; }
.sa-know { margin: var(--space-3) 0 0; line-height: 1.5; color: var(--color-text); }
.sa-know__label { font-weight: 600; }
.sa-changes { margin-top: var(--space-4); }
.sa-change-list { list-style: none; margin: 0 0 var(--space-3); padding: 0; overflow: hidden; }
.sa-change-list li + li { border-top: 1px solid var(--color-border-light); }
.sa-change {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 56px;
  padding: var(--space-3) var(--space-4);
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  color: var(--color-text);
  cursor: pointer;
}
.sa-change:focus-visible { outline: 3px solid var(--color-border-focus); outline-offset: -3px; }
.sa-change__text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.sa-change__title { font-weight: 600; }
.sa-change__line { font-size: var(--text-sm); color: var(--color-text-secondary); }
.sa-change__chev { color: var(--color-text-secondary); display: inline-flex; }
