/* ══════════════════════════════════════════════════════════════════════════
   BARRE — css/a11y.css
   Accessibility corrections. Self-contained; NOT yet wired into index.html.

   WHERE TO IMPORT IT
     index.html, as the LAST stylesheet — after css/motion.css:

         <link rel="stylesheet" href="css/motion.css">
         <link rel="stylesheet" href="css/a11y.css">   <!-- add this line -->

     It must load last for two reasons: the token overrides in §1 have to beat
     the definitions in tokens.css, and the focus rules in §2 have to beat the
     single `:focus-visible` rule in base.css. It also needs adding to the
     precache list in sw.js.

   WHAT IT DOES NOT DO
     Everything here is presentational. It cannot fix the four defects that are
     structural (the `aria-*=""` false states, the checkbox nested inside a
     button, focus loss on navigation, the missing live region on the quiz
     explainer). Those are described in specs/09-accessibility.md §2 and must be
     patched in JS.

   MEASUREMENTS
     Every ratio in the comments below was computed against the real rendered
     colours at 375 x 812, DPR 2, from the app served on :8094. See
     specs/09-accessibility.md §3 for the full table.
   ══════════════════════════════════════════════════════════════════════ */


/* ── 1. Text colour tokens ────────────────────────────────────────────────
   The five muted pinks below all sit under 3:1 on the paper ground #FBF2F0,
   at 9–13px. There is not enough luminance range between --text-muted (5.00:1)
   and the 4.5:1 AA floor to keep five distinguishable steps, so the scale
   collapses to two accessible values and the hierarchy is carried by size,
   weight and letter-spacing instead — which components.css already varies.

   New values keep the original hue and only lose lightness.
   ────────────────────────────────────────────────────────────────────── */
:root {
  /* was #7A6270 — 5.00 on paper. Deepened so it stays clearly above --a11y-faint. */
  --text-muted:        #6E5866;   /* 5.86 paper · 6.46 card · 5.01 blush · 5.32 seg  */

  /* One faint value replaces four. All were 1.89–2.85 on paper. */
  --text-faint:        #7B6B70;   /* was #A98A96 · 2.82 → 4.56 paper, 5.02 card      */
  --text-kicker:       #7B6B70;   /* was #B0919C · 2.58 → 4.56                       */
  --text-label:        #7B6B70;   /* was #BCA2AB · 2.14 → 4.56                       */
  --text-hint:         #7B6B70;   /* was #C7ADB5 · 1.89 → 4.56                       */
  --text-tab-inactive: #7B6B70;   /* was #C0A7AF · 2.03 → 4.56                       */

  /* Semantic micro-labels: keep the hue, clear the floor. */
  --text-mistake:      #8B6572;   /* was #C48EA0 · 2.72 → 4.54 card                  */
  --success-soft:      #597663;   /* was #7FA98D · 2.64 → 4.54 card ("The fix")      */
  --text-disabled:     #786D6F;   /* was #D6C3C7 · 1.53 → 4.52 (empty grid numeral)  */

  /* Coloured foregrounds on their own tinted chip grounds. All four sat in the
     4.0–4.4 band — close, but under AA at 9–12px. Small nudges, same hue. */
  --status-none-fg:      #6E5866; /* was #A98A96 on #F1E7E5 · 2.56 → 5.32            */
  --status-learning-fg:  #A24765; /* was #B4506E on #FBE3E7 · 4.00 → 4.76            */
  --status-ready-fg:     #7C5F35; /* was #8A6A3C on #F6EAD6 · 4.20 → 4.98            */
  --status-performed-fg: #476E55; /* was #4F7A5F on #E5EDE7 · 4.11 → 4.85            */
  --gold-text:           #7C5F35; /* Pre-pointe badge, gold track · 4.20 → 4.98      */
  --accent-rose-deep:    #A24765; /* outline-pill text · 4.42 paper → 5.26           */
  --success:             #476E55; /* quiz "Yes — exactly" on #F7EDEB · 4.27 → 5.03   */

  /* Studio Mode: read at arm's length, on #170A1A. */
  --text-on-studio-60: #9E8C99;   /* was rgba(247,233,238,.6)  · 3.39 → 6.09         */
  --text-on-studio-50: #9E8C99;   /* was rgba(247,233,238,.5)  · 2.86 → 6.09         */
  --text-on-studio-40: #8E7C8A;   /* was rgba(247,233,238,.4)  → 4.55                */
  --text-on-studio-35: #8E7C8A;   /* was rgba(247,233,238,.35) → 4.55                */

  /* Focus-indicator palette. #D0637F (the current ring) is 2.99 on the
     segmented track #F4E6E4 and 2.82 on blush #F7DCE3 — under the 3:1 that
     SC 1.4.11 asks of a focus indicator. Ink clears 11:1 on every light
     ground in the app. */
  --a11y-focus-ink:  #2A0F26;     /* 15.95 paper · 17.58 card · 12.45 seg · 11.74 blush */
  --a11y-focus-halo: #FBF2F0;
  --a11y-tap:        44px;        /* the target this file works to               */
}


/* ── 2. Focus indicator ───────────────────────────────────────────────────
   base.css declares `outline: 2px solid var(--accent-rose); outline-offset:2px`.
   Two problems: the rose fails 3:1 on two of the app's grounds, and a 2px ring
   at arm's length on a phone is easy to miss. This replaces it with a 3px ink
   ring separated from the control by a paper-coloured halo, so it reads on a
   white card, on the paper wash, on a tinted chip and on the plum panel.
   ────────────────────────────────────────────────────────────────────── */
:focus-visible {
  outline: 3px solid var(--a11y-focus-ink);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--a11y-focus-halo);
}

/* Elements sitting inside a clipping ancestor get the ring INSET instead, or
   the ancestor's overflow eats it.
     .b-chip        → .b-chiprow      is overflow-x:auto (so overflow-y:auto too)
     .b-mistake__head → .b-mistake    is overflow:hidden                        */
.b-chip:focus-visible,
.b-mistake__head:focus-visible,
.b-quizopt:focus-visible,
.b-move-row:focus-visible,
.b-row:focus-visible,
.b-routine:focus-visible,
.b-teaser:focus-visible {
  outline-offset: -3px;
  box-shadow: inset 0 0 0 2px var(--a11y-focus-halo);
}

/* Dark grounds: invert. The plum focus panel, Studio Mode and the video tile. */
.b-focus :focus-visible,
.b-studio :focus-visible,
.b-video :focus-visible,
.b-videoscrub :focus-visible {
  --a11y-focus-ink: #F7E9EE;      /* 16.30 on #170A1A · 15.31 on #2A0F26 */
  --a11y-focus-halo: #170A1A;
}

/* Windows High Contrast / forced colours: let the OS draw it. */
@media (forced-colors: active) {
  :focus-visible { outline: 3px solid Highlight; box-shadow: none; }
}


/* ── 3. Touch targets ─────────────────────────────────────────────────────
   Measured sizes are in specs/09-accessibility.md §3.1. Each rule below adds
   an invisible ::after that extends the hit area WITHOUT changing layout, so
   the visual design is untouched. Every control that measured under 44 x 44 is
   listed; the comment gives its real rendered size.
   ────────────────────────────────────────────────────────────────────── */

/* The generic expander. `inset` is set per-control below. */
.st-close,
.b-section__meta,
.b-tick,
.b-video__page,
.b-videometa__links .b-link,
.b-videoctl__btn,
.b-chip,
.b-seg__opt,
.b-iconbtn,
.b-pill--outline-xs,
.b-pill--outline-sm,
.b-cuechip,
.b-link {
  position: relative;
}

.st-close::after,
.b-section__meta::after,
.b-tick::after,
.b-video__page::after,
.b-videometa__links .b-link::after,
.b-videoctl__btn::after,
.b-chip::after,
.b-seg__opt::after,
.b-iconbtn::after,
.b-pill--outline-xs::after,
.b-pill--outline-sm::after,
.b-cuechip::after,
.b-link::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  min-width: var(--a11y-tap);
  min-height: var(--a11y-tap);
  width: 100%;
  height: 100%;
}

/* Leave the studio close ::after; it needs its own, much larger expansion. */
.st-close {                       /* measured 9.3 x 16 — the worst target here */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--a11y-tap);
  height: var(--a11y-tap);
  margin: calc(var(--a11y-tap) / -2 + 8px) 0;   /* keep the 16px visual row height */
  border-radius: var(--radius-pill, 100px);
  font-size: 22px;                              /* readable from the studio floor */
}
.st-close::after { content: none; }
.st-close:hover,
.st-close:focus-visible { background: rgba(247, 233, 238, .10); }

/* "Rest day" / "Practising after all" — measured 64.6 x 11. It is a real
   toggle and the smallest text control on TODAY. Grown with padding and pulled
   back with a matching negative margin, so the section heading does not move. */
.b-section__meta {
  display: inline-flex;
  align-items: center;
  min-height: var(--a11y-tap);
  padding-inline: var(--space-4, 6px);
  margin-block: calc((var(--a11y-tap) - 11px) / -2);
  margin-inline-end: calc(var(--space-4, 6px) * -1);
}
.b-section__meta::after { content: none; }
/* The heading row must not collapse around the taller button. */
.b-section--split { align-items: center; }

/* The done-tick on a practice row — measured 26 x 26. NOTE: the real fix is
   structural (see spec §2.2); this only widens the hit area of the element as
   it stands, and it will steal ~44px of width from the row it is nested in. */
.b-tick::after { min-width: var(--a11y-tap); min-height: var(--a11y-tap); }

/* Video paging arrows — measured 26 x 26 each, 8px apart. */
.b-video__page::after { min-width: var(--a11y-tap); min-height: var(--a11y-tap); }
.b-video__paging { gap: var(--space-10, 12px); }   /* so the two do not overlap */

/* --text-faint (#7B6B70) is 4.56 on paper but only 4.37 on the tinted panel
   ground --surface-tint (#F7EDEB), which is under the floor. Two elements sit
   on it. Send both to --text-muted (5.62).
     .b-drill__kicker  — "Strength drill" eyebrow, move detail
     .s-quiz__srs      — "Coming back soon · you'll see this one again"
   The second only exists once a question has been ANSWERED, which is why a
   route-by-route sweep does not find it: measure the post-answer state too. */
.b-drill__kicker,
.s-quiz__srs { color: var(--text-muted); }         /* 5.62 */

/* Filter chips — style row 32px tall, status row 27px tall. Grown for real
   rather than faked with ::after, because these sit in a horizontally scrolling
   row where an overlapping pseudo-element would swallow the neighbour's first
   few pixels. min-height, not padding: the chip text is centred by flex. */
.b-chip,
.b-chip--status {
  min-height: var(--a11y-tap);
  min-width: var(--a11y-tap);      /* "All" is only 38.2 wide on its own */
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.b-chip::after, .b-chip--status::after { content: none; }
.b-chiprow { gap: var(--space-6, 8px); }        /* keep 8px clear between targets */

/* Segmented control — stage measured 75.8 x 35.8, view 103 x 31.5. The
   `--view` size has its own padding rule in components.css, so set the height
   directly on both. */
.b-seg__opt,
.b-seg--view .b-seg__opt {
  min-height: 40px;                 /* 40, not 44: the 5px-padded track would
                                       otherwise push the control to 50px and
                                       break the design's vertical rhythm.
                                       ::after carries the remaining 4px.    */
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Inactive segment text: --text-faint is only 4.14 on the #F4E6E4 track. */
.b-seg__opt:not([aria-pressed="true"]) { color: var(--text-muted); }   /* 5.32 */

/* Back arrow — 36 x 36. */
.b-iconbtn::after { min-width: var(--a11y-tap); min-height: var(--a11y-tap); }

/* Text-link controls — "Cue tools" 52.8 x 20, "Open on YouTube" 100.3 x 20. */
.b-videometa__links { gap: var(--space-14, 16px); row-gap: var(--space-8, 10px); }

/* Small outline pills — "Use one" and "Add to home screen" measured 32 tall,
   "Back up now" / "Restore a backup" 36.5. */
.b-pill--outline-xs { padding-block: var(--space-10, 12px); }
.b-pill--outline-sm { padding-block: var(--space-9, 11px); }

/* …and the plain modifier, which the two rules above do not reach and which no
   ::after expander covers either — so "Back up now" was still a 36.5px box with
   a 36.5px hit area, and the three Me-screen buttons re-measured 36.5 / 36.5 /
   38.0. This is the one selector all three carry, so one rule takes the whole
   family to a real 44px box rather than a 44px pseudo-element over a smaller
   one. min-height, not padding: .b-pill is already inline-flex + centred, so
   the label stays put and only the box grows.

   Measured cost at 375 x 812, the only layout this moves:
     ME     .s-me__actions   97.5 → 111.0 px (two wrapped rows, +6.5 and +6.0)
            .b-freezes        38.0 → 44.0 px  ("Use one")
            screen height   1256 → 1276 px
     SKILLS "Work on …" CTA  36.5 → 44.0 px, screen height 975 → 983 px
     QUIZ   "Take it again" and the missed-move pills, same +7.5
     SHEET  Merge / Replace everything, same +7.5
   No horizontal overflow anywhere: the widest of them is 146.7px. */
.b-pill--outline { min-height: var(--a11y-tap); }

/* Nothing may overlap the tab bar's own targets. */
.b-tabbar .b-tab { min-height: var(--a11y-tap); }


/* ── 4. Studio Mode at arm's length ───────────────────────────────────────
   She is standing back from a propped-up phone, mid-balance. The clock and the
   exercise name are already large (46px / 34px). These three are not.
   ────────────────────────────────────────────────────────────────────── */
.st-reps       { font-size: 15px; letter-spacing: .06em; }   /* was 12,   3.39 → 6.09 */
.st-next       { font-size: 14px; }                          /* was 11.5, 3.39 → 6.09 */
.st-list__name { font-size: 14px; }                          /* was 12.5              */
.st-list__secs { font-size: 14px; }                          /* was 12.5, 2.86 → 6.09 */
.st-counter    { font-size: 12px; }                          /* was 10.5              */

/* Start / Pause is the control she reaches for blind. Make it the obvious one. */
.st-actions [data-action="studio:toggle"] {
  min-width: 148px;
  min-height: 56px;
  font-size: 16px;
}
.st-actions [data-action="studio:prev"],
.st-actions [data-action="studio:next"] { min-height: 52px; min-width: 76px; }
.st-actions { gap: var(--space-10, 12px); }


/* ── 5. Motion ────────────────────────────────────────────────────────────
   motion.css already carries the blanket duration guard. These are the gaps it
   does not cover.
   ────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  /* pulseRing is a growing box-shadow, i.e. an expanding object rather than a
     fade. Duration alone leaves a hard flash; remove it outright and keep a
     static ring so the play affordance survives. */
  .b-video__ring {
    animation: none !important;
    box-shadow: 0 0 0 3px rgba(232, 169, 184, .30);
  }

  /* The 90-second timer sweep is continuous large-area motion in the
     peripheral vision of someone who is balancing. Step it instead. */
  .st-ring__prog { transition: none !important; }

  /* riseIn translates; with the blanket guard the card still jumps 10px in
     0.01ms. Land it flat. */
  .b-mistake,
  .b-explainer,
  .anim-rise,
  .anim-rise-q,
  .b-sheet { animation: none !important; opacity: 1; transform: none; }

  html { scroll-behavior: auto; }
}


/* ── 6. Utilities the JS patches in spec §2 will need ─────────────────────
   Add these before the structural work, so the JS can use them on day one.
   ────────────────────────────────────────────────────────────────────── */

/* Visually hidden but announced. For the accessible names on icon-only and
   name-from-content controls. */
.b-sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.b-sr-only:focus-visible,
.b-sr-only-focusable:focus-visible {
  position: static !important;
  width: auto; height: auto;
  margin: 0; overflow: visible;
  clip: auto; clip-path: none;
  white-space: normal;
}

/* Skip link, for the keyboard route past the tab bar. Wired up in index.html
   with data-action="skip:main" — NOT as a plain href, which the hash router
   would read as an unknown route. */
.b-skip {
  position: absolute;
  left: var(--gutter, 22px);
  top: -100px;
  min-height: var(--a11y-tap);   /* 43.6px otherwise; absolute, so it moves nothing */
  z-index: var(--z-update, 60);
  padding: 12px 18px;
  border-radius: var(--radius-pill, 100px);
  background: var(--surface-inverse, #2A0F26);
  color: var(--text-on-inverse, #FBF2F0);
  font: var(--font-label, 500 13px/1 sans-serif);
  transition: top .12s var(--ease, ease);
}
.b-skip:focus-visible { top: calc(env(safe-area-inset-top, 0px) + 12px); }

/* A disabled control must still read as disabled, not just look faded. */
[disabled],
[aria-disabled="true"] { cursor: default; }


/* ── 7. Preferences the app should honour but does not ───────────────── */

/* Some phones expose a contrast preference. Take it. */
@media (prefers-contrast: more) {
  :root {
    --text-faint: #5C4655;
    --text-kicker: #5C4655;
    --text-label: #5C4655;
    --text-hint: #5C4655;
    --text-muted: #40303C;
    --border-card: #D8C4C0;
  }
  :focus-visible { outline-width: 4px; }
}
