/* ============================================================================
   Shea's Forest — the Block Alphabet  ·  styles/block.css  ·  DP-012 slice ①
   ----------------------------------------------------------------------------
   ONE stylesheet, keyed entirely to tokens.css. This is the design system's
   alphabet — the reusable vocabulary every Forest view is spelled in, so that
   "mail is the first tree, not a special case" (DP-012) becomes true in the
   bytes. The atoms compose up: chip · line · field · badge  →  row · record ·
   strip  →  the three-region view. A new view spells itself in these blocks and
   inherits the whole proportional system for free.

   THE PROPORTIONAL SYSTEM (JT-5). The type scale (--t-*), the type faces
   (--face-*), the leading (--leading-*), AND the spacing rhythm (--s-*, the 4px
   modular scale) all come from tokens.css. Self-similarity is enforced by
   construction: every block measures itself in the same --s-* steps the rest of
   the app already uses (shell.css consumes --s-* throughout), so views stay in
   one rhythm rather than each inventing its own.

   NOTE (byte-truth, S05.1452): Run Book 11 was grounded @ b610abd8 and said
   "tokens has no space scale yet — author a base unit into block.css." At the
   build's HEAD, tokens.css carries a full --s-1..--s-8 scale (0.25rem base) that
   shell.css already consumes. Authoring a second, competing base here would
   FRAGMENT the very rhythm JT-5 exists to unify, so this file CONSUMES --s-*
   rather than introducing --block-space. The runbook's stale line is routed to a
   Settle (owed), not churned mid-arc — same discipline the el()-count catch took.

   NO RAW HEX — every color/type value is a var(--…) from tokens (acceptance #3).
   Slice ① is deliberately INVISIBLE: these classes are net-new (mail emits its
   own mail-* classes today), so nothing on screen changes. The visible payoff is
   slice ② (badge first-class) and slice ③ (three-region view → calendar/contacts
   for free). This file is the substrate those slices stand on.
   ============================================================================ */

/* ---- ATOM · chip -----------------------------------------------------------
   The smallest labelled token — a tag, a filter pill, a pace marker. Reads in
   the survey (mono) face at label size; the pill radius is the token's. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-1) var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-caps);
  color: var(--moss-ink);
  background: color-mix(in srgb, var(--moss) 12%, transparent);
  border: 1px solid var(--line-moss);
  border-radius: var(--r-pill);
  white-space: nowrap;
}
.chip--lit {                    /* the risen chip — where attention rests */
  color: var(--ink);
  background: color-mix(in srgb, var(--sunlight) 18%, transparent);
  border-color: var(--line-gold);
}
.chip__x {                      /* the remove control on a dismissable chip (× clears the filter) */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: var(--s-1);
  padding: 0;
  width: 1.1em;
  height: 1.1em;
  font-family: var(--face-survey);
  font-size: var(--t-label);
  line-height: 1;
  color: var(--moss-ink);
  background: transparent;
  border: none;
  border-radius: var(--r-pill);
  cursor: pointer;
}
.chip__x:hover { color: var(--ink); background: color-mix(in srgb, var(--moss) 22%, transparent); }
.chip__x:focus-visible { outline: 1px solid var(--line-gold); outline-offset: 1px; }

/* ---- the From-sender chip row (email-app #22b, the removable-chip upgrade) ----
   Active sender filters render as removable chips below the "From" picker: a flex
   row that wraps, sits in the strip rhythm, and vanishes when the set is empty. */
.mail__from-chips-slot:empty { display: none; }   /* no empty gap when nothing is filtered */
.mail__from-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  margin-top: var(--s-1);
}

/* ---- the Label chip row (email-app, the multi-label removable-chip filter) ----
   Same removable-chip pattern as From, over m.labels (UNION facet); vanishes empty. */
.mail__label-chips-slot:empty { display: none; }
.mail__label-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  margin-top: var(--s-1);
}

/* ---- ATOM · line -----------------------------------------------------------
   A single labelled line of data — one key → one value, the grain of a detail
   pane. The label sits quiet in the survey face; the value reads in the body. */
.line {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-1) 0;
}
.line__label {
  flex: 0 0 auto;
  min-width: 6rem;
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
}
.line__value {
  flex: 1 1 auto;
  font-family: var(--face-path);
  font-size: var(--t-body);
  line-height: var(--leading-body);
  color: var(--ink);
}
.line--empty .line__value { color: var(--ink-soft); font-style: italic; }
/* SL-4 · muted line — a secondary/hint line (stub "coming" text, quiet notes),
   dimmed a step off body ink. The --ink-soft muted convention, matching
   .line--empty above; the modifier promises a distinction, this delivers it. */
.line--muted { color: var(--ink-soft); }

/* ---- ATOM · field ----------------------------------------------------------
   A labelled control — the input grain of a compose/edit surface. Label above,
   control below, on the app rhythm. */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  margin-bottom: var(--s-4);
}
.field__label {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
}
.field__control {
  font-family: var(--face-path);
  font-size: var(--t-body);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-s);
  padding: var(--s-2) var(--s-3);
}
.field__control:focus {
  outline: none;
  border-color: var(--line-gold);
  box-shadow: var(--shadow-lit);
}
/* SL-4 · inline field — label and control on ONE baseline row (the labeled-row
   bone), against the base .field's stacked column. A real layout distinction. */
.field--inline {
  flex-direction: row;
  align-items: baseline;
  gap: var(--s-3);
}
/* SL-4 · numeric control — numbers align right and sit on tabular figures so a
   column of dwell numbers lines up. A real distinction on a numeric input. */
.field__control--num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* SL-4 · example field group — a placeholder group carrying EXAMPLE values,
   marked with a dashed rail so it reads as example, not real (same clearly-
   example safety idiom as .badge--example). The base .field-group is a bare
   structural wrapper; this modifier is the one that paints. */
.field-group--example {
  border-left: 2px dashed var(--line-bark);
  padding-left: var(--s-3);
}
/* DP-014 · Cc reveal — the compose field + its reveal control honor [hidden].
   A class selector (.field { display:flex }) outranks the UA [hidden]{display:none},
   so the attribute alone would NOT hide a .field; this restores the semantic hide —
   which also drops the node from the a11y tree and tab order (the honest hide, not a
   visual-only one). The reveal sits on the field rhythm; Cc opens in its place. */
.mail-compose__field[hidden],
.mail-compose__cc-reveal[hidden],
.mail-compose__bcc-reveal[hidden],
.mail-compose__attach[hidden] { display: none; }
.mail-compose__cc-reveal, .mail-compose__bcc-reveal { margin-bottom: var(--s-4); }

/* email-app compose containment (DP-014 §6.1 — PANE, "just like Gmail", DECIDED S10.1336).
   The compose floats OVER the live mailbox as a fixed, bottom-right docked overlay (Gmail's
   behavior — the list stays visible + interactive behind it, non-modal, no backdrop). The
   overlay bounds the width; the record fills it. The base .mail-compose.record bound below is a
   fallback for any direct (non-overlay) mount. */
.mail-compose-overlay {
  position: fixed;
  right: var(--s-5);
  bottom: 0;
  z-index: 50;                       /* above the mailbox (10-20), below the shell overlay (60) + door (1000) */
  width: min(40rem, calc(100vw - 2 * var(--s-5)));
  max-height: 92vh;
  overflow: auto;
}
.mail-compose-overlay .mail-compose.record { max-width: none; margin: 0; }

/* SL-1 — GENESIS OPENS A PANE (method/the-shape-law-register-v1.md §4). The calendar's
   create/edit form used to WIPE the grid (`host.textContent = ""`); it now floats OVER the
   live grid in this overlay, exactly as compose floats over the live mailbox above. Same
   law, same shape, one suite. The grid stays visible and mounted behind it. */
.calendar-form-overlay {
  position: fixed;
  right: var(--s-5);
  bottom: 0;
  z-index: 50;                       /* the mail-compose band: above the grid, below the shell overlay */
  width: min(40rem, calc(100vw - 2 * var(--s-5)));
  max-height: 92vh;
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-m) var(--r-m) 0 0;
  padding: var(--s-5);
}
.calendar-form-overlay .calendar-newform { max-width: none; margin: 0; }
/* SL-1 · New Contact floats over the list, the calendar/mail Compose shape. The POSITIONING
   mirrors .calendar-form-overlay (fixed, bottom-right band); the card CHROME (border/bg/padding)
   stays on the inner .contacts-create, which already carries it, so the overlay is a pure
   positioning wrapper and there is no double card. */
.contacts-form-overlay {
  position: fixed;
  right: var(--s-5);
  bottom: 0;
  z-index: 50;                       /* the mail-compose band: above the list, below the shell overlay */
  width: min(28rem, calc(100vw - 2 * var(--s-5)));
  max-height: 92vh;
  overflow: auto;
}
.contacts-form-overlay .contacts-create { margin: 0; }

/* SL-1 · Butcher — New order, and an open order, float over the live board. The
   POSITIONING is .calendar-form-overlay verbatim (fixed, bottom-right band, the
   mail-compose z-index); the only butcher-specific value is the order pane's
   wider bound, because an order carries a chain and a census the calendar's
   28-40rem forms do not. Zero new grammar: every token below already exists. */
.butcher-form-overlay {
  position: fixed;
  right: var(--s-5);
  bottom: 0;
  z-index: 50;                       /* the mail-compose band: above the board, below the shell overlay */
  width: min(40rem, calc(100vw - 2 * var(--s-5)));
  max-height: 92vh;
  overflow: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-m) var(--r-m) 0 0;
  padding: var(--s-5);
}
.butcher-form-overlay--order { width: min(52rem, calc(100vw - 2 * var(--s-5))); }

/* SL-3 — the top-× dismiss, and it is the ONLY new affordance on the pane: the
   surface keeps its own Cancel, untouched. `‹ Back to board` is gone rather than
   restyled — under SL-1 you never left the board, so there is nowhere to go back
   to, and the rail's "Order board" slot was always the real way home. */
.butcher-pane__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--line);
}
.butcher-pane__title {
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink);
}
/* The × and the Cancel are `.record__dismiss` / `.record__action--quiet`, already
   styled above — butcher adds NO rule for either. The pane only has to anchor
   them, which block.css:676 already says is the pane container's job. */
.butcher-form-overlay { position: fixed; }

/* The bench control — who is signing. The rail vocabulary, one notch quieter
   than a view slot, because it is a state you set once a shift, not a place. */
.rail__bench {
  margin-top: auto;
  padding: var(--s-3) var(--s-3) var(--s-2);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.rail__bench-label {
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-quiet);
}
.rail__bench-pick {
  width: 100%;
  padding: var(--s-1) var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-s);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
}
.rail__bench-pick:focus-visible { outline: none; border-color: var(--line-gold); box-shadow: 0 0 0 3px var(--ring); }
.mail-compose.record {
  max-width: min(40rem, 100%);
  margin-inline-end: auto;
}

/* the compose formatting toolbar (slice-2 §6.2, Markdown DECIDED S10.1336): a light row of quiet
   Markdown controls above the body — the Burnisher's calm, not a dense toolbar. */
.mail-compose__format {
  display: flex;
  gap: var(--s-1);
  margin-bottom: var(--s-2);
}
.mail-compose__format-btn {
  display: inline-flex;
  align-items: center;
  padding: var(--s-1) var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--moss-ink);
  background: transparent;
  border: 1px solid var(--line-bark);
  border-radius: var(--r-s);
  cursor: pointer;
}
.mail-compose__format-btn[aria-label="Bold"] { font-weight: 700; }
.mail-compose__format-btn[aria-label="Italic"] { font-style: italic; }
.mail-compose__format-btn:hover, .mail-compose__format-btn:focus-visible {
  color: var(--ink);
  border-color: var(--line-gold);
  background: color-mix(in srgb, var(--sunlight) 8%, transparent);
  outline: none;
}
/* email-app A1 · the Preview TOGGLE's active state — carried by [aria-pressed], never colour
   alone (the Higgins Test / honest-badge ethos): a pressed toggle reads as a filled, gold-lined
   chip, distinct at rest with colour stripped. Same fmt-btn geometry; no new hue. */
.mail-compose__preview-toggle[aria-pressed="true"] {
  color: var(--ink);
  border-color: var(--line-gold);
  background: color-mix(in srgb, var(--sunlight) 12%, transparent);
  font-weight: 600;
}

/* email-app A1 · the live Markdown preview pane — the textarea's twin, so the swap is calm and
   preview reads with the SAME weight as the message it replaces: same reading face, size, ink,
   paper, border, radius, and padding as .field__control (DP-012, no new hue). It renders the
   send-path's own toDOM output (h*/p/ul/ol/li/blockquote/pre·code/strong/em/a), so a little prose
   rhythm keeps it legible. A class selector outranks the UA [hidden]{display:none}, so the pane
   needs its OWN [hidden] rule to stay hidden at rest (the exact pattern the compose fields use). */
.mail-compose__preview {
  display: block;
  margin-top: var(--s-2);
  font-family: var(--face-path);
  font-size: var(--t-body);
  line-height: var(--leading-body);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-s);
  padding: var(--s-2) var(--s-3);
  overflow-wrap: break-word;
}
.mail-compose__preview[hidden] { display: none; }
.mail-compose__preview > :first-child { margin-top: 0; }
.mail-compose__preview > :last-child { margin-bottom: 0; }
.mail-compose__preview p { margin: 0 0 var(--s-3); }
.mail-compose__preview h1, .mail-compose__preview h2, .mail-compose__preview h3,
.mail-compose__preview h4, .mail-compose__preview h5, .mail-compose__preview h6 {
  margin: var(--s-3) 0 var(--s-2);
  line-height: var(--leading-tight);
}
.mail-compose__preview ul, .mail-compose__preview ol { margin: 0 0 var(--s-3); padding-left: var(--s-4); }
.mail-compose__preview li { margin: 0 0 var(--s-1); }
.mail-compose__preview blockquote {
  margin: 0 0 var(--s-3);
  padding-left: var(--s-3);
  border-left: 2px solid var(--line-bark);
  color: var(--moss-ink);
}
.mail-compose__preview code {
  font-family: var(--face-code, monospace);
  font-size: 0.92em;
}
.mail-compose__preview pre {
  margin: 0 0 var(--s-3);
  padding: var(--s-2) var(--s-3);
  background: color-mix(in srgb, var(--moss-ink) 6%, transparent);
  border-radius: var(--r-s);
  overflow-x: auto;
}
.mail-compose__preview a { color: var(--line-gold); text-decoration: underline; }

/* ---- ATOM · badge (form-first — DP-006 §3f / H3) ---------------------------
   The honest badge as an alphabet atom. State is carried by FORM, never by
   colour alone: a `chip` form is a filled token; a `ring` form is a hollow,
   dashed ring with no fill. With colour stripped, a clear badge and a stale one
   must not look alike — the fabricated-green lie this block forbids. (The badge
   as a first-class, fully-voiced component is slice ②; here the class exists and
   reads state from form + tokens.) */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-1) var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  line-height: var(--leading-tight);
  border-radius: var(--r-pill);
  border: 1px solid transparent;
}
/* DP-020 P3 s2 — the owner ("You") badge on the unified My Card. POSITION/label only, no new hue:
   reuses the badge atom's survey face + neutral --badge-known fill (DP-006 tokens-only; DP-017 gold
   stays scarce). Marks the owner's record without a special record grammar (Lens 1: same record shell). */
.contacts-record__owner-badge {
  display: inline-flex;
  align-items: center;
  margin-left: var(--s-2);
  padding: var(--s-1) var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  line-height: var(--leading-tight);
  border-radius: var(--r-pill);
  color: var(--ink);
  background: var(--badge-known);
}
/* form = chip → a FILLED token (the reachable-truth states)
   NOTE (slice ②): a bare .badge[data-form="chip"] with NO state modifier defaults
   to --badge-known; the honest-badge component always pairs form + state so it
   never renders bare, but the first-class badge work should not rely on this
   default silently. */
.badge[data-form="chip"] {
  color: var(--ink);
  background: var(--badge-known);
  border-color: transparent;
}
.badge--overdue[data-form="chip"] {   /* verified past-due — heavier, still no red */
  color: var(--badge-overdue-ink);
  background: var(--badge-overdue);
}
.badge--known-due[data-form="chip"] { /* needs you today (amber) */
  background: var(--badge-known-due);
}
/* SL-4 · example badge — a clearly-marked EXAMPLE token (leg 18: an example
   number must never read as a real one). Follows the design's own rule — "the
   FORM is the tell, so it never mimics a clear badge": hollow, dashed, soft ink,
   no fill. A safety distinction, not decoration. */
.badge--example {
  background: transparent;
  border: 1px dashed var(--line-bark);
  color: var(--ink-soft);
}
/* form = ring → a HOLLOW, DASHED ring, NO fill (the unreachable state; the FORM
   is the tell, so it never mimics a clear badge) */
.badge[data-form="ring"] {
  color: var(--badge-unreachable-ink);
  background: var(--badge-unreachable);           /* transparent — no fill */
  border-style: var(--badge-unreachable-form);    /* dashed */
  border-color: var(--badge-unreachable-line);
}

/* ---- MOLECULE · row --------------------------------------------------------
   A horizontal record-in-a-list — the grain of a list view (a mail message, a
   contact, a calendar entry). Atoms sit along it; the trailing slot holds the
   badge/date. A calm hairline separates rows; the active row carries the disc. */
.row {
  display: flex;
  align-items: flex-start;               /* multi-line body (title/meta/snippet) tops-align */
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line-bark);
  cursor: default;
}
.row:hover { background: color-mix(in srgb, var(--sunlight) 5%, transparent); }
.row[role="button"], .row--clickable { cursor: pointer; }
/* A-2 (S27.0027): butcher's board lane summary is div.line.line--clickable with role=button +
   tabindex=0 and a live handler; .line carries no cursor, so the affordance existed for keyboard
   users only. Same --clickable form as the .row family above -- a new NOUN in the existing
   grammar, not a new form (parent ux-evolution-plan-v1.md section 6 holds strict). */
.line--clickable { cursor: pointer; }
.row__lead   { flex: 0 0 auto; padding-top: 2px; }
.row__body   {
  flex: 1 1 auto;
  min-width: 0;                          /* min-width:0 lets the children truncate */
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.row__title  {
  font-family: var(--face-canopy);
  font-size: var(--t-body);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;                   /* JT-4: one line, truncate-with-affordance (title attr = the reveal) */
}
.row__meta   {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: 0.02em;
  color: var(--moss-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* a generic preview line — mail's snippet, a calendar entry's location, a
   contact's subtitle. One line, truncated: never a wall of body text in a list. */
.row__snippet {
  font-family: var(--face-path);
  font-size: var(--t-small);
  line-height: var(--leading-tight);
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;                   /* JT-4: the preview reads, it never blows the row height */
}
.row__trail  {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  color: var(--moss-ink);
}
.row--active {
  background: color-mix(in srgb, var(--sunlight) 8%, transparent);
  box-shadow: inset 3px 0 0 0 var(--sunlight);       /* the disc, drawn as an edge */
}
.row--unread .row__title { font-weight: 600; }        /* weight, never a red count */
/* inside a bordered list container, the last row's rule would double the edge */
.view__list .row:last-child { border-bottom: none; }

/* ---- ①b — row affordances (monogram · quick actions · density) --------------
   Promotions onto the shared `row` grammar (JT-7): a message row grows a calm
   sender monogram at its lead and, when managing is live, a hover/focus-revealed
   action cluster at its trail. Token-keyed; every other block gets the same free. */
.row__monogram {
  flex: 0 0 auto;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--face-survey);
  font-size: var(--t-label);
  color: var(--moss-ink);
  background: color-mix(in srgb, var(--moss) 12%, transparent);
  border: 1px solid var(--line-moss);
  margin-top: 2px;
}
.row__actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s-1);
  opacity: 0;                                          /* calm at rest — revealed on hover/focus */
  transition: opacity 120ms ease;
}
.row:hover .row__actions,
.row:focus-within .row__actions { opacity: 1; }
.row__action {
  cursor: pointer;
  padding: 0 var(--s-1);
  border-radius: var(--r-s);
  color: var(--ink-soft);
  font-size: var(--t-body);
  line-height: 1.4;
}
.row__action:hover, .row__action:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 8%, transparent);
  color: var(--ink);
  outline: none;
}
.row__star--on { color: var(--line-gold); }            /* the lit star — gold, the app's one warm accent */
.row__importance--on { color: var(--moss-ink); font-weight: 600; }   /* weight + moss, never red */

/* ①c "Send to Forest" — the row file control + its tree picker. The 🌲 trigger
   is a .row__action (inherits the shared button rest/hover above); its menu is a
   sibling that drops below the actions cluster. */
.row__actions { position: relative; }                  /* anchor for the absolute file menu */
.row__file[data-filed="1"] { color: var(--line-gold); }   /* confirmed filed — the same gold the lit star wears (fire-and-confirm, never a faked persistent toggle) */
.row__file[data-busy="1"]  { opacity: 0.55; cursor: default; }  /* in-flight to /soil/file */
/* the tree menu — a small raised paper card dropping from the actions cluster.
   Renderer toggles it via inline display; these rules style the OPEN state. */
.row__file-menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 10;                                          /* above sibling rows; no layering token in the system */
  margin-top: var(--s-1);
  min-width: 9rem;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-1);
  background: var(--surface-card);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-m);
  box-shadow: var(--shadow-card);
}
.row__file-opt {
  cursor: pointer;
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-s);
  font-family: var(--face-path);
  font-size: var(--t-body);
  line-height: 1.4;
  color: var(--ink);
  white-space: nowrap;
}
.row__file-opt:hover, .row__file-opt:focus-visible {
  background: color-mix(in srgb, var(--moss) 12%, transparent);   /* the same quiet moss wash the survey chips use */
  color: var(--ink);
  outline: none;
}

/* density — a CSS-only rhythm swap (JT-1: a BEM modifier on the list, not a token).
   Comfortable is the resting `.row` padding above; cozy/compact tighten it for triage. */
.view--density-cozy .row    { padding: var(--s-2) var(--s-4); }
.view--density-compact .row { padding: var(--s-1) var(--s-4); gap: var(--s-2); }
.view--density-compact .row__snippet { display: none; }   /* compact drops the preview line, keeps from·subject */

/* ---- MOLECULE · record -----------------------------------------------------
   A single record opened in full — the detail card (an opened message, a
   contact card, an event). A raised paper surface on the app rhythm, headed by
   its title and lines. */
.record {
  position: relative;                    /* SL-3: anchors the top-× dismiss (.record__dismiss) */
  background: var(--surface-card);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-m);
  box-shadow: var(--shadow-card);
  padding: var(--s-5);
}
/* SL-3 · the universal top-× dismiss on a genesis pane. A control, not decoration — the hand's
   first reflex for "close." Quiet until hovered; the labeled Cancel below it remains. */
.record__dismiss {
  position: absolute;
  top: var(--s-3);
  right: var(--s-3);
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--moss-ink);
  font-size: var(--t-title);
  line-height: 1;
  cursor: pointer;
}
.record__dismiss:hover, .record__dismiss:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 18%, transparent);
  color: var(--ink);
  outline: none;
}
/* L3 · the top MINIMIZE beside the × — set the draft aside into the app-shell dock without
   losing it. Same quiet chrome as the dismiss, sitting one control to its LEFT. A distinct
   family from record__dismiss (minimize is not a dismiss). */
.record__minimize {
  position: absolute;
  top: var(--s-3);
  right: calc(var(--s-3) + 2rem + var(--s-2));
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--moss-ink);
  font-size: var(--t-title);
  line-height: 1;
  cursor: pointer;
}
.record__minimize:hover, .record__minimize:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 18%, transparent);
  color: var(--ink);
  outline: none;
}
/* L3 · a minimized (docked) record pane is hidden in place — DOM + typed state preserved,
   restored when its dock tab is clicked. !important so it wins over a pane's own display. */
.record--docked { display: none !important; }
/* SL-3: the calendar/contacts genesis panes are not `.record` cards but host a
   `.record__dismiss` — anchor it to their own pane container so the × sits top-right.
   .calendar-form-overlay and .contacts-form-overlay are FLOATING panes (position: fixed,
   block.css:185 — the mail-compose band); `fixed` is ALREADY a positioning context for the
   absolute ×, so the overlay must NOT be pinned to `relative`, or the pane drops out of the float
   into normal flow and renders BELOW the collection instead of over it (the
   New-Event-below-the-grid bug fixed S19.1358; the New-Contact-below-the-list twin fixed here).
   .contacts-create is the INNER form and stays `relative` so its own × anchors to it INSIDE the
   floating overlay. */
.contacts-create { position: relative; }
.record__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line-bark);
}
/* a quiet return affordance — leads the record, reads like a path back not a button */
.record__back {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  margin-bottom: var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--moss-ink);
  cursor: pointer;
}
.record__back:hover, .record__back:focus-visible { color: var(--ink); outline: none; }
.record__title {
  font-family: var(--face-canopy);
  font-size: var(--t-title);
  line-height: var(--leading-tight);
  color: var(--ink);
  min-width: 0;
  overflow-wrap: anywhere;               /* JT-4: a 4000-char / no-space subject wraps, never a horizontal blowout */
  display: -webkit-box;
  -webkit-line-clamp: 3;                  /* JT-4 legibility floor: clamp to a readable height, no micro-wall */
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* the from · date line, in the data face */
.record__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: 0.02em;
  color: var(--moss-ink);
}
.record__body {
  font-family: var(--face-path);
  font-size: var(--t-body);
  line-height: var(--leading-body);
  color: var(--ink);
  overflow-wrap: anywhere;               /* JT-4: a giant unbroken string wraps rather than blowing the frame */
}
.record__body > p { margin: 0 0 var(--s-3); }
.record__body > p:last-child { margin-bottom: 0; }
.record__body > p:empty { display: none; }
/* JT-4: the one-giant-table body case — bounded scroll inside the region, so the
   record contains a huge body without deforming the frame; the reader reaches all
   of it by choice (scroll), never a wall shrunk to fit. */
.record__body--bounded {
  max-height: 60vh;
  overflow-y: auto;
  padding-right: var(--s-2);
}
/* email-app #9 — the attachment surface (READ half): a quiet list below the body naming what the
   message carries (filename + type/size). DP-012 Block Alphabet — moss-tinted, survey face for the
   head, path face for filenames; no download control yet (the byte-fetch route is the next slice, so
   an item is a listing, not a button — flag-don't-fake). */
.mail-attachments {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line-bark);
}
.mail-attachments__head {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--moss-ink);
  margin-bottom: var(--s-2);
}
.mail-attachments__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.mail-attachments__item {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-2);
  background: color-mix(in srgb, var(--moss) 8%, transparent);
  border: 1px solid var(--line-moss);
  border-radius: var(--r-soft, 6px);
}
.mail-attachments__name {
  font-family: var(--face-path);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1 1 auto;
}
.mail-attachments__meta {
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--moss-ink);
  flex: 0 0 auto;
  white-space: nowrap;
}
/* email-app #9 (download) — a downloadable attachment's filename is a link (gmail-sourced); it reads as
   the same path-face name but carries a link affordance (gold on hover/focus, underline). A non-gmail
   attachment keeps the plain .mail-attachments__name span (no link) — the list still shows it. */
.mail-attachments__download {
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
}
.mail-attachments__download:hover, .mail-attachments__download:focus-visible {
  color: var(--line-gold);
  text-decoration: underline;
  outline: none;
}
/* email-app #19 (attachments on send) — the compose-side attach-picker + its removable chips. Sits below
   the Message field; the chips echo the read-side .mail-attachments look a notch lighter (send is a
   staging area, not a landed record). An empty picker renders just the file input, no chip row. */
.mail-compose__attach {
  margin-top: var(--s-2);
}
.mail-attachment-chips {
  list-style: none;
  margin: var(--s-2) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
}
.mail-attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-1) var(--s-2);
  background: color-mix(in srgb, var(--moss) 8%, transparent);
  border: 1px solid var(--line-moss);
  border-radius: var(--r-soft, 6px);
  max-width: 100%;
}
.mail-attachment-chip__name {
  font-family: var(--face-path);
  font-size: var(--t-small);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.mail-attachment-chip__remove {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--moss-ink);
  font-size: 1rem;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  flex: 0 0 auto;
}
.mail-attachment-chip__remove:hover, .mail-attachment-chip__remove:focus-visible {
  color: var(--line-gold);
  outline: none;
}
/* the command row inside a record — reply / archive / mark-unread. Few, wide,
   calm; not a dense toolbar. */
.record__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line-bark);
}
.record__action {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-2) var(--s-4);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--ink);
  background: color-mix(in srgb, var(--sunlight) 10%, transparent);
  border: 1px solid var(--line-gold);
  border-radius: var(--r-pill);
  cursor: pointer;
}
.record__action:hover, .record__action:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 18%, transparent);
  outline: none;
}
.record__action--quiet {                 /* a secondary action — present but not lit */
  background: transparent;
  border-color: var(--line-bark);
  color: var(--moss-ink);
}
.record__status {                        /* an inline live-region result line */
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--moss-ink);
  margin-left: auto;
}

/* ---- MOLECULE · strip ------------------------------------------------------
   A horizontal band of controls/chips — a filter/sort/search toolbar above a
   list. Chips and fields sit along it in the survey face, on the app rhythm. */
.strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding: var(--s-3) 0;
  font-family: var(--face-survey);
  font-size: var(--t-label);
}
.strip__group { display: flex; align-items: center; gap: var(--s-2); }
.strip__spacer { flex: 1 1 auto; }
/* the search field — the widest affordance on the strip, grows to fill */
.strip__search {
  flex: 1 1 12rem;
  min-width: 8rem;
  font-family: var(--face-path);
  font-size: var(--t-body);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-pill);
  padding: var(--s-2) var(--s-4);
}
.strip__search::placeholder { color: var(--ink-soft); }
.strip__search:focus {
  outline: none;
  border-color: var(--line-gold);
  box-shadow: var(--shadow-lit);
}
/* a labeled select control (sort / filter / label picker) */
.strip__field { display: inline-flex; align-items: center; gap: var(--s-1); }
.strip__field-label {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
}
/* (v9 · command-surface finish, styling half) — the quiet strip controls, brought onto
   the grove: native browser chrome removed (appearance:none + the grove's own chevron),
   a warm --floor wash, the strip's pill grammar, and a real keyboard-reachable gold focus
   RING (FN-4: warm wash + gold hairline, no new hue; the lit compose action stays the one
   bright thing). The "few wide affordances" structural restructure — collapsing this row to
   fewer, wider controls — is the next slice, recorded in the handoff. */
.strip__select {
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--ink);
  background-color: var(--floor);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" stroke="%237D7259" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4.5 L6 7.5 L9 4.5"/></svg>');
  background-repeat: no-repeat;
  background-position: right var(--s-2) center;
  background-size: 0.68rem;
  border: 1px solid var(--line-bark);
  border-radius: var(--r-pill);
  padding: var(--s-1) calc(var(--s-4) + 0.35rem) var(--s-1) var(--s-2);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  transition: border-color 120ms ease, box-shadow 120ms ease, background-color 120ms ease;
}
.strip__select:hover { border-color: var(--line-gold); }
.strip__select:focus { outline: none; border-color: var(--line-gold); }
.strip__select:focus-visible {
  border-color: var(--line-gold);
  box-shadow: 0 0 0 3px var(--ring);
}
/* the lit action on the strip — the one thing you'd reach for (compose) */
.strip__action {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-2) var(--s-4);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--ink);
  background: color-mix(in srgb, var(--sunlight) 16%, transparent);
  border: 1px solid var(--line-gold);
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
}
.strip__action:hover, .strip__action:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 26%, transparent);
  outline: none;
}

/* ---- FN-4 · affordance states — the press + the honest disabled -----------
   Completes the affordance family's state set. rest / hover / focus are built
   above, per component; these two finish it, shared across the button
   affordances (.strip__action · .record__action · .row__action).

   • :active — the PRESS. A wash-deepen one step past each affordance's own
     hover (never a fill, never a new hue — the Zero-Ornament guard). It is
     transient interaction feedback; the luminance step reads on a dim screen
     and is colorblind-safe (lightness, not colour).

   • disabled — the honest unavailable state, mirroring honest-badge.js: the
     distinguisher is NON-WASH — opacity ↓ (a luminance drop, colorblind-safe)
     + cursor:not-allowed + pointer interaction removed — NOT a lighter wash.
     This is load-bearing, not cosmetic: the dim-screen clause compresses
     wash-lightness, so a wash-only disabled would read as `rest` on a dim
     screen and fail the bar. The programmatic axis is the attribute the
     renderer sets ([aria-disabled="true"] / [disabled]) — the state lives in
     the NODE, not only in the paint, exactly as honest-badge keeps the
     structural bit on the element. The renderer owns the other half of the
     a11y contract: mark [aria-disabled="true"] AND drop the element from tab
     order (native <button disabled>, or tabindex="-1"). CSS removes pointer
     interaction here; keyboard-focus removal is the node's, so the disabled
     state is honestly unreachable, never a lit affordance you can still tab to. */
.strip__action:active  { background: color-mix(in srgb, var(--sunlight) 34%, transparent); }
.record__action:active { background: color-mix(in srgb, var(--sunlight) 26%, transparent); }
.row__action:active    { background: color-mix(in srgb, var(--sunlight) 14%, transparent); }

.strip__action:disabled,  .strip__action[disabled],  .strip__action[aria-disabled="true"],
.record__action:disabled, .record__action[disabled], .record__action[aria-disabled="true"],
.row__action:disabled,    .row__action[disabled],    .row__action[aria-disabled="true"] {
  opacity: 0.45;                 /* non-wash: a luminance drop, never a lighter wash */
  cursor: not-allowed;
  pointer-events: none;          /* removes pointer interaction (and pointer-focus) */
}

/* ---- FN-4 · :focus-visible — the keyboard focus RING for the button family -----
   rest/hover are built per-component above; there, `:hover, :focus-visible` SHARE
   one wash-shift rule with outline:none — so keyboard focus rendered IDENTICAL to
   mouse-hover, a wash-step a keyboard user on a dim screen cannot reliably read
   (FN-4's own bar: "a state a keyboard user cannot see is not finished"; the same
   dim-screen gap the compose slice closes on the actions the writer's hand lands on
   — Send / Confirm / Cancel / Keep editing). The finish: the SAME real gold ring the
   form controls (.strip__select · .strip__toggle) and .rail__compose already carry —
   a box-shadow in the established --ring gold, zero new hue, zero ornament. It is a
   separate :focus-visible declaration (box-shadow only), so :hover stays wash-only
   and keyboard-focus reads distinctly from a mouse hover. Shared across the button
   affordance family, exactly as the :active / disabled states above are. */
.strip__action:focus-visible,
.record__action:focus-visible,
.row__action:focus-visible {
  box-shadow: 0 0 0 3px var(--ring);
}

/* ---- (v9 · B1 — the last two select families join the calm command surface) ---
   `.strip__select` already wears the grove (native chrome off + the grove's own
   chevron). B1 brings the remaining raw dropdowns onto that same surface so NO
   browser-default `<select>` chrome survives anywhere in mail: the SETTINGS
   selects (`select.field__control` — density / action / undo-window) and the
   SNOOZE select (`.row__snooze`). No new material — `--grove-chevron` is a local
   DRY handle for the *identical* glyph `.strip__select` ships (same baked
   `--moss-ink` stroke): zero new hue, zero new token. CSS-only, zero renderer
   edit (the ①-grain: low blast radius). Each family keeps its own context — the
   settings select stays a paper FIELD (panel sibling to the text inputs), the
   snooze select becomes a real calm select (border-warm hover + gold ring), no
   longer a native dropdown wearing a button. The chevron is element-qualified on
   `select.field__control` so a text input wearing `.field__control` keeps its
   native caret. */
select.field__control,
.row__snooze {
  --grove-chevron: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" stroke="%237D7259" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4.5 L6 7.5 L9 4.5"/></svg>');
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: var(--grove-chevron);
  background-repeat: no-repeat;
  background-position: right var(--s-2) center;
  background-size: 0.68rem;
  cursor: pointer;
}
/* settings select — keep the paper field (base `.field__control` wins bg-color);
   `select.field__control` (element+class) outranks the base `background:` shorthand
   for the image, and `.field__control:focus` sets no background, so the chevron
   holds across rest + focus with no re-assert needed. Just open room for it. */
select.field__control { padding-right: calc(var(--s-4) + 0.35rem); }
/* snooze select — a real calm select on the row. It wears `.row__action` too, whose
   :hover/:active use the `background:` SHORTHAND (which would blank a chevron), so
   this owns its background in every state with longhands + re-asserts the glyph, and
   sits AFTER the `.row__action` states above so equal-specificity ties resolve here
   by source order. Hover reads as a warm border, focus as the shared gold ring —
   the strip convention, not a button fill. */
.row__snooze {
  background-color: var(--floor);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-pill);
  padding: var(--s-1) calc(var(--s-4) + 0.15rem) var(--s-1) var(--s-2);
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--ink);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.row__snooze:hover,
.row__snooze:active {
  background-color: var(--floor);
  background-image: var(--grove-chevron);
  border-color: var(--line-gold);
  color: var(--ink);
  outline: none;
}
.row__snooze:focus-visible {
  background-color: var(--floor);
  background-image: var(--grove-chevron);
  border-color: var(--line-gold);
  color: var(--ink);
  outline: none;
  box-shadow: 0 0 0 3px var(--ring);
}

/* ---- (v9 · command-surface restructure — the "few wide affordances" slice) -----
   The row of eight labelled selects folds to TWO wide affordances + a chip row:
   Sort stays inline; Show/Label/From/Group/Saved collapse behind ONE "Filters"
   toggle that opens a calm expanding PANEL (not a popover — no z-index games,
   keyboard-trivial); every ACTIVE filter shows once as a removable chip below the
   strip. Reuses the .chip atom + the existing chipRow infra (labelChips/fromChips);
   no new hue/badge/motion — the lit compose action stays the one bright thing.
   Density leaves the strip for Settings (a set-and-forget preference, not a filter). */

/* the Filters opener — a quiet pill BUTTON on the strip, sibling to strip__select's
   grammar (floor wash, bark border, pill, gold focus ring). NOT the lit gold
   strip__action, which is reserved for compose. Shows an active-count when set. */
.strip__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  font-family: var(--face-survey);
  font-size: var(--t-small);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
  background-color: var(--floor);
  border: 1px solid var(--line-bark);
  border-radius: var(--r-pill);
  padding: var(--s-1) var(--s-3);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 120ms ease, box-shadow 120ms ease, background-color 120ms ease;
}
.strip__toggle:hover { border-color: var(--line-gold); }
.strip__toggle:focus { outline: none; border-color: var(--line-gold); }
.strip__toggle:focus-visible { border-color: var(--line-gold); box-shadow: 0 0 0 3px var(--ring); }
/* the disclosure caret rotates open when aria-expanded=true */
.strip__toggle-caret { display: inline-block; font-size: 0.72em; opacity: 0.7; transition: transform 120ms ease; }
.strip__toggle[aria-expanded="true"] .strip__toggle-caret { transform: rotate(180deg); }
/* the active-filter count — a small moss token when >0; hidden at zero (:empty) */
.strip__toggle-count {
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--ink);
  background: color-mix(in srgb, var(--moss) 22%, transparent);
  border-radius: var(--r-pill);
  padding: 0 var(--s-1);
  min-width: 1.25em;
  text-align: center;
}
.strip__toggle-count:empty { display: none; }

/* the calm Filters PANEL — an expanding region (a display bit, not a popover). Drops
   to its own row under the strip (flex-basis 100%) and holds the collapsed selects in
   a soft floor-washed card on the app rhythm. Hidden by default; the toggle flips [hidden]. */
.mail__filters-panel {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-2);
  padding: var(--s-3) var(--s-4);
  background: color-mix(in srgb, var(--floor) 70%, var(--paper));
  border: 1px solid var(--line-bark);
  border-radius: var(--r-m);
}
.mail__filters-panel[hidden] { display: none; }

/* the unified ACTIVE-FILTER chip row — one row below the strip showing EVERY set facet
   (Show · Label · From · Group) as a removable chip. Reuses the .chip atom + the existing
   chipRow infra verbatim for Label/From; Show/Group add their own matching chips. Drops to
   its own line (flex-basis 100%); vanishes at rest (:empty) — no empty gap. */
.mail__active-filters {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-1);
  margin-top: var(--s-1);
}
.mail__active-filters:empty { display: none; }
.mail__active-filters-label {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
  margin-right: var(--s-1);
}

/* ---- AFFORDANCE · the reveal (JT-4 / SM-4) --------------------------------
   The completeness-preserving affordance beside truncated / bounded content:
   "there's more — tap for all of it," never "we cut this." Warm, discoverable,
   sits where the eye expects it — so a truncation is TRUSTED, not feared. */
.reveal {
  display: inline;
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--moss-ink);
  border-bottom: 1px dotted color-mix(in srgb, var(--moss-ink) 55%, transparent);
  cursor: pointer;
}
.reveal:hover, .reveal:focus-visible {
  color: var(--ink);
  border-bottom-color: var(--line-gold);
  outline: none;
}

/* ---- ORGANISM · view (the three-region template) --------------------------
   The reusable shape a whole capability is spelled in: a LIST region (browse),
   a DETAIL region (the open record), and a CONTEXT region (the surrounding
   surface — filters, related, actions). Mail is the first view; slice ③ maps
   calendar and contacts onto the same template for free. Regions collapse
   gracefully on narrow widths. */
.view {
  display: grid;
  grid-template-columns: minmax(16rem, 24rem) minmax(0, 1fr) minmax(0, 18rem);
  gap: var(--s-4);
  align-items: start;
  max-width: var(--measure);
  margin: 0 auto;
}
.view__list {
  border: 1px solid var(--line-bark);
  border-radius: var(--r-m);
  overflow: hidden auto;                                /* JT-4: a long list scrolls in its own region */
  max-height: 78vh;
  background: var(--surface-card);
  list-style: none;                                     /* a <ul> works as the list region, clean */
  margin: 0;
  padding: 0;
}
/* ---- THE TWO REGIONS THE GRAMMAR WAS MISSING (SHELL 1.56) -----------------
   `.view` is a PAGE (three columns). `.view__list` is a list region WITH card
   chrome — a border, a background, and an own-scroll cap at 78vh. Between them
   the grammar had a hole: there was no class for *a bare stack of rows* and no
   class for *a wrapping row of controls*. Six renderers reached for `.view` to
   fill that hole and each one handed a LIST to a PAGE template, so its rows
   became grid CELLS and `.row__title`'s ellipsis ate the primary key.

   Diagnosed one instance at a time at SHELL 1.50, 1.52, 1.53, 1.54 and again on
   the cooler log. Each fix corrected the composition downstream in butcher.css
   and none named the family, because the family's real cause is HERE: a renderer
   cannot pick the right class when the right class does not exist. These two are
   that class. Additive — no existing surface changes. */
.view__rows {
  display: block;                                       /* a stack, not a page */
  max-width: var(--measure);
  margin: 0;
}
.view__rows > .row:last-child { border-bottom: none; }  /* no line into empty space */
.view__cluster {
  display: flex;
  flex-wrap: wrap;                                      /* controls wrap; they never clip */
  align-items: center;
  gap: var(--s-2);
  margin: 0;
}
.view__detail { min-width: 0; }                        /* min-width:0 lets it shrink */
.view__context {
  font-family: var(--face-survey);
  font-size: var(--t-small);
  color: var(--ink-soft);
}
/* a quiet header over a region (LIST / DETAIL / CONTEXT) — survey caps, muted */
.view__region-label {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
  margin: 0 0 var(--s-2);
}
/* the empty context slot reads as calm, not broken */
.view__context-empty { color: var(--ink-soft); font-style: italic; }
/* fold to one column when there isn't room for three — the context region
   drops first, then the list rides above the detail */
@media (max-width: 60rem) {
  .view { grid-template-columns: minmax(14rem, 20rem) minmax(0, 1fr); }
  .view__context { display: none; }
}
@media (max-width: 40rem) {
  .view { grid-template-columns: 1fr; }
}

/* ---- MOLECULE · rail (email-app ①a) — the mailbox navigation region ---------
   A left column of system-folder slots (Inbox · Unread · Starred · Important ·
   All mail), the Compose pill first. JT-6's one genuinely-new region; every part
   is token-keyed so it inherits the grove proportions. The count rides the calm
   `chip` grammar (muted moss), never a red unread pill — the app's "weight, never
   a red count" ethos carried into navigation. On narrow widths the rail folds to
   a horizontal scrolling strip above the list (the list is what you came for). */
/* DP-018 §7.2 — .mail__layout COLLAPSES TO THE LIST. The rail moved home to the frame's
   left column (.menu__body); this wrapper's two-column flex row had exactly one job — put
   the rail beside the list — and that job is gone. What remains is the list's measure and
   centering, which was always a separate concern that merely rode the same node.
   `.mail__rail-host` RETIRES with it (it survives in JS as an unstyled fallback host for a
   frame with no [data-app-menu]; in normal block flow it needs no rule). */
.mail__layout {
  max-width: var(--measure);
  margin: 0 auto;
}
.mail__list-view { min-width: 0; }
/* DP-018 §7.2 — `position: sticky` RETIRED. It pinned the rail while a long list scrolled
   PAST it in the body pane. In its new home the rail is the only content of a column that
   scrolls as a whole, under an anchor that is itself sticky (shell.css .menu__anchor) — so
   sticky here is at best a no-op and at worst fights the anchor. The column scrolls; the
   nameplate stays. That is the whole behaviour, and it belongs to the frame now. */
.rail {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
/* .contacts-owner-masthead (contacts leg 3 — the layout redesign) — the OWNER'S crown at the
   top of the rail: My Card, photo-first, ABOVE compose (renderRail appends it first). It is the
   book's distinguished ROOT (Lens 1), distinguished by POSITION (crown of the rail) + FORM
   (photo disc + Fraunces name) — NEVER a new hue: gold stays DP-017's lone scarce warm point,
   so the owner disc carries no gold ring. A rail element, so it lives with the rail block. It
   REUSES .contacts-record__avatar for the disc (real photo or initials, Real-or-Made) and the
   rail slots' own --sunlight active wash for `.is-active` (lit while My Card is the open record),
   layered over the raised --surface-2 plane. Tokens-only. */
.contacts-owner-masthead {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-3);
  margin-bottom: var(--s-1);
  border-bottom: 1px solid var(--line);       /* the hairline that seats the root above the Do/Find/Dwell stack */
  border-radius: var(--r-s);
  background: var(--surface-2);                /* raised: the crown reads as its own plane */
  cursor: pointer;
}
.contacts-owner-masthead:hover,
.contacts-owner-masthead:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 6%, var(--surface-2));   /* the rail-slot hover step, on the raised plane */
  outline: none;
}
.contacts-owner-masthead:focus-visible { box-shadow: 0 0 0 3px var(--ring); }   /* the house focus ring — zero new hue */
.contacts-owner-masthead.is-active {
  background: color-mix(in srgb, var(--sunlight) 10%, var(--surface-2));   /* REUSE the .rail__slot--active wash, raised-plane variant */
}
.contacts-owner-masthead__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.contacts-owner-masthead__name {
  font-family: var(--face-canopy);            /* Fraunces — the owner's name in the display face (Lens 3's distinction) */
  font-size: var(--t-body);
  color: var(--ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.contacts-owner-masthead__sub {
  font-family: var(--face-survey);            /* Spline Sans Mono — the quiet "MY CARD" label line */
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--ink-faint);
}
/* first-run (no owner profile yet): the name reads as an INVITATION to set up, not a claimed
   identity — a quieter ink. The disc stays the neutral "Me" initials (Real-or-Made). Zero new hue. */
.contacts-owner-masthead--unset .contacts-owner-masthead__name { color: var(--ink-soft); }
/* email-app seq=13 — Compose returns to the TOP of the left rail, and it is the
   app's ONE compose home (the command-strip "New message" retired). Unlike the
   wash-pill affordances (.strip__action), Compose is the PRIMARY action, so it is
   a filled `--canopy` button — the structure colour used as a fill for the primary
   act, prominent by weight + placement (top of rail, full width), per "weight marks
   the place," not a colour alarm and not a new hue. The one filled button in the
   surface; everything else stays a wash. */
.rail__compose {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  margin-bottom: var(--s-1);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--ink-on-accent);          /* dark label on the bright accent (6.88:1) — see tokens.css */
  background: var(--accent);            /* WAS --canopy: 1.00:1, an invisible button in prod */
  border: 1px solid var(--accent);
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
}
.rail__compose:hover {
  background: var(--accent-strong);     /* lift on dark, DEEPEN on light — never a contrast drop */
  border-color: var(--accent-strong);
}
.rail__compose:focus-visible {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  outline: none;
  /* The gold focus RING (the strip__select/strip__toggle convention). Load-bearing here:
     the wash-shift focus that the light affordances use reads as accent->accent-strong on a
     filled button — a step a keyboard user on a dim screen can miss. The ring is the honest
     focus signal on a filled button (FN-4: "a state a keyboard user cannot see is not
     finished"). Zero new hue (--ring is the established focus gold), zero ornament. */
  box-shadow: 0 0 0 3px var(--ring);
}
.rail__compose:active { background: color-mix(in srgb, var(--accent) 88%, var(--shadow)); }  /* the press: a luminance step, colorblind-safe */

/* .rail__search — the rail's FIND slot. Sits directly under .rail__compose and above
   the .rail__slot list: Do (compose/create) · Find (search) · Dwell (the slots). That
   order is the whole layout argument, and it is the order every app in the house wants.

   ADDED BY CALENDAR AS THE PROPOSING TRACK (DP-020 §6 Call 1 -> (b), operator ruling
   S12.1918), and deliberately GENERIC — the class knows nothing about events. Mail and
   contacts consume it unchanged; the only app-specific bytes are the placeholder and the
   aria-label, which live in each renderer, not here. It is an ADDITIVE class (a new name,
   not an edit to an existing one), so the shared-block collision surface is near zero —
   no live sibling's CSS changes meaning because this exists.

   Geometry follows .rail__slot (same inset, same rhythm) so the rail reads as one column
   rather than a stack of unrelated widgets. `min-width: 0` is load-bearing, not hygiene:
   the rail is a narrow flex column, and without it a long placeholder or a long typed
   query forces the input past its track and shoulders the whole left column wider. */
.rail__search {
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  margin-bottom: var(--s-1);
}
.rail__search-input {
  width: 100%;
  min-width: 0;                          /* the flex child must be allowed to shrink, not just the wrapper */
  padding: var(--s-1) var(--s-2);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-moss);
  border-radius: var(--r-m);
  text-overflow: ellipsis;
}
.rail__search-input::placeholder { color: var(--moss-ink); }
.rail__search-input:focus-visible {
  outline: none;
  border-color: var(--figure);
  box-shadow: 0 0 0 3px var(--ring);     /* the house focus ring — zero new hue */
}
.rail__slot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-s);
  cursor: pointer;
  color: var(--ink);
  font-family: var(--face-survey);
  font-size: var(--t-body);
}
.rail__slot:hover, .rail__slot:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 6%, transparent);
  outline: none;
}
.rail__slot--active {
  background: color-mix(in srgb, var(--sunlight) 10%, transparent);
}
.rail__slot--active .rail__slot-label { font-weight: 600; }   /* weight marks the place, never a colour alarm */
.rail__slot-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail__count { flex: 0 0 auto; margin-left: auto; }           /* the calm chip sits at the trailing edge */

/* .rail__slot--checked — the rail's MULTI-SELECT slot. Proposed by calendar in DP-018
   Track-B §5 and routed to convergence; landed here by calendar as the PROPOSING TRACK
   (the same route the operator ruled for `.rail__search`, Call 1 option (b) — announce on
   the Switchboard, THEN edit). Generic and ADDITIVE: it adds a modifier and two children,
   touches no existing selector, and mail + contacts keep consuming `.rail__slot` unchanged.

   WHY IT IS A SEPARATE MODIFIER AND NOT `--active`. `--active` is EXCLUSIVE-select: exactly
   one view is current, and weight marks the place. `--checked` is INCLUSIVE-select: any number
   of calendars can be on at once. They are different arithmetic and they must not share a
   class, or a future edit to one silently re-scopes the other. Calendar carries BOTH in one
   rail — the four views above (exclusive), the calendars below (inclusive).

   NO COLOUR ALARM (DP-018-B's rule, kept): the check state is carried by the BOX, not by
   painting the row. The per-calendar dot is IDENTITY, not status — it is the calendar's own
   colour from the tool, and it is the only place that colour appears, so it never competes
   with the category colour the event chips already use. Two colour axes, two surfaces, no
   collision, and no re-skin of anything already shipped. */
.rail__slot--checked { cursor: pointer; }
.rail__slot--checked .rail__slot-label { font-weight: 400; }  /* never bolded: checked is not "current" */
.rail__slot-box {
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: var(--r-xs, 3px);
  border: 1px solid var(--moss-ink);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6875rem;
  line-height: 1;
  color: var(--figure);
  background: transparent;
}
/* ON: the box fills. The tick is a glyph the renderer writes, so the state survives a
   stylesheet that never loaded — a checkbox whose only "on" signal is a background colour
   is invisible the moment CSS fails, and the owner cannot tell which calendars are showing. */
/* ON: the box does NOT fill with gold. Gold is scarce (DP-017 — the lone warm, the
   mark's bit + the active heartbeat), and a gold swatch on EVERY checked row turned the
   rail into a wall of it (which reads brown at this size). The ✓ glyph is renderer-written
   and CSS-fail-safe, so it already carries the on-state on its own; checked just firms the
   box's frame — an ink-soft border + an ink tick — so the check sits in a clean, quiet box.
   No brand colour on a control that repeats down the whole column. (S19.1358 skin pass.) */
.rail__slot--checked[aria-checked="true"] .rail__slot-box {
  border-color: var(--ink-soft);
  color: var(--ink);
}
.rail__slot--checked:hover .rail__slot-box,
.rail__slot--checked:focus-visible .rail__slot-box { border-color: var(--ink); }
/* The identity dot — the calendar's OWN colour, inline-styled by the renderer from the
   tool's `color` column. Cosmetic only; absent colour simply renders no dot (honest). */
.rail__slot-dot {
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  margin-left: var(--s-1);
}

/* ---- Calendar "My calendars" rail — reveal-on-need controls (S18.1145) --------
   The clip fix. Four controls parked on every row starved the NAME to ~6 chars,
   so distinct calendars ("MJTodo Work/Home/Personal") collapsed to one visible
   string. Fix: the calendar's inclusive-select slots (.rail__slot--checked) give
   the NAME the full column at rest; the colour dot LEADS (identity + the recolor
   trigger, always shown); solo + delete move into a .rail__slot-actions cluster
   that is CALM at rest and revealed on hover/focus — the same reveal idiom as
   .row__actions (mail), but SCOPED here so mail/contacts rails (a bare label, no
   dot/solo/del) are untouched. Zero new hue: gold-family + existing tokens only.
   Companion to the renderer's Distinguishable-Under-Clip pass (leading-ellipsis on
   a colliding long name; full name always on title + aria-label). */
.rail__slot--checked { position: relative; }              /* anchor for the absolute actions cluster */
.rail__slot--checked .rail__slot-box   { order: 0; }
.rail__slot--checked .rail__slot-dot   { order: 1; margin-left: 0; margin-right: var(--s-1); }
.rail__slot--checked .rail__slot-label { order: 2; flex: 1 1 auto; }   /* the name takes the whole column at rest */
.rail__slot--checked .rail__slot-actions {
  order: 3;
  position: absolute;
  right: var(--s-2);
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: var(--s-1);
  padding-left: var(--s-3);
  /* a soft fade to the rail's own surface so the revealed cluster reads cleanly
     over the name's tail (the rail bg is var(--surface), see .app-menu) */
  background: linear-gradient(to right, transparent, var(--surface) var(--s-3));
  opacity: 0;                                             /* CALM at rest — the clip fix depends on this being out of flow */
  transition: opacity 120ms ease;
}
.rail__slot--checked:hover .rail__slot-actions,
.rail__slot--checked:focus-within .rail__slot-actions { opacity: 1; }
/* touch has no hover — the cluster can't be revealed, so keep it reachable in flow */
@media (hover: none) {
  .rail__slot--checked .rail__slot-actions {
    opacity: 1; position: static; transform: none; background: none; padding-left: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .rail__slot--checked .rail__slot-actions { transition: none; }
}
/* solo + delete — bare glyph buttons with a real hit-target (they had NO CSS at
   all before, which is why the ✕'s two-step confirm was invisible in the column) */
.rail__slot-solo, .rail__slot-del {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.25rem; height: 1.25rem;
  padding: 0; border: 0; border-radius: var(--r-s);
  background: transparent; cursor: pointer;
  color: var(--ink-soft); font-size: var(--t-body); line-height: 1;
}
.rail__slot-solo:hover, .rail__slot-solo:focus-visible,
.rail__slot-del:hover,  .rail__slot-del:focus-visible {
  background: color-mix(in srgb, var(--sunlight) 8%, transparent);
  color: var(--ink); outline: none;
}
.rail__slot-solo.is-soloed { color: var(--figure); }      /* the filled ring reads as "on" */
/* the inline two-step DELETE confirm — a small readable pill that REPLACES the ✕
   in place while armed (beginCalDelete). It must be legible in the narrow column,
   so it gets its own width; focus-within keeps the cluster revealed while armed. */
.rail__slot-del-confirm {
  display: inline-flex; align-items: center;
  padding: 0 var(--s-2); height: 1.25rem;
  border: 1px solid var(--line-gold); border-radius: var(--r-s);
  background: color-mix(in srgb, var(--line-gold) 12%, var(--surface));
  color: var(--ink); cursor: pointer; white-space: nowrap;
}
.rail__slot-del-confirm-label {
  font-family: var(--face-survey); font-size: var(--t-label); line-height: 1;
}
.rail__slot-del-confirm:hover, .rail__slot-del-confirm:focus-visible {
  background: color-mix(in srgb, var(--line-gold) 22%, var(--surface)); outline: none;
}

/* DP-018 §7.2 — the narrow-width HORIZONTAL FOLD is RETIRED, and this is a correctness fix,
   not a tidy-up. The fold existed because the rail sat *inside the pane*, where a 13rem column
   beside the list stops paying for itself on a phone — so it folded to a scrolling strip above
   the list. The rail no longer lives there. Its home is `.app-menu`, which is a VERTICAL column
   at EVERY width (shell.css:1349 `flex: 0 0 200px`; :1405 narrows to `148px` under 640px — it
   narrows, it never folds). Left in force, `flex-direction: row; overflow-x: auto` would turn the
   rail inside a 148px column into a horizontal scroller at exactly the width where that is worst.
   A rule that was right in the old house is a bug in the new one. The frame owns the column's
   responsive behaviour now; the block stops second-guessing it. */

/* ---- MOLECULE · row (leg 15) — the collapsed CONVERSATION face -------------
   A thread with >1 message renders as ONE foldable `row` (NOT a `.record` —
   that is the detail object you open a message INTO; a collapsed conversation
   lives in the LIST region, so it is a row). The count `chip` + fold caret ride
   `.row__trail`, already alphabet parts, so the only genuinely new marginal CSS
   is the caret's quiet form and the indent for a conversation's expanded
   members. Token-keyed — it inherits the grove proportions like every block. */
.mail-thread__caret {
  flex: 0 0 auto;
  font-family: var(--face-survey);
  font-size: var(--t-label);
  line-height: 1;
  color: var(--moss-ink);                          /* a quiet fold mark, never an alarm */
}
/* an expanded conversation's members: indented under their head row, with a
   quiet left edge (the same inset-edge idiom as .row--active) so the nesting
   reads without shouting. */
.row--thread-child {
  padding-left: var(--s-7);
  box-shadow: inset 2px 0 0 0 var(--line-bark);
}

/* ---- BLOCK^3 · settle -------------------------------------------------------
   The completion-settle primitive (JT-2) — the quiet visual that a bounded
   surface has come to rest. Shared by The Clearing (view-scale) and The Rest
   (record-scale); the Broom-at-zero flows into the Clearing's settle (JP-1).

   Drawn under SM-3 (states nothing, counts nothing), Constraint 2 (calm, not
   casino — no motion, no payout) and JT-0 (one alphabet). Every value is a
   var(--…) from tokens.css (the Ink Law) — no raw hex. The mark is a soft,
   still forest-clearing glyph (a low, settled sun over moss): it carries the
   "at rest" reading with the word stripped (the Higgins Test). */
.settle {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  text-align: center;
  color: var(--moss-ink);
}
/* The mark — a soft, still disc: a low sun settled over the clearing. No motion
   (calm, not casino); the softness is the whole message. Text-free (Higgins). */
.settle__mark {
  display: block;
  width: var(--s-6);
  height: var(--s-6);
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--sunlight) 26%, transparent);
  box-shadow: 0 0 0 1px var(--line-moss),
              0 0 0 var(--s-2) color-mix(in srgb, var(--moss) 8%, transparent);
}
/* The quiet caption — the clearing-label idiom (survey face, moss ink, caps
   tracking), small and calm. Never a count. Omitted entirely for the mark-only
   Higgins form. */
.settle__word {
  font-family: var(--face-survey);
  font-size: var(--t-label);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--moss-ink);
}
/* view-scale — a whole cleared surface at rest: fills the pane, generous
   breathing, so the emptiness reads as arrival, not absence. */
.settle--view {
  min-height: 12rem;
  padding: var(--s-8) var(--gutter);
  gap: var(--s-4);
}
.settle--view .settle__mark {
  width: var(--s-7);
  height: var(--s-7);
}
/* record-scale — a single finished record at rest: compact, sits under the
   record's last line, a quiet coda rather than a pane. */
.settle--record {
  padding: var(--s-4) 0 var(--s-2);
  gap: var(--s-2);
}

/* ============================================================================
   REACH RECOVERY (S12.2305) — the recovery bar under an unreachable pane.

   The honest-badge says "we could not reach it." This is the part that DOES
   something about it. It hangs under mail's / calendar's / contacts' honest
   fail node: a quiet "Try again" button and a live note that says, truthfully,
   what the ladder is about to do ("Trying again in 5s…") — and, when the ladder
   is spent, that it is spent ("Still can't reach it").

   VOICE (SM-1, and it matters here more than usual): this is a QUIET affordance,
   not an alarm. The pane is already wearing a hollow ring; a red button under it
   would manufacture urgency on top of a state that is usually a five-second
   daemon restart. So: hairline border, faint ink, the same pill the rest of the
   shell uses. It should read as "the app is handling it," because it is.
   ============================================================================ */
.reach-recovery {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-3);
  flex-wrap: wrap;
}
.reach-recovery__retry {
  padding: var(--s-1) var(--s-3);
  font-family: var(--face-survey);
  font-size: var(--t-label);
  letter-spacing: var(--tracking-caps);
  color: var(--ink-muted, var(--ink));
  background: transparent;                 /* NO FILL — same restraint as the hollow ring above it */
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
}
.reach-recovery__retry:hover {
  border-color: var(--sunlight);
  color: var(--ink);
}
.reach-recovery__retry:focus-visible {
  outline: none;
  border-color: var(--sunlight);
  box-shadow: 0 0 0 2px var(--sunlight);   /* the shell's gold focus ring convention */
}
/* aria-busy is set by reach-recovery.js while a read is in flight. The button dims
   rather than spinning: a spinner would imply progress we cannot actually measure. */
.reach-recovery__retry[aria-busy="true"] {
  opacity: 0.55;
  cursor: default;
}
.reach-recovery__note {
  margin: 0;
  color: var(--moss);
  font-size: var(--t-fine, var(--t-label));
}
