/* =====================================================================
   coach.css: the Stica Coach app layer, on top of stica.css
   Prototype, 2026-08-14.

   stica.css is the law: tokens, type scale, buttons, forms, focus,
   dark mapping. This file adds only what an app needs that a marketing
   page does not: a narrower working column, a top bar, the session
   thread, its state vocabulary, and the client-page blocks.

   ---------------------------------------------------------------------
   FIRST, THE ONE RULE THAT HAS TO WIN

   Any class carrying a display value beats the `hidden` attribute, so
   `el.hidden = true` silently does nothing and the element stays on
   screen. This has now bitten twice in one day: once on the sign-in
   page, where the "or" divider showed with Google sign-in switched off,
   and once on the dashboard, where hiding the profile link left an empty
   avatar circle sitting on the first screen an invited coach ever sees,
   looking like something had failed to load.

   Both were fixed at the call site the first time, which fixes the road
   and not the hazard. This fixes the hazard: `hidden` means hidden,
   everywhere, whatever a class says.
   --------------------------------------------------------------------- */

[hidden]{display:none !important;}

/* =====================================================================

   Rules carried through from the brief:
   - Working-state colours are facts: amber waits on the coach, cobalt
     is upcoming, green is released, muted is past, oxide is a fault.
     A state is always a word with a dot, never colour alone.
   - No coloured left borders, no cards inside cards, no page-load
     choreography. Motion is state change only, on the shared easing.
   - Every interactive thing is at least 44px tall.
   ===================================================================== */


/* ---------------------------------------------------------------------
   0. THE GROUND (rebuilt for the sheet-and-empty-states spec,
   .cowork/SPEC-app-sheet-and-empty-states.md §5.1, 2026-09-01)

   The body carries class="ch-ground" alone: display:flex so the footer
   sits at the bottom of a short page. The ground itself is the plain
   desk stica.css paints on <body> (the cloth-weave beige by day, charcoal
   at night). Nothing draws lines on the ground; the ruling lives on the
   sheet, because paper is where ruling is real (direction A §4).

   The working column IS the sheet now, not a flat --bg fill standing on
   the desk. `.ch-ground .ch-main` takes `.st-sheet`'s own treatment
   (--paper, one-axis ruling, the --line-edge hairline, the
   --shadow-sheet contact ring): every signed-in `<main>` also carries
   `st-sheet` in its markup, so there is one definition of the sheet in
   the estate and this rule only adds what a scrolling app needs beyond
   it. `flex:1` is gone: the old comment said it existed "so a nearly-
   empty page reads as a sheet on a drawing board, not as a screen that
   ran out of content", but a column that is actually paper, ruled, with
   a contact ring, reads as a sheet at any height; a sheet with no bottom
   edge running off the window did not. `min-height:60svh` does that job
   instead, without needing the column to fill the viewport.
   --------------------------------------------------------------------- */

.ch-ground{display:flex;flex-direction:column;min-height:100svh;}
.ch-ground .ch-main{
  min-height:60svh;
  padding-block:var(--s7);
}


/* ---------------------------------------------------------------------
   1. THE COLUMN
   The app is one thread, so the page is one column: 764px, not the
   marketing site's 1080. Wide enough for a row of when + who + action,
   narrow enough that the eye never has to travel.
   --------------------------------------------------------------------- */

.ch-wrap{max-width:764px;margin-inline:auto;padding-inline:var(--gutter);}
.ch-wrap--wide{max-width:980px;} /* first run only: teaching beside the form */
.ch-main{padding-block:var(--s6) var(--s10);}

/* Two-column teaching layout (first run only). */
.ch-split{display:grid;grid-template-columns:1fr 1fr;gap:var(--s7);align-items:start;}
.ch-split--wide{max-width:980px;}
@media (max-width:920px){
  .ch-split{grid-template-columns:1fr;gap:var(--s6);}
}


/* ---------------------------------------------------------------------
   2. TOP BAR
   Sits inside .st-head so it inherits the sticky hairline shell. Left:
   who we are. Right: who you are, and the light switch. Actions live in
   the page, next to their context.
   --------------------------------------------------------------------- */

/* THE MARK IN THIS APP IS VIOLET, NOT TEAL (Ian, 2026-08-29,
   ans-20260829-151101-bac0: "I like the main site being teal, and coach
   being violet. As long as it is all consistently applied.")

   stica.css re-pins the whole shell subtree to brand teal, because on the
   marketing site one mark has to survive a visitor walking from a Craft
   page to a Coach page. This app is not that site. Every page it serves
   is Stica Coach, so the mark never changes colour mid-journey here, and
   the two door pages (login.html, invite.html) already drew it violet
   because they carry no .st-head to re-pin them. Six pages teal and two
   violet was the accident; this makes it eight violet.

   All four accent tokens move, not just the dot's --acc. Moving --acc alone
   left the current tab's underline drawn in teal two centimetres from a
   violet dot, which was measured in a real render rather than argued about:
   a 68x2 CSS bar of #0F909D (--a-brand-line) beside the mark. Half a mark is
   not a mark. The coach line declares a full set for both palettes
   (--a-coach-line, --a-coach-line-d, --a-coach-fill, --a-coach-text-d), so
   nothing here invents a value or loses a contrast ratio.

   This mirrors stica.css's own shell block exactly, one line lower in the
   cascade and at the same specificity, so it wins by order. The mail this
   app sends already draws the dot in --a-coach (lib/mailer.js COACH_ACCENT),
   so screen and inbox now match. */
.st-head,.st-skip{
  --acc:var(--a-coach); --acc-line:var(--a-coach-line);
  --acc-fill:var(--a-coach-fill); --acc-text:var(--a-coach-fill);
}
:root[data-theme="dark"] .st-head,:root[data-theme="dark"] .st-skip{
  --acc-line:var(--a-coach-line-d); --acc-text:var(--a-coach-text-d);
}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-head,:root[data-theme="auto"] .st-skip{
    --acc-line:var(--a-coach-line-d); --acc-text:var(--a-coach-text-d);
  }
}

.ch-top{
  max-width:764px;margin-inline:auto;padding:var(--s3) var(--gutter);
  display:flex;align-items:center;gap:var(--s2);
}
.ch-top .st-brand{font-size:24px;}
/* Direction A retires the uppercase letterspaced mono interface label
   (this app name badge, "Coach" beside the wordmark, was one). Public Sans
   600, sentence case, no letter-spacing games: it reads as a plain word
   next to the mark, the way "Coach" already is in the copy everywhere
   else. No pen rule here: that prefix is for an eyebrow standing before a
   block of its own; this is one word riding along the brand line. */
.ch-appname{
  font-family:var(--sans);font-size:var(--fs-eyebrow);font-weight:600;
  color:var(--mut);
  margin-left:4px;position:relative;top:2px;
}
.ch-top__right{margin-left:auto;display:flex;align-items:center;gap:var(--s3);}

/* The two places a coach goes: Sessions (the dashboard) and Clients. The
   tab-per-client row left the dashboard on 2026-08-20 with the clients
   page and nothing replaced it, so a client's record had no road to it
   (Ian, 2026-08-21). Same underline-as-current as .ch-tab. */
.ch-nav{display:flex;align-items:center;gap:var(--s3);margin-left:var(--s4);}
.ch-nav__link{
  display:inline-flex;align-items:center;min-height:44px;padding:0 var(--s1);
  text-decoration:none;white-space:nowrap;
  font-size:var(--fs-body-sm);font-weight:500;color:var(--ink2);
  box-shadow:inset 0 -2px 0 0 transparent;
  transition:color var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease);
}
.ch-nav__link:hover{color:var(--ink);}
.ch-nav__link[aria-current="page"]{color:var(--ink);font-weight:600;box-shadow:inset 0 -2px 0 0 var(--acc-line);}
@media (max-width:640px){
  .ch-appname{display:none;} /* the word Coach gives its room to the two links */
  .ch-nav{margin-left:var(--s2);}
}
/* A phone cannot hold brand, two links, avatar and the light switch on one
   line (measured 441px on a 390px screen), so the links take a second row
   under the brand, full width, and nothing is hidden to make room. */
@media (max-width:520px){
  .ch-top{flex-wrap:wrap;row-gap:0;}
  .ch-nav{order:3;flex-basis:100%;margin-left:0;}
}
/* A person who coaches here and is also coached here carries a THIRD link
   ("My coach", coach/*.html, revealed by api/coach/me.js's alsoAClient).
   Two links already needed the whole row at 390px, per the note above, so
   the third one moves the wrap up to 640px — but only for that person. A
   coach with two links keeps the layout measured above, unchanged. */
@media (max-width:640px){
  .ch-top:has(.ch-nav__link#nav-my-coach:not([hidden])){flex-wrap:wrap;row-gap:0;}
  .ch-nav:has(#nav-my-coach:not([hidden])){order:3;flex-basis:100%;margin-left:0;}
}

.ch-me{
  display:inline-flex;align-items:center;gap:var(--s2);
  min-height:44px;padding:0 var(--s2);border-radius:var(--r-md);
  text-decoration:none;color:var(--ink2);
  font-size:var(--fs-body-sm);font-weight:600;
  transition:color var(--dur-1) var(--ease);
}
.ch-me:hover{color:var(--ink);}
.ch-avatar{
  width:30px;height:30px;border-radius:var(--r-dot);flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  background:color-mix(in srgb,var(--acc) 14%,var(--surface));
  border:1px solid var(--line2);
  font-family:var(--mono);font-size:11px;font-weight:500;color:var(--ink);
}
@media (max-width:640px){
  .ch-me__name{display:none;} /* the avatar alone, with its aria-label */
}


/* ---------------------------------------------------------------------
   3. THE ASK: one plain sentence answering "what needs me"
   The sentence is the h1: it IS the headline of the coach's day.
   --------------------------------------------------------------------- */

.ch-ask{display:flex;align-items:center;justify-content:space-between;gap:var(--s4) var(--s6);flex-wrap:wrap;}
.ch-ask__text{
  margin:0;font-size:20px;font-weight:600;line-height:1.4;
  letter-spacing:-.3px;color:var(--ink);max-width:44ch;
}
.ch-ask__text b{color:var(--ink);font-weight:600;} /* semantic hook only; no gradient tricks */
.ch-ask__also{
  flex-basis:100%;margin:0;padding-top:var(--s3);border-top:1px solid var(--line);
  font-size:var(--fs-body-sm);line-height:1.5;color:var(--mut);
}
.ch-ask__also a{color:var(--acc-text);text-underline-offset:3px;}


/* ---------------------------------------------------------------------
   4. FILTER BAR: clients are a filter on the thread, not a second nav
   Text tabs with the sanctioned active-tab underline in --acc-line.
   Scrolls sideways on a phone rather than wrapping into a wall.
   --------------------------------------------------------------------- */

.ch-bar{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2) var(--s4);
  margin:var(--s6) 0 var(--s2);flex-wrap:wrap;
}
.ch-tabs{display:flex;align-items:center;gap:var(--s3);overflow-x:auto;max-width:100%;}
.ch-tab{
  display:inline-flex;align-items:center;min-height:44px;padding:0 var(--s1);
  white-space:nowrap;text-decoration:none;
  font-size:var(--fs-body-sm);font-weight:500;color:var(--ink2);
  box-shadow:inset 0 -2px 0 0 transparent;
  transition:color var(--dur-1) var(--ease),box-shadow var(--dur-1) var(--ease);
}
.ch-tab:hover{color:var(--ink);}
.ch-tab[aria-current="page"]{color:var(--ink);font-weight:600;box-shadow:inset 0 -2px 0 0 var(--acc-line);}
/* Prototype only: a filter for a client whose page is not built is a span,
   not a dead link, so it stays honest under the cursor. */
span.ch-tab{cursor:default;}
span.ch-tab:hover{color:var(--ink2);}


/* ---------------------------------------------------------------------
   5. THE THREAD
   A list of session rows, newest first. Hairlines divide, nothing is a
   card. Desktop: when | body | action. Phone: stacked, action last.
   --------------------------------------------------------------------- */

.ch-thread{list-style:none;margin:0;padding:0;}

/* The two headings on the clients list (2026-08-28): clients on Stica, and
   clients the coach hands over themselves. Only ever rendered when BOTH
   groups have somebody in them, so this spacing is always separating two
   lists and never floating over one. The sub-line carries the consequence,
   which is the whole reason the grouping earns its place over a chip. */
/* THE UPLOAD TILE (Ian, 2026-08-31). One pressable line above the session
   list instead of the open form that used to fill the first screen. It is a
   .st-disclosure, so the open/close affordance, the border and the body
   padding all come from the shared stylesheet; this adds only the two-line
   summary and the accent edge that marks it as the one thing on this page
   you do rather than read. */
.ch-upload{margin:0 0 var(--s6);border-left:3px solid var(--acc-line);}
.ch-upload > summary{align-items:baseline;gap:var(--s3);flex-wrap:wrap;}
.ch-upload__title{font-weight:600;}
.ch-upload__sub{color:var(--mut);font-size:14px;font-weight:400;}
.ch-upload .st-disclosure__body{padding-top:var(--s4);}
@media (max-width:520px){ .ch-upload__sub{flex-basis:100%;} }

.ch-grouphead{margin:var(--s6) 0 0;}
.ch-thread + .ch-grouphead{margin-top:var(--s7);}
.ch-groupsub{margin:var(--s2) 0 var(--s3);color:var(--ink2);font-size:14px;line-height:1.5;}
.ch-row{
  display:grid;grid-template-areas:"when body act";
  grid-template-columns:104px 1fr auto;gap:var(--s1) var(--s4);
  align-items:center;padding:var(--s4) 0;border-top:1px solid var(--line);
}
.ch-thread > .ch-row:first-child{border-top:0;}

.ch-when{grid-area:when;font-family:var(--mono);font-size:12px;line-height:1.5;color:var(--ink2);white-space:nowrap;align-self:start;padding-top:3px;}
.ch-when small{display:block;font-size:12px;color:var(--mut);}

.ch-body{grid-area:body;min-width:0;}
.ch-who{
  margin:0;font-size:16px;font-weight:600;line-height:1.4;color:var(--ink);
  display:flex;align-items:baseline;gap:var(--s3);flex-wrap:wrap;
}
.ch-note{margin:2px 0 0;font-size:var(--fs-body-sm);line-height:1.5;color:var(--mut);max-width:52ch;}

.ch-act{grid-area:act;justify-self:end;}
.st-btn.ch-act,.st-btn--sec.ch-act{font-size:14px;padding:9px 14px;}
.st-btn--ghost.ch-act{font-size:14px;}

@media (max-width:640px){
  .ch-row{grid-template-areas:"when" "body" "act";grid-template-columns:1fr;}
  .ch-when{padding-top:0;}
  .ch-when small{display:inline;margin-left:var(--s2);}
  .ch-act{justify-self:start;margin-top:var(--s2);}
}

/* The state vocabulary: a dot and a word, in the working-state colour.
   Every value here clears 4.5:1 on both grounds (measured; see the
   build report). The word is always present; colour never stands alone. */
.ch-state{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--mono);font-size:10.5px;font-weight:500;
  letter-spacing:.8px;text-transform:uppercase;white-space:nowrap;
}
.ch-state::before{content:"";width:6px;height:6px;border-radius:var(--r-dot);background:currentColor;flex:none;}
.ch-state--ready{color:var(--amber);}      /* waiting on the coach */
.ch-state--upcoming{color:var(--cobalt);}  /* a session still to come */
.ch-state--released{color:var(--green);}   /* the client has it */
.ch-state--writing{color:var(--mut);}      /* the machine is working */
.ch-state--failed{color:var(--oxide);}     /* a fault needing a hand */
/* A state this app does not recognise. Hollow rather than filled, because a
   filled dot in any colour reads as a state the system is holding, and this
   is the absence of one. It must never look like --writing, which is the
   fault it exists to stop repeating. */
.ch-state--unknown{color:var(--mut);}
.ch-state--unknown::before{background:transparent;box-shadow:inset 0 0 0 1px currentColor;}

/* "You send it": a client the coach added themselves (Ian, 2026-08-28).
   NOT A STATE, which is why it is hollow and muted and never takes one of
   the five working colours above. It says who does the delivering, which is
   true of this client for ever and is nothing the coach is waiting on, so
   an amber (waiting on you) or an oxide (a fault) would both be lies about
   a perfectly ordinary client. Same hollow dot as --unknown, for the same
   reason said there: a filled dot reads as a state the system is holding.
   The muted ink clears 4.5:1 on both grounds, same as every value above,
   and the word is always present so the colour never stands alone.
   Rendered by coach/dashboard.html's byHandChip and coach/client.html's. */
.ch-state--byhand{color:var(--mut);}
.ch-state--byhand::before{background:transparent;box-shadow:inset 0 0 0 1px currentColor;}

/* The chips under a name on a session row, when there is more than one of
   them. A bare pair of inline-flex spans has only a collapsed space between
   it, which reads as one long chip at a glance. */
.ch-chips{display:flex;align-items:baseline;gap:var(--s3);flex-wrap:wrap;}

/* The one hand object the dashboard carries when it has data (sheet spec
   §8): a Kalam aside sitting beside the first standing prompt's name. */
.ch-row__note{margin-left:var(--s2);}

/* The client-invitation state vocabulary (WP-a-client-is-invited, wave 4).
   Six named states, each an alias of a colour that already means this
   above, so a coach is never asked to learn a second palette: invited and
   delivered read as "still to come", waiting as "ready for the coach",
   bounced and complained as faults needing a hand, active as the client
   colour already used across the app. */
.ch-state--invited{color:var(--cobalt);}
.ch-state--delivered{color:var(--cobalt);}
.ch-state--waiting{color:var(--amber);}
.ch-state--bounced{color:var(--oxide);}
.ch-state--complained{color:var(--mut);}
.ch-state--declined{color:var(--mut);}
.ch-state--active{color:var(--green);}
/* Wave 6 (WP-the-person-closes-their-account): the one state that can
   follow "active", never a fault and never a fresh invitation, so it gets
   the same muted reading as declined and complained rather than a new
   colour to learn. */
.ch-state--former{color:var(--mut);}

/* The bounced row's inline fix: an email field and a resend button, sitting
   inside .ch-body (already grid-area:body) rather than claiming a grid
   area of its own - the prototype's own .ch-row__fix carried a dead
   grid-area:body rule doing nothing a parent grid area does not already do
   (design-fixes item 11). Plain flex, wraps cleanly at narrow widths. */
.ch-fixrow{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center;margin-top:var(--s2);}
.ch-fixrow .st-input{flex:1 1 220px;min-height:44px;}

/* Past rows step back so the present reads first. */
.ch-row--past .ch-who{color:var(--ink2);}
.ch-row--past .ch-when{color:var(--mut);}
.ch-row--past .ch-when small{color:var(--mut);}

/* The "now" seam: everything above it is still to come, everything
   below has happened. One hairline and one small word. */
.ch-nowline{
  display:flex;align-items:center;gap:var(--s3);margin:0;padding:var(--s2) 0;
  font-family:var(--mono);font-size:10.5px;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--mut);
}
.ch-nowline::before,.ch-nowline::after{content:"";height:1px;background:var(--line2);flex:1;}

/* The thread with nothing in it yet: one quiet line, not a shrug. */
.ch-none{
  margin:0;padding:var(--s6) 0;border-top:1px solid var(--line);
  font-size:var(--fs-body-sm);color:var(--mut);
}


/* ---------------------------------------------------------------------
   6. CLIENT PAGE BLOCKS
   --------------------------------------------------------------------- */

.ch-back{
  display:inline-flex;align-items:center;gap:var(--s2);min-height:44px;
  font-size:var(--fs-body-sm);font-weight:600;color:var(--ink2);text-decoration:none;
  transition:color var(--dur-1) var(--ease);
}
.ch-back:hover{color:var(--ink);}
.ch-back svg{flex:none;}
.ch-backrow{margin:0 0 var(--s2);}

/* The one place the real name lives. It fails every card test (nothing
   lays it beside a sibling, it is not a click target, it does not float),
   so per the sheet-and-empty-states spec §3.2 it is a block: the rule
   above it and air, the icon carried on, not a bordered inset any more. */
.ch-namefact{
  display:flex;gap:var(--s3);align-items:flex-start;
  border-top:1px solid var(--line);padding-top:var(--s4);margin-top:var(--s5);
}
.ch-namefact svg{flex:none;margin-top:3px;color:var(--mut);}
.ch-namefact p{margin:0;font-size:var(--fs-body-sm);line-height:1.55;color:var(--ink2);max-width:58ch;}

.ch-clientmeta{
  margin:var(--s2) 0 0;font-family:var(--mono);font-size:var(--fs-small);
  line-height:1.8;color:var(--mut);
}

/* Commitments: what the client agreed to, each with its state said in
   words on the right. The dot repeats the state colour; the words carry it. */
/* A list of rows, per spec §3.3: the sheet's own paper colour as a flat
   fill (no background-image), which switches the ruling off underneath
   it, and --line2 between rows. No box around the list, no per-row box. */
.ch-commits{list-style:none;margin:0;padding:0;background-color:var(--paper);}
.ch-commit{
  display:flex;align-items:baseline;gap:var(--s3);
  padding:var(--s3) 0;border-top:1px solid var(--line2);
}
.ch-commits > .ch-commit:first-child{border-top:0;}
.ch-commit::before{
  content:"";width:7px;height:7px;border-radius:var(--r-dot);flex:none;
  position:relative;top:-1px;background:var(--line-ui);
}
.ch-commit--kept::before{background:var(--green);}
.ch-commit--open::before{background:var(--cobalt);}
.ch-commit--quiet::before{background:var(--amber);}
.ch-commit__what{margin:0;font-size:var(--fs-body);line-height:1.5;color:var(--ink);}
.ch-commit__state{
  margin-left:auto;font-family:var(--mono);font-size:var(--fs-small);
  color:var(--mut);white-space:nowrap;padding-left:var(--s3);
}
.ch-commit--kept .ch-commit__state{color:var(--green);}
.ch-commit--quiet .ch-commit__state{color:var(--amber);}
@media (max-width:640px){
  .ch-commit{flex-wrap:wrap;}
  .ch-commit__state{margin-left:calc(7px + var(--s3));padding-left:0;flex-basis:100%;white-space:normal;}
}


/* ---------------------------------------------------------------------
   7. THE DOOR (sign-in)
   One column, centred, on the plain desk. It should feel like
   arriving, not like a form.
   --------------------------------------------------------------------- */

/* A column, not a centred grid cell: the sheet and the site line beneath it
   are two things, and place-items:center would give each its own row of the
   viewport and drive them apart. */
.ch-door{
  min-height:100svh;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s5);
  padding:var(--s8) var(--gutter);
}

/* Direction A, §5: "Sign-in is a taped paper scrap on the plain desk, one
   Kalam line, no grid." That removes the "one card centred on bare grid
   with a Google button" archetype the comment below used to defend,
   without inventing a new component: the card now carries stica.css's own
   .st-stuck (§16, the tape-and-shadow paper object every other pinned
   note on the site already uses), and this block only overrides its
   sizing for a form rather than a note. .st-stuck itself supplies the
   background, the tape strip, the tilt and the shadow, and keeps its
   white paper in dark mode the way every other pinned scrap does, so the
   card never needs its own dark-mode background rule.

   The width went from 400 to 488 with the padding, kept from the old
   card: everything here is border-box, so leaving 400 would have kept
   the sheet's outer edge where the bare text used to end and squeezed the
   form inside it to 312px. 488 is 400px of form plus the inset either
   side. */
.ch-door__card{
  width:100%;max-width:488px;
  padding:var(--s7);
}
/* On a phone the sheet keeps its edge but not its generous inset: 44px of
   padding either side of a 360px screen leaves the form 272px wide. */
@media (max-width:520px){
  .ch-door{padding:var(--s5) var(--gutter);}
  .ch-door__card{padding:var(--s5);}
}
.ch-door .st-brand{font-size:38px;}

/* No highlighter behind the wordmark on the door, and here only.
   The highlight is sized in em off the glyph box, and dark mixes it at 32%
   against light's 20%. Everywhere else the wordmark is 24-30px, so both
   choices are right. The door is the one place it renders at 38px, and big
   glyphs times the stronger dark mix stop reading as a marker stroke and
   start reading as a solid slab clipped under the word. The dot carries the
   identity here instead. Remove this rule to put the highlight back. */
.ch-door .st-brand{background-image:none;padding-left:0;}
.ch-door__head{margin-bottom:var(--s6);}
.ch-door__brand{margin:0 0 var(--s5);}
.ch-door__head h1{margin-bottom:var(--s2);}
.ch-door__head p:last-child{margin:0;}
.ch-door__field{margin-bottom:var(--s4);}
.ch-door .st-note-xs{margin:var(--s3) 0 0;}
.ch-door__toggle{position:fixed;top:var(--s4);right:var(--s4);z-index:50;}

.ch-block{width:100%;}

.ch-or{
  display:flex;align-items:center;gap:var(--s3);margin:var(--s5) 0;
  font-family:var(--mono);font-size:var(--fs-small);letter-spacing:1px;
  text-transform:uppercase;color:var(--mut);
}
.ch-or::before,.ch-or::after{content:"";height:1px;flex:1;background:var(--line);}

/* Our own Google button (2026-08-14, replacing Google's drop-in widget).
   The widget was an iframe from Google's origin: it could not be restyled,
   their branding terms forbid trying, and every variant they offer puts a
   white slab or a white logo tile on our dark page. Ian called it scrappy
   three times, so it is gone. Google's branding rules do allow a custom
   button on the redirect flow, which is what this is.

   It is .st-btn--sec, the design system's own secondary button, so it
   matches the primary above it exactly: same 44px floor, same radius, same
   hover transition, same full width. That match is the thing the iframe
   could never do, since Google's tallest size is 40px against our 48.

   The mark itself is Google's official four-colour G, unaltered and given
   its clear space, which is what their rules require of anyone drawing
   their own button. Fixed 18px: it is a logo, not an icon in our set, so
   it does not take the 1.5px currentColor stroke every ch-ic uses. */
.ch-google{gap:var(--s3);}
.ch-google__g{flex:none;width:18px;height:18px;}
/* .st-btn--sec sets display:inline-flex as an author rule, which beats the
   browser's own [hidden]{display:none} at equal specificity - so a Google
   button shipped hidden in the markup would show anyway on a deployment
   with Google switched off. This is the same trap .ch-or documents one
   rule up; it bites any hidden element carrying a class that sets display.
   Attribute plus class outranks the class alone. */
.ch-google[hidden]{display:none;}

/* Sits on the ground below the sheet now, so the column's own gap sets the
   distance and the old margin would double it. */
.ch-door__foot{
  margin:0;text-align:center;
  font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);
}
.ch-door__foot a{color:inherit;}

/* The consent list on the acceptance page (WP-a-client-is-invited, wave 2,
   invite.html). Rows, not cards, per design-fixes.md: this is information
   to read, not something to interact with, except the recording checkbox
   below it, which is its own field and never one of these rows. */
.ch-consent{list-style:none;margin:0 0 var(--s5);padding:0;}
.ch-consent li{padding:var(--s3) 0;border-top:1px solid var(--line);font-size:var(--fs-body-sm);line-height:1.55;color:var(--ink2);}
.ch-consent li:first-child{border-top:0;}
.ch-consent b{color:var(--ink);font-weight:600;display:block;margin-bottom:2px;}

/* The one tick this page asks for. Kept apart from the informational rows
   above (consent-checklist.md's rule 4: recording gets its own tick, never
   bundled with anything else), and from any button below it, so nobody
   presses "yes" without the checkbox in view first. */
.ch-consent-tick{display:flex;align-items:flex-start;gap:var(--s3);cursor:pointer;}
.ch-consent-tick input{margin-top:3px;flex:none;width:18px;height:18px;}
.ch-consent-tick span{font-size:var(--fs-body-sm);line-height:1.5;color:var(--ink2);}

/* Decline reads as the one restrictive option (consent-checklist.md's rule
   6: any highlighting must fall on the most restrictive answer, never the
   reverse), while staying the same st-btn--sec size and weight as the two
   yes buttons above it, per design-fixes.md's item 4. */
.st-btn--sec.ch-danger{color:var(--oxide);border-color:color-mix(in srgb,var(--oxide) 35%,var(--line));}
.st-btn--sec.ch-danger:hover{border-color:var(--oxide);}


/* ---------------------------------------------------------------------
   8. SMALL SHARED PIECES
   --------------------------------------------------------------------- */

/* Confirmation line after an action that changed something. */
.ch-done{
  display:inline-flex;align-items:center;gap:var(--s2);
  font-size:var(--fs-body-sm);font-weight:600;color:var(--green);
}
.ch-done::before{content:"";width:7px;height:7px;border-radius:var(--r-dot);background:currentColor;flex:none;}

/* Section gap inside the app column: quieter than the marketing 80px. */
/* .ch-sect is this platform's block (spec §5.3): a titled section lying
   on the sheet, divided from its neighbour by a rule and by air, the
   sheet's own ruling running through it. Not a box: no background, no
   border but the rule above, no radius, no shadow. */
.ch-sect{
  margin-top:var(--s5);
  border-top:1px solid var(--line);
  padding-top:var(--s5);
}
.ch-sect:first-child{border-top:0;padding-top:0;margin-top:0;}
.ch-sect > h2{margin-bottom:var(--s4);}

/* Forms: a measure, not the full column. */
.ch-form{max-width:560px;}
.ch-fieldset{border:0;padding:0;margin:0 0 var(--s5);}
.ch-fieldset legend{margin-bottom:var(--s2);padding:0;}
/* .st-field is a column flex, which stretches an inline-flex segment to
   full width; a two-option control should be the size of its options. */
.st-field > .st-segment{align-self:flex-start;}
.st-btnrow .st-small{margin:0;}
.ch-after{margin:var(--s3) 0 0;}

/* The worked example under "how long your write-ups run": the same moment
   from one session, shown at whichever length is selected, so a coach reads
   the difference instead of being told about it.

   It borrows .ch-namefact's shape (the one existing inset panel in this
   file) rather than inventing a second card idiom, and differs only where
   it has to: no icon column, so no flex, and the text sits at the same
   measure as the form it belongs to. Deliberately quieter than the fields
   above it -- it is something to read while choosing, not another thing to
   fill in.

   THE SPEC SAID PROFILE.HTML HAD NO BOXES (sheet-and-empty-states spec
   §5.3): reading the code, this one does. It fails every card test in
   turn (nothing beside it, not a click target, does not float), so it is
   a block like everything else here, not the exception the spec missed. */
.ch-example{
  border-top:1px solid var(--line);padding-top:var(--s4);margin-top:var(--s3);
}
.ch-example > .st-small{margin:0 0 var(--s3);color:var(--mut);}
.ch-example__h{
  margin:var(--s3) 0 var(--s1);font-size:var(--fs-small);
  font-weight:600;color:var(--ink);
}
.ch-example__h:first-child{margin-top:0;}
.ch-example__p{
  margin:0 0 var(--s2);font-size:var(--fs-body-sm);
  line-height:1.55;color:var(--ink2);max-width:58ch;
}
.ch-example__p:last-child{margin-bottom:0;}
.ch-example__list{
  margin:0;padding-left:var(--s4);font-size:var(--fs-body-sm);
  line-height:1.55;color:var(--ink2);max-width:58ch;
}
.ch-example__list li{margin-bottom:var(--s1);}
.ch-example__list li:last-child{margin-bottom:0;}

/* The recording, on a session page. A transcript is one utterance per line
   with a speaker prefix, so it is pre-formatted text and not prose: wrapping
   it into paragraphs would destroy the only structure it has. Scrolls inside
   its own pane rather than making the page thousands of lines long, and
   wraps rather than scrolling sideways, because a long line is a long
   sentence somebody said and not a table.

   BLOCK, PER THE CARD TEST, WITH A NOTE (sheet-and-empty-states spec §3.2):
   it is not a click target, nothing lays it beside a sibling, it does not
   float, so the mechanical answer is block, not card. Flagged rather than
   guessed at: the max-height/overflow scroll pane still needs SOME visible
   edge for a coach to see where the scroll region ends, which a bare top
   rule does not give it. Kept scrollable and flat rather than reintroducing
   a border on my own judgement; worth Morris's eye. */
.ch-transcript{
  margin:var(--s3) 0 0;border-top:1px solid var(--line);padding-top:var(--s4);
  max-height:60vh;overflow-y:auto;
  font-family:var(--mono);font-size:var(--fs-small);line-height:1.7;
  color:var(--ink2);
  white-space:pre-wrap;overflow-wrap:anywhere;
}
.st-tile > .st-body-sm{margin-bottom:0;}

/* Upload drop zone internals. */
.st-drop > .ch-ic{color:var(--mut);}
.ch-drop__lead{margin:var(--s2) 0 var(--s1);font-size:var(--fs-body-sm);color:var(--ink);}
.ch-drop__meta{margin:0 0 var(--s4);}

/* Destructive text action: oxide is the fault colour, and losing data is
   the fault being risked. Word carries it; the colour agrees. */
.st-btn--ghost.ch-danger{color:var(--oxide);}
.st-btn--ghost.ch-danger:hover{color:var(--oxide);text-decoration-thickness:2px;}

/* Icon defaults: drawn SVG, one stroke weight, inherits currentColor. */
.ch-ic{display:inline-block;vertical-align:-3px;}
.ch-ic path,.ch-ic circle,.ch-ic line,.ch-ic rect,.ch-ic polyline{
  fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;
}

/* The light switch, made findable (Ian's ask, 2026-08-14 second round).
   The 34px bare glyph read as nothing at all: Ian looked at all six
   pages and asked for a feature they already had. So the control gains
   the word stica.css already gives it in the mobile nav panel ("Dark
   mode" / "Light mode", stating what the next press gives, matching the
   icon rule), a 44px target, and a boundary in --line-ui, which clears
   3:1 against the ground in both themes. It stays quieter than any
   button: no fill at rest, regular weight, muted-side text. The svgs
   keep their st-themetoggle__ classes, so the sun/moon specificity
   repair above still applies to it unchanged. */
.ch-theme{
  width:auto;height:auto;min-height:44px;
  gap:var(--s2);padding:0 var(--s4);
  border-color:var(--line-ui);
  font-family:var(--sans);font-size:var(--fs-body-sm);font-weight:500;
  color:var(--ink2);
}
.ch-theme:hover{color:var(--ink);border-color:var(--line-ui);background:color-mix(in srgb,var(--ink) 5%,transparent);}
.ch-theme::after{content:"Dark mode";}
:root[data-theme="dark"] .ch-theme::after{content:"Light mode";}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .ch-theme::after{content:"Light mode";}
}

/* German acceptance page (invite.html): the toggle's visible word,
   never only its aria-label. Wave 9 fix -- wave 9's own toggle work set
   the aria-label from STRINGS but left this ::after content in English,
   so a German visitor read "Light mode"/"Dark mode" on the button
   itself. Keyed off the <html lang> invite.html already sets to
   "de-DE" once the invitation's own language is known, mirroring the
   pair immediately above. */
:root[lang="de-DE"] .ch-theme::after{content:"Heller Modus";}
:root[lang="de-DE"][data-theme="dark"] .ch-theme::after{content:"Dunkler Modus";}

/* Repair, measured in this prototype: stica.css's `.st-themetoggle svg`
   (0,1,1) outranks `.st-themetoggle__sun` (0,1,0), so both icons showed
   at once. Re-stated at the winning specificity for all three theme
   states. Belongs upstream in stica.css eventually. */
.st-themetoggle svg.st-themetoggle__sun{display:none;}
:root[data-theme="dark"] .st-themetoggle svg.st-themetoggle__sun{display:block;}
:root[data-theme="dark"] .st-themetoggle svg.st-themetoggle__moon{display:none;}
@media (prefers-color-scheme: dark){
  :root[data-theme="auto"] .st-themetoggle svg.st-themetoggle__sun{display:block;}
  :root[data-theme="auto"] .st-themetoggle svg.st-themetoggle__moon{display:none;}
}

/* ---- Step 2: read and release (coach/session.html) ---------------------
   Four classes, added 2026-08-15 with the session loop. Everything else on
   that screen is st-* or the ch-commit component above, reused rather than
   restyled. */

/* Two ways to save, or two ways to say something, side by side. They wrap on
   a phone rather than shrinking, because both labels are sentences and a
   truncated one would hide which button tells the client. */
.ch-actions{display:flex;flex-wrap:wrap;gap:var(--s3);margin-top:var(--s4);}
.ch-actions > .st-btn{flex:0 1 auto;}

/* The follow-up plan sits under its commitment rather than beside it: the
   commitment row is a baseline flex row and a form in it would ride the
   text. Full-width basis pushes it onto its own line inside the same row. */
.ch-commit{flex-wrap:wrap;}
.ch-planform{flex:1 0 100%;display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center;margin-top:var(--s2);}
.ch-planform .st-input{width:auto;}

/* Notes and messages: one thread, both audiences, each line saying which it
   is. Kept quieter than the document above it. */
.ch-notes{list-style:none;margin:0 0 var(--s4);padding:0;}
.ch-note-row{padding:var(--s3) 0;border-top:1px solid var(--line);}
.ch-notes > .ch-note-row:first-child{border-top:0;}
.ch-note-row p{margin:0;}
.ch-note-row p + p{margin-top:var(--s1);color:var(--mut);}


/* ---------------------------------------------------------------------
   12. THE WRITE-UP, IN PARTS

   Ian, 2026-08-15, on the first real reports: they "are very lengthy and
   present as a wall of text". Measured before this existed: 2,380 words in
   ONE textarea, of which the record of the session was 1,827. The document
   is still one document; only the reading of it is broken up.

   ROWS, NOT CARDS, and that is this file's own brief being obeyed rather
   than a shortcut: "no cards inside cards". These sit inside the panel that
   already exists, separated the same way .ch-commit rows are, so the screen
   gains a structure without gaining a second frame around everything.

   A CLOSED PART SAYS HOW BIG IT IS. Collapsing is only honest if the coach
   can see the size of what they are choosing not to open; without the word
   count it is hiding.
   --------------------------------------------------------------------- */

.ch-parts{list-style:none;margin:0 0 var(--s4);padding:0;}
.ch-part{border-top:1px solid var(--line);}
.ch-parts > .ch-part:first-child{border-top:0;}

/* The whole header is the control, so the target is the row rather than a
   chevron a thumb has to find. 44px minimum, like everything else here. */
.ch-part__head{
  width:100%;min-height:44px;display:flex;align-items:center;gap:var(--s3);
  padding:var(--s3) 0;margin:0;background:none;border:0;
  font:inherit;color:var(--ink);text-align:left;cursor:pointer;
  transition:color var(--dur-1) var(--ease);
}
/* Same reason as .ch-linkbtn above: the raw accent is 4.35:1 on this
   ground, so a hover state painted with it drops below the floor the rest
   of the row clears. Hover colours are read, not decorative. */
.ch-part__head:hover{color:var(--acc-text);}
.ch-part__chev{
  flex:none;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform var(--dur-2) var(--ease);
}
.ch-part__head[aria-expanded="true"] .ch-part__chev{transform:rotate(90deg);}
.ch-part__title{font-weight:600;font-size:var(--fs-body);}
.ch-part__size{
  margin-left:auto;font-family:var(--mono);font-size:var(--fs-small);
  color:var(--mut);white-space:nowrap;padding-left:var(--s3);
}

/* An unrecognised part. It is shown, opened and named after its own
   heading (see lib/document-sections.js); the dot is what says the app does
   not know what it is, rather than dressing it up as one of the four it
   does know. */
.ch-part--unknown .ch-part__title::after{
  content:"";display:inline-block;width:7px;height:7px;margin-left:var(--s2);
  border-radius:var(--r-dot);border:1px solid var(--line-ui);
  position:relative;top:-1px;
}

.ch-part__body{padding:0 0 var(--s4);}
.ch-part__hint{margin:0 0 var(--s2);}
.ch-part__body .st-textarea{margin:0;}

/* The dates and check-ins, sitting immediately under the part that names
   the actions rather than in a panel of their own further down. Before this
   a coach read the same six commitments twice: once as prose inside the
   document, once as rows under a separate heading. Same two facts, one
   place, so the eye crosses them once. */
.ch-plan{margin:0 0 var(--s4);padding:var(--s3) 0 0;border-top:1px dashed var(--line);}
.ch-plan__lead{margin:0 0 var(--s2);}


/* ---------------------------------------------------------------------
   13. ADDING A CLIENT FROM ANYWHERE

   The control used to exist only on the first-run screen, so a coach could
   add their first client and never a second (Ian found it live, 2026-08-15).
   It now sits at the end of the client tabs, which is where the clients
   already are, and a second way in sits under the upload dropdown, which is
   where a coach notices someone is missing.

   It borrowed .ch-tab until 2026-08-26, on the reasoning that it sat in a
   row of related things and a filled button would claim to be the most
   important item on the page. That reasoning expired when the per-client
   tabs were retired: the button was left alone in a tab row of one, so the
   underline-shaped, bare-text treatment made the page's only action read as
   a filter that happened to be switched off. Ian read it that way (2026-08-
   26) and he is right; there is nothing beside it to be related to.

   It is now an outlined button, which reads as a thing you press without
   claiming primacy the way a filled one would. The original caution stands
   and is still what rules out .st-btn on its own here.
   --------------------------------------------------------------------- */

.ch-addrow{margin:var(--s4) 0 var(--s5);}
.ch-add::before{content:"+";margin-right:var(--s1);font-weight:600;}
/* Open is a state of the panel, not a selected filter: the emphasis says
   "this is doing something now" and nothing more. */
.ch-add[aria-expanded="true"]{color:var(--ink);border-color:var(--ink2);}

/* A button that reads as a link, inside a line of help text. Not an <a>:
   it goes nowhere, and an anchor with no href is a trap for a keyboard. */
/* --acc-text, NOT --acc (2026-08-31). This control is 11px micro-copy, and
   the raw brand violet measures 4.35:1 in light and 3.72:1 in dark against
   a 4.5:1 floor. The same violet passes at 14px, which is why it was never
   caught by eye. --acc-text is the readable partner and already resolves
   in both palettes (--a-coach-fill light, --a-coach-text-d dark), giving
   5.01:1 and 5.04:1. This is the identical fault removed from the
   marketing site's privacy and terms pages the same afternoon; when one
   accent is corrected, every sibling product needs the same grep. */
.ch-linkbtn{
  background:none;border:0;padding:0;font:inherit;color:var(--acc-text);
  text-decoration:underline;text-underline-offset:2px;cursor:pointer;
}
.ch-linkbtn:hover{color:var(--ink);}
/* An anchor wearing this class picks up the stylesheet's visited colour
   otherwise, so the same control reads as two different things depending on
   whether the coach has been there before. */
a.ch-linkbtn:visited{color:var(--acc-text);}

/* The line under the briefing that says what kind of thing it is. Set apart
   from the lists above it, because the claim it makes (these are facts, the
   judgement is yours) is the reason a coach can trust the rest. */
.ch-briefnote{margin-top:var(--s4);padding-top:var(--s3);border-top:1px solid var(--line);}

/* A part's editor grows to its text (autosize() in session.html) and stops
   here. Without a ceiling, the record of the session opens as a box a couple
   of hundred lines tall and the Save button ends up somewhere below the
   horizon. */
.ch-part__body .st-textarea{max-height:70vh;min-height:64px;}

/* The 120px floor the design system puts under every textarea is right for
   a form field somebody is about to fill in and wrong for a two-line list
   that is already written. autosize() gives these their real height, so the
   floor only has to keep a one-line part comfortably above the 44px touch
   minimum. */

/* The two halves of the briefing, what they owe and what you owe. They need
   air above them: without it the second heading sits on the last line of the
   list before it and the two lists read as one. */
.ch-brief__h{margin-top:var(--s5);}

/* A commitment's text stays BESIDE its dot however long it is. .ch-commit
   wraps so the follow-up form can drop onto its own line, and the side
   effect was an orphan: a paragraph long enough to need two lines wrapped
   below the dot, leaving a lone dot sitting on a line of its own above the
   text. Invisible on the short fixtures, obvious the moment a real
   commitment ran past one line. Growing and shrinking from a zero basis is
   what keeps it on the dot's line; min-width:0 lets it actually wrap its own
   text rather than refusing to go below its longest word. */
.ch-commit__what{flex:1 1 0;min-width:0;}

/* Marking an action finished, on the client page and on the session page
   both. The buttons drop onto their own line inside the commitment row, the
   same way the follow-up form does, so a long action never has three
   buttons riding its text. They are the design system's secondary button at
   its small size rather than a new kind of control: this is an ordinary
   thing a coach does, not an alarming one, and the destructive-looking
   treatment would be wrong for something with an undo beside it. */
.ch-commit__acts{flex:1 0 100%;display:flex;flex-wrap:wrap;gap:var(--s2);margin-top:var(--s2);}

/* COMPACT ON PURPOSE, and this is the second version. The first used the
   secondary button at full size, which looked fine on a three-item fixture
   and was a wall on real data: one client has twenty-one open actions, so
   three full-sized buttons a row is sixty-three of them down one page,
   shouting louder than the actions they are about. These keep the 44px
   touch target the design system insists on and give back everything else:
   smaller type, tight padding, a hairline instead of a fill. */
.ch-mark{
  font-size:13px;font-weight:600;padding:6px 12px;min-height:44px;
  background:transparent;color:var(--mut);border:1px solid var(--line);
}
.ch-mark:hover{color:var(--ink);border-color:var(--acc-line);background:transparent;}

/* What it says once it is closed, with the way back on the same line. Quiet,
   because a closed action is finished business and should not shout over the
   ones still open above it. */
.ch-commit__done{flex:1 0 100%;margin:var(--s2) 0 0;font-size:var(--fs-small);color:var(--mut);}
.ch-commit__done .ch-linkbtn{margin-left:var(--s1);}


/* ---------------------------------------------------------------------
   WHAT THE COACH THEMSELVES OWES (2026-08-16, rebuilt as rows coach-review-2
   E2, wave 3)

   Ian asked for the reminder to be clean and clear and to add no noise. It
   used to be a folded `<details class="ch-owed">`, its own quieter widget
   above the session list; it is now two kinds of row IN that list
   (checkInRowsHtml()/promiseRowsHtml() in coach/dashboard.html), so the
   .ch-owed* rules that styled the fold are gone with it -- .ch-row and its
   own parts above carry these rows now. `.ch-row--prompt` is a bare hook,
   not a size or colour swap: a coach reads a prompt row the same way they
   read a session row, never louder.

   The date box beside a promise and the add-one box share the follow-up
   form's shape, because they are the same gesture in the same kind of row.
   -------------------------------------------------------------------- */
.ch-row__sub{display:block;font-size:var(--fs-body-sm);color:var(--mut);margin-top:2px;}

/* The date sits in the same row as the three answers and saves on change, so
   a promise is one line of controls rather than two. */
.ch-duebox{width:auto;min-height:44px;padding:6px 10px;font-size:13px;}
.ch-addpromise{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center;margin-top:var(--s3);}
.ch-addpromise .st-input{flex:1 1 240px;}

/* ---------------------------------------------------------------------
   14. THE THREE QUIET LINES UNDER AN ACTION (migration 054, 2026-08-16)

   When and where, the first step, done when. Quieter than the plan form
   above them on purpose: those are dates a coach sets to run the follow-up,
   these are three facts about what was actually said, and the page should
   not shout the second kind as loud as the first.

   THE LABEL IS ALWAYS THERE, MUTED, WHETHER THE LINE IS FILLED OR NOT. "a
   blank line shows its greyed label rather than vanishing, so a coach can
   see there is somewhere to put it" is the plan's own words: a label that
   only appeared once something was typed would be exactly the vanishing it
   refuses, so the colour never changes between empty and filled.
   --------------------------------------------------------------------- */
.ch-quiet{flex:1 0 100%;display:flex;flex-direction:column;gap:var(--s1);margin-top:var(--s3);padding-top:var(--s2);border-top:1px dashed var(--line);}
.ch-quiet__row{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s2);}
.ch-quiet__label{
  flex:0 0 auto;min-width:9ch;
  font-size:var(--fs-small);color:var(--mut);
}
.ch-quiet__field{
  flex:1 1 200px;min-height:32px;width:auto;
  border:0;border-bottom:1px solid transparent;background:none;
  font:inherit;font-size:var(--fs-small);color:var(--ink);padding:2px 0;
}
.ch-quiet__field:hover,.ch-quiet__field:focus{border-bottom-color:var(--line-ui);outline:none;}

/* ---------------------------------------------------------------------
   15. THE TICK, AND WHAT APPROVING THE LAST PART OFFERS
   (WP-coach-stands-alone, "the check-in build" section 3b; decided
   memory/decisions/coach-report-parts-tick-but-never-block.md)

   The tick means "I have read and approved this part", nothing more, and
   it never blocks Release — there is no gate here, only a control. It
   sits INSIDE the part body rather than in .ch-part__head, because the
   head is already a <button> and a control cannot nest inside one without
   breaking both the markup and a keyboard user's model of what they are
   pressing.
   --------------------------------------------------------------------- */
.ch-part__tickrow{margin:0 0 var(--s3);}
.ch-tickbtn{
  display:inline-flex;align-items:center;gap:var(--s2);
  font-size:13px;font-weight:600;padding:6px 12px;min-height:44px;
  background:transparent;color:var(--mut);border:1px solid var(--line);
  border-radius:var(--r-lg);cursor:pointer;
}
.ch-tickbtn svg{flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;opacity:.35;}
.ch-tickbtn:hover{color:var(--ink);border-color:var(--acc-line);}
.ch-tickbtn--on{color:var(--green);border-color:var(--green);}
.ch-tickbtn--on svg{opacity:1;}

/* What ticking every part offers: a covering message and the actions it
   covers, both pre-filled and both the coach's to change or discard.
   Nothing here creates anything until "Send it" is pressed — showing this
   panel is not creating, and closing the tab with it on screen leaves no
   trace behind (WP-coach-stands-alone, "the check-in build" section 3b:
   "if they close it, dismiss it, or never touch it, no check-in exists").
   Built as an ordinary .st-block rather than a new kind of surface, so it
   reads as one more thing on the page a coach can do, not an interruption. */
.ch-checkin__items{list-style:none;margin:var(--s2) 0 var(--s4);padding:0;display:flex;flex-direction:column;gap:var(--s2);}
.ch-checkin__itemlabel{display:flex;align-items:flex-start;gap:var(--s2);font-size:var(--fs-body-sm);cursor:pointer;}
.ch-checkin__itemlabel input{margin-top:3px;flex:none;}


/* ---------------------------------------------------------------------
   16. THE SECOND DOOR ONTO THE SAME PANEL (Ian, 2026-08-16)

   A coach who has not ticked a single part can still open the check-in
   offer above from here - one line, `.ch-linkbtn` again (the same button-
   as-a-link idiom "Undo" already uses beside a closed action), never a
   button that looks like it belongs beside Save or Release. It sits in a
   band of its own, top and bottom border like .ch-owed above, so it is
   findable between two full-width panels rather than a stray line of text
   glued to whichever one happens to be above it - "quiet" is the tone, not
   the same as "easy to miss", and a control nobody notices is not a second
   door, it is a decoration. No count, no colour that reads as urgent: this
   screen already carries the one line .ch-owed and .ch-part__size use for
   that, and this is not a second one. --------------------------------- */
.ch-checkin-opener{
  margin:0 0 var(--s5);padding:var(--s3) 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  max-width:none;
}

/* What the recording says about a write-up. Deliberately quiet: a flag is
   "look at this line", not an alarm, and a panel that shouts is one a coach
   learns to skip. The quoted text is the thing being pointed at, so it gets
   the emphasis; the label and the explanation sit around it. */
.ch-checks{list-style:none;margin:0;padding:0;}
.ch-check{
  border-left:2px solid var(--line-ui);padding:0 0 0 var(--s3);
  margin:0 0 var(--s4);
}
.ch-check:last-child{margin-bottom:0;}
.ch-check__what{
  margin:0;font-size:var(--fs-small);font-weight:600;color:var(--ink);
}
.ch-check__quote{
  margin:var(--s1) 0;font-size:var(--fs-body-sm);line-height:1.55;
  color:var(--ink2);max-width:58ch;
}
.ch-check__caveat{color:var(--mut);}

/* ---------------------------------------------------------------------
   17. THE REVIEW SCREEN, REBUILT ON IAN'S OWN READING OF IT (2026-08-17)

   Nine things he found, reading a real write-up rather than a fixture. The
   four that are visual live here; the rest are behaviour and live in
   coach/session.html and the route behind it.
   --------------------------------------------------------------------- */

/* ONE MEASURE PER COLUMN, which is the whole of his "span of the text boxes"
   note: "for many of the text boxes, they appear to not span the full width
   of the column ... all of those seem to be having a right indent applied".

   Nothing was indented. Body copy carries the design system's reading measure
   (.st-body-sm is 48ch, .st-lede 52ch) while every input, textarea and button
   fills its panel, so inside a 764px column the prose stopped about 190px
   short of controls that did not, on line after line. Both rules are right on
   their own and wrong together, and the eye reads the difference as a mistake
   rather than as typography.

   So inside this app's blocks the copy fills the column. It is safe here in
   a way it would not be site-wide: .ch-wrap is 764px, a block's inner width
   is under 600, and every one of these is a sentence or two of guidance
   rather than an article. The one exception stays capped: .ch-check__quote is
   somebody's actual words being quoted back, which is reading rather than
   guidance.

   .st-panel became .st-block on the sheet (2026-09-01, sheet-and-empty-
   states spec §5.4: "no .st-panel inside a .st-sheet, anywhere"), so this
   selector follows the markup rename. */
.ch-main .st-block .st-body-sm,
.ch-main .st-block .st-small,
.ch-main .st-block .st-help,
.ch-main .ch-confirm .st-body-sm,
.ch-main .ch-confirm .st-small,
.ch-main .ch-lengthbar .st-small,
.ch-main > .st-lede{max-width:none;}
.ch-main .st-block .ch-check__quote{max-width:58ch;}

/* THE LENGTH, AT THE TOP, AS THREE NAMES (his first note: the control for
   making the write-up shorter or longer "is so far down the page", and he
   wants it "as a visual thing up at the top of the page"). A band rather than
   a panel: it is one line of settings about the write-up below it, not a
   section of it, and giving it a panel of its own would say it was as
   important as the document. */
.ch-lengthbar{
  margin:0 0 var(--s4);padding:var(--s3) 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.ch-lengthbar__row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);}
.ch-lengthbar__label{font-size:var(--fs-small);color:var(--mut);}
.ch-lengthbar__note{margin:var(--s2) 0 0;color:var(--mut);}
.ch-lengthbar--pending .st-body-sm{margin:0;}
.ch-lenopts{display:flex;flex-wrap:wrap;gap:var(--s1);}
/* The three sit together as one control, so the current one reads as a
   position rather than as a disabled button. The one you are on is not
   pressable, which is why it is a span: there is nothing to ask for. */
.ch-lenopt{
  font-size:13px;font-weight:600;padding:8px 14px;min-height:44px;
  display:inline-flex;align-items:center;
  background:transparent;color:var(--mut);
  border:1px solid var(--line);border-radius:var(--r-sm);
}
.ch-lenopt:hover{color:var(--ink);border-color:var(--acc-line);cursor:pointer;}
.ch-lenopt--on{
  color:var(--ink);border-color:var(--acc-line);background:var(--surface);
  cursor:default;
}
.ch-lenopt--on:hover{color:var(--ink);}

/* AN ACTION, EDITED WHERE IT LIVES. The wording is a box now, because the row
   is the only place it can be changed; it is styled as text rather than as a
   form field so a coach reading down the list sees prose, and finds an editor
   only when they put the cursor in it. */
/* BESIDE THE DOT, NOT UNDER IT. At 1 1 100% this took a line of its own and
   left the row's bullet sitting alone above the text, which is the exact
   orphan the .ch-commit__what comment above describes being fixed once
   already. Growing and shrinking from a zero basis keeps it on the dot's
   line; min-width:0 lets it wrap its own text rather than refusing to go
   below its longest word. */
.ch-commit__main{flex:1 1 0;min-width:0;}
.ch-commit__edit{
  width:100%;display:block;resize:none;overflow:hidden;
  font:inherit;font-size:var(--fs-body);line-height:1.5;color:var(--ink);
  background:transparent;border:1px solid transparent;border-radius:var(--r-sm);
  padding:2px 6px;margin:0 0 0 -6px;
}
.ch-commit__edit:hover{border-color:var(--line);}
.ch-commit__edit:focus{border-color:var(--acc-line);background:var(--surface);outline:none;}
.ch-commit__saverow{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);margin:var(--s2) 0 0;}
.ch-commit__saynote{color:var(--mut);}

/* The three quiet lines, behind one press. Nine empty fields on a session
   with three actions was the "wall" he saw; the labels still never vanish
   once the block is open, which was the original rule. */
.ch-quiet-wrap{flex:1 0 100%;margin-top:var(--s2);}
.ch-quiet-sum{
  font-size:var(--fs-small);color:var(--mut);cursor:pointer;list-style:none;
  min-height:32px;display:flex;align-items:center;gap:var(--s2);
}
.ch-quiet-sum::-webkit-details-marker{display:none;}
.ch-quiet-sum::before{content:"";width:6px;height:6px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);transition:transform .15s ease;}
.ch-quiet-wrap[open] .ch-quiet-sum::before{transform:rotate(45deg);}
.ch-quiet-sum:hover{color:var(--ink);}
.ch-quiet-wrap .ch-quiet{margin-top:var(--s1);}

/* A note, and the two things a coach can do to their own. The editor is the
   same row, swapped, rather than a screen of its own. */
.ch-note-row__body{margin:0;}
.ch-note-row__edit{margin:var(--s2) 0;}
.ch-note-row__caveat{color:var(--mut);}


/* ---------------------------------------------------------------------
   18. ONE WAY TO ASK WHAT SOMETHING MEANS (Ian, 2026-08-17)

   He read the review screen and found the same shape everywhere: a heading,
   then a sentence in small grey type explaining what the heading meant.
   "Asking for another length keeps this version. Nothing is replaced."
   "Their actions. Change the wording here." "Drop one if it is not real."
   Each one is true and worth saying, and printed all at once they are the
   wall of text the page was supposed to stop being. His ask: "an info icon
   that, if people hover on it, it has a little pop-up that displays that ...
   applied consistently across the entire platform here."

   So: one component, in the stylesheet every coach page and the client's own
   page already load, rather than a treatment invented per screen.

   THREE WAYS IN, BECAUSE THERE ARE THREE KINDS OF READER. Hover for a mouse,
   focus for a keyboard, press for a thumb. The press is what makes it real on
   a phone, where hover either does not exist or fires on the tap before the
   one that dismisses it.

   IT IS A BUTTON AND IT SAYS WHAT IT IS. `aria-expanded` for state,
   `aria-describedby` so the sentence is read out as the description of the
   thing it explains rather than as a stray note somewhere else on the page.

   THE HIT AREA IS 44px AND THE ICON IS 18px. A 44px circle in the middle of a
   sentence would push the line apart; a pseudo-element does the touching and
   the drawn ring does the seeing.

   NOTHING IMPORTANT LIVES IN HERE. A popover is a place for the sentence a
   coach needs once, on their first session, and never again. Anything they
   need every time stays on the page: what a control does at the moment of
   pressing it is never behind a press of its own.
   --------------------------------------------------------------------- */

.st-info{position:relative;display:inline-flex;align-items:center;}
.st-info__btn{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  flex:none;width:18px;height:18px;padding:0;
  background:transparent;border:1px solid var(--line-ui);border-radius:var(--r-dot);
  font-family:var(--sans);font-size:11px;font-weight:700;line-height:1;
  color:var(--mut);cursor:pointer;
  transition:color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease);
}
/* The 44px target, invisible, centred on the ring. */
.st-info__btn::after{content:"";position:absolute;inset:-13px;}
.st-info__btn:hover,.st-info__btn[aria-expanded="true"]{color:var(--ink);border-color:var(--acc-line);}

.st-info__bubble{
  position:absolute;z-index:60;top:calc(100% + 8px);
  left:calc(-9px + var(--st-info-nudge, 0px));
  width:max-content;max-width:min(300px,72vw);
  padding:var(--s3);
  background:var(--surface);border:1px solid var(--line-ui);border-radius:var(--r-md);
  box-shadow:var(--shadow-stuck);
  font-family:var(--sans);font-size:var(--fs-small);font-weight:400;line-height:1.55;
  color:var(--ink2);text-align:left;text-transform:none;letter-spacing:0;
  white-space:normal;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity var(--dur-1) var(--ease),visibility var(--dur-1) var(--ease);
  /* A CLOSED BUBBLE TAKES NO SPACE (STICA-566, 2026-08-29). visibility:hidden
     keeps the layout box, and an absolutely positioned box still counts towards
     the page's scrollable width, so a shut bubble whose icon sat near the right
     edge made /coach/session wider than the phone. Chrome then shrinks the page
     to fit, which widens the layout viewport, and THAT is what made the nudge
     below unable to rescue it: placeInfo() measures against window.innerWidth,
     which by then is the widened figure, so it can only ever pull the bubble
     back to the wide edge and never to the real one. Measured on the seeded
     review screen in a 390px phone: 431 unplaced, 420 with the nudge doing its
     best, 390 once a shut bubble stops being laid out at all. Zero-sizing is
     safe for the nudge because every path that measures the box (hover, focus,
     and the click handler, which sets aria-expanded first) has already matched
     one of the open rules below by the time offsetWidth is read. */
  width:0;height:0;overflow:hidden;
}
/* Near an edge, a left-anchored bubble runs off the screen, and a sentence half
   off the screen is worse than no sentence. session.html's placeInfo() measures
   the real box on the way in and sets --st-info-nudge to exactly how far it
   overhangs, either side; a bubble that already fits gets no nudge at all. It is
   not flipped to the other edge, because on a narrow window an icon can be too
   close to BOTH and flipping just moves which third gets cut off. */
.st-info:hover .st-info__bubble,
.st-info__btn:focus-visible ~ .st-info__bubble,
.st-info__btn[aria-expanded="true"] ~ .st-info__bubble{
  opacity:1;visibility:visible;pointer-events:auto;
  /* The size comes back with the bubble, and is not transitioned: it snaps to
     full size as the fade begins, so nothing is ever read half-clipped. */
  width:max-content;height:auto;overflow:visible;
}
/* SPANS, NOT PARAGRAPHS, and the reason is in session.html's info(): several of
   these icons sit at the end of a line of body copy, and a <p> inside a <p> is
   closed by the parser, which threw the whole bubble out of its wrapper and
   printed it as page text. A block-level span nests anywhere. */
.st-info__line{display:block;}
.st-info__line + .st-info__line{margin-top:var(--s2);}


/* ---------------------------------------------------------------------
   19. THE REVIEW SCREEN AS THREE STAGES (Ian, 2026-08-17, second round)

   He talked through the built page and asked for the whole thing to be
   readable by "someone who's never looked at this page before": tighter
   spacing, "a really nice, clean, crisp list", the explanations behind an
   icon, and the takeaway after the facts rather than before them.

   Nothing here is a new surface. The stages are the panels that already
   existed, in a different order, with a band between two of them.
   --------------------------------------------------------------------- */

/* A heading with the things that belong to it on its own line: the icon that
   explains it, the size of it, the tick that approves it. They used to be
   stacked under the heading, one line each, which on five parts was fifteen
   lines of chrome around the content. */
.ch-panelhead{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2) var(--s3);
  margin:0 0 var(--s3);
}
.ch-panelhead > h2{margin:0;}
.ch-panelhead > .ch-part__size{margin-left:0;padding-left:0;}
.ch-panelhead > .ch-tickbtn{margin-left:auto;}

/* Where the work has got to. Three words, the reached ones marked, and
   nothing that reads as a form to fill in: it is a map of the page, not a
   wizard, and every stage stays on the same page and stays reachable. */
.ch-steps{margin:0 0 var(--s4);}
.ch-steps__list{
  list-style:none;margin:0;padding:0;
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);
}
.ch-step{display:flex;align-items:center;gap:var(--s2);}
.ch-step + .ch-step::before{
  content:"";width:14px;height:1px;background:var(--line-ui);flex:none;
}
.ch-step a{
  font-size:var(--fs-small);color:var(--mut);text-decoration:none;
  min-height:32px;display:inline-flex;align-items:center;
}
.ch-step a:hover{color:var(--ink);}
.ch-step--now a{color:var(--ink);font-weight:600;}
.ch-step--done a{color:var(--ink2);}
.ch-step--done a::before{
  content:"\2713";margin-right:var(--s1);color:var(--green);font-weight:700;
}

/* The stage boundary. A band rather than a panel, for the same reason the
   length bar is one: it is a moment in the page, not a section of it. */
.ch-confirm{
  margin:0 0 var(--s5);padding:var(--s4) 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.ch-confirm > .st-body-sm{margin:0 0 var(--s3);}
.ch-confirm__left{margin:var(--s2) 0 0;color:var(--mut);}
.ch-confirm--done{padding:var(--s3) 0;}
.ch-confirm--done > .st-body-sm{margin:0;color:var(--ink2);}
.ch-confirm__tick{color:var(--green);font-weight:700;margin-right:var(--s1);}

/* The takeaway before it is opened, and the offer to write it again. Both are
   quiet: neither is a warning, and a folded part is not an error state. */
.ch-folded > .st-body-sm{color:var(--ink2);}
.ch-rewrite{
  margin:0 0 var(--s4);padding:var(--s3) var(--s4);
  border:1px solid var(--line-ui);border-radius:var(--r-md);
  background:var(--surface);
}
.ch-rewrite > .st-body-sm{margin:0;}
.ch-rewrite .ch-actions{margin-top:var(--s3);}
.ch-rewrite > .st-small{margin:var(--s2) 0 0;color:var(--mut);}

/* A flag from the recording check. The one thing on this page that is allowed
   to catch an eye, and even then by a hairline on one edge rather than by
   colour across the whole panel: it means "look at this line", never "this is
   wrong". */
.ch-flagged{border-left:2px solid var(--amber);}

/* The record and the recording, at the foot. */
.ch-recording{margin-top:var(--s4);padding-top:var(--s4);border-top:1px solid var(--line);}
.ch-recording > .st-body-sm{margin:0;}
.ch-checkline{
  margin:var(--s4) 0 0;padding-top:var(--s3);
  border-top:1px solid var(--line);color:var(--mut);
}
.ch-verline{margin:0 0 var(--s2);color:var(--mut);}

/* An icon that does something, rather than a word pretending to be a link.
   Two of them sit at the end of a note's own line. 44px of target under an
   18px glyph, the same trick .st-info__btn uses and for the same reason: a
   44px box drawn in the middle of a line of small print would push the line
   apart. */
.ch-iconbtn{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;padding:0;flex:none;
  background:transparent;border:1px solid transparent;border-radius:var(--r-sm);
  color:var(--mut);cursor:pointer;
  transition:color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease);
}
.ch-iconbtn::after{content:"";position:absolute;inset:-9px;}
.ch-iconbtn:hover{color:var(--ink);border-color:var(--line-ui);}
.ch-iconbtn--danger:hover{color:var(--oxide);border-color:var(--oxide);}
.ch-iconacts{display:inline-flex;align-items:center;gap:var(--s1);margin-left:auto;}
.ch-note-row__meta{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2);}

/* THE TICK AS A ROW CONTROL. The same component as a part's tick, sized to sit
   at the end of a one-line row rather than beside a heading. */
.ch-tickbtn--row{
  min-height:32px;padding:4px 10px;font-size:12px;align-self:center;
}
.ch-tickbtn--row::after{content:"";position:absolute;inset:-6px;}
.ch-tickbtn{position:relative;}

/* ---- tighter, which was half of what he asked for ---------------------

   "I'm not so keen on the spacing of all of this ... I really like the idea of
   it being a lot cleaner in terms of a list of commitments." Measured on the
   fixture before this block: one action occupied 178px of vertical space, of
   which 30 was the text. Three actions filled a laptop screen.

   Nothing here shrinks a touch target. The 44px minimum is kept on every
   control; what comes back is the air around them, the double margins where a
   row's last child met the next row's first, and the second border where a
   dashed rule sat a few pixels from a solid one. */
.ch-part{border-top-color:var(--line);}
.ch-part__head{padding:var(--s2) 0;min-height:40px;}
.ch-part__headrow{
  display:flex;align-items:center;gap:var(--s2) var(--s3);
  min-height:44px;
}
.ch-part__headrow > .ch-part__head{flex:1 1 auto;width:auto;}
.ch-part__headrow > .ch-tickbtn{flex:none;min-height:32px;padding:4px 10px;font-size:12px;}
.ch-part__body{padding:var(--s1) 0 var(--s3);}

.ch-plan{margin:0;padding:0;border-top:0;}
.ch-commit{padding:var(--s3) 0;gap:var(--s2) var(--s3);}
.ch-commit__edit{margin-bottom:0;}
.ch-commit__acts{margin-top:var(--s1);}
.ch-quiet-wrap{margin-top:var(--s1);}
.ch-quiet{margin-top:var(--s1);padding-top:var(--s2);gap:var(--s2);}
.ch-quiet__label{min-width:16ch;}
.ch-commit__state{align-self:center;}

/* The date box inside the fold is a field in a row of fields now, not a
   control riding a line of buttons, so it stops borrowing the button height. */
.ch-quiet__row > .ch-duebox{min-height:32px;padding:4px 8px;}

/* A promise is one line and a fold. The three-answer row only exists inside
   the fold now, so it needs no top margin fighting the date above it. */
.ch-commit .ch-quiet .ch-commit__acts{flex:1 0 100%;margin-top:var(--s2);}

/* ---------------------------------------------------------------------
   19. what a session told a coach about the person, and the record read
       rather than edited (2026-08-17)
   --------------------------------------------------------------------- */

/* A part shown rather than typed into. It has to read as prose and not as a
   disabled editor: no box, no border, no grey fill, because every one of those
   says "you could type here if something were not wrong". */
.ch-part__read > .st-body-sm{margin:0 0 var(--s2);}
.ch-part__read > .st-body-sm:last-child{margin-bottom:0;}
.ch-read__list{margin:0 0 var(--s2);padding-left:1.1em;max-width:48ch;}
.ch-read__list li{
  margin:0 0 var(--s1);
  font-size:var(--fs-body-sm);line-height:1.55;color:var(--ink2);
}
.ch-read__list:last-child{margin-bottom:0;}

/* One thing the session taught the coach, and the one or two controls that
   answer it. The text takes the measure and the controls sit at the end of the
   line, dropping under it when there is not room for both. */
.ch-insight-group{margin-top:var(--s4);}
.ch-insight-group .ch-panelhead{margin-bottom:var(--s1);}
/* The same weight and size a part's own title carries, because it is the same
   thing one level down: a name for a short list underneath it. Deliberately
   not .st-h4, which is the panel heading above it and would make the two read
   as siblings. */
.ch-insight-group__title{
  font-size:var(--fs-body);font-weight:600;line-height:1.35;
  color:var(--ink);margin:0;
}
.ch-insights{list-style:none;margin:0;padding:0;}
.ch-insight{
  display:flex;align-items:flex-start;flex-wrap:wrap;
  gap:var(--s2) var(--s3);
  padding:var(--s3) 0;border-top:1px solid var(--line);
}
.ch-insight__text{flex:1 1 26ch;min-width:0;}
.ch-insight__text > .st-body-sm{margin:0;}
.ch-insight__text > .st-small{margin:var(--s1) 0 0;color:var(--mut);}
.ch-insight__quote{
  margin:var(--s1) 0 0;padding-left:var(--s3);
  border-left:2px solid var(--line-ui);
  font-size:var(--fs-body-sm);line-height:1.55;font-style:italic;color:var(--mut);
}
.ch-insight__do{display:flex;align-items:center;gap:var(--s2);flex:none;margin-left:auto;}
.ch-insight__state{
  font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);white-space:nowrap;
}
/* Removed, and still legible. Struck through would make a coach squint at the
   thing they may want back; muted says the same and stays readable. */
.ch-insight--out .ch-insight__text{opacity:0.55;}

/* The same quoted line on the client's own page, where it sits inside a row
   built for commitments rather than for insights. Same treatment as
   .ch-insight__quote, and it is a second selector rather than a shared class
   because the two pages share no stylesheet section and merging them would
   couple a row that can be ticked to one that cannot. */
/* THE QUOTE UNDER THE LINE, NEVER BESIDE IT (coach-review-2, F, wave 3).
   .ch-commit is `display:flex;flex-wrap:wrap`, and .ch-commit__what carries
   `flex:1 1 0;min-width:0` (this file's own comment above that rule: "lets
   it actually wrap its own text rather than refusing to go below its
   longest word"). Without a basis of its own, this quote sat as a flex
   SIBLING of that column on the same row whenever it was short enough to
   fit, and .ch-commit__what shrank to make room for it -- squeezing a
   client's own words to one word per line ("He / said / the / transparent
   ...") at 1280, seen on the live page and still there after the first
   review. flex:1 0 100% is the same fix .ch-planform already uses for a
   follow-up form beside a commitment row, for the identical reason. */
/* SAME FIX, SAME REASON, A THREAD'S OWN "Waiting on" (coach-review-2, F,
   wave 3): another bare paragraph that used to sit as a flex sibling of
   .ch-commit__what with no basis of its own, squeezed beside it the same
   way the quote above was. Body size too (coach-review-2, E2): it used to
   be .st-small (--fs-small, a footnote), and a thread a coach is waiting
   on is something they act on, not a caption. */
.ch-commit__waiting{
  flex:1 0 100%;
  margin:var(--s1) 0 0;font-size:var(--fs-body-sm);color:var(--mut);
}
.ch-learned__quote{
  flex:1 0 100%;
  margin:var(--s1) 0 0;padding-left:var(--s3);
  border-left:2px solid var(--line-ui);
  font-size:var(--fs-body-sm);line-height:1.55;font-style:italic;color:var(--mut);
}

.ch-addinsight{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);
  margin-top:var(--s4);padding-top:var(--s3);border-top:1px solid var(--line);
}
.ch-addinsight > .st-field__label{flex:1 0 100%;margin:0;}
.ch-addinsight > select{flex:0 1 22ch;}
.ch-addinsight > input{flex:1 1 24ch;min-width:0;}

@media (max-width:560px){
  /* The tick drops under the heading rather than squeezing the title to two
     words. Everything else already wraps. */
  .ch-part__headrow{flex-wrap:wrap;}
  .ch-panelhead > .ch-tickbtn{margin-left:0;}
  .ch-iconacts{margin-left:0;}
  /* The controls go under the sentence they are about rather than crushing it
     to a four-word column. */
  .ch-insight__do{margin-left:0;}
}

/* ===================================================================
   THE REVIEW SCREEN, REBUILT (front-end review, 2026-08-19).
   "Check what came out, then send it" -- moved here from the prototype's
   own <style> block (design/prototype/2026-08-19-review/check-and-send.html)
   with the rv- prefix kept exactly, per the brief ("keep the rv- prefix;
   light and dark both hold because they use the tokens"). Dark is pinned
   in the prototype; here it follows the device, as every other coach
   screen already does, so no data-theme override is copied across.
   =================================================================== */
.rv-main{max-width:1180px;margin-inline:auto;padding-inline:var(--gutter);padding-block:var(--s6) var(--s10);}

.rv-head{margin:0 0 var(--s5);}
.rv-head .st-h1{font-size:34px;margin-bottom:var(--s2);}
.rv-head .st-lede{font-size:var(--fs-body);margin-bottom:var(--s4);}
.rv-steps{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);margin:0 0 var(--s3);padding:0;list-style:none;}
.rv-step{display:flex;align-items:center;gap:var(--s2);font-size:var(--fs-body-sm);color:var(--mut);min-height:32px;}
.rv-step + .rv-step::before{content:"";width:18px;height:1px;background:var(--line-ui);margin-right:var(--s1);}
.rv-step__n{width:22px;height:22px;border-radius:var(--r-dot);border:1px solid var(--line-ui);display:inline-flex;align-items:center;justify-content:center;font-family:var(--mono);font-size:11px;color:inherit;flex:none;}
.rv-step--now{color:var(--ink);font-weight:600;}
.rv-step--now .rv-step__n{background:var(--acc-fill);border-color:var(--acc-fill);color:#fff;}
.rv-fresh{margin:0;font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);}
.rv-stale{margin:0 0 var(--s4);padding:var(--s3) var(--s4);border:1px solid var(--line-ui);border-radius:var(--r-lg);background:var(--amber-bg);color:var(--amber);}

/* THE TITLE AND ITS "Change the date" LINK, SIDE BY SIDE (coach-review-2,
   D3, wave 3): a plain flex row so the link sits beside the date rather than
   under it, wrapping onto its own line at phone width where the title
   itself already wraps. */
.rv-head__title{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s3);margin-bottom:var(--s2);}
.rv-head__title .st-h1{margin-bottom:0;}
.rv-datechange{font-size:var(--fs-body-sm);}
span.rv-datechange{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);}
span.rv-datechange .st-input{width:auto;}

/* "Last refreshed" and the notes toggle, side by side under the explaining
   sentence (coach-review-2, D2). */
.rv-headrow{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);margin:var(--s2) 0 0;}

/* Two columns: the document (60) and the recording (40). */
.rv-cols{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:var(--s6);align-items:start;}
@media (max-width:960px){.rv-cols{grid-template-columns:1fr;}}

.rv-sect{margin:0 0 var(--s6);}
.rv-sect__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s3);margin:0 0 var(--s3);}
.rv-sect__head .st-h3{margin:0;font-size:18px;}
.rv-sect__count{font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);white-space:nowrap;}
/* WHOSE WORDS, AND WHOSE VOICE (coach-review-2, C3): the one-line sub-heading
   under a section title that says who a list is written to and in what
   voice, so a coach never has to infer it from which card they are reading. */
.rv-sect__sub{margin:2px 0 0;font-size:var(--fs-small);color:var(--mut);}

.rv-cards{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2);}
.rv-card{
  position:relative;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s3) var(--s4) var(--s3) 34px;
  transition:border-color var(--dur-1) var(--ease);
}
.rv-card:hover,.rv-card.is-hover{border-color:var(--line2);}
.rv-card:focus-visible{outline:3px solid var(--acc-line);outline-offset:2px;}
.rv-card.is-selected{box-shadow:inset 3px 0 0 0 var(--acc-line);}
.rv-grip{
  position:absolute;left:8px;top:14px;width:16px;height:22px;border:0;background:none;cursor:grab;padding:0;color:var(--line-ui);
}
.rv-grip:hover{color:var(--mut);}
.rv-grip svg{display:block;}
.rv-card:not(:hover):not(.is-hover):not(:focus-within) .rv-grip,
.rv-card:not(:hover):not(.is-hover):not(:focus-within) .rv-more{opacity:0;pointer-events:none;}
.rv-grip,.rv-more{transition:opacity var(--dur-1) var(--ease);}
.rv-card__row{display:flex;align-items:flex-start;gap:var(--s3);}
.rv-card__main{flex:1 1 auto;min-width:0;}
.rv-edit{
  margin:0;font-size:var(--fs-body);line-height:1.5;font-weight:500;color:var(--ink);
  padding:2px 0;border-bottom:1px dotted transparent;display:inline-block;max-width:100%;
  transition:border-color var(--dur-1) var(--ease);cursor:text;
}
.rv-card:hover .rv-edit,.rv-card.is-hover .rv-edit,.rv-edit:hover{border-bottom-color:var(--line-ui);}
.rv-edit:focus{outline:none;border-bottom-color:var(--acc-line);border-bottom-style:solid;}
.rv-edit--plain{cursor:default;}
.rv-edit--plain:hover,.rv-edit--plain:focus{border-bottom-color:transparent;}
.rv-pencil{display:none;color:var(--mut);vertical-align:-2px;margin-left:6px;}
.rv-card:hover .rv-pencil,.rv-card.is-hover .rv-pencil{display:inline-block;}
.rv-hint{
  display:none;margin-left:var(--s2);vertical-align:2px;
  font-family:var(--mono);font-size:var(--fs-chip);letter-spacing:.5px;text-transform:uppercase;color:var(--mut);white-space:nowrap;
}
.rv-card:hover .rv-hint,.rv-card.is-hover .rv-hint{display:inline-block;}
.rv-date{
  flex:none;display:inline-flex;align-items:center;gap:6px;min-height:36px;padding:0 10px;
  font-family:var(--mono);font-size:var(--fs-small);letter-spacing:.3px;color:var(--ink2);
  background:transparent;border:1px solid var(--line-ui);border-radius:var(--r-md);cursor:pointer;white-space:nowrap;
  transition:border-color var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
}
.rv-date:hover{border-color:var(--acc-line);color:var(--ink);}
.rv-date--none{border-style:dashed;color:var(--mut);}
.rv-date svg{flex:none;}
.rv-detail{margin:var(--s1) 0 0;font-size:var(--fs-body-sm);line-height:1.5;color:var(--mut);}
.rv-detail b{font-weight:500;color:var(--ink2);}
.rv-detail .sep{margin:0 6px;color:var(--line-ui);}
.rv-card__acts{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);margin-top:var(--s3);}
/* THREE WEIGHTS, NOT THREE COLOURS (Ian, 2026-08-28, sending a screenshot of
   this row: "add nicely formatted buttons instead of the hyperlink text we
   have across the site and ensure good spacing and legibility").
   The row under an action card holds three different kinds of thing -- a
   toggle that changes the client's record, a jump to the place in the
   recording, and a destructive remove -- and they were identical grey text
   with no border, separated only by colour. Nothing said which of them
   pressed, and the one that deletes looked exactly like the one that reads.

   The size was already right: a 2026-08-19 round raised these to
   --fs-body-sm/44px because they are act-on controls and not captions, and
   that stays. What was missing was the shape, and it was already written
   twenty lines up as .ch-mark, whose comment carries the caution that still
   binds: three full-sized buttons a row is sixty-three of them down one
   page, shouting louder than the actions they are about. So this borrows
   .ch-mark exactly -- 44px target, tight padding, a hairline instead of a
   fill -- rather than reaching for .st-btn. It also makes this row and the
   client page's own controls one control instead of two, which is the same
   complaint one screen over.

   The weights, quietest first: --src is a reference, so it is dashed, the
   same way .rv-date--none says "nothing here yet" in this stylesheet; the
   base and --danger are hairline and differ only on hover, because a remove
   should not shout until it is under the cursor; --pin is the act a coach
   takes most, so it alone carries ink-coloured text unpressed, and the green
   it turns when set is the client page's own tick colour. */
.rv-quietbtn{
  min-height:44px;padding:6px 12px;border-radius:var(--r-md);
  display:inline-flex;align-items:center;gap:6px;
  font:inherit;font-size:var(--fs-body-sm);font-weight:500;
  color:var(--mut);background:transparent;border:1px solid var(--line2);
  cursor:pointer;text-decoration:none;
  transition:color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease);
}
.rv-quietbtn:hover{color:var(--ink);border-color:var(--line-ui);}
.rv-quietbtn--src{border-style:dashed;}
.rv-quietbtn--src:hover{color:var(--ink);border-color:var(--acc-line);}
.rv-quietbtn--danger:hover{color:var(--oxide);border-color:var(--oxide);}
/* "Bring this up next time", the one control an action, a thread left open
   and a learned line all share (2026-08-28). A quiet button like the rest
   of the act bar, not a checkbox: it used to be a checkbox on threads
   alone, and a coach met the same decision in two shapes. The tick is
   ghosted until it is set, so an unpressed one does not read as already
   done, and the set state carries the same green the client page's own
   tick uses (.ch-tickbtn--on above). */
/* A CLIENT'S NAME IN A TITLE IS STILL THE TITLE (2026-08-28, the round that
   made every name a door). The session page's h1 is "{name}'s write-up ·
   session of 27 August", and the name arriving as an ordinary link put a
   bold violet underline through the largest text on the screen: the door
   was shouting louder than the heading it sits in. It keeps the accent
   colour, so it is visibly a link, and takes the underline only on hover
   and on keyboard focus, where a person is asking what it does. */
.ch-namelink,.st-h1 a{color:var(--acc-text);text-decoration:none;}
.ch-namelink:hover,.st-h1 a:hover,.ch-namelink:focus-visible,.st-h1 a:focus-visible{text-decoration:underline;text-underline-offset:3px;}

.rv-quietbtn--pin{color:var(--ink2);border-color:var(--line-ui);}
.rv-quietbtn--pin svg{flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;opacity:.3;}
.rv-quietbtn--pin[aria-pressed="true"]{color:var(--green);border-color:var(--green);font-weight:600;}
.rv-quietbtn--pin[aria-pressed="true"] svg{opacity:1;}
.rv-card__acts .push{margin-left:auto;}
.rv-minibtn{
  min-height:32px;padding:0 12px;border-radius:var(--r-md);cursor:pointer;
  font:inherit;font-size:13px;font-weight:600;color:var(--ink);
  background:color-mix(in srgb,var(--ink) 5%,transparent);border:1px solid var(--line-ui);
  transition:border-color var(--dur-1) var(--ease),background-color var(--dur-1) var(--ease);
}
.rv-minibtn:hover{border-color:var(--acc-line);background:color-mix(in srgb,var(--ink) 9%,transparent);}
.rv-detail--flag{display:flex;align-items:flex-start;gap:8px;}
.rv-detail--flag .rv-flagmark{margin-top:1px;}
.rv-flagmark{
  display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:var(--r-dot);flex:none;
  background:var(--amber-bg);color:var(--amber);border:0;cursor:pointer;padding:0;
}
.rv-more-panel{margin-top:var(--s2);padding-top:var(--s2);border-top:1px dashed var(--line);}
.rv-add{
  border:1px dashed var(--line2);border-radius:var(--r-lg);padding:var(--s3) var(--s4);background:transparent;
}
.rv-add label{display:block;font-size:13px;color:var(--mut);margin:0 0 var(--s2);}
.rv-tally{margin:var(--s3) 0 0;font-size:var(--fs-body-sm);color:var(--mut);}
/* THE TALLY LINE READS TRUE AT ITS OWN WIDTH (coach-review-2, D4/F, wave 3):
   "The recording is on the right" was true at desk width and false the
   moment the recording becomes a drawer -- the same breakpoint .rv-rec's
   own @media (max-width:960px) block below switches it into one. Desk
   keeps a plain sentence; phone gets a button that opens the drawer,
   never a sentence describing a layout that is not there. */
.rv-tally__phone{display:none;}
@media (max-width:960px){
  .rv-tally__desk{display:none;}
  .rv-tally__phone{display:inline-flex;}
}
.rv-row{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--s3) var(--s4);margin-bottom:var(--s2);}
.rv-quote{font-style:italic;color:var(--ink);font-size:var(--fs-body);line-height:1.5;margin:0;}
.rv-check{display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--ink2);cursor:pointer;min-height:32px;}
.rv-check input{width:16px;height:16px;accent-color:var(--acc-fill);margin:0;}
/* BODY SIZE, NOT A FOOTNOTE (coach-review-2, E2, wave 3): "Only you see
   this" is a promise a coach relies on when they add one, not a caption. */
.rv-only{margin:var(--s2) 0 0;font-size:var(--fs-body-sm);color:var(--mut);}
.rv-fold{border-top:1px solid var(--line);border-bottom:1px solid var(--line);margin:0 0 var(--s6);}
.rv-fold summary{list-style:none;display:flex;align-items:center;gap:var(--s3);min-height:44px;cursor:pointer;font-weight:600;color:var(--ink);}
.rv-fold summary::-webkit-details-marker{display:none;}
.rv-fold summary::before{content:"";width:7px;height:7px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(-45deg);margin-left:4px;flex:none;color:var(--mut);}
.rv-fold[open] summary::before{transform:rotate(45deg);}
.rv-fold summary .n{margin-left:auto;font-family:var(--mono);font-size:var(--fs-small);font-weight:400;color:var(--mut);}
.rv-fold__body{padding:0 0 var(--s4) 22px;}

.rv-approve{margin:var(--s6) 0 0;padding:var(--s5) 0 0;border-top:1px solid var(--line);}
.rv-approve .st-body{margin-bottom:var(--s4);max-width:60ch;}
.rv-approve__row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3) var(--s4);}
.rv-kbd{font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);}
.rv-kbd kbd{font:inherit;border:1px solid var(--line-ui);border-radius:var(--r-sm);padding:1px 5px;color:var(--ink2);}
.rv-approve__after{margin:var(--s3) 0 0;font-size:13px;color:var(--mut);}
.rv-approve__self{margin:var(--s3) 0 0;font-size:13px;}
.rv-approve__self a{color:var(--mut);display:inline-flex;align-items:center;min-height:32px;text-underline-offset:2px;}
.rv-approve__self a:hover{color:var(--ink);}
.rv-approve__note{margin:0;font-size:13px;color:var(--mut);}

.rv-toast{
  position:sticky;bottom:var(--s4);margin:var(--s5) 0 0;
  display:inline-flex;align-items:center;gap:var(--s4);
  padding:10px 14px;border-radius:var(--r-lg);background:var(--ink);color:var(--bg);
  font-size:13px;box-shadow:var(--shadow);
}
.rv-toast button{background:none;border:0;padding:0;min-height:24px;font:inherit;font-weight:700;color:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer;}

/* Stage two's quiet line where the approve band used to be, and the notes
   drawer that replaces it (and the released view's own message panel)
   everywhere else: row 2 of the front-end review repair, 2026-08-19. */
.rv-checked{margin:var(--s6) 0 0;padding:var(--s5) 0 0;border-top:1px solid var(--line);}
.rv-covernote{margin:var(--s5) 0;}
.rv-covernote .st-textarea{min-height:60px;}
/* THE NOTES BOX, NOW IN THE HEADER (coach-review-2, D2, wave 3): opened by
   notesToggleHtml()'s own link beside "Last refreshed", in place, rather
   than a <details> drawer at the foot of the page. Plain block, the same
   border and padding the record fold's own read/edit body already carries,
   so it reads as one more thing in the header rather than a stray box. */
.rv-notesinline{margin:var(--s4) 0 0;padding:var(--s4);border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);}

/* The recording pane's own close button, reachable at every width and
   meaningful only at the drawer breakpoint (row 6 of the front-end review
   repair, 2026-08-19). */
.rv-recclose{
  display:none;position:absolute;top:var(--s3);right:var(--s3);
  width:32px;height:32px;border-radius:var(--r-md);border:0;background:none;
  font-size:20px;line-height:1;color:var(--mut);cursor:pointer;
}
.rv-recclose:hover{color:var(--ink);background:var(--line2);}

/* A card's own in-place remove confirm (row 3 of the front-end review
   repair, 2026-08-19: Backspace on a focused card). */
.rv-detail--confirm{color:var(--amber);font-weight:600;}

/* RIGHT: the recording. Sticky, its own scroll.
   OFFSET BELOW THE STICKY HEADER, NOT THE VIEWPORT (row 8 of the front-end
   review repair, 2026-08-19). assets/stica.css's own .st-head is
   position:sticky, top:0, and about 69px tall; a right-hand pane also
   sticking to top:16px sat UNDER it every time a coach scrolled, its own
   top third hidden behind the header. There is no shared header-height
   variable to read here (checked; stica.css names none), so this is the
   measured value, matched by the max-height's own headroom. */
.rv-rec{
  position:sticky;top:76px;max-height:calc(100vh - 96px);
  display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
}
.rv-rec__head{padding:var(--s4) var(--s4) var(--s3);border-bottom:1px solid var(--line);position:relative;}
.rv-rec__head .st-h3{font-size:18px;margin:0 0 var(--s3);}
.rv-search{position:relative;}
.rv-search .st-input{padding-left:36px;min-height:40px;font-size:var(--fs-body-sm);}
.rv-search svg{position:absolute;left:12px;top:12px;color:var(--mut);}
/* AMBER IS THE WARNING'S OWN, NOT THE STRIP'S (Ian, 2026-08-28). This lived
   on .rv-flags, so the two states that carry GOOD news -- "nothing in it
   disagrees", and "not checked yet" -- were painted in the alarm colour, on
   a panel where nothing else is. Only .rv-flags--warn, which flagsStripHtml()
   sets on the flagged branch alone, asks for it now. */
.rv-flags{border-bottom:1px solid var(--line);}
.rv-flags--warn{background:var(--amber-bg);}
.rv-flags--plain{padding:var(--s3) var(--s4);color:var(--mut);}
.rv-flags summary{
  list-style:none;display:flex;align-items:center;gap:var(--s2);min-height:44px;padding:0 var(--s4);
  font-size:13px;font-weight:600;color:var(--amber);cursor:pointer;
}
.rv-flags summary::-webkit-details-marker{display:none;}
.rv-flags summary .chev{margin-left:auto;width:7px;height:7px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg);}
.rv-flags[open] summary .chev{transform:rotate(-135deg);}
.rv-flag{padding:0 var(--s4) var(--s3);}
.rv-flag p{margin:0 0 var(--s2);font-size:13px;line-height:1.5;color:var(--ink);}
.rv-turns{list-style:none;margin:0;padding:var(--s3) 0 var(--s3) 0;overflow-y:auto;flex:1 1 auto;}
.rv-turn{
  display:grid;grid-template-columns:64px 1fr;gap:0 var(--s3);
  padding:6px var(--s4) 6px 0;position:relative;
}
.rv-rec__meta{margin:0;padding:var(--s2) var(--s4) 0;border-bottom:1px solid var(--line);}
.rv-rec__metarow{display:grid;grid-template-columns:minmax(72px,auto) 1fr;gap:var(--s2);padding:2px 0;font-size:var(--fs-small);color:var(--mut);}
/* A LABEL WRAPS RATHER THAN RUNNING OVER ITS NEIGHBOUR (Ian, 2026-08-28:
   "overlapping text below that"). The markdown fix in coach/session.html is
   what makes these labels short again, but a transcript is somebody else's
   file and the next one will carry something long: a speaker column that
   cannot wrap prints the label straight across the words beside it. */
.rv-rec__metarow dt{font-family:var(--mono);text-align:right;overflow-wrap:anywhere;min-width:0;}
.rv-rec__metarow dd{margin:0;min-width:0;}
.rv-turn__who{font-family:var(--mono);font-size:var(--fs-small);color:var(--mut);text-align:right;padding-top:3px;overflow-wrap:anywhere;min-width:0;}
.rv-turn__who--c{color:var(--acc-text);}
.rv-turn__what{margin:0;font-size:var(--fs-body-sm);line-height:1.55;color:var(--ink2);}
.rv-turn mark{
  background:color-mix(in srgb,var(--acc) var(--hl-mix),transparent);color:var(--ink);
  border-radius:var(--r-sm);padding:1px 2px;box-shadow:0 0 0 1px color-mix(in srgb,var(--acc) 40%,transparent);
}
.rv-turn--hit{background:color-mix(in srgb,var(--acc) 6%,transparent);}
.rv-turn--hit .rv-turn__what{color:var(--ink);}
.rv-turn.is-selected{box-shadow:inset 3px 0 0 0 var(--acc-line);}
.rv-mark{
  position:absolute;right:var(--s2);top:6px;
  font-family:var(--mono);font-size:var(--fs-chip);letter-spacing:.5px;text-transform:uppercase;
  color:var(--mut);opacity:.75;white-space:nowrap;
}
.rv-mark--on{color:var(--acc-text);opacity:1;font-weight:500;}
.rv-turn--marked .rv-turn__what{padding-right:64px;}

/* Stage two: the phone-shaped preview of the client's own page. */
.rv-phoneframe{display:flex;justify-content:center;margin:var(--s4) 0;}
.rv-phone{
  width:390px;height:640px;max-width:100%;border:8px solid var(--ink);border-radius:28px;
  background:var(--bg);
}

/* At phone width the recording is a drawer, opened from any card's "From
   the recording" link -- not tuned further than that (the brief's own
   words). */
@media (max-width:960px){
  .rv-rec{
    position:fixed;inset:0 0 0 auto;top:0;height:100vh;max-height:none;width:min(420px,100vw);
    z-index:40;transform:translateX(100%);transition:transform var(--dur-2) var(--ease);border-radius:0;
  }
  .rv-rec--open{transform:translateX(0);}
  .rv-recclose{display:block;}
  /* The backdrop a tap outside the open drawer closes it against (row 6 of
     the front-end review repair, 2026-08-19); the close itself is JS
     (wireRecordingPane's own document-level click listener), this is only
     what makes "outside" visible as somewhere real. */
  .rv-rec--open::before{
    content:'';position:fixed;inset:0;z-index:-1;background:color-mix(in srgb, var(--ink) 32%, transparent);
  }
}

/* THE ONE SCREEN A COACH READS BEFORE ADDING A CLIENT THEMSELVES (Ian's
   ruling 2026-08-26, wave 2). Ian chose the full wording every time over
   once per coach, on one condition: it fits without scrolling, because a
   dialog that has to be scrolled is one that gets clicked through by the
   fifth client. So the box is capped at 90% of the viewport's own height
   and the type inside it stays at the small body size -- if a later change
   makes this scroll, the change is what is wrong, not the cap.

   A native <dialog>: its backdrop swallows a click rather than dismissing,
   which is the behaviour asked for, and it takes the page's own focus trap
   and Escape handling from the browser rather than from JS we would have
   to keep right.

   THIS IS A CARD, CASE C (sheet-and-empty-states spec §3.2): it floats
   above the page in a dialog, the one place --shadow survives. --surface
   plus --line2, not --bg with a --line hairline: a box lifted off the
   page is an object on the sheet, the same as any other card, and it
   keeps the same edge every other card takes. */
.ch-attest{
  width:min(34rem,calc(100vw - 2rem));max-height:90vh;
  padding:var(--sp-5,20px);border:1px solid var(--line2);border-radius:var(--r-lg);
  background:var(--surface);color:var(--ink);box-shadow:var(--shadow);
}
.ch-attest::backdrop{background:color-mix(in srgb, var(--ink) 40%, transparent);}
.ch-attest p{margin:0 0 .7em;}
/* A card never contains a card, so the tick row inside this dialog is a
   block: the rule above it and air, not a second box inside the first. */
.ch-attest__tick{
  display:flex;gap:.5rem;align-items:flex-start;margin:1rem 0;padding-top:.7rem;
  border-top:1px solid var(--line);
}
.ch-attest__tick input{margin-top:.15rem;flex:0 0 auto;}
.ch-attest__acts{display:flex;gap:.6rem;flex-wrap:wrap;}

/* A sign-out that sits inside a sentence (the "not open yet" panel on the
   dashboard): the ghost button's 44px tap height and 15px weight would
   break the line, so it takes the paragraph's own size and no box. */
.st-btn--ghost.ch-inline-btn{display:inline;min-height:0;padding:0;font-size:inherit;line-height:inherit;vertical-align:baseline;}

/* ---------------------------------------------------------------------
   THE CLIENT PAGE IS TILES (2026-08-28, Ian: "Build it",
   ans-20260828-190013-67e8)

   It was one scroll 5,853px deep across ten sections, two of which were 44%
   of it, with 620px of once-a-year settings sitting under everything a coach
   needs daily. Six tiles now land above the fold and each one opens its
   section in place. Every section still renders; a panel that is not showing
   is hidden, not absent.

   The control on a tile is a BUTTON. It borrows .rv-quietbtn, which borrowed
   .ch-mark, and the caution on .ch-mark still binds: full-sized buttons at
   this density shout louder than the things they act on. Two weights only,
   because exactly one act can be waiting on the coach: --do carries the
   accent hairline and ink text, the rest are grey until hovered. */
.ch-tiles{
  display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s2);margin:var(--s3) 0 0;
}
.ch-tile{
  grid-column:span 1;display:flex;flex-direction:column;align-items:flex-start;gap:6px;
  padding:var(--s3);min-height:0;border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);color:var(--ink);cursor:pointer;
  transition:border-color var(--dur-1) var(--ease);
}
.ch-tile:hover{border-color:var(--acc-line);}
.ch-tile--wide{grid-column:span 2;}
.ch-tile--do{border-color:var(--acc-line);box-shadow:inset 3px 0 0 var(--acc);}
.ch-tile--quiet{
  min-height:0;flex-direction:row;align-items:center;gap:var(--s2);
  padding:4px var(--s3);background:transparent;flex-wrap:wrap;
}
.ch-tile--quiet .ch-tile__title{font-size:var(--fs-body-sm);font-weight:400;color:var(--mut);}
.ch-tile--quiet .ch-tilebtn{margin-left:auto;}
.ch-tile--quiet.ch-tile--wide{grid-column:span 2;}
.ch-tile__eyebrow{margin:0;font-size:var(--fs-small);color:var(--mut);}
.ch-tile__title{margin:0;font-size:var(--fs-body);font-weight:600;line-height:1.3;}
.ch-tile__p{margin:0;font-size:var(--fs-body-sm);color:var(--ink2);line-height:1.4;}
.ch-tile__quote{color:var(--mut);}
.ch-tile__list{
  margin:0;padding-left:1.1em;font-size:var(--fs-body-sm);color:var(--ink2);line-height:1.45;
}
.ch-tile__list li{margin:0 0 2px;}
.ch-tilebtn{
  margin-top:auto;min-height:44px;padding:6px 14px;border-radius:var(--r-md);
  display:inline-flex;align-items:center;font:inherit;font-size:var(--fs-body-sm);font-weight:500;
  color:var(--mut);background:transparent;border:1px solid var(--line2);cursor:pointer;
  transition:color var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease);
}
.ch-tilebtn:hover{color:var(--ink);border-color:var(--line-ui);}
.ch-tile:hover .ch-tilebtn{border-color:var(--line-ui);}
.ch-tilebtn--do{color:var(--ink2);border-color:var(--acc-line);font-weight:600;}
.ch-tilebtn--do:hover{color:var(--ink);border-color:var(--acc);}

/* The way back out of a panel. Same shape as the "All sessions" link at the
   top of the page, but a button, because it changes what this page is
   showing rather than going anywhere. */
.ch-back[data-panel]{appearance:none;background:none;border:0;font:inherit;cursor:pointer;padding:0;}

@media (max-width:820px){
  .ch-tiles{grid-template-columns:repeat(2,1fr);}
  .ch-tile--wide,.ch-tile--quiet.ch-tile--wide{grid-column:span 2;}
}

/* ON A PHONE A TILE IS A ROW, NOT A CARD. Six stacked cards at 390 wide came
   out longer than the scroll this screen exists to remove. Every tile keeps
   its name and its button; only the tile carrying the act you owe keeps its
   preview text, because a preview on all six rebuilds the wall.

   Both wide spans are reset here and that is not belt and braces: leaving
   the quiet one at span 2 made the single-column grid grow an implicit
   second column to hold it, and the grid came out 98px and 244px wide while
   the one-column rule sat there doing nothing. */
@media (max-width:520px){
  .ch-tiles{grid-template-columns:1fr;gap:8px;}
  .ch-tile--wide,.ch-tile--quiet.ch-tile--wide{grid-column:span 1;}
  .ch-tile{
    flex-direction:row;flex-wrap:wrap;align-items:center;min-height:0;
    padding:var(--s2) var(--s3);column-gap:var(--s2);row-gap:4px;
  }
  .ch-tile__eyebrow{flex:1 0 100%;order:1;}
  .ch-tile__title{flex:1 1 auto;order:2;font-size:var(--fs-body-sm);}
  .ch-tilebtn{order:3;margin-top:0;margin-left:auto;flex:none;}
  .ch-tile__p,.ch-tile__list{order:4;flex:1 0 100%;display:none;}
  .ch-tile--do .ch-tile__p,.ch-tile--do .ch-tile__list{display:block;}
  /* The act comes first on a small screen; the note about their name is an
     explanation and goes under the tiles. */
  #client-home{display:flex;flex-direction:column;}
  #client-home .ch-tiles{order:1;}
  #client-home .ch-namefact{order:2;margin-top:var(--s4);}
}

/* ---------------------------------------------------------------------
   20. A ROW IS PART OF THE SHEET (reversed 2026-09-01, per
   .cowork/SPEC-app-sheet-and-empty-states.md §5.2)

   This section used to be "A ROW IS A SHEET OF PAPER" (Ian, 2026-08-29):
   it painted every row --paper with a --line2 edge and a shadow, because
   the ground under it was the beige desk and --line measured 1.10:1 on
   that ground, invisible. That reasoning is gone now that .ch-main IS
   the sheet (§0 above): a --paper row on a --paper sheet is paper on
   paper with a drop shadow, twenty small sheets glued to a big one,
   which is exactly what direction A's card rule exists to stop. Measured
   on the sheet rather than the desk: --line2 is 1.57:1 by day and 1.96:1
   by night, both clear of the 1.10:1 this section was written to fix.

   So a row goes back to being part of the list, not an object on it:
   .ch-thread takes the sheet's own paper colour as a FLAT fill (no
   background-image), which switches the ruling off underneath the list
   per direction A §4 ("behind a dense table: the table sits on paper or
   surface, ruling off, row rules from --line"), and each .ch-row is
   divided from the next by a hairline rather than a box.
   --------------------------------------------------------------------- */

.ch-thread{
  display:flex;flex-direction:column;
  background-color:var(--paper);
}

.ch-row{
  padding:var(--s4) 0;
  background:none;border:0;border-radius:0;box-shadow:none;
  border-top:1px solid var(--line2);
}
.ch-thread > .ch-row:first-child{border-top:0;}

/* The two standing prompts are the things waiting on the coach. The
   accent bar is a fact marker and it stays; the shadow goes with every
   other shadow a row used to carry. */
.ch-row--prompt{box-shadow:inset 3px 0 0 var(--acc);}

/* A finished session still reads as part of the list; it is the words on
   it that quieten, which §5 already handles. Nothing to add, nothing to
   take away. */

@media (max-width:640px){
  .ch-row{padding:var(--s3) 0;}
}

/* ---- THE TILES: STILL CARDS, NOW ON THE SHEET ------------------------

   Unlike the rows above, the client page's tiles pass the card test:
   .ch-tiles lays them 2 or 3 in a row from 820px up (procedure case A),
   and every tile is itself the click target via its data-panel handler
   (case B). So a tile stays a card, but a card on the sheet is never
   --paper, never ruled, never shadowed (spec §1): --surface, an edge, no
   shadow. The edge is --line-ui, not --line2, because the whole tile is
   a control and a clickable card's boundary has to clear the 3:1
   non-text bar (--line-ui measures 3.35:1 by day, 4.28:1 by night; see
   .st-card--act in stica.css). Radius stays --r-lg, the token's own
   scale value for "tiles, cards, buttons, panels", and padding stays at
   16px: nothing here needed a row to match any more, because rows are no
   longer boxes.

   The caution recorded on .ch-tilebtn in the block above still binds and
   is NOT being overturned: full-sized buttons at this density shout
   louder than the things they act on. So only the ONE tile carrying an
   act the coach owes gets a filled button; the rest keep the visible
   edge at rest this section already gave them.
   --------------------------------------------------------------------- */

.ch-tile{
  padding:var(--s4);
  background:var(--surface);
  border-color:var(--line-ui);
  border-radius:var(--r-lg);
  box-shadow:none;
}
.ch-tile--do{border-color:var(--acc-line);box-shadow:inset 3px 0 0 var(--acc);}
.ch-tile--quiet{background:transparent;box-shadow:none;border-color:transparent;}
.ch-tile--quiet:hover{border-color:var(--line-ui);}

.ch-tilebtn{border-color:var(--line-ui);color:var(--ink2);}
.ch-tilebtn:hover{color:var(--ink);border-color:var(--ink2);}
.ch-tile:hover .ch-tilebtn{border-color:var(--ink2);}
.ch-tilebtn--do{
  background:var(--acc-fill);border-color:var(--acc-fill);color:#fff;font-weight:600;
}
.ch-tilebtn--do:hover{
  background:color-mix(in srgb,var(--acc-fill) 88%,#000);
  border-color:color-mix(in srgb,var(--acc-fill) 88%,#000);color:#fff;
}

/* The phone rule in the block above turns a tile into a row and sets its
   own padding there; that value is considered and is left alone. */
