/* B-lite prototype — register-led CSS.
   Full provenance record + a documented gap: ../../BUILD-BOOK.md §5.

   Palette + type tokens — VERIFIED verbatim against the site's own source
   tree (src/styles.css on the design SSD snapshot; see BUILD-BOOK.md §5 for
   the exact path and line numbers). Do not invent new tokens here — extend
   this file, don't fork it.

   Motion tokens (portal-reveal, scroll-breathe — "MOTION TOKENS" section
   below) — VERIFIED against the LIVE deployed staging build (CSSOM
   inspection of the-rose-tablet-staging.netlify.app, 2026-07-19; twice):
   keyframe definitions and the 1.6s ease-out staggered application match
   the deploy exactly. They are absent from the SSD *source snapshot*
   (2026-07-15) because the deployed build postdates it — provenance is the
   live deployment, not the source tree (full record: BUILD-BOOK.md §5).
   The 200/300/700ms hover timings DO carry source-tree provenance
   (Tailwind duration-* utilities in the site's components). Known minor
   divergence: the live build also uses 1.1s/1.4s stagger slots and a 3s
   breathe period; this file uses the first three slots. Keep timings and
   easings exact if refactoring. */

@font-face {
  font-family: "Editor";
  src: url("fonts/Editor-Regular.woff2") format("woff2"),
       url("fonts/Editor-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Editor";
  src: url("fonts/Editor-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Editor";
  src: url("fonts/Editor-Italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Editor";
  src: url("fonts/Editor-LightItalic.woff2") format("woff2");
  font-weight: 300;
  font-style: italic;
  font-display: swap;
}

/* Avenir Next — Bart's body face (his file, via Stephen, 2026-07-31 rehearsal).
   local() first: Apple devices already ship the family and skip the download;
   everyone else gets Bart's weights as woff2 (extracted from his TTC — the
   source collection is kept at the-rose-tablet/reference/fonts/). */
@font-face {
  font-family: "Avenir Next";
  src: local("Avenir Next"), url("fonts/AvenirNext-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Avenir Next";
  src: local("Avenir Next Italic"), url("fonts/AvenirNext-Italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Avenir Next";
  src: local("Avenir Next Medium"), url("fonts/AvenirNext-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Avenir Next";
  src: local("Avenir Next Demi Bold"), url("fonts/AvenirNext-DemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --serif: "Editor", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  /* Avenir Next leads (Bart's direction, 2026-07-31) — it was already named in
     this stack but sat behind Open Sans, which no @font-face ever loaded. */
  --sans: "Avenir Next", "Open Sans", Avenir, "Gill Sans", "Segoe UI", Helvetica, Arial, sans-serif;

  /* the site's named tokens — source of truth, do not rename */
  --sacred-stone:  #F6F2EC;  /* page ground */
  --linen:         #DCCFB9;  /* borders, wells, selection bg */
  --olive-wash:    #8A957A;  /* quiet secondary accents */
  --terracotta:    #A86A4D;  /* warm accent — Amara's bubbles, links, the acknowledgment gesture */
  --oxblood:       #5B1F28;  /* deep accent — buttons, focus rings */
  --midnight-ink:  #0F1B2A;  /* text */
  --soft-gold:     #B3944A;  /* reserved — sacred meaning only, at most one hairline use */

  /* semantic aliases used throughout this file, mapped to the tokens above */
  --ink:          var(--midnight-ink);
  --ink-strong:   var(--midnight-ink);
  --muted:        #666E5A;  /* darkened for WCAG AA text contrast — see 2026-07-24-sitewide-polish-rhaenyra.md §3; --olive-wash itself is untouched */
  --kicker:       var(--oxblood);
  --accent:       var(--terracotta);
  --accent-deep:  var(--oxblood);
  --label:        var(--midnight-ink);
  --quote-ink:    var(--oxblood);
  --panel-bg:     var(--linen);
  --panel-border: var(--linen);
  --hairline:     var(--linen);
  --hairline-soft:#E9E1D3;  /* midpoint of sacred-stone/linen, for quieter dividers */

  --paper:        var(--sacred-stone);
  --paper-raised: #FFFFFF;  /* cards / panels lifted off the page — quiet, no loud shadow */

  /* Amara's chat-bubble tint, derived from terracotta over paper-raised */
  --bubble-user-bg:     #F1E7E3;
  --bubble-user-border: #C5A288;

  /* cream text sitting over hero imagery, ~95% opacity per brief */
  --hero-ink: rgba(246, 242, 236, 0.95);

  /* list-row hover tint: linen at ~22% */
  --linen-wash: rgba(220, 207, 185, 0.22);

  /* ---- interaction motion — NEW (2026-07-24 sitewide polish) ----
     Entrances (portal-reveal / hero-settle / scroll-breathe, above) are
     LOCKED — provenance-verified against the live staging build. Do not
     change their durations or easing. These two tokens + one easing govern
     hover/focus/panel motion only — the layer that's currently inconsistent
     (see 2026-07-24-sitewide-polish-rhaenyra.md §1). */
  --dur-quick: 200ms;   /* hover, focus, small state flips — already the site's own verified duration (nav-item, drawer, overlay) */
  --dur-calm:  300ms;   /* row/background settles, panel-scale changes — already verified (commons-post/pod-post row tint) */
  --ease-settle: cubic-bezier(0.22, 1, 0.36, 1); /* one curve, used everywhere in this layer: starts responsive, decelerates to a stop, never overshoots — "paper settling," not a UI spring */
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

::selection { background: var(--linen); color: var(--midnight-ink); }

/* Base flipped to sans 2026-08-03, matching the production website's system:
   Avenir Next carries everything by default; Editor serif is applied
   deliberately — headings (below) and the explicitly-serif editorial
   moments that keep their own rules. */
body {
  margin: 0;
  font-family: var(--sans);
  color: var(--ink);
  background: var(--paper);
  font-size: 18px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent-deep); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* quiet underline-growth utility for inline links. UX polish pass
   (2026-07-20 §2): a faint underline is now persistent at rest and
   strengthens on hover/focus, instead of appearing from nothing -- on
   touch, where :hover never fires, the old 0->100% growth carried zero
   affordance, ever. */
.hover-underline { position: relative; text-decoration: none; }
.hover-underline::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: currentColor; opacity: 0.28;
}
.hover-underline:hover { text-decoration: none; }
.hover-underline:hover::after,
.hover-underline:focus-visible::after { opacity: 0.7; }
@media (prefers-reduced-motion: no-preference) {
  .hover-underline::after { transition: opacity 0.25s ease; }
}

/* .quiet-link / .foot-profile-link carried zero affordance except color
   (indistinguishable from surrounding italic prose) -- same root cause as
   .hover-underline above, same fix: a faint persistent underline that
   strengthens on hover/focus (UX polish pass, 2026-07-20 §2). */
.quiet-link {
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  opacity: 0.85;
}
.quiet-link:hover { opacity: 1; text-decoration-color: var(--oxblood); }

/* page settles in gently on load */
@media (prefers-reduced-motion: no-preference) {
  .page { animation: page-in 200ms ease-out; }
}
@keyframes page-in { from { opacity: 0; } to { opacity: 1; } }

.page { padding: 56px 40px; }

/* Site footer — quiet colophon at the true bottom of every page (main-wrap
   is a flex column with min-height so short pages don't strand it mid-air). */
.main-wrap { display: flex; flex-direction: column; min-height: 100vh; }
.main-wrap .page { flex: 1 0 auto; }
.site-foot {
  flex-shrink: 0;
  text-align: center;
  padding: 40px 24px 34px;
  margin-top: 24px;
  border-top: 1px solid var(--linen);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.site-foot .foot-mark { color: var(--terracotta); font-size: 15px; line-height: 1; }
.site-foot .foot-program {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}
.site-foot .foot-copy {
  font-family: var(--serif);
  font-size: 12.5px;
  font-style: italic;
  color: var(--muted);
  opacity: 0.75;
}

h1, h2, h3, h4 { font-family: var(--serif); }
h1 { font-weight: 300; color: var(--ink-strong); line-height: 1.18; letter-spacing: 0.01em; }
h1 em { font-style: italic; }
h2 { font-weight: 500; color: var(--ink-strong); }
h3 { font-weight: 600; color: var(--accent-deep); font-size: 0.9em; letter-spacing: 0.04em; }

.lede { color: var(--quote-ink); font-style: italic; }
.quiet-link { font-style: italic; color: var(--muted); }

.week-line {
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--kicker);
  margin: 0 0 14px;
}

/* ==========================================================================
   MOTION TOKENS — lifted from the staging build's own motion language.
   Keep these exact; every entrance/hover on the site should trace to one of
   these three primitives.
   ========================================================================== */

/* 1. portal-reveal — the signature threshold entrance */
@keyframes portal-reveal {
  0%   { opacity: 0; transform: translateY(14px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* 2. scroll-breathe — slow ambient loop, used on the module threshold's
   descend cue; defined generally per the site's motion vocabulary even where
   unused, for fidelity. */
@keyframes scroll-breathe {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.9; }
}

/* 3. hero-settle — barely-perceptible slow zoom-out on hero photography.
   Different element/property than portal-reveal (transform:scale vs.
   opacity+translateY on the text above it), so the two don't fight. */
@keyframes hero-settle {
  from { transform: scale(1.035); }
  to   { transform: scale(1); }
}

/* All of the above — plus the hover timings below — are fully disabled
   under reduced motion by simply never being declared outside this guard;
   elements fall back to their plain, final, motionless state. */
@media (prefers-reduced-motion: no-preference) {
  .hero-img { animation: hero-settle 16s ease-out forwards; }

  .hero-kicker { opacity: 0; animation: portal-reveal 1.6s ease-out 0.2s both; }
  .hero-title  { opacity: 0; animation: portal-reveal 1.6s ease-out 0.5s both; }
  .lede        { opacity: 0; animation: portal-reveal 1.6s ease-out 0.8s both; }

  /* the module threshold's quiet "descend" marker: enters on the same
     stagger beat (0.8s, the "meta/supporting" slot), then breathes */
  .descend-cue {
    opacity: 0;
    animation: portal-reveal 1.6s ease-out 0.8s both,
               scroll-breathe 3.6s ease-in-out 2.4s infinite;
  }

  /* chat + interactive entrances: same family, smaller scale, no stagger */
  .msg-row.msg-in, .post-in { animation: portal-reveal 0.45s ease-out both; }

  /* the typing indicator's dots are kin to scroll-breathe (opacity+scale
     pulse rather than a plain opacity loop — see .typing-dots below) */

  /* nav/menu reveals */
  .nav-item, .sidebar-brand { transition: color 200ms ease, border-left-color 200ms ease; }
  /* mobile drawer + profile popover: token-ized (2026-07-24 §1.5), with a
     small translateY settle added below so they join the hero/message
     entrance vocabulary instead of a flat cross-fade. .witness-overlay
     keeps its own original values -- untouched, out of this spec's scope. */
  .mobile-drawer, .profile-overlay {
    transition: opacity var(--dur-quick) var(--ease-settle), visibility 0s linear var(--dur-quick), transform var(--dur-quick) var(--ease-settle);
  }
  .mobile-drawer.is-open, .profile-overlay.is-open {
    transition: opacity var(--dur-quick) var(--ease-settle), visibility 0s linear 0s, transform var(--dur-quick) var(--ease-settle);
  }
  .witness-overlay { transition: opacity var(--dur-quick) var(--ease-settle), visibility 0s linear var(--dur-quick); }
  .witness-overlay.is-open { transition: opacity var(--dur-quick) var(--ease-settle), visibility 0s linear 0s; }
  /* the compact box's two motion concerns (corner redesign, 2026-07-25 §2):
     appear/disappear (collapsed <-> open) is opacity + an 8px translateY
     settle on --dur-quick, the same delayed-visibility idiom as
     .mobile-drawer/.profile-overlay above; grow/shrink (open <-> expanded)
     is width/height/right/bottom/border-radius on --dur-calm, the token
     named for panel-scale changes. Both live on the one class pair so every
     transition path lands in the same timing regardless of which edge
     triggered it. */
  .witness-slideover {
    transition: opacity var(--dur-quick) var(--ease-settle),
                visibility 0s linear var(--dur-quick),
                transform var(--dur-quick) var(--ease-settle),
                width var(--dur-calm) var(--ease-settle),
                height var(--dur-calm) var(--ease-settle),
                right var(--dur-calm) var(--ease-settle),
                bottom var(--dur-calm) var(--ease-settle),
                border-radius var(--dur-calm) var(--ease-settle);
  }
  .witness-slideover.is-open {
    transition: opacity var(--dur-quick) var(--ease-settle),
                visibility 0s linear 0s,
                transform var(--dur-quick) var(--ease-settle),
                width var(--dur-calm) var(--ease-settle),
                height var(--dur-calm) var(--ease-settle),
                right var(--dur-calm) var(--ease-settle),
                bottom var(--dur-calm) var(--ease-settle),
                border-radius var(--dur-calm) var(--ease-settle);
  }
  /* No transition declared for .is-joined's border snap (can't meaningfully
     animate `border-style` to none -- see the comment above
     .witness-affordance.is-joined) or for .witness-expand-toggle (already
     covered for free by the sitewide `button{}` rule above). Nothing here
     touches .msg-row.msg-in, .typing-dots/witness-breathe, or
     .witness-thread's smooth-scroll -- inherited verbatim from the reused
     inner panel, out of this redesign's scope. All of the above -- like
     every rule in this guard -- is simply never declared outside it, so
     reduced-motion users fall back to the plain, final, motionless state
     with no separate `transition: none` override needed (see this file's
     own comment at the top of this block, line ~237). */

  /* list-row hovers: background tints to linen at ~22% -- extended to
     .reflection-card/.note-item (2026-07-24 §1.7): true siblings of
     .pod-post/.commons-post in the same feeds, now unified under one idiom
     instead of one getting a false lift and the other nothing. */
  .pod-post:not(.mine), .commons-post, .reflection-card, .note-item {
    transition: background-color var(--dur-calm) var(--ease-settle);
  }

  /* imagery opacity warms */
  .film-frame { transition: opacity 700ms ease; }

  /* global button family — the single highest-leverage rule in this file:
     every <button> on every page inherits a smooth state change unless a
     more specific rule overrides `transition` (none currently do). */
  button {
    transition: background-color var(--dur-quick) var(--ease-settle),
                border-color var(--dur-quick) var(--ease-settle),
                color var(--dur-quick) var(--ease-settle),
                opacity var(--dur-quick) var(--ease-settle);
  }

  /* quiet in-card / in-row link text that darkens on hover */
  .since-text, .index-title, .room-invite, .avatar-circle,
  .quiet-link, .foot-profile-link, .threshold-link, .desk-quicknav a {
    transition: color var(--dur-quick) var(--ease-settle),
                background-color var(--dur-quick) var(--ease-settle),
                opacity var(--dur-quick) var(--ease-settle);
  }
}

/* ---- rose mark (SVG asset via CSS mask, so we control the exact color) ----
   Platform usage of the rose mark is PENDING a doctrine ruling: TRT
   non-negotiables §7.4 reads as internal-register-only for this mark, while
   the public site uses it in nav. Contradiction parked for Bart/Kristine —
   this prototype uses it (per Stephen's direction for this iteration) so he
   can judge the effect; do not treat its presence here as the doctrine
   answer. Rendered in midnight-ink (quieter on sacred-stone than oxblood). */
.rose-mark {
  display: inline-block;
  width: 52px; height: 52px;
  flex: none;
  background-color: var(--midnight-ink);
  -webkit-mask-image: url("logos/Rose.svg");
  mask-image: url("logos/Rose.svg");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}

/* Bart's Study lockup (rose + wordmark + STUDY, one SVG) — the sidebar
   brand since 2026-08-03. Same mask technique as .rose-mark so it takes
   the ink color; the compact rose+word pair stays for the low topbar. */
.study-mark {
  display: inline-block;
  width: 116px; height: 158px; /* 792:1080, the SVG's own ratio */
  flex: none;
  background-color: var(--midnight-ink);
  -webkit-mask-image: url("logos/Study.svg");
  mask-image: url("logos/Study.svg");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}

/* ---- brand lockup (rose mark + wordmark) ---- */

.sidebar-brand {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  text-decoration: none; margin-bottom: 36px;
}
.sidebar-brand:hover { text-decoration: none; }
.brand-word {
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--midnight-ink); margin-top: 2px;
}

/* ---- top bar (mobile/tablet chrome) ---- */

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; border-bottom: 1px solid var(--hairline);
  background: var(--sacred-stone);
  position: sticky; top: 0; z-index: 6;
}
.topbar .rose-mark { width: 30px; height: 30px; }
.topbar .sidebar-brand { flex-direction: row; align-items: center; gap: 10px; margin-bottom: 0; }
.topbar .brand-word { margin-top: 0; font-size: 12px; }
.menu-toggle {
  margin: 0; padding: 9px 16px; font-size: 11px; letter-spacing: 0.18em;
  background: transparent; color: var(--oxblood); border: 1px solid var(--oxblood);
  min-height: 44px; display: inline-flex; align-items: center;
}
.menu-toggle:hover { background: var(--oxblood); color: #fff; }

/* ---- left sidebar (desktop) ---- */

.sidebar { display: none; }

.sidebar-nav { display: flex; flex-direction: column; gap: 2px; margin-bottom: auto; }
.nav-item {
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--muted); padding: 10px 4px 10px 14px; border-left: 2px solid transparent;
}
.nav-item:hover { color: var(--oxblood); text-decoration: none; }
.nav-item.active { color: var(--oxblood); border-left-color: var(--terracotta); }

.sidebar-foot {
  display: flex; align-items: center; gap: 12px;
  padding-top: 24px; margin-top: 24px; border-top: 1px solid var(--hairline);
}
.sidebar-foot-text { display: flex; flex-direction: column; }
.foot-name { font-family: var(--serif); font-style: italic; font-size: 15px; color: var(--ink); }
.foot-week { font-family: var(--sans); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }

@media (min-width: 1001px) {
  .topbar { display: none; }
  .sidebar {
    display: flex; flex-direction: column;
    position: fixed; top: 0; left: 0; bottom: 0; width: 240px;
    background: var(--sacred-stone); border-right: 1px solid var(--hairline);
    padding: 34px 26px 26px; overflow-y: auto; z-index: 5;
  }
  .main-wrap { margin-left: 240px; }
}

/* medium desktop: slim the rail rather than break the three-column layout */
@media (min-width: 1001px) and (max-width: 1300px) {
  .sidebar { width: 188px; padding: 26px 16px 20px; }
  .main-wrap { margin-left: 188px; }
  .sidebar-brand { margin-bottom: 26px; }
  .sidebar .rose-mark { width: 44px; height: 44px; }
  .nav-item { font-size: 11px; letter-spacing: 0.13em; padding: 13px 2px 13px 10px; } /* 44px touch target -- this width range is where a touch-primary iPad-landscape gets the sidebar, not the drawer */
  .module-layout { grid-template-columns: minmax(0, 1fr) 320px; gap: 40px; }
  .transmission { font-size: 18px; line-height: 1.72; }
  .transmission-title { font-size: 26px; }
  .inquiry-fold p { font-size: 18px; line-height: 1.72; }
  .hero-content { padding: 0 32px; }
}

/* ---- mobile drawer ---- */

.mobile-drawer {
  position: fixed; inset: 0; z-index: 70;
  background: var(--sacred-stone); overflow-y: auto;
  padding: 28px 24px 40px;
  opacity: 0; visibility: hidden; transform: translateY(-6px);
}
.mobile-drawer.is-open { opacity: 1; visibility: visible; transform: translateY(0); }
.drawer-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 32px; }
.drawer-head .sidebar-brand { flex-direction: row; align-items: center; gap: 10px; margin-bottom: 0; }
.drawer-head .rose-mark { width: 40px; height: 40px; }
.drawer-close {
  margin: 0; padding: 8px 16px; font-size: 11px; letter-spacing: 0.16em;
  background: transparent; color: var(--muted); border: 1px solid var(--hairline);
  min-height: 44px; display: inline-flex; align-items: center;
}
.drawer-close:hover { background: var(--linen); color: var(--oxblood); }
.mobile-drawer .nav-item { display: block; font-size: 14px; padding: 14px 6px; border-bottom: 1px solid var(--hairline-soft); border-left: none; }
.mobile-drawer .sidebar-foot { margin-top: 32px; }

/* ---- the preview banner (operator/founder lens) ----
   base.html renders this above her page while an operator is previewing.
   Unstyled until the 2026-07-26 sweep, which meant the bare sitewide
   button{} rule filled "Leave preview" solid oxblood — a loud call-to-action
   sitting on top of the quiet reading surface, reading like part of HER
   experience. It's operator chrome: a thin band, quiet type, and the exit
   demoted to the same underlined-text idiom every other secondary control
   on the platform uses. */
.preview-banner {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  margin: -16px 0 32px; padding: 10px 0 12px;
  border-bottom: 1px solid var(--hairline);
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted);
}
.preview-banner form { margin: 0; display: inline-flex; }
.preview-exit {
  margin: 0; padding: 0; background: none; border: none; cursor: pointer;
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--accent-deep);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px;
  min-height: 44px; display: inline-flex; align-items: center;
}
.preview-exit:hover, .preview-exit:focus-visible { background: none; color: var(--oxblood); }

/* ---- profile popover ---- */

.profile-overlay {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(15, 27, 42, 0.35);
  display: flex; align-items: center; justify-content: center; padding: 20px;
  opacity: 0; visibility: hidden;
}
.profile-overlay.is-open { opacity: 1; visibility: visible; }
.profile-card {
  position: relative; background: var(--paper-raised); border: 1px solid var(--hairline);
  border-radius: 8px; padding: 30px 32px; max-width: 320px; width: 100%;
  transform: translateY(6px);
}
@media (prefers-reduced-motion: no-preference) {
  .profile-card { transition: transform var(--dur-quick) var(--ease-settle); }
}
/* the card settles independently of its backdrop -- the scrim shouldn't move, only the card */
.profile-overlay.is-open .profile-card { transform: translateY(0); }
.profile-name { font-style: italic; font-weight: 300; font-size: 21px; color: var(--oxblood); margin: 0 0 6px; }
.profile-loc { font-family: var(--sans); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); margin: 0 0 14px; }
.profile-words { font-style: italic; color: var(--ink); margin: 0; }
.popover-close {
  position: absolute; top: 10px; right: 12px; background: none; border: none;
  font-size: 20px; line-height: 1; color: var(--muted); cursor: pointer; padding: 4px 8px; margin: 0;
}
/* hit-slop: visually ~28px, under the 44px touch-target floor -- grown via
   an invisible pseudo-element rather than visible padding, same technique
   already used for .avatar-circle (2026-07-24 sitewide-polish §sitewide).
   Zero visual change; fixes this everywhere .popover-close is used
   (.profile-card's close button included, not just the Witness). */
.popover-close::before { content: ""; position: absolute; inset: -9px; }
.popover-close:hover { background: none; color: var(--oxblood); }

/* scoped override: inside the Witness controls cluster only, .popover-close
   drops its own absolute top/right (it's laid out by the flex row + gap
   instead) but keeps `position: relative` so its own ::before hit-slop
   still sizes against ITS box, not the cluster's. Everywhere else on the
   site (.profile-card) .popover-close is completely unchanged. */
.witness-panel-controls .popover-close { position: relative; top: auto; right: auto; }

/* ==========================================================================
   HERO BANDS — full-bleed threshold imagery. "The reading surface never
   sits on imagery": hero content lives in its own {% block hero %}, always
   above (never inside) .page.
   ========================================================================== */

.hero {
  position: relative; width: 100%; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
}
.hero-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: saturate(0.84) contrast(0.96);
}
.hero-veil {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(15, 27, 42, 0.42) 0%,
    rgba(15, 27, 42, 0.40) 55%,
    rgba(15, 27, 42, 0.20) 78%,
    var(--sacred-stone) 100%);
}
.hero-content { position: relative; z-index: 2; padding: 0 48px; max-width: 900px; }
.hero-kicker {
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--hero-ink); margin: 0 0 14px;
}
.hero-title { font-weight: 300; color: var(--hero-ink); margin: 0; line-height: 1.16; }
.hero-title em { font-style: italic; }

.descend-cue { display: block; margin-top: 22px; font-size: 20px; color: var(--hero-ink); }

.hero-module { height: 52vh; min-height: 380px; }
.hero-module .hero-title { font-size: 46px; }

/* 2026-07-24 (Stephen, operator call): every hero rises to the module page's
   immersive height -- the taller, grounded feel wanted site-wide, dropping the
   shallower "index register" band from docs/modules-hero-ruling-2026-07-21.md §3c.
   The modules still-life keeps its top-anchored crop below; the desktop 52vh band
   still ends near ~52% of the image, holding the legible AI-script line (~66-69%)
   out of frame (ruling §2). Mobile leans on .hero-veil, per that ruling's caveat. */
.hero-commons { height: 52vh; min-height: 380px; }
.hero-gatherings { height: 52vh; min-height: 380px; }
.hero-pod { height: 52vh; min-height: 380px; }
.hero-modules { height: 52vh; min-height: 380px; }
/* keeps the legible script line in the still-life out of the visible crop —
   see docs/modules-hero-ruling-2026-07-21.md §2 before changing this value */
.hero-modules .hero-img { object-position: 50% 15%; }
.hero-commons .hero-title, .hero-gatherings .hero-title, .hero-pod .hero-title, .hero-modules .hero-title { font-size: 34px; }

.hero-home { height: 52vh; min-height: 380px; }
.hero-home .hero-title { font-size: 38px; }

/* the paper-only line right below any hero — kicker/title/lede is the
   full 3-step portal-reveal stagger for pages without a descend-cue */
.welcome-note { max-width: 680px; margin: 40px auto 8px; }

h1, .hero-title { /* n/a — keep both weight rules distinct, see above */ }

/* ---- home ---- */

/* "Since you were last here" — the margin rule (Stephen's pick, option A,
   2026-07-26). The dividing hairlines cut one moment into a table; they're
   replaced by a single quiet vertical line with a small terracotta dot per
   item. The dot is NOT new decoration — it's the mark this platform already
   means something by (a filled terracotta dot = "written" on the steward
   cadence grid, and the held mark on a post), so it reads as "something is
   here" without teaching a new symbol. CSS-only by design: home.html's
   markup is byte-pinned by tests/golden/home_demo.html. */
.since-last { max-width: 680px; margin: 36px auto 0; }
.since-list { list-style: none; margin: 0; padding: 0 0 0 26px; position: relative; }
.since-list::before {
  content: ""; position: absolute; left: 4px; top: 14px; bottom: 14px;
  width: 1px; background: var(--linen);
}
.since-item { border-bottom: none; }
.since-link { display: block; position: relative; padding: 11px 4px; color: var(--ink); }
.since-link::before {
  content: ""; position: absolute; left: -26px; top: 17px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--terracotta);
  border: 2px solid var(--paper);   /* a paper ring so the dot sits ON the line, not behind it */
}
.since-link:hover { text-decoration: none; }
.since-link:hover::before, .since-link:focus-visible::before { background: var(--oxblood); }
.since-kicker {
  display: block; font-family: var(--sans); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 3px;
}
.since-text { display: block; font-family: var(--serif); font-style: italic; font-size: 20px; color: var(--ink-strong); }
.since-link:hover .since-text, .since-link:focus-visible .since-text { color: var(--accent-deep); }
@media (prefers-reduced-motion: no-preference) {
  .since-link::before { transition: background-color var(--dur-quick) var(--ease-settle); }
}

.this-week { max-width: 680px; margin: 44px auto; }
.module-card {
  display: block;
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  padding: 34px 36px;
  color: var(--ink);
}
.module-card:hover, .module-card:focus-visible { text-decoration: none; border-left-color: var(--accent-deep); }
.card-kicker { display: block; font-family: var(--sans); font-size: 12px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--kicker); margin-bottom: 14px; }
.card-title { display: block; font-family: var(--sans); font-size: 15px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); }
.card-theme { display: block; font-size: 30px; font-weight: 300; font-style: italic; color: var(--ink-strong); margin: 4px 0 14px; }
.card-note { display: block; color: var(--muted); font-size: 16px; }

.pod-glance { max-width: 680px; margin: 0 auto; }
.pod-glance h2, .workbook-fold h2, .since-last h2 {
  font-family: var(--sans); font-size: 15px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent-deep); border-bottom: 1px solid var(--hairline); padding-bottom: 12px; margin: 0 0 14px;
}
/* Home's pod glance wears the SAME note-slip design as the pod room
   (Stephen, 2026-07-26): the two surfaces show the same kind of writing, so
   they read as one object in two places. CSS-only by design — home.html's
   markup is byte-pinned by the golden snapshot (tests/golden/home_demo.html),
   and its <li> already carries the same avatar/name/body shape .note-item
   does. Deliberately NO hover tint: unlike the pod feed these three rows
   aren't scanned or interactive. (This used to cite .gathering-card as its
   fellow no-hover case; .gathering-card became a real disclosure control on
   2026-08-14 and now takes the tint, so the cross-reference is dropped rather
   than left pointing at a sibling that no longer agrees. The reasoning here is
   unchanged and stands on its own: nothing on this list opens or navigates.) */
.glance-list {
  list-style: none; margin: 0 0 16px; padding: 0;
  display: flex; flex-direction: column; gap: 12px;
}
.glance-list li {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  padding: 14px 18px; font-size: 16px;
  background: var(--paper-raised); border: 1px solid var(--hairline);
  border-radius: 18px;
}
.glance-name { font-style: italic; color: var(--accent-deep); }
.glance-body { color: var(--ink); font-size: 16px; flex: 1 1 260px; }
.empty { color: var(--muted); font-style: italic; }
/* Every quiet "nothing here yet" sentence carries the same small terracotta
   fleuron the site already trusts (foot-mark, intake-mark, rose-open) — a
   visual anchor, not a color change to the words (design pass 2026-08-03,
   Rhaenyra §3; Stephen's go 2026-08-03). The `/ ""` alt keeps it decorative
   for screen readers. */
.empty::before {
  content: "\2767" / "";
  color: var(--terracotta);
  font-style: normal;
  margin-right: 10px;
}

/* ---- module page: fourth fold, the workbook ----
   A quiet gift, not a download CTA (module-one.md §The Digital Workbook: "every optional
   element named on the page as a gift rather than a task"). Same card family as .module-card/
   .gathering-card/.reflection-card -- a distinct-medium object (a PDF, like .film-frame is a
   distinct-medium video placeholder), so it earns a frame the way continuous-reading/social
   folds (.transmission, .pod-glance) don't. workbook-fold-design-rhaenyra.md has the full spec. */
.workbook-fold { max-width: 680px; margin: 0 auto 56px; }

.workbook-card {
  display: block;
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  padding: 26px 28px;
  color: var(--ink);
}
.workbook-card:hover, .workbook-card:focus-visible {
  text-decoration: none;
  border-left-color: var(--accent-deep);
}
.workbook-card:hover .workbook-arrow, .workbook-card:focus-visible .workbook-arrow {
  color: var(--accent-deep);
}

.workbook-title {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: 21px;
  color: var(--ink-strong);
  margin: 0 0 8px;
}
.workbook-arrow { color: var(--accent); margin-left: 3px; }

.workbook-meta {
  display: block;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}

.workbook-note {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-size: 16px;
  color: var(--muted);
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--hairline-soft);
}

/* ---- module page: fifth fold, the inquiry ----
   The week's question (P1i; module-one.md Part Six: "The Inquiry --
   surfaced on the platform as the week's question, beside the video and the
   written transmission"; "Fifth fold: the inquiry, set alone"). Quiet and
   uncarded, unlike .workbook-card -- this is prose to sit with, not an
   object/action to engage (no link, no CTA), so it takes the
   section-kicker idiom (.card-kicker/.reflection-kicker family) over an h2
   title, and transmission-adjacent type (.transmission p's own size/
   line-height/color) for the question itself, rather than the workbook-card
   frame. module.html only emits this section when module.inquiry_md is
   present (NULL for any module the extractor can't reach it for -- see
   extract_books_of_truth.py's module docstring). */
.inquiry-fold { max-width: 680px; margin: 0 auto 56px; }
.inquiry-kicker {
  display: block; font-family: var(--sans); font-size: 12px; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--kicker); margin-bottom: 14px;
}
.inquiry-fold p {
  font-family: var(--serif); font-size: 20px; line-height: 1.78; color: var(--ink); margin: 0;
}

/* ---- modules index ---- */

.module-index { max-width: 680px; margin: 40px auto 0; padding: 0; list-style: none; }
.index-row { border-bottom: 1px solid var(--hairline); }
.index-row a, .index-row div { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 4px 20px; padding: 20px 4px; }
.index-num { grid-column: 1; font-family: var(--sans); font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); }
.index-title { grid-column: 1; grid-row: 2; font-size: 22px; font-style: italic; color: var(--ink-strong); }
.index-state { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-family: var(--sans); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--kicker); }
.index-row.veiled { opacity: 0.6; }
.index-row.veiled .index-title { color: var(--muted); }
.index-row.open a:hover { text-decoration: none; }
.index-row.open a:hover .index-title, .index-row.open a:focus-visible .index-title { color: var(--accent-deep); }

/* Restores the movement structure ("the sequence is the medicine") the flat
   list dropped -- UX polish pass, 2026-07-20 §5. Same quiet section-break
   idiom as .since-last h2 / .steward h2. */
.movement-header {
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent-deep); border-bottom: 1px solid var(--hairline); padding-bottom: 10px;
  margin: 56px 0 4px; max-width: 680px; margin-left: auto; margin-right: auto;
}
.movement-header:first-of-type { margin-top: 8px; }

.veiled-page { max-width: 620px; margin: 0 auto; text-align: center; }
.veiled-page h1 { font-size: 34px; }

/* ---- waiting (Task 9): the holding screen for a wave that hasn't started ---- */
/* Vertically centered in the viewport, not parked at the top of an empty page
   (Stephen, live rehearsal 2026-07-31): she may sit on this screen for weeks,
   so the stillness should read as composed, not unfinished. 70vh rather than
   100vh leaves room for the page's own footer without a scrollbar. */
.waiting-page {
  max-width: 620px; margin: 0 auto; text-align: center;
  display: flex; flex-direction: column; justify-content: center;
  min-height: 70vh;
}
.waiting-page h1 { font-size: 34px; }
/* Her waiting lines run in the body face, not the display serif (Stephen,
   2026-07-31 rehearsal — Bart's Avenir direction). The heading stays serif. */
.waiting-page p { font-family: var(--sans); }

/* THE DOORWAY BUTTON, shared by two surfaces (flow redesign §3, 2026-08-12).
   It began as intake.css's .intake-continue — the single interactive element
   on the threshold screen, carrying the weight of one — and moved here when
   the holding page's invitation became the same discrete control (Kristine's
   walk: "stage the form behind a clear call to action"). waiting.html links
   only this stylesheet, so one definition here is what keeps the two from
   drifting into two buttons. Values are unchanged from intake.css's own. */
.intake-continue {
  font-family: var(--sans);
  font-size: 1rem;
  letter-spacing: 0.04em;
  color: var(--paper);
  background: var(--accent-deep);
  border-radius: 2px;
  padding: 0.75rem 1.6rem;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: opacity var(--dur-quick) var(--ease-settle);
}
.intake-continue:hover { opacity: 0.88; text-decoration: none; }

/* The holding page centres its column, so the button centres with it. */
.waiting-cta { margin: 4px 0 20px; }

/* ---- module page layout ---- */

.module-layout {
  display: grid;
  /* Witness column widened 360 → 420 (2026-08-03, Stephen's ask): at 16px
     bubble text, 360px gave ~34 characters a line — conversation read as a
     strip. 420px brings a bubble line to ~42ch without crowding the
     transmission column at the 1240px canvas. */
  grid-template-columns: minmax(0, 1fr) 420px;
  gap: 56px;
  max-width: 1240px;
  margin: 40px auto 0;
  align-items: start;
}
.reading-col { min-width: 0; }

/* film — an elegant still frame, never a gray box */
.film { margin: 0 0 52px; }
/* The playing state: video-player.js replaces .film-frame with a bare
   <video> (mint-on-play), which otherwise renders at its intrinsic width
   (1920px) and blows out the reading column -- found on the first real
   play, 2026-08-24. Size it to the column it sits in, same radius as the
   frame it replaced. The audio element gets the same column width. */
.film video { width: 100%; height: auto; display: block; border-radius: 4px; }
.audio-transmission audio { width: 100%; display: block; }
/* The audio transmission affordance (redesigned 2026-08-24, Stephen: the old
   empty bordered box "looks blank, no play button"). Same visual language as
   the film: a circled play glyph + an explicit invitation, obviously
   clickable, comfortably above the 44px touch floor. */
.audio-transmission { margin: 44px 0 56px; }
.practice-line { margin: 0 0 14px; font-size: 15px; font-style: italic; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.audio-affordance {
  display: flex; align-items: center; gap: 16px;
  min-height: 64px; padding: 12px 18px;
  border: 1px solid var(--panel-border); border-radius: 4px;
  background: rgba(255, 255, 255, 0.55);
  cursor: pointer;
  transition: opacity 300ms ease;
}
.audio-affordance:hover { opacity: 0.8; }
.audio-play {
  flex: none;
  font-size: 18px;
  color: var(--accent-deep);
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  padding-left: 3px;
  border: 1px solid var(--panel-border);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
}
.audio-invite { font-size: 15px; font-style: italic; color: var(--muted); }
.film-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 4px;
  border: 1px solid var(--panel-border);
  background:
    radial-gradient(120% 140% at 30% 20%, #f0ece4 0%, #e6ddcd 42%, #d6c7ae 100%);
  box-shadow: inset 0 0 90px rgba(15, 27, 42, 0.10);
  display: flex;
  align-items: center;
  justify-content: center;
  filter: saturate(0.92);
  opacity: 0.80;
}
/* PENDING STEPHEN'S CALL (2026-07-24 sitewide polish §2 module page,
   flagged not asserted): suppressed to opacity: 0.80 (== rest state, i.e.
   no hover reaction) because .film-frame has no <a>/click handler anywhere
   -- the play triangle + hover brightening implies "click to play" on a
   deliberate pre-content placeholder (film shoots August). If this hover is
   intentional foreshadowing, revert to opacity: 0.95. */
.film-frame:hover { opacity: 0.80; }
.film-play {
  font-size: 34px;
  color: var(--accent-deep);
  width: 78px; height: 78px;
  display: flex; align-items: center; justify-content: center;
  padding-left: 6px;
  border: 1px solid var(--panel-border);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.72);
}
.film figcaption { margin-top: 12px; font-size: 14px; font-style: italic; color: var(--muted); display: flex; align-items: center; gap: 8px; }
/* the one hairline-thin use of the reserved gold — echoing the video script's
   "gold marker" that opens each recorded section (Book of Truth, Part Two) */
.gold-marker { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--soft-gold); flex: none; }

/* the written transmission — the printed page */
.transmission {
  max-width: 65ch;
  margin: 0 0 64px;
  font-size: 20px;
  line-height: 1.78;
  color: var(--ink);
}
.transmission-title { font-size: 30px; font-weight: 300; font-style: italic; margin: 0 0 28px; color: var(--ink-strong); }
.transmission p { margin: 0 0 22px; }
.transmission .rose-open { text-align: center; color: var(--accent); font-size: 16px; margin: 0 0 22px; }
.transmission .rose-close { text-align: center; color: var(--accent); font-size: 22px; margin-top: 40px; }

/* the drop cap targets the paragraph immediately after the title, not
   :first-of-type on .transmission p -- the .rose-open mark above is also a
   <p>, which would make :first-of-type ambiguous. A still, printed-page
   gesture (illuminated first letters predate the web) -- not a borrowed
   Living Room device; LR's register is kinetic type/motion, the opposite of
   this. See docs/ux-beautify-2-spec-2026-07-20.md §2a. */
.transmission-title + p::first-letter {
  font-family: var(--serif);
  font-weight: 400;
  font-style: normal;
  font-size: 4.4em;
  line-height: 0.72;
  float: left;
  padding: 0.04em 0.1em 0 0;
  color: var(--accent-deep);
}

/* ---- avatars + member presence ---- */

.avatar-circle {
  position: relative;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--linen); color: var(--oxblood);
  font-family: var(--sans); font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; cursor: pointer; flex: none; padding: 0; margin: 0;
}
/* invisible hit-slop: brings the 34px mark up to a 44px tap target with
   zero visual change (2026-07-24 §sitewide) -- it's load-bearing at its
   visual size in facepiles/inline author marks, so it can't just grow.
   Trade-off named in the spec: in a tight 8px-gap facepile, neighboring
   avatars' invisible zones overlap by a couple of px near the midpoint --
   a minor ambiguity, far better than the fully-cramped target before. */
.avatar-circle::before { content: ""; position: absolute; inset: -5px; }
.avatar-circle:hover, .avatar-circle:focus-visible { background: var(--terracotta); color: #fff; }
.avatar-circle.avatar-photo {
  background-size: cover;
  background-position: center;
}
.facepile { display: flex; gap: 8px; margin-bottom: 20px; }
.post-author { display: inline-flex; align-items: center; gap: 10px; }

/* ---- pod thread / commons posts (shared post styling) ---- */

.pod-thread { max-width: 65ch; border-top: 1px solid var(--hairline); padding-top: 40px; }
.pod-thread h2 { font-family: var(--sans); font-size: 15px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--accent-deep); margin: 0 0 6px; }
.section-note { color: var(--muted); font-style: italic; font-size: 16px; margin: 0 0 28px; }
.pod-post {
  padding: 22px 12px; margin: 0 -12px; border-radius: 4px;
  border-bottom: 1px solid var(--hairline-soft);
}
/* Member-written words run in Avenir Next (Bart via Stephen, 2026-07-31):
   the editorial serif stays ours; what the women themselves write reads in
   the body face — pod postings, commons posts, reflections, replies. */
.pod-post p, .commons-post p, .reflection-card p, .reply-body, .note-body {
  font-family: var(--sans);
}
.pod-post p, .commons-post p { margin: 0 0 12px; }
/* Commons posts join the raised-card family (redesign 2026-07-25) — the
   same paper the reflection cards sit on, so the wave's voices carry the
   weight of the pod's spine instead of floating as bare hairline rows. */
.commons-post {
  background: var(--paper-raised); border: 1px solid var(--hairline);
  border-radius: 6px; padding: 26px 28px;
}
.commons-post p:last-of-type { margin-bottom: 0; }
.pod-post.mine { background: var(--panel-bg); border-left: 2px solid var(--panel-border); margin: 8px -12px; border-bottom: none; }
.pod-post:not(.mine):hover, .commons-post:hover, .reflection-card:hover, .note-item:hover { background-color: var(--linen-wash); }
.post-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 12px; margin-bottom: 10px; }
.post-name { font-style: italic; color: var(--accent-deep); font-size: 18px; }
.post-date { font-family: var(--sans); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }

/* ---- the Held gesture (redesign 2026-07-25) ----
   One reserved row under a post: the control renders FIRST, the aggregated
   held-line second — a new hold only ever grows the row rightward, so the
   control is never displaced (the old .ack-row put the line first and
   shoved the button sideways the moment anyone held). The mark speaks the
   cadence grid's own vocabulary (steward: filled terracotta dot = written,
   hollow ring = quiet): a hollow ring that fills when held. Holding stays
   one-way — an act of witness, not a toggle. This does not assert the
   gesture's in-world name ("Held" is still the founder's placeholder,
   doctrine/decisions.md 2026-07-19) — swap .hold-word's text in
   _macros.html + held_line() in app.py when she names it. */
.held-row {
  margin-top: 14px; display: flex; align-items: flex-start;
  gap: 6px 12px; min-height: 44px; /* reserves the control's own footprint
    even after it settles into the bare mark -- the row never collapses
    (spec §2.4's reserved-layout discipline, adapted to this control).
    flex-start (Barristan fix, spec §2.4's own reasoning): a wrapped
    two-line held-line grows the row DOWNWARD only -- center would re-seat
    the control/mark mid-row as the height changes. The paddings below pin
    every state's optical centerline to the same ~22px from the row top. */
}
.ack-form { flex: none; margin: 0; display: inline-flex; } /* fixed footprint -- never displaced */
.hold-control {
  margin: 0; padding: 6px 10px 6px 4px; min-height: 44px;
  display: inline-flex; align-items: center; gap: 10px;
  background: none; border: none; border-radius: 999px;
  color: var(--terracotta); cursor: pointer;
}
.hold-control:hover, .hold-control:focus-visible { background: none; color: var(--oxblood); }
/* a brief tactile down-scale on press (spec §2.5) — the one micro-state
   this control earns that ordinary buttons don't: an act of witness
   deserves to feel like a deliberate press, not a flat click */
.hold-control:active { transform: scale(0.97); }
/* while the hold is in flight the fill IS the feedback — don't let the
   global button:disabled dim read as "broken" */
.hold-control:disabled { opacity: 1; cursor: default; }
.hold-mark {
  display: inline-block; width: 15px; height: 15px; border-radius: 50%;
  border: 1.5px solid var(--terracotta); background: transparent;
  flex: none; position: relative;
}
.hold-control:hover .hold-mark, .hold-control:focus-visible .hold-mark { border-color: var(--oxblood); }
.hold-control.is-holding .hold-mark,
.hold-mark.is-held { background: var(--terracotta); border-color: var(--terracotta); }
.hold-word {
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: currentColor;
}
.held-line {
  flex: 1 1 auto; min-width: 0; /* takes the remaining space and wraps WITHIN
    it -- its length can never push the control (spec §2.4) */
  font-style: italic; font-size: 15px; color: var(--accent-deep); margin: 0;
  padding-top: 10px; /* first-line center ≈ 22px from row top -- level with
    the 44px control's own centered word (flex-start row, see .held-row) */
}
/* the bare settled mark (direct child, no control around it): same 22px
   optical centerline as the control it replaced -- nothing re-seats */
.held-row > .hold-mark { margin-top: 15px; }

/* the bloom: a soft scale settle + a halo that breathes outward once —
   kin to portal-reveal's calm, never a confetti pop. Terracotta halo is
   --terracotta (#A86A4D) at 38% fading to nothing. */
@keyframes hold-bloom {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.28); }
  100% { transform: scale(1); }
}
@keyframes hold-halo {
  from { box-shadow: 0 0 0 0 rgba(168, 106, 77, 0.38); }
  to   { box-shadow: 0 0 0 16px rgba(168, 106, 77, 0); }
}
@keyframes held-line-in {
  from { opacity: 0; transform: translateY(3px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: no-preference) {
  .hold-control { transition: color var(--dur-quick) var(--ease-settle), transform var(--dur-quick) var(--ease-settle); }
  .hold-mark {
    transition: background-color var(--dur-quick) var(--ease-settle),
                border-color var(--dur-quick) var(--ease-settle);
  }
  .hold-mark.just-held {
    animation: hold-bloom 480ms var(--ease-settle),
               hold-halo 700ms ease-out;
  }
  .held-line.line-in { animation: held-line-in var(--dur-calm) var(--ease-settle) both; }
}

.compose { margin-top: 28px; }
.compose textarea { border-radius: 6px; } /* matches the cards now sitting directly beneath (spec §4.1) */

/* The session-expired notice (posts.js / pod.js insert this after a composer
   when her doorway timed out mid-post). It had NO styling at all until the
   2026-07-26 sweep — a bare browser-default <p> at the one moment she most
   needs calm: she has just written something and the door closed behind her.
   Same gentle-well vocabulary as .me-error, in the reading serif because
   this is a sentence to her, not a field label. */
.timed-out-notice {
  font-family: var(--serif); font-style: italic; font-size: 16px;
  color: var(--oxblood); background: var(--linen);
  border-radius: 6px; padding: 12px 16px; margin: 14px 0 0; max-width: 60ch;
}
.compose label { display: block; font-family: var(--sans); font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); margin-bottom: 10px; }
textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--panel-border);
  border-radius: 4px;
  padding: 14px 16px;
  resize: vertical;
}
textarea:focus { outline: none; border-color: var(--oxblood); box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12); }
button {
  margin-top: 14px;
  font-family: var(--sans);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #fff;
  background: var(--oxblood);
  border: none;
  border-radius: 4px;
  padding: 12px 24px;
  cursor: pointer;
}
button:hover { background: var(--ink-strong); }
button:focus-visible, .avatar-circle:focus-visible, a:focus-visible, summary:focus-visible {
  outline: 2px solid var(--oxblood); outline-offset: 2px;
}

/* ---- Commons ---- */

.commons-compose-wrap { max-width: 680px; margin: 0 auto 40px; }
.commons-list { max-width: 680px; margin: 0 auto; display: flex; flex-direction: column; gap: 16px; } /* same rhythm .week-entries already uses (spec §2.1) */

/* ---- The pod room: reflections (the weekly spine) + notes (casual) ----
   See templates/pod.html for the anti-presence doctrine this room enforces:
   no presence/read-receipt/typing/last-seen/delivery-state chrome, ever. */

.pod-head { max-width: 680px; margin: 0 auto 8px; }
.pod-tagline { color: var(--muted); font-style: italic; font-size: 16px; margin: 4px 0 0; }

.pod-compose-wrap { max-width: 680px; margin: 24px auto 40px; }
.compose-toggle {
  display: flex; gap: 24px; margin-bottom: 14px;
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.03em; color: var(--muted);
}
/* higher specificity than the generic ".compose label" rule, deliberately —
   these are inline toggle labels, not the block uppercase compose label */
.compose-toggle label.toggle-option {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  text-transform: none; letter-spacing: 0.02em; font-size: 15px; color: var(--ink); margin-bottom: 0;
}
.toggle-option input { accent-color: var(--oxblood); }

.week-group { max-width: 680px; margin: 0 auto; }
.week-header {
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent-deep); border-bottom: 1px solid var(--hairline); padding-bottom: 10px;
  margin: 44px 0 20px;
}
.week-group:first-of-type .week-header { margin-top: 8px; }
.week-entries { display: flex; flex-direction: column; gap: 16px; }

.reflection-card {
  background: var(--paper-raised); border: 1px solid var(--hairline); border-radius: 6px;
  padding: 26px 28px;
}
.reflection-card p { margin: 0 0 12px; }
.reflection-card p:last-of-type { margin-bottom: 0; }
/* Labels the reflection/note visual split as intentional grammar (UX polish
   pass, 2026-07-20 §1a) rather than a rendering inconsistency -- same
   treatment as .card-kicker on the home module card. */
.reflection-kicker {
  display: block; font-family: var(--sans); font-size: 11px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--kicker); margin-bottom: 12px;
}

/* Notes as soft slips (redesign 2026-07-25, Stephen's instinct): the same
   raised paper as the cards but at a smaller, rounder scale — a slip of
   paper passed across the table, not a chat bubble (no tail, no side
   alternation, uniform left edge; the anti-presence doctrine above still
   binds). Two weights of one family: reflections = 6px cards with a
   kicker, notes = 16px slips with none. */
.note-item {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  padding: 14px 18px; font-size: 16px;
  background: var(--paper-raised); border: 1px solid var(--hairline);
  border-radius: 18px; /* distinctly rounder than .reflection-card's 6px --
    the "softer sibling" cue (spec §3.2). No radius token exists in this
    file to bind to; every card hardcodes its own value already. */
}
.note-author { font-style: italic; color: var(--accent-deep); }
.note-body { color: var(--ink); flex: 1 1 260px; }
/* Her paragraph breaks are hers, not noise (Stephen's rehearsal find,
   2026-07-31: a two-paragraph note rendered glued into one line by the old
   display:inline). A single-paragraph note reads exactly as before; a
   multi-paragraph note now stacks its paragraphs inside the same row. */
.note-body p { margin: 0 0 6px; }
.note-body p:last-child { margin-bottom: 0; }
/* A note that grew past one paragraph stops pretending to be a one-liner
   (Stephen, same find: the row layout centred the name against a tall body
   and stranded the edit link in dead space). These notes flip to the
   stacked shape — avatar + name + date on a top line, her paragraphs
   full-width beneath, actions after. One-paragraph notes keep the row. */
.note-item:has(.note-body p + p) { align-items: center; }
.note-item:has(.note-body p + p) .note-body {
  flex-basis: 100%; order: 1; margin-top: 2px;
}
.note-item:has(.note-body p + p) .note-date { margin-left: auto; }
.note-item:has(.note-body p + p) .held-row,
.note-item:has(.note-body p + p) .edit-block,
.note-item:has(.note-body p + p) .replies { order: 2; }
.note-date { font-family: var(--sans); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.note-item .held-row { flex-basis: 100%; margin-top: 8px; }
.note-item .held-line { font-size: 14px; } /* the note's own lighter register (spec §3.3) */

.replies {
  margin-top: 18px; padding-left: 22px; border-left: 2px solid var(--hairline-soft);
  display: flex; flex-direction: column; gap: 10px;
}
.replies:empty { display: none; margin: 0; border: none; padding: 0; }
.reply-item { font-size: 15px; }
.reply-author { font-style: italic; color: var(--accent-deep); margin-right: 6px; }
.reply-body { color: var(--ink); }

/* ---- editing her own words (2026-07-26) ----
   Quiet by construction: the affordance only renders while the words are
   still only hers (server-side can_edit), so it is never a dead control. The
   toggle borrows .reply-toggle's exact idiom — this is the same weight of
   act, a small deliberate thing done inside her own post. <details> handles
   the disclosure natively; there is no JS behind any of this. */
.edit-block { margin-top: 10px; }
.edit-block > summary.edit-toggle {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  min-height: 44px; padding: 0;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted);
  text-decoration: underline; text-decoration-color: currentColor;
  text-decoration-thickness: 1px; text-underline-offset: 3px; opacity: 0.85;
}
.edit-block > summary.edit-toggle::-webkit-details-marker { display: none; }
.edit-block > summary.edit-toggle:hover { color: var(--oxblood); opacity: 1; }
.edit-block[open] > summary.edit-toggle { color: var(--accent-deep); opacity: 1; }
.edit-form { margin-top: 10px; }
.edit-form textarea { font-size: 16px; }
.edit-form button { margin-top: 8px; }
/* inside a note slip / a reply line, the block owns its own row so the
   disclosure never fights the inline layout around it */
.note-item .edit-block, .reply-item .edit-block { flex-basis: 100%; margin-top: 4px; }

.reply-toggle {
  margin-top: 16px; padding: 0; background: none; border: none; color: var(--muted);
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; cursor: pointer;
  text-decoration: underline; text-decoration-color: currentColor;
  text-decoration-thickness: 1px; text-underline-offset: 3px; opacity: 0.85;
  min-height: 44px; display: inline-flex; align-items: center;
}
.reply-toggle:hover { color: var(--oxblood); background: none; opacity: 1; }
.reply-form { margin-top: 12px; }
.reply-form textarea { font-size: 15px; }
.reply-form button { margin-top: 8px; }

/* ---- Gatherings ---- */

.gathering-list { list-style: none; padding: 0; margin: 40px auto 0; max-width: 680px; display: flex; flex-direction: column; gap: 18px; }
/* UX polish pass (2026-07-20 §6, baseline): brings gathering cards to the
   same visual weight as .module-card's left-accent convention -- no new
   imagery, no new tokens. The gold-marker idea for these cards is flagged
   to Bart (canon authority on gold usage), not specced -- do not build it.

   SHARED WITH THE OPERATOR DESK: desk_index.html renders its own gathering rows
   with these same class names (.gathering-card/-name/-date/-note on plain <p>s, no
   <details>). So this base rule stays exactly as the desk has always had it, and
   everything the 2026-08-14 expand pass changes is scoped under .gathering-list --
   the member page's list, which the desk (.desk-gatherings) never uses. Check the
   desk before touching anything unscoped here. */
.gathering-card { background: var(--paper-raised); border: 1px solid var(--hairline); border-radius: 6px; padding: 26px 28px; border-left: 3px solid var(--accent); }
.gathering-card.past { opacity: 0.72; border-left-color: var(--hairline); }
/* PADDING MOVES TO THE SUMMARY on the member page: the card is a <details> whose
   <summary> IS its whole face, and only the summary's own box is clickable. Left on
   the <li>, 26px/28px would be a dead border all the way round a card the woman is
   being invited to click. overflow:hidden joins it so the summary's hover tint is
   clipped by the card's own 6px radius -- both scoped, since the desk card holds an
   edit form whose focus rings that same overflow would clip. */
.gathering-list .gathering-card { padding: 0; overflow: hidden; }
.gathering-face { padding: 26px 28px; cursor: pointer; list-style: none; }
.gathering-face::-webkit-details-marker { display: none; }
.gathering-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.gathering-status { display: block; font-family: var(--sans); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted); }
.gathering-card.upcoming .gathering-status { color: var(--terracotta); }
.gathering-name { font-size: 24px; font-weight: 300; font-style: italic; color: var(--ink); margin: 6px 0 4px; }
.gathering-date { display: block; font-family: var(--sans); font-size: 13px; letter-spacing: 0.06em; color: var(--ink); margin: 0 0 10px; }
.gathering-note { display: block; color: var(--muted); font-style: italic; margin: 0; }
.gathering-link { display: block; margin: 14px 0 0; font-size: 13px; font-style: italic; color: var(--muted); border-top: 1px solid var(--hairline-soft); padding-top: 12px; }

/* -- the cue: "What to expect", top-right, on the status line's own baseline --
   Kristine's longer copy is worth finding, and a bare chevron in a corner is not
   an invitation anyone reads. Words + chevron, in the status line's exact register
   (same sans, size, tracking, caps) and sharing its row, so the card gains an
   affordance without gaining a line -- the page stays the scannable "when are the
   three moments" list it is meant to be. The chevron is drawn from two borders
   rather than set as a glyph: U+2304 and its neighbours render at wildly different
   weights and baselines across the site's font stack, and this one is always the
   same 6px mark at currentColor. */
.gathering-cue {
  flex: none; display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--muted);
}
.gathering-cue::after {
  content: ""; width: 6px; height: 6px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
.gathering-details[open] .gathering-cue::after { transform: translateY(1px) rotate(-135deg); }

/* Kristine's paragraph. Sits in the card's own body register (the .gathering-note
   voice at reading size), divided from the face rather than boxed -- it is the same
   card continuing, not a panel that appeared. */
.gathering-detail {
  margin: 0; padding: 0 28px 26px;
  color: var(--ink); font-size: 16px; line-height: 1.7;
}
.gathering-detail::before {
  content: ""; display: block;
  border-top: 1px solid var(--hairline-soft);
  margin-bottom: 16px;
}
/* ONE rule per card, never two. An upcoming card already ends its face with
   .gathering-link's own border-top ("The room opens here, when it is time."), so
   adding this one below it drew a second hairline ~40px under the first and boxed
   that sentence into a band of its own -- a strip the card doesn't have and doesn't
   mean. .upcoming is exactly the set that carries .gathering-link (gatherings.html
   renders it under `if not e.is_past`, the same condition that sets this class), so
   scoping on it is precise rather than approximate; the padding replaces the
   suppressed rule's own 16px of breathing room. Held cards have no link line, so
   they keep the divider -- it is their only one. */
.gathering-card.upcoming .gathering-detail { padding-top: 14px; }
.gathering-card.upcoming .gathering-detail::before { display: none; }

/* the forward-invitation pattern -- "Read/Continue in your pod's room ->",
   the only two forward links on the site (confirmed by grep, UX beautify
   pass 2, 2026-07-20 §4). Stephen's pick, option A, 2026-07-26: a centred
   oxblood pill -- his ask ("a little bubble with white text") kept in the
   page's OWN voice, serif italic rather than the sitewide button's sans
   capitals, so it reads as an invitation someone extended and not as system
   furniture competing with the module card (which stays home's loudest
   action). Terracotta was set aside deliberately: it is the Hold's colour,
   and spending the witness mark on navigation would cost it meaning.
   No border -- the fill alone carries the shape, so the hover has nothing
   to transition but colour + background (already covered by the shared
   transition group near the top of this file). Now emphatically distinct
   from .gathering-link above, which is italic/muted/hairline-topped and is
   NOT a link -- the old warning against pattern-matching that styling here
   is moot in the safest direction. */
.room-invite {
  display: flex; width: fit-content; margin: 24px auto 0;
  align-items: center; justify-content: center; text-align: center;
  min-height: 44px; padding: 11px 30px; border-radius: 999px;
  background: var(--oxblood); color: #fff;
  font-family: var(--serif); font-style: italic; font-size: 17px;
}
.room-invite:hover, .room-invite:focus-visible {
  background: var(--ink-strong); color: #fff; text-decoration: none;
}

/* ---- Witness panel: chat bubbles ---- */

.witness-col { position: sticky; top: 32px; }
.witness-panel {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 64px);
}
.witness-head { border-bottom: 1px solid var(--hairline); padding-bottom: 16px; margin-bottom: 18px; }
.witness-head h2 { font-family: var(--sans); font-size: 15px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--accent-deep); margin: 0 0 6px; }
.witness-head p { margin: 0; font-style: italic; color: var(--muted); font-size: 15px; }

.witness-thread { overflow-y: auto; flex: 1; padding-right: 4px; }
/* Quiet the scroll (2026-08-03, Stephen's ask): the default gutter read as
   a heavy vertical bar beside the chat. Thin, linen-toned, visible only
   while the thread is hovered/scrolled — plus soft fade masks so cut-off
   messages dissolve instead of clipping at the panel edge. */
.witness-thread {
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
}
.witness-thread:hover { scrollbar-color: var(--bubble-user-border) transparent; }
.witness-thread::-webkit-scrollbar { width: 5px; }
.witness-thread::-webkit-scrollbar-track { background: transparent; }
.witness-thread::-webkit-scrollbar-thumb { background: transparent; border-radius: 999px; }
.witness-thread:hover::-webkit-scrollbar-thumb { background: var(--bubble-user-border); }
@media (prefers-reduced-motion: no-preference) {
  .witness-thread { scroll-behavior: smooth; }
}

.msg-row { display: flex; flex-direction: column; margin-bottom: 16px; }
.msg-row.assistant { align-items: flex-start; }
.msg-row.user { align-items: flex-end; }

.bubble {
  max-width: 86%;
  padding: 14px 18px;
  border-radius: 8px;
  font-size: 16px;
  line-height: 1.6;
}
.bubble p { margin: 0 0 8px; }
.bubble p:last-child { margin-bottom: 0; }
.bubble.assistant { background: var(--paper-raised); border: 1px solid var(--linen); color: var(--ink); border-bottom-left-radius: 3px; }
.bubble.user { background: var(--bubble-user-bg); border: 1px solid var(--bubble-user-border); color: var(--ink); border-bottom-right-radius: 3px; }

.msg-time {
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); margin-top: 6px; opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .msg-time { transition: opacity var(--dur-quick) var(--ease-settle); }
}
@media (prefers-reduced-motion: reduce) {
  /* hover-to-reveal doesn't work for a user who can't hover OR one who's
     asked for no motion -- same "always faintly visible" fallback the site
     already uses for touch, below. */
  .msg-time { opacity: 0.55; }
}
.msg-row:hover .msg-time, .msg-row:focus-within .msg-time { opacity: 1; }

/* the typing indicator's breathing dots — kin to scroll-breathe (opacity),
   with an added scale pulse suited to a small dot */
@keyframes witness-breathe {
  0%, 80%, 100% { opacity: 0.28; transform: scale(0.82); }
  40% { opacity: 1; transform: scale(1); }
}
.typing-dots { display: inline-flex; gap: 5px; align-items: center; padding: 2px 0; }
.typing-dots span { width: 6px; height: 6px; border-radius: 50%; background: var(--terracotta); animation: witness-breathe 1.3s ease-in-out infinite; }
.typing-dots span:nth-child(2) { animation-delay: 0.18s; }
.typing-dots span:nth-child(3) { animation-delay: 0.36s; }
@media (prefers-reduced-motion: reduce) { .typing-dots span { animation: none; opacity: 0.55; } }

.witness-compose { margin-top: 16px; }
.witness-compose textarea { font-size: 16px; max-height: 140px; overflow-y: auto; }
.witness-compose button { margin-top: 10px; }
/* generalized (2026-07-24 §4 adoption #2) -- was scoped to .witness-compose
   only; now every compose field/button on the platform gets the same quiet
   disabled-while-sending feedback, not just the Witness. */
button:disabled, textarea:disabled { opacity: 0.55; cursor: not-allowed; }
.witness-mode { margin: 14px 0 0; font-size: 13px; font-style: italic; color: var(--muted); text-align: center; }

/* ---- Witness everywhere: site-wide corner affordance ----
   Three states -- collapsed dock (default) -> open/compact (no scrim) ->
   expanded focus (scrim returns) -- on the six non-module student pages
   (see app.py's WITNESS_AFFORDANCE_PAGE_KINDS). The panel it opens reuses
   .witness-panel/.witness-thread/.bubble/.witness-compose/.witness-mode
   verbatim (see _witness_affordance.html) -- only the outer chrome below is
   new. Corner redesign, 2026-07-25 --
   2026-07-24-witness-corner-redesign-rhaenyra.md §1: a quiet typographic
   corner tab (a bookmark ribbon, not a chat bubble), a compact box that
   never scrims the page behind it, and an explicit Expand for the
   full-height focus view. Register bind: B-lite design §3.1 (margin
   companion, typographic, calm -- never Messenger chrome); see the spec's
   own §5 point-by-point argument. */

.witness-affordance {
  position: fixed; right: 28px; bottom: 0; z-index: 40;
  margin: 0; padding: 12px 20px; min-height: 44px;
  display: inline-flex; align-items: center;
  background: var(--paper-raised);
  border: 1px solid var(--hairline); border-bottom: none;
  border-radius: 6px 6px 0 0;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--accent-deep);
  cursor: pointer;
}
.witness-affordance:hover, .witness-affordance:focus-visible {
  background: var(--linen-wash); color: var(--oxblood);
}
/* needs no transition rule of its own -- it's a <button>, already covered by
   the sitewide `button { transition: background-color/border-color/color/
   opacity ... }` rule above. */

/* while the compact box sits directly above it, the tab sheds its own top
   edge so the two read as one continuous object, not two stacked chips.
   border-top/border-radius snap instantly rather than transition -- CSS
   can't meaningfully animate a border from `1px solid` to `none`, and this
   snap is coincident with the box's own quick fade-in below, so it isn't
   perceptible as a separate event. */
.witness-affordance.is-joined { border-top: none; border-radius: 0; }
/* the expanded panel's footprint already covers this corner -- hidden
   rather than left as an invisible, still-tabbable control underneath it */
.witness-affordance.is-eclipsed { display: none; }

.witness-overlay {
  /* explicit display beats the [hidden] attribute in the markup (same fix as
     .profile-overlay above) -- visibility/opacity own showing and hiding
     from here on, not the attribute. Only ever shown during the EXPANDED
     state -- the compact/open state carries no scrim at all. */
  display: block;
  position: fixed; inset: 0; z-index: 85;
  background: rgba(15, 27, 42, 0.35);
  opacity: 0; visibility: hidden;
}
.witness-overlay.is-open { opacity: 1; visibility: visible; }

/* -- open: the compact box that doesn't obstruct the page behind it -- */
.witness-slideover {
  display: flex;
  position: fixed; right: 28px; bottom: 44px; z-index: 90;
  width: min(360px, calc(100vw - 56px));
  height: min(520px, calc(100vh - 96px));
  background: var(--paper-raised);
  border: 1px solid var(--hairline); border-bottom: none;
  border-radius: 6px 6px 0 0;
  opacity: 0; visibility: hidden; transform: translateY(8px);
}
.witness-slideover.is-open { opacity: 1; visibility: visible; transform: translateY(0); }

.witness-panel--slideover {
  position: relative;
  border: none; border-radius: 0; max-height: none; height: 100%;
}

/* header controls cluster -- the Expand/Collapse toggle plus the existing × */
.witness-panel-controls {
  position: absolute; top: 2px; right: 2px; z-index: 1;
  display: flex; align-items: center; gap: 10px; /* >= the popover-close hit-slop
     radius below, so the two controls' touch targets never overlap */
}
.witness-expand-toggle {
  margin: 0; padding: 12px 10px; min-height: 44px;
  display: inline-flex; align-items: center;
  background: none; border: none;
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); cursor: pointer;
}
.witness-expand-toggle:hover, .witness-expand-toggle:focus-visible { color: var(--oxblood); }
/* also a <button> -- covered by the sitewide button transition, same as the tab. */

/* the compact box tightens the module page's own header/padding a touch --
   there's less room to work with. Bubbles, thread, composer, mode note:
   untouched. Scoped off the outer state class, not a second class the JS
   has to toggle. */
.witness-slideover:not(.is-expanded) .witness-panel--slideover { padding: 20px 18px 16px; }
.witness-slideover:not(.is-expanded) .witness-head { padding-bottom: 10px; margin-bottom: 12px; }
.witness-slideover:not(.is-expanded) .witness-head h2 { font-size: 13px; }

/* -- expanded: the focus view -- the full-height side panel, demoted from
   "the only option" to "what you get when you explicitly ask to focus only
   on the Witness." Scrim returns only here. -- */
.witness-slideover.is-expanded {
  right: 0; bottom: 0;
  width: min(440px, 100%); height: 100vh;
  border-radius: 0; border: none; border-left: 1px solid var(--hairline);
}

/* ---- gentle hover lift on cards ----
   THE LIFT is reserved strictly for cards that are themselves a single
   click-through link (2026-07-24 §1.7) -- .gathering-card and .reflection-card
   were plain containers (no <a>, no click handler) and got the identical lift,
   implying a navigation that doesn't exist: the platform's single most "why did
   that just move" moment. .reflection-card joins .note-item under the quiet
   row-tint below instead, matching its true sibling role in the same
   .week-entries feed as .commons-post/.pod-post.

   .gathering-card's entry here is NEW (2026-08-14, Stephen's ask), and it is a
   change of fact, not a reversal of that ruling. The ban was on a LIFT over an
   inert container. The card is no longer inert: it is a <details> summary that
   opens Kristine's "what to expect" copy, so a hover response is now owed rather
   than misleading. It gets deliberately NOT the module-card lift, which is the
   grammar for "this takes you somewhere," but the row-tint + warming accent below
   -- the site's own vocabulary for "this responds in place." Restore the no-hover
   rule the moment these cards stop opening. */
@media (prefers-reduced-motion: no-preference) {
  .module-card, .workbook-card {
    transition: transform var(--dur-quick) var(--ease-settle),
                box-shadow var(--dur-quick) var(--ease-settle),
                border-left-color var(--dur-quick) var(--ease-settle);
  }
  .module-card:hover, .module-card:focus-visible,
  .workbook-card:hover, .workbook-card:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 10px 26px rgba(15, 27, 42, 0.07);
  }
}

/* -- .gathering-card: "this opens in place" -- three things, all quiet: the paper
   takes the sitewide list-row tint, the left accent deepens, and the chevron nudges
   1px the way it points (down when closed, up when open). Colour changes live
   OUTSIDE the reduced-motion query so the affordance still answers at that setting
   -- only the easing of it is a motion preference. Same idiom .module-card uses. */
/* The tint goes on the <details>, NOT the <summary>, though the summary is what
   you hover. An open card is taller than its summary, so tinting the summary
   painted the top half only and left a hard seam across the middle of the card --
   it read as two stacked objects, which is the opposite of what this card is. The
   <details> box wraps face and paragraph both, so one declaration covers the whole
   card and no seam is possible. :has() keeps the trigger where it belongs: the
   clickable face, never the paragraph. */
.gathering-card:has(.gathering-face:hover) .gathering-details,
.gathering-card:has(.gathering-face:focus-visible) .gathering-details { background: var(--linen-wash); }
.gathering-face:hover .gathering-cue, .gathering-face:focus-visible .gathering-cue { color: var(--accent-deep); }
.gathering-card.upcoming:has(.gathering-face:hover) { border-left-color: var(--accent-deep); }
.gathering-card.past:has(.gathering-face:hover) { border-left-color: var(--accent); }
/* Inset ring: .gathering-card's overflow:hidden would clip the global +2px offset
   one, the same clash .mobile-tabs .tab solves this exact way. */
.gathering-face:focus-visible { outline: 2px solid var(--oxblood); outline-offset: -3px; }

@media (prefers-reduced-motion: no-preference) {
  .gathering-card { transition: border-left-color var(--dur-quick) var(--ease-settle); }
  .gathering-details { transition: background var(--dur-calm) var(--ease-settle); }
  .gathering-cue { transition: color var(--dur-quick) var(--ease-settle); }
  .gathering-cue::after { transition: transform var(--dur-quick) var(--ease-settle); }
  .gathering-face:hover .gathering-cue::after,
  .gathering-face:focus-visible .gathering-cue::after { transform: translateY(-1px) rotate(45deg); }
  .gathering-details[open] .gathering-face:hover .gathering-cue::after,
  .gathering-details[open] .gathering-face:focus-visible .gathering-cue::after { transform: translateY(0) rotate(-135deg); }

  /* The open/close itself. <details> has no native height animation, so this is
     ::details-content + interpolate-size, the only route that animates to auto
     without JS measuring anything. content-visibility transitions discretely so the
     paragraph stays rendered while it closes instead of vanishing on frame one.
     PROGRESSIVE ENHANCEMENT, not a dependency: where ::details-content isn't
     implemented the browser drops these rules and the card simply opens instantly
     -- fully functional, just without the settle. interpolate-size is scoped to
     this component rather than :root so it can't change sizing behaviour sitewide. */
  .gathering-details { interpolate-size: allow-keywords; }
  .gathering-details::details-content {
    block-size: 0; overflow: hidden; opacity: 0;
    transition: block-size var(--dur-calm) var(--ease-settle),
                opacity var(--dur-calm) var(--ease-settle),
                content-visibility var(--dur-calm) allow-discrete;
  }
  .gathering-details[open]::details-content { block-size: auto; opacity: 1; }
}

/* ---- mobile / tablet (≤1000px): topbar + drawer replace the sidebar ---- */

.mobile-tabs { display: none; }

@media (max-width: 1000px) {
  .page { padding: 32px 20px; }
  .welcome-note, .since-last, .this-week, .pod-glance { padding: 0 0; }

  .hero-content { padding: 0 22px; }
  .hero-module { height: 35vh; min-height: 260px; }
  .hero-module .hero-title { font-size: 30px; }
  .hero-commons { height: 35vh; min-height: 260px; }
  .hero-gatherings { height: 35vh; min-height: 260px; }
  .hero-pod { height: 35vh; min-height: 260px; }
  /* exception: at the shared 35vh the still-life's one legible AI-script line
     ("...1873") clears the veil into view on phone aspect; hold it at the older
     22vh so that line stays in the bottom wash (ruling §2). The real fix is to
     edit that line out of the source image -- then this can rejoin its siblings. */
  .hero-modules { height: 22vh; min-height: 170px; }
  /* phone aspect shows more of the journal page than the desktop band --
     anchor to the image's top edge so the script (and its one legible line)
     stays below the visible crop; ruling §2's mobile caveat */
  .hero-modules .hero-img { object-position: 50% 0%; }
  .hero-commons .hero-title, .hero-gatherings .hero-title, .hero-pod .hero-title, .hero-modules .hero-title { font-size: 26px; }
  .hero-home { height: 35vh; min-height: 260px; }
  .hero-home .hero-title { font-size: 28px; }
  .hero-kicker { font-size: 11px; }
  .descend-cue { margin-top: 14px; font-size: 16px; }

  .mobile-tabs {
    display: flex;
    gap: 0;
    max-width: 480px;
    margin: 0 auto 28px;
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    overflow: hidden;
  }
  .mobile-tabs .tab {
    flex: 1;
    margin: 0;
    border-radius: 0;
    font-family: var(--sans);
    background: var(--paper-raised);
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-size: 12px;
    padding: 12px 8px;
  }
  .mobile-tabs .tab[aria-selected="true"] { background: var(--oxblood); color: #fff; }
  /* .mobile-tabs' own overflow:hidden clips the global focus-visible ring's
     positive outline-offset -- inset the ring instead so it draws inside the
     clipping box, worst on the two end tabs otherwise (2026-07-24 §1.4). */
  .mobile-tabs .tab:focus-visible {
    outline: 2px solid var(--oxblood);
    outline-offset: -3px;
  }

  .module-layout { grid-template-columns: 1fr; gap: 0; margin-top: 24px; }
  .transmission { font-size: 18px; line-height: 1.72; }
  .transmission-title { font-size: 24px; }
  .inquiry-fold p { font-size: 18px; line-height: 1.72; }

  .witness-col { position: static; margin-top: 40px; }
  .witness-panel { max-height: none; }
  .msg-time { opacity: 0.55; } /* no hover on touch — keep it faintly visible instead */

  .witness-affordance { right: 16px; font-size: 11px; padding: 12px 16px; } /* min-height: 44px inherited */
  .witness-slideover { right: 16px; width: min(360px, calc(100vw - 32px)); }
  .witness-slideover.is-expanded { right: 0; width: 100%; } /* unchanged from today's mobile behavior */

  /* tab behaviour: show one surface at a time */
  .module-body[data-view="reading"] .witness-col { display: none; }
  .module-body[data-view="witness"] .reading-col { display: none; }
  .module-body[data-view="witness"] .witness-col { margin-top: 0; }
}

/* --------------------------------------------------------------------------
   Threshold pages — login / doorway / doorway-closed (Task 6). A bare closed
   door, like the /enter gate: no shell, no nav, no footer. Reuses the same
   palette tokens as enter.html — no new visual language, just the gate's look
   lifted into shared classes so the three standalone templates stay DRY.
   -------------------------------------------------------------------------- */
.threshold {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--sacred-stone);
  padding: 2rem 1.5rem;
  text-align: center;
}
.threshold-card { max-width: 30rem; width: 100%; }
.threshold-card h1 {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2.2rem, 5vw, 3.2rem);
  color: var(--midnight-ink);
  margin: 0 0 0.5em;
}
.threshold-card p {
  font-family: var(--serif);
  font-style: italic;
  color: var(--oxblood);
  margin: 0 auto 2.4rem;
}
.threshold-card form {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  align-items: center;
  width: 100%;
}
.threshold-card label {
  font-family: var(--sans);
  font-size: 0.8rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);  /* was var(--olive-wash) directly -- same WCAG AA fix as §3 */
}
.threshold-card input {
  width: 100%;
  max-width: 21rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--linen);
  background: #fff;
  font-family: var(--sans);
  font-size: 1.05rem;
  text-align: center;
  color: var(--midnight-ink);
}
.threshold-card input:focus { outline: 2px solid var(--oxblood); outline-offset: 1px; }
.threshold-card button {
  font-family: var(--sans);
  font-size: 0.8rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  background: var(--oxblood);
  color: var(--sacred-stone);
  border: 0;
  padding: 0.8rem 2.4rem;
  cursor: pointer;
}
.threshold-card button:hover { opacity: 0.88; }
@media (prefers-reduced-motion: no-preference) {
  .threshold-card h1, .threshold-card p, .threshold-card form {
    animation: portal-reveal 1.6s ease-out both;
  }
  .threshold-card p { animation-delay: 0.3s; }
  .threshold-card form { animation-delay: 0.6s; }
  .threshold-card button { transition: opacity var(--dur-quick) var(--ease-settle); }
}

/* Quiet brand header for the standalone threshold pages (U2) — the rose mark +
   wordmark, centered above the card. Presence, not decoration. */
.threshold-brand {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  text-decoration: none; margin-bottom: 2.6rem;
}
.threshold-brand:hover { text-decoration: none; }
.threshold-brand .brand-word { margin-top: 0; }
@media (prefers-reduced-motion: no-preference) {
  .threshold-brand { animation: portal-reveal 1.6s ease-out both; }
}

/* The quiet "back to the front door" link on the error pages (U3). */
.threshold-link {
  font-family: var(--sans); font-size: 0.8rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--oxblood); text-decoration: none;
}
.threshold-link:hover { text-decoration: underline; }

/* The operator nav's "Leave" — a POST /leave form styled as a nav item (U1). */
.nav-leave-form { margin: 0; }
button.nav-item.nav-leave {
  background: none; border: 0; border-left: 2px solid transparent;
  width: 100%; text-align: left; cursor: pointer; display: block;
}

/* ---- steward — the cadence grid & the page as one calm surface ----
   docs/steward-page-design-spec.md. Founder-facing (Kristine reads it):
   composed, editorial, unhurried. Content-blind — nothing here renders a
   woman's words. Append-only; every selector is scoped under .steward or
   the .cadence table so the rest of the site is untouched. */

.steward { max-width: 1000px; margin: 40px auto 0; }
.steward h1 { font-size: 34px; margin: 0 0 14px; }

.steward h2 {
  font-family: var(--sans);
  font-size: 14px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--accent-deep);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 12px;
  margin: 64px 0 24px;
}
.steward h2:first-of-type { margin-top: 8px; }

/* The summary line — one quiet sentence, not a stat strip. */
.wave-line {
  font-style: italic;
  font-size: 19px;
  color: var(--quote-ink);
  max-width: 60ch;
  margin: 0 0 48px;
}

/* The program-management lens (waves design §6b, Task 11): plain fact lines,
   one per line — never italic (that register is .wave-line's, for the one
   quiet summary sentence), because these ARE the page's content now, said
   plainly, not a caption underneath it. */
.wave-fact { max-width: 60ch; margin: 0 0 10px; color: var(--ink); }

/* The optional founder note — the site's raised-card shape. */
.steward-note {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  padding: 20px 24px;
  margin: 0 0 48px;
}
.steward-note p { margin: 0 0 8px; color: var(--ink); }
.steward-note .dated {
  margin: 0; font-family: var(--sans); font-size: 12px;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}

/* The cadence table — the core fix. Fixed layout so week-marks sit under
   their week number; scroll is contained to .cadence-scroll, never the body. */
.cadence-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.cadence {
  border-collapse: collapse;
  table-layout: fixed;
  width: 898px;      /* 190 name + 12x52 week + 84 witness — intrinsic, not stretched */
  min-width: 898px;
  font-size: 15px;
}
.cadence th, .cadence td { padding: 13px 6px; }

.cadence th:first-child, .cadence td:first-child { width: 190px; text-align: left; }
.cadence th:not(:first-child):not(:last-child),
.cadence td:not(:first-child):not(:last-child)  { width: 52px; text-align: center; }
.cadence th:last-child, .cadence td:last-child   { width: 84px; text-align: center; }

.cadence thead th {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-weight: 500;
  text-align: center;
  border-bottom: 1px solid var(--accent-deep);
  padding-bottom: 14px;
}
.cadence thead th:first-child { text-align: left; text-transform: uppercase; letter-spacing: 0.16em; }
.cadence thead th:last-child  { text-transform: uppercase; letter-spacing: 0.1em; }

/* The three weekly marks — never conflated. A filled terracotta dot for a
   written week; a hollow linen ring for an arrived-but-unwritten week; a
   faint dash for a week not yet arrived (Dunk gate, 2026-07-20) — quieter
   than the ring, so a not-yet week never reads as gone quiet. */
.cadence .mark { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }
.cadence td.written .mark { background: var(--terracotta); }
.cadence td.quiet   .mark { background: transparent; border: 1px solid var(--hairline); }
.cadence td.future  .mark {
  width: 10px; height: 2px; border-radius: 1px;
  background: var(--hairline);   /* a faint dash: still ahead, not absent */
}

.cadence .who-name { font-style: italic; color: var(--accent-deep); font-size: 16px; }
.cadence .flag-mark {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  border: 1.5px solid var(--oxblood); margin-left: 8px; vertical-align: middle;
}

.cadence .witness-count { font-family: var(--sans); color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---- steward polish (steward-polish-spec-2026-07-21.md) ----
   The who-cell now carries more than a name (Task 5's login-support
   additions, 2026-07-20): a last-seen line and up to two per-row utility
   actions. Stack them predictably instead of the ambient inline flow that
   ran the name and the last-seen text together with no line break. The
   name (+ its flag ring, if any) is untouched -- still the grid's own
   first line, per docs/steward-page-design-spec.md §4.3. */
.cadence td.who { vertical-align: top; }
.cadence .who-meta {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 6px; margin-top: 8px;
}
.cadence .who-seen {
  display: block; font-family: var(--sans); font-size: 13px; color: var(--muted);
}

/* Per-row utility actions are routine, reversible, and secondary to the
   page's one real reading task (scanning the grid). The site's plain
   button{} default fills them oxblood -- right for a page's ONE primary
   action, wrong repeated down a column of rows, and (190px name column,
   fixed table-layout) too wide to fit without overflow regardless.
   Demoted to the exact quiet text-link treatment /me's own photo-remove
   control already uses for the SAME action -- same weight, same action,
   zero new CSS vocabulary. Keeps faith with the steward design spec's own
   ruling against chip/pill treatment on this page. */
/* Ghost treatment (2026-08-03, Stephen's call): still the quiet per-row
   voice — no fill, no repetition of the page's one loud button — but a
   faint always-present border gives the action a visible edge the bare
   underline lacked when scanning for "how do I resend her doorway". The
   border firms and inks on hover/focus; the underline goes, the pill IS
   the affordance now. */
.cadence .who-meta button {
  background: none; cursor: pointer;
  border: 1px solid var(--hairline); border-radius: 999px;
  padding: 4px 12px; margin: 0;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.06em;
  color: var(--muted); text-decoration: none;
}
.cadence .who-meta button:hover, .cadence .who-meta button:focus-visible {
  border-color: var(--oxblood); color: var(--oxblood); background: var(--linen-wash);
}

/* Horizontal hairlines only — no vertical grid (a full grid reads spreadsheet).
   A very faint zebra band aids scanning a woman's 12-week pattern left-to-right. */
.cadence tbody tr { border-bottom: 1px solid var(--hairline-soft); }
.cadence tbody tr:nth-child(even) { background: rgba(220, 207, 185, 0.10); }

.grid-note { color: var(--muted); font-style: italic; font-size: 14px; margin: 16px 0 0; max-width: 60ch; }
.grid-note .scroll-hint { display: none; }

/* The pods — quiet rows, not SaaS status pills. Same type in every state; the
   words carry the fact, so no icon or color-coding on top. */
.pod-list { list-style: none; margin: 0; padding: 0; }
.pod-row {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 14px 0; border-bottom: 1px solid var(--hairline-soft);
}
.pod-row .pod-name   { font-style: italic; color: var(--accent-deep); font-size: 17px; }
.pod-row .pod-status { color: var(--muted); font-size: 15px; }
/* Mail visibility (Task 11, design §1.0): "Didn't go through" takes the same
   ink .diverged already uses for "this needs attention" -- word-only, no
   badge, no icon. One property, the existing --accent-deep token, scoped to
   the word wherever it appears (a .pod-status span, a worklist cell, the
   room-summary sentence's failure clause). */
.mail-state-failed { color: var(--accent-deep); }
/* the room link holds the row's right edge (Stephen, 2026-07-26) — mid-row
   it read as one more fact instead of an action */
.pod-row > .quiet-link { margin-left: auto; }

/* Waiting to be placed / The Witness — light typography, no alert-box. */
.vestibule { list-style: none; margin: 0; padding: 0; }
.vestibule li {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 12px 0; border-bottom: 1px solid var(--hairline-soft); color: var(--ink); font-size: 16px;
}
.vestibule li:last-child { border-bottom: none; }
.vestibule .waiting-since { font-family: var(--sans); font-size: 13px; color: var(--muted); }

.steward .flags-line, .steward .reconcile-line, .steward .mail-failure-note { font-size: 17px; color: var(--ink); max-width: 60ch; }
.steward .doorway-confirm { max-width: 60ch; }

/* Screen-reader-only utility — carries each mark's plain-language state
   (content-blind, same as the visual mark). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

@media (max-width: 1000px) {
  .steward { max-width: none; }
  .cadence { font-size: 14px; }
  .grid-note .scroll-hint { display: inline; }
}

/* ---- Pod management (design: ../2026-07-26-steward-pod-management-design.md)
   — the vestibule's working surface + the pods list's repair actions. Same
   discipline as the rest of this page: quiet rows, words carry state (never
   color-coding), one primary action per form, per-row utility actions demoted
   to the quiet text-link treatment (.cadence .who-meta button's own precedent
   above), never a filled button repeated down a column. ---- */

/* the vestibule row: a checkbox + her name (italic-serif, like .cadence
   .who-name), then her facts in the sans metadata voice — .vestibule li's
   own flex-column stacking (above) is untouched and does the layout work. */
.vestibule label.toggle-option {
  display: flex; align-items: center; gap: 8px; cursor: pointer; min-height: 44px;
}
.vestibule .who-name { font-style: italic; color: var(--accent-deep); font-size: 16px; }

.pod-form-hint { color: var(--muted); font-style: italic; font-size: 15px; margin: 16px 0 0; max-width: 60ch; }
.pod-form-name { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 16px; }
.pod-form-name input[type="text"] {
  font-family: var(--sans); font-size: 17px; color: var(--ink);
  border: 1px solid var(--hairline-soft); border-radius: 4px;
  padding: 10px 14px; background: var(--paper-raised); min-width: 220px; min-height: 44px;
}
.pod-form-name input[type="text"]:focus {
  outline: none; border-color: var(--oxblood); box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}

/* the pods list's grown row: the living count, and — only when it is true —
   the depleted state in words plus its two repair actions. */
.pod-row .pod-count { color: var(--muted); font-size: 15px; }
.pod-row .pod-repair-state { flex-basis: 100%; color: var(--ink); font-style: italic; font-size: 15px; margin-top: 2px; }
.pod-reserve-form, .pod-merge-form {
  flex-basis: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 4px;
}
.pod-reserve-form select, .pod-merge-form select, .pod-merge-form input[type="text"] {
  font-family: var(--sans); font-size: 14px; color: var(--ink);
  border: 1px solid var(--hairline-soft); border-radius: 4px;
  padding: 8px 10px; background: var(--paper-raised); min-height: 44px;
}
.pod-reserve-form select:focus, .pod-merge-form select:focus, .pod-merge-form input[type="text"]:focus {
  outline: none; border-color: var(--oxblood); box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}
/* repair actions are routine per-row utility, not the page's one primary
   action — demoted to the exact quiet text-link treatment .cadence
   .who-meta button already established above, for the same reason
   (steward-polish-spec item 1). */
.pod-reserve-form button, .pod-merge-form button {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  font-family: var(--sans); font-size: 13px; color: var(--muted);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px;
  min-height: 44px; display: inline-flex; align-items: center;
}
.pod-reserve-form button:hover, .pod-merge-form button:hover { color: var(--oxblood); background: none; }

/* ---- Moving a waiting woman to another wave (design §6c, Task 10) ----
   The disclosure borrows .edit-block's exact idiom (<details>, no JS,
   underweighted) under its own class -- this is a different feature, so the
   treatment is borrowed, not the name. Renders as its own small pod-list-
   style list rather than another column in the pod-form table above: that
   table is already one bulk-selection <form>, and a second <form> nested
   inside it would silently break (HTML forms cannot nest). */
.wave-move { margin-top: 4px; }
.wave-move > summary.wave-move-toggle {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  min-height: 44px; padding: 0;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted);
  text-decoration: underline; text-decoration-color: currentColor;
  text-decoration-thickness: 1px; text-underline-offset: 3px; opacity: 0.85;
}
.wave-move > summary.wave-move-toggle::-webkit-details-marker { display: none; }
.wave-move > summary.wave-move-toggle:hover { color: var(--oxblood); opacity: 1; }
.wave-move[open] > summary.wave-move-toggle { color: var(--accent-deep); opacity: 1; }
.wave-move form {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 8px;
}
.wave-move form select {
  font-family: var(--sans); font-size: 14px; color: var(--ink);
  border: 1px solid var(--hairline-soft); border-radius: 4px;
  padding: 8px 10px; background: var(--paper-raised); min-height: 44px;
}
.wave-move form select:focus {
  outline: none; border-color: var(--oxblood); box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}

/* the one deliberate action on the pod-management forms and the break-glass
   threshold (design §7) — same declarations as .desk-edit/.desk's own
   .primary-save, so "one primary action per form" reads identically
   wherever it appears. */
.steward .primary-save, .steward-room-threshold .primary-save {
  background: var(--oxblood); color: #fff; border: none;
  font-size: 14px; padding: 12px 28px; margin-top: 24px; cursor: pointer;
  min-height: 44px; display: inline-flex; align-items: center;
}
.steward .primary-save:hover, .steward-room-threshold .primary-save:hover {
  background: var(--ink-strong); color: #fff;
}

/* ---- The break-glass room (design §3) — the confirmation threshold and the
   opened room. Entries/replies reuse .reflection-card/.note-item/.replies
   verbatim (above) so the framed view reads as the true room; only the band
   and the page shell below are new. ---- */
.steward-room, .steward-room-threshold { max-width: 1000px; margin: 40px auto 0; }
.steward-room h1, .steward-room-threshold h1 { font-size: 34px; margin: 0 0 14px; }
.steward-room-threshold p { max-width: 60ch; }
.steward-room-threshold .quiet-link { display: inline-block; margin-top: 16px; }

.steward-room-band {
  max-width: 1000px; margin: 0 auto 32px; padding: 14px 20px;
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.04em;
  color: var(--oxblood); background: var(--paper-raised);
  border: 1px solid var(--oxblood); border-radius: 6px;
}
.steward-room .reflection-card, .steward-room .note-item { margin-bottom: 20px; }
.note-item .replies { flex-basis: 100%; margin-top: 8px; }
.steward-room .quiet-link { display: inline-block; margin-top: 8px; }

/* ---- The vestibule worklist (2026-07-26) ----
   Stephen's rulings, same day: the vestibule must work like a real placement
   queue at 100+ women, and admin-facing surfaces put function before the
   letter of the register. So: a dense table in the cadence grid's own
   family (horizontal hairlines, faint zebra, no vertical rules), a sticky
   header for long lists, visible search/filter controls, and a sticky
   selection bar so the chosen four stay in hand three screens deep. Brand
   material (type, palette) kept for coherence; preciousness dropped. */
.steward .worklist-summary {
  font-family: var(--sans); font-size: 13px; color: var(--muted); margin: 0 0 14px;
}
.steward .worklist-controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 18px;
}
.steward .worklist-controls input[type="search"],
.steward .worklist-controls select {
  font-family: var(--sans); font-size: 14px; color: var(--ink);
  background: var(--paper-raised); border: 1px solid var(--linen);
  border-radius: 4px; padding: 9px 12px; min-height: 40px;
}
.steward .worklist-controls input[type="search"] { width: 220px; }
.steward .worklist-controls input[type="search"]:focus,
.steward .worklist-controls select:focus {
  outline: none; border-color: var(--oxblood);
  box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}
.steward .worklist-controls button {
  margin: 0; padding: 9px 18px; font-size: 12px; letter-spacing: 0.1em;
  min-height: 40px;
}
.steward .worklist-clear {
  font-family: var(--sans); font-size: 13px; color: var(--muted);
  text-decoration: underline; text-underline-offset: 3px;
}
.steward .worklist-clear:hover { color: var(--oxblood); }

.steward .worklist-scroll { max-height: 62vh; overflow-y: auto; border-bottom: 1px solid var(--hairline); }
.steward .worklist {
  width: 100%; border-collapse: collapse; font-family: var(--sans); font-size: 14px;
}
.steward .worklist thead th {
  position: sticky; top: 0; z-index: 1; background: var(--sacred-stone);
  text-align: left; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  font-weight: 500; color: var(--muted); padding: 10px 12px 10px 4px;
  border-bottom: 1px solid var(--accent-deep);
}
.steward .worklist-sort { color: var(--muted); text-decoration: none; }
.steward .worklist-sort:hover { color: var(--oxblood); text-decoration: none; }
.steward .worklist-sort.is-active {
  color: var(--oxblood);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px;
}
.steward .worklist tbody td {
  padding: 9px 12px 9px 4px; border-bottom: 1px solid var(--hairline-soft);
}
.steward .worklist tbody tr:nth-child(even) { background: rgba(220, 207, 185, 0.10); }
.steward .worklist tbody tr:hover { background: var(--linen-wash); }
/* the chosen four carry their state on the whole row — CSS-only */
.steward .worklist tbody tr:has(input:checked) { background: var(--bubble-user-bg); }
.steward .worklist-check-col { width: 36px; }
.steward .worklist input[type="checkbox"] {
  width: 17px; height: 17px; accent-color: var(--oxblood); margin: 0;
}
.steward .worklist-name {
  font-family: var(--serif); font-style: italic; font-size: 16px;
  color: var(--accent-deep); cursor: pointer;
}
.steward .worklist-fact { color: var(--muted); font-size: 13px; white-space: nowrap; }

.steward .pod-form-bar {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  padding: 12px 4px; background: var(--sacred-stone);
  border-top: 1px solid var(--hairline);
}
.steward .pod-form-bar .pod-form-count {
  font-family: var(--sans); font-size: 13px; color: var(--ink); flex: 1 1 240px;
}
.steward .pod-form-bar input[type="text"] {
  font-family: var(--sans); font-size: 15px; color: var(--ink);
  background: var(--paper-raised); border: 1px solid var(--linen);
  border-radius: 4px; padding: 9px 12px; min-height: 40px; width: 220px;
}
.steward .pod-form-bar input[type="text"]:focus {
  outline: none; border-color: var(--oxblood);
  box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}
.steward .pod-form-bar .primary-save { margin: 0; }
.steward .pod-form-bar .primary-save:disabled { opacity: 0.45; cursor: not-allowed; }

/* ---- The wave switcher (2026-07-26) ----
   One row of waves across the top of the console; the selected one is the page.
   Same family as the worklist controls above (sans type, hairline borders, the
   existing palette — no new tokens): the operator is choosing a scope, not
   being sold a dashboard. Plain links, so it needs no script to work.
   Shared with the content desk (Task 7): gatherings are per-wave too, so
   desk_index.html's own switcher reuses this exact family rather than a
   second set of rules. */
.steward .wave-switcher, .desk .wave-switcher {
  display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 28px;
}
.steward .wave-chip, .desk .wave-chip {
  display: block; text-decoration: none;
  padding: 10px 14px; border: 1px solid var(--linen); border-radius: 4px;
  background: var(--paper-raised);
}
.steward .wave-chip:hover, .desk .wave-chip:hover { border-color: var(--accent-deep); text-decoration: none; }
.steward .wave-chip.is-selected, .desk .wave-chip.is-selected {
  border-color: var(--oxblood); background: var(--linen-wash);
}
.steward .wave-chip-name, .desk .wave-chip-name {
  display: block; font-family: var(--serif); font-style: italic; font-size: 16px;
  color: var(--accent-deep);
}
.steward .wave-chip.is-selected .wave-chip-name, .desk .wave-chip.is-selected .wave-chip-name { color: var(--oxblood); }
.steward .wave-chip-facts, .desk .wave-chip-facts {
  display: block; font-family: var(--sans); font-size: 12px; color: var(--muted);
  margin-top: 2px;
}
.steward .wave-line .quiet-link { font-style: normal; font-size: 14px; }

@media (max-width: 1000px) {
  .steward .worklist-fact { white-space: normal; }
  .steward .worklist-controls input[type="search"] { width: 100%; }
}

/* The content desk (Plan 3) — a quiet editing room, not an admin panel. Plain
   type, generous breathing room, hairlines instead of boxes; the same restraint
   the steward console carries. Operator/founder surface only. */
.desk, .desk-edit { max-width: 68ch; }
.desk-lede, .desk-edit .reminder {
  color: var(--muted); max-width: 60ch; font-size: 16px; line-height: 1.6;
}
.desk h2, .desk-edit h2 {
  font-family: var(--sans); font-size: 14px; letter-spacing: 0.06em;
  /* accent-deep, not muted (2026-08-03 design pass): these were the one
     section-header family on the site left grey — every sibling header
     (.since-last h2, .pod-thread h2, .steward h2, .movement-header) uses
     the oxblood accent. */
  text-transform: uppercase; color: var(--accent-deep); margin: 32px 0 12px;
}
.desk ul { list-style: none; margin: 0; padding: 0; }
/* (.desk-modules and .desk .state removed 2026-07-21 — the redesigned index
   uses .module-index rows + .chip; nothing renders those classes anymore.) */
.desk-blocks li, .desk-gatherings li {
  padding: 12px 0; border-bottom: 1px solid var(--hairline-soft);
  color: var(--ink); font-size: 16px;
}
.desk-blocks a { color: var(--accent-deep); }
.desk .diverged, .desk-edit .diverged {
  display: block; font-style: italic; color: var(--accent-deep);
  font-size: 15px; margin-top: 4px; max-width: 60ch;
}
/* The register warnings — a soft aside, never an alert box. */
.desk-edit .warnings {
  border-left: 2px solid var(--hairline-soft); padding: 4px 0 4px 16px;
  margin: 20px 0; color: var(--muted); font-size: 15px; max-width: 60ch;
}
.desk-edit .warnings ul { list-style: disc; margin: 6px 0 0; padding-left: 18px; }
/* Each field: label, textarea, live preview. (The old baseline publish-form
   rule was removed 2026-07-21 — fully shadowed by the redesign's publish-zone
   rule further down.) */
.desk-edit .field-form { margin: 28px 0; }
.desk-edit label {
  display: block; font-family: var(--sans); font-size: 13px;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
  margin-bottom: 6px;
}
.desk-edit textarea {
  width: 100%; box-sizing: border-box; font-family: var(--serif, serif);
  font-size: 16px; line-height: 1.6; color: var(--ink);
  border: 1px solid var(--hairline-soft); border-radius: 2px;
  padding: 12px; background: transparent; resize: vertical;
}
.desk-edit .preview {
  margin: 10px 0; padding: 12px 0; color: var(--muted);
  border-top: 1px solid var(--hairline-soft); min-height: 1em;
}
.desk-edit button {
  font-family: var(--sans); font-size: 14px; color: var(--ink);
  background: transparent; border: 1px solid var(--hairline-soft);
  border-radius: 2px; padding: 8px 16px; margin-top: 10px; cursor: pointer;
}
.desk-edit button:hover { border-color: var(--accent-deep); color: var(--accent-deep); }
.desk-edit .history { margin-top: 12px; }
.desk-edit .history li {
  padding: 10px 0; border-bottom: 1px solid var(--hairline-soft);
  font-size: 15px; color: var(--muted);
}
.desk-edit .history button {
  font-size: 13px; padding: 4px 10px; margin: 0 0 0 10px;
}

/* ---- Desk redesign (desk-redesign-spec-2026-07-21.md) ---- */

/* fieldset-as-bubble: native grouping (fieldset/legend) + shaded common
   region in one element — this IS "sectioned or bubbled." */
.desk-edit fieldset.field-group {
  border: 1px solid var(--hairline); border-radius: 6px;
  background: var(--paper-raised); padding: 28px 32px 8px; margin: 0 0 32px;
}
.desk-edit fieldset.field-group legend {
  font-family: var(--sans); font-size: 14px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--accent-deep); font-weight: 500;
  padding: 0 4px; margin-left: -4px;
}
.desk-edit fieldset.field-group > label:not(:first-of-type) { margin-top: 24px; }

.desk-edit .field-hint {
  font-family: var(--sans); font-size: 13px; line-height: 1.5; color: var(--muted);
  margin: 0 0 8px; max-width: 60ch;
}
.desk-edit input[type="text"] {
  width: 100%; box-sizing: border-box; font-family: var(--serif, serif);
  font-size: 18px; color: var(--ink); border: 1px solid var(--hairline-soft);
  border-radius: 2px; padding: 10px 12px; background: transparent;
}
.desk-edit input[type="text"]:focus {
  outline: none; border-color: var(--oxblood); box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}
.desk-edit .module-form { margin: 28px 0; }

/* the one strongly-styled primary action per form */
.desk-edit .primary-save, .desk .primary-save {
  background: var(--oxblood); color: #fff; border: none;
  font-size: 14px; padding: 12px 28px; margin-top: 24px; cursor: pointer;
}
.desk-edit .primary-save:hover, .desk .primary-save:hover {
  background: var(--ink-strong); color: #fff;
}

/* publish: deliberate but not primary-content — reuses the pod room's own
   acknowledgment token (terracotta outline -> fill), not a new color */
.desk-edit .publish-zone {
  margin: 24px 0 40px; padding: 20px 24px; border: 1px solid var(--hairline);
  border-radius: 6px; background: var(--paper-raised);
}
.desk-edit .publish-form { display: flex; align-items: center; gap: 14px; margin: 0; }
.desk-edit .publish-form button {
  background: transparent; color: var(--terracotta); border: 1px solid var(--terracotta);
  font-size: 12px; letter-spacing: 0.14em; padding: 8px 18px; margin: 0;
}
.desk-edit .publish-form button:hover { background: var(--terracotta); color: #fff; }
.desk-edit .publish-hint { margin: 14px 0 0; color: var(--muted); font-size: 14px; max-width: 60ch; }
/* Drip state + Open now (spec 2026-08-05): the state reads like the hint above
   it; the form's own hint is a span, so it needs the block shape restated. */
.desk-edit .drip-state { margin: 14px 0 0; color: var(--muted); font-size: 14px; }
.desk-edit .open-now-form { display: flex; align-items: center; gap: 14px; margin: 10px 0 0; flex-wrap: wrap; }
.desk-edit .open-now-form .publish-hint { display: block; margin: 0; }

/* status chip — shared vocabulary between the index rows and the edit page.
   Color never carries the meaning alone: the word is always in the pill. */
.chip {
  display: inline-block; font-family: var(--sans); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; line-height: 1; padding: 5px 12px; border-radius: 20px; border: 1px solid;
}
.chip-published { color: var(--terracotta); border-color: var(--terracotta); }
.chip-draft { color: var(--muted); border-color: var(--hairline); }

/* history: page-section-level disclosure (same look as the desk h2s) */
.desk-edit details.history-block { margin-top: 40px; }
.desk-edit details.history-block > summary.section-toggle {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px;
  font-family: var(--sans); font-size: 14px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--hairline); padding-bottom: 12px; margin: 0;
}
.desk-edit details.history-block > summary.section-toggle::-webkit-details-marker { display: none; }
.desk-edit details.history-block > summary.section-toggle::before { content: "+"; color: var(--accent-deep); font-size: 16px; }
.desk-edit details.history-block[open] > summary.section-toggle::before { content: "\2212"; }
.desk-edit details.history-block > summary.section-toggle:hover { color: var(--accent-deep); }
.desk-edit details.history-block .history { margin-top: 16px; }

/* block-editor captions (item 5) */
.desk-edit .key-caption {
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.08em; color: var(--muted);
  margin: -8px 0 18px;
}
.desk-edit .where-line { color: var(--muted); font-size: 16px; max-width: 60ch; margin: 0 0 20px; }

/* the index: quick-nav strip (anchor links, no JS) */
.desk-quicknav {
  display: flex; flex-wrap: wrap; gap: 4px 22px; margin: 28px 0 8px;
  padding-bottom: 18px; border-bottom: 1px solid var(--hairline);
}
.desk-quicknav a {
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted);
}
.desk-quicknav a:hover { color: var(--oxblood); text-decoration: none; }
.desk h2 { scroll-margin-top: 80px; }        /* keeps a jumped-to heading clear of the mobile sticky topbar */
.desk h2:first-of-type { margin-top: 16px; } /* tucks "Modules" close under the quicknav */

/* the module list reuses /modules' own .module-index/.index-row component —
   these overrides un-center it inside the desk's left-aligned column */
.desk .module-index { margin: 24px 0 0; max-width: none; }
.desk .movement-header { max-width: none; margin-left: 0; margin-right: 0; }
.desk .index-state-chip { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
/* Drip state line (spec 2026-08-05): third column, same quiet register as the
   chip beside it — the row's grid gains a slot rather than the span wrapping
   loose below the title. */
.desk .index-row a { grid-template-columns: 1fr auto auto; }
.desk .index-drip-state { grid-column: 3; grid-row: 1 / span 2; align-self: center;
  font-family: var(--sans); font-size: 13px; color: var(--muted); }

/* humanized block rows (item 5) */
.desk-blocks li { display: flex; flex-direction: column; gap: 2px; padding: 16px 0; }
.desk-blocks .block-title { font-size: 17px; font-style: italic; color: var(--accent-deep); }
.desk-blocks .block-key { font-family: var(--sans); font-size: 11px; letter-spacing: 0.06em; color: var(--muted); }
.desk-blocks .block-preview { color: var(--muted); font-size: 15px; margin: 2px 0 0; max-width: 60ch; }

/* gatherings: the card is the li — drop the plain-list hairline so the card's
   own border carries the separation (matches .gathering-list's 18px rhythm) */
.desk .desk-gatherings li { border-bottom: none; padding: 0; margin: 0 0 18px; }
.desk details.gathering-edit { margin-top: 14px; }
.desk details.gathering-edit > summary.gathering-toggle {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); padding: 4px 0;
}
.desk details.gathering-edit > summary.gathering-toggle::-webkit-details-marker { display: none; }
.desk details.gathering-edit > summary.gathering-toggle::before { content: "+"; color: var(--accent-deep); }
.desk details.gathering-edit[open] > summary.gathering-toggle::before { content: "\2212"; }
.desk details.gathering-edit > summary.gathering-toggle:hover { color: var(--oxblood); }
.desk details.gathering-edit .field-form { margin-top: 16px; }

/* index-page forms (the gathering edits, the wave review) — the same quiet
   field treatment the edit pages carry; without this they fall back to
   browser-default inputs, off-register beside the new sections.
   Task 8 (fix round 1): extended to .steward .field-form (the enrolment
   form's "Her full name" / "Her email") rather than duplicating the block --
   Rhaenyra §2's own build note asks for exactly that. input[type="email"] is
   added alongside input[type="text"] for the .steward side of each selector
   list, since "Her email" needs the same treatment "Her full name" gets and
   the .desk side has no email field to cover today. */
.desk .field-form label, .steward .field-form label {
  display: block; font-family: var(--sans); font-size: 13px;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
  margin: 14px 0 6px;
}
.desk .field-form input[type="text"], .desk .field-form textarea,
.steward .field-form input[type="text"], .steward .field-form input[type="email"],
.steward .field-form textarea {
  width: 100%; box-sizing: border-box; font-family: var(--serif, serif);
  font-size: 16px; line-height: 1.6; color: var(--ink);
  border: 1px solid var(--hairline-soft); border-radius: 2px;
  padding: 10px 12px; background: transparent; resize: vertical;
}
.desk .field-form input[type="text"]:focus, .desk .field-form textarea:focus,
.steward .field-form input[type="text"]:focus, .steward .field-form input[type="email"]:focus,
.steward .field-form textarea:focus {
  outline: none; border-color: var(--oxblood); box-shadow: 0 0 0 2px rgba(91, 31, 40, 0.12);
}

/* the wave's start: a bordered zone + a confirmation page — the gravity comes
   from the structure, not from alarm color or shouting copy */
.desk .danger-zone, .desk-edit .danger-zone {
  border: 1px solid var(--oxblood); border-radius: 6px;
  padding: 24px 28px; margin-top: 8px; background: var(--paper-raised);
}
.desk-edit .confirm-facts { margin: 20px 0 28px; padding: 0; }
.desk-edit .confirm-row {
  display: flex; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--hairline-soft);
}
.desk-edit .confirm-row dt {
  flex: 0 0 160px; margin: 0; font-family: var(--sans); font-size: 12px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.desk-edit .confirm-row dd { margin: 0; color: var(--ink); font-style: italic; }

/* ---- The per-person operator view (Task 13) — band 1, "who she is" ----
   Same flat fact-row shape as .desk-edit's own .confirm-facts/.confirm-row
   above (extend this file, don't fork it): a fixed-width sans label, an
   italic value, one hairline per row. Named for its own context rather than
   reusing "confirm-row" wholesale, since this page confirms nothing. */
.steward .person-facts { margin: 20px 0 28px; padding: 0; }
.steward .person-fact {
  display: flex; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--hairline-soft);
}
.steward .person-fact dt {
  flex: 0 0 160px; margin: 0; font-family: var(--sans); font-size: 12px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.steward .person-fact dd { margin: 0; color: var(--ink); font-style: italic; }

/* band 2, "what has happened" — her event timeline, kind and timestamp
   only (never the payload). Same quiet-row family as .vestibule/.pod-list
   above: no icon, no color-coding, the words carry the fact. */
.person-timeline { list-style: none; margin: 0; padding: 0; }
.person-timeline li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 14px;
  padding: 12px 0; border-bottom: 1px solid var(--hairline-soft); color: var(--ink); font-size: 16px;
}
.person-timeline li:last-child { border-bottom: none; }
.person-timeline .event-when {
  font-family: var(--sans); font-size: 13px; color: var(--muted); white-space: nowrap;
}

/* ---- /me: her quiet profile page + the sidebar profile link ---- */
/* A real button since 2026-08-03 (Stephen's ask) — same quiet outlined
   vocabulary as .menu-toggle, sized down for the sidebar foot. */
.foot-profile-link {
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 8px; padding: 6px 14px; min-height: 32px;
  font-family: var(--sans); font-size: 10.5px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--oxblood);
  border: 1px solid var(--oxblood); border-radius: 999px;
  text-decoration: none;
}
.foot-profile-link:hover, .foot-profile-link:focus-visible {
  background: var(--oxblood); color: #fff; text-decoration: none;
}

/* Redesigned 2026-07-25 (spec §1): the page stopped being a photo drop-box
   and became her quiet, fully-editable introduction. The inline pattern:
   each input IS the display text (font: inherit, no box at rest), only
   revealing itself as a control on hover/focus via the site's own
   underline idiom. The photo: the circle itself is the button (click/tap
   opens the picker; the hover veil is decoration, never the mechanism),
   live preview, explicit Save-this-photo / Cancel. */
.me-page { max-width: 460px; }

.me-name { font-family: var(--serif); font-size: 30px; font-weight: 300; color: var(--ink); margin: 0 0 4px; }
.me-name .me-field-form { display: inline-flex; align-items: baseline; gap: 10px; max-width: 100%; }
.me-loc {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  font-family: var(--sans); font-size: 13px; letter-spacing: 0.06em;
  color: var(--muted); margin: 0 0 20px;
}
.me-field-inline { display: inline-flex; align-items: baseline; gap: 8px; }
.me-loc-sep { color: var(--hairline); }
.me-email { font-family: var(--sans); font-size: 13px; color: var(--muted); margin: 0 0 4px; }

.me-field-input {
  font: inherit; color: inherit; background: transparent;
  border: none; border-bottom: 1px solid transparent; border-radius: 0;
  padding: 2px 1px; margin: 0; width: auto; max-width: 100%;
  field-sizing: content; min-width: 3ch; /* hug the text she actually typed,
    so each quiet Save sits beside its words (server-set size="" is the
    fallback where field-sizing isn't supported yet) */
}
.me-field-input::placeholder { color: var(--muted); opacity: 0.7; font-style: italic; }
.me-field-select { padding-right: 4px; } /* native arrow is the one deliberate "this is a choice" cue -- not stripped */
/* With the Save control retired under JS (.is-enhanced, below), the resting
   underline becomes the only thing saying "these words are yours to change" —
   hairline-weight, so it reads as an invitation and not as a form field. */
.me-field-input { border-bottom-color: var(--hairline); }
.me-field-input:hover { border-bottom-color: currentColor; }
.me-field-input:focus {
  outline: none; border-bottom-color: var(--oxblood); background: var(--linen-wash);
}

.me-field-save {
  margin: 0; padding: 0; background: none; border: none; cursor: pointer;
  font-family: var(--sans); font-size: 12px; color: var(--muted);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px;
  min-height: 44px; display: inline-flex; align-items: center; /* touch-target floor */
}
.me-field-save:hover, .me-field-save:focus-visible { color: var(--oxblood); background: none; }
/* The no-JS submit, retired the moment me.js takes over the commit. Server
   HTML ships it visible on purpose — hiding it here rather than in the
   template is what keeps the no-JS path whole. */
.me-field-form.is-enhanced .me-field-save { display: none; }

.me-saved-flash {
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent-deep); height: 1.4em; opacity: 0; margin: 0 0 20px;
}
.me-saved-flash.is-visible { opacity: 1; }

.me-error {
  font-family: var(--sans); font-size: 14px; color: var(--oxblood);
  background: var(--linen); border-radius: 4px; padding: 10px 14px; margin: 0 0 20px;
}

.me-avatar-block { margin-bottom: 28px; }
.me-avatar-btn {
  position: relative; display: inline-flex; padding: 0; margin: 0; border: none;
  background: none; border-radius: 50%; cursor: pointer; width: 128px; height: 128px;
}
.me-avatar-btn:hover { background: none; }
.me-avatar-visual { display: block; width: 128px; height: 128px; border-radius: 50%; overflow: hidden; }
.me-avatar-img {
  width: 128px; height: 128px; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--hairline); display: block;
}
.avatar-circle-large {
  width: 128px; height: 128px; font-size: 40px; cursor: pointer;
}
.me-avatar-veil {
  position: absolute; inset: 0; border-radius: 50%;
  background: rgba(15, 27, 42, 0.35); opacity: 0;
}
.me-avatar-cue {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 0 14px;
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: #fff; opacity: 0;
}
.me-avatar-btn:hover .me-avatar-veil, .me-avatar-btn:focus-visible .me-avatar-veil,
.me-avatar-btn:hover .me-avatar-cue, .me-avatar-btn:focus-visible .me-avatar-cue { opacity: 1; }
.me-avatar-btn:focus-visible { outline: 2px solid var(--oxblood); outline-offset: 3px; }

.me-photo-form { margin-top: 14px; }
.me-photo-form input[type="file"] { font-family: var(--sans); font-size: 13px; color: var(--muted); }
.me-photo-form input[type="file"]::file-selector-button {
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent-deep); background: transparent; border: 1px solid var(--accent-deep);
  border-radius: 4px; padding: 8px 14px; margin-right: 10px; cursor: pointer;
}
.me-photo-form input[type="file"]::file-selector-button:hover { background: var(--accent-deep); color: #fff; }
/* once me.js is awake the circle is the affordance — the raw input steps
   offstage (sr-only-style, still focusable/functional), and the confirm row
   only appears when a file is actually chosen */
.me-photo-form.is-enhanced input[type="file"] {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* CSS-owned visibility (no inline JS under the CSP, no first-paint flash):
   the row exists exactly while a file is chosen — the required file input
   is :valid only then. Cancel/save simply clear the input. */
.me-avatar-confirm { display: none; }
.me-photo-form:has(input[type="file"]:valid) .me-avatar-confirm {
  display: flex; align-items: center; gap: 16px; margin-top: 14px;
}
.me-avatar-save {
  margin: 0; padding: 10px 20px; font-size: 12px; letter-spacing: 0.14em;
  background: var(--oxblood); color: #fff; border: none; border-radius: 4px;
}
.me-avatar-save:hover { background: var(--ink-strong); }
.me-avatar-cancel {
  margin: 0; padding: 10px 4px; background: none; border: none; cursor: pointer;
  font-family: var(--sans); font-size: 12px; color: var(--muted);
  text-decoration: underline; text-underline-offset: 3px;
  min-height: 44px; display: inline-flex; align-items: center; /* touch-target floor */
}
.me-avatar-cancel:hover { color: var(--oxblood); background: none; }

.me-photo-remove { margin-top: 12px; }
.me-photo-remove button {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: 13px; color: var(--muted);
  text-decoration: underline;
}
.me-photo-remove button:hover { color: var(--oxblood); background: none; }

.me-leave-form { margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--hairline); }
/* Leaving is an act, not a link. It used to carry the same sans-12-muted
   treatment as the field Saves, so "save my city" and "sign out" read as the
   same kind of thing; it now borrows the drawer-close ghost-button register
   so its shape says what it costs. Confirm dialog stays (data-confirm). */
.me-leave-btn {
  margin: 0; padding: 10px 18px; background: transparent; cursor: pointer;
  border: 1px solid var(--hairline);
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); min-height: 44px; display: inline-flex; align-items: center;
}
.me-leave-btn:hover, .me-leave-btn:focus-visible { background: var(--linen); color: var(--oxblood); }

@media (max-width: 480px) {
  .me-avatar-btn, .me-avatar-visual { width: 104px; height: 104px; }
  .me-avatar-visual .avatar-circle-large, .me-avatar-visual .me-avatar-img { width: 104px; height: 104px; }
  .me-avatar-visual .avatar-circle-large { font-size: 32px; }
}

@media (prefers-reduced-motion: no-preference) {
  .me-avatar-veil, .me-avatar-cue { transition: opacity var(--dur-quick) var(--ease-settle); }
  .me-field-input { transition: border-color var(--dur-quick) var(--ease-settle), background-color var(--dur-quick) var(--ease-settle); }
  .me-saved-flash { transition: opacity var(--dur-calm) var(--ease-settle); }
}

/* ---- steward header restructure (Stephen, 2026-07-29) ----
   The header's facts as scannable figures (his 2026-07-26 "function first"
   ruling reaching the header), and the page's actions gathered on one row.
   Same tokens, same plain words; the serif carries the numbers, the sans
   meta voice carries the labels — the who-meta/waiting-since idiom, lifted
   to the top of the page. Appended, never edited in place: everything above
   this block is untouched. */

.steward .wave-stats {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px 48px;
  margin: 10px 0 0;
}
.steward .wave-stat { display: flex; flex-direction: column; gap: 3px; }
.steward .wave-stat-n {
  font-size: 27px; line-height: 1.1; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.steward .wave-stat-soft { font-size: 16px; color: var(--muted); }
.steward .wave-stat-label {
  font-family: var(--sans); font-size: 11.5px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted);
}

.steward .steward-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 28px;
  margin: 22px 0 52px; padding-top: 16px;
  border-top: 1px solid var(--hairline-soft);
}
.steward .steward-actions .quiet-link { font-style: normal; font-size: 14px; }
.steward .preview-enter-form { margin: 0 0 0 auto; }
.steward .preview-enter-form button { font-size: 13px; padding: 10px 18px; }
.steward .preview-unavailable {
  margin-left: auto; font-family: var(--sans); font-size: 13px;
  color: var(--muted); font-style: italic; max-width: 34ch; text-align: right;
}

/* the pod row, two lines (Stephen, 2026-07-29): the name + its one action,
   then the facts in the grid's own sans meta voice. The old single-baseline
   run of name/status/count/link is retired; .pod-row's flex-wrap stays for
   the repair forms and the wave-move list, both untouched. */
.pod-row { padding: 16px 0; }
.pod-row .pod-row-head {
  flex-basis: 100%; display: flex; align-items: baseline;
  justify-content: space-between; gap: 16px;
}
.pod-row .pod-row-head .pod-name { font-size: 18px; }
.pod-row .pod-row-facts {
  flex-basis: 100%; margin: 4px 0 0;
  font-family: var(--sans); font-size: 13.5px; color: var(--muted);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
}
.pod-row .pod-row-facts .pod-count,
.pod-row .pod-row-facts .pod-status {
  font-family: inherit; font-size: inherit; color: inherit;
}
.pod-row .pod-row-facts .pod-status::before {
  content: "\00B7"; margin-right: 10px; color: var(--hairline);
}

/* the quiet answer under a placement/formation confirm — what was (and was
   not) sent. Copy DRAFT, same status as the confirms above it. */
.steward .placement-note {
  font-family: var(--sans); font-size: 13.5px; color: var(--muted);
  max-width: 60ch; margin: -2px 0 0;
}

/* the wave-detail page's dates line (2026-07-29, same restructure): one sans
   meta line under the stat strip, closed by the same hairline the console's
   action row carries — start and close are facts, not headline figures. */
.steward .wave-dates {
  font-family: var(--sans); font-size: 13.5px; color: var(--muted);
  margin: 20px 0 48px; padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline-soft);
  text-transform: none; letter-spacing: normal;
}
/* capitalize the status word when it stands in the week-stat's place
   ("forming" / "closed" arrive lowercase from the derived read) */
.steward .wave-stat-n { text-transform: capitalize; }
.steward .wave-stat-soft { text-transform: none; }

/* the person page's meta line (2026-07-29, same restructure): who she is in
   the system, one sans line under her name — the italic sentence retired,
   its facts deduped out of the "Who she is" list. Same hairline close the
   wave page's dates line carries. */
.steward .person-meta {
  font-family: var(--sans); font-size: 13.5px; color: var(--muted);
  margin: 4px 0 44px; padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline-soft);
}

/* the one real button a pod-row head may carry (Send another doorway):
   kept a true button — it posts — but sized to sit on the row's right
   edge without shouting over the list it closes. */
.pod-row .pod-row-head .doorway-send { margin: 0; }
.pod-row .pod-row-head .doorway-send button { font-size: 13px; padding: 8px 14px; }
/* a details-toggle in the head (Move her wave) keeps its control on the
   right edge, matching the room link's own position on the console rows */
.pod-row .pod-row-head details.wave-move { margin-left: auto; }

/* ---- The arrival walks (First Arrival brief). One scrim, one spotlight,
   per-stop cards. Shipped 2026-08-12 for the pre-wave shape (two cards, the
   holding screen, _arrival_walk_prewave.html); reused verbatim 2026-08-13 by
   the wave-start shape (four cards, home, _arrival_walk_wavestart.html) —
   the two are mutually exclusive branches of the same route, so the shared
   #arrival-walk id can never collide (v2 spec §12). Every
   value below reuses the platform's own vocabulary: the witness-overlay dim
   color, the focus-ring oxblood outline as the spotlight ring, portal-reveal
   as the card entrance, --dur/--ease tokens for every transition. Sits above
   the witness corner chrome (z 85/90) — the walk is the first thing she
   meets, so nothing floats over it. ---- */
#arrival-walk .arrival-scrim {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(15, 27, 42, 0.35);           /* the platform's one dim (witness-overlay) */
  opacity: 0; visibility: hidden;
}
#arrival-walk.is-on .arrival-scrim { opacity: 1; visibility: visible; }
#arrival-walk .arrival-spotlight {
  position: fixed; z-index: 101; pointer-events: none;
  border-radius: 6px;
  outline: 2px solid var(--oxblood); outline-offset: 4px;  /* the site's own focus-ring idiom */
  box-shadow: 0 0 0 9999px rgba(15, 27, 42, 0.35);         /* dim everywhere but here */
  opacity: 0; visibility: hidden;
}
#arrival-walk.is-on .arrival-spotlight { opacity: 1; visibility: visible; }
#arrival-walk .arrival-card {
  position: fixed; z-index: 102;
  width: min(420px, calc(100vw - 32px));
  background: var(--paper-raised); border: 1px solid var(--hairline);
  border-radius: 6px; padding: 22px 24px;
}
#arrival-walk .arrival-card-line { margin: 0 0 16px; font-family: var(--sans); font-size: 15px; line-height: 1.55; color: var(--ink); }
#arrival-walk .arrival-actions { display: flex; align-items: center; gap: 18px; }
#arrival-walk .arrival-continue {
  min-height: 44px; padding: 10px 18px; cursor: pointer;
  background: var(--oxblood); color: var(--paper-raised);
  border: 1px solid var(--oxblood); border-radius: 4px;
  font-family: var(--sans); font-size: 14px;
}
#arrival-walk .arrival-skip {
  min-height: 44px; padding: 10px 4px; cursor: pointer;
  background: none; border: none; color: var(--muted);
  font-family: var(--sans); font-size: 13.5px; font-style: italic;
}
/* Hover states (v2 spec §11, closing a gap in the block above as shipped:
   cursor + focus ring existed, but a mouse-over changed nothing visible).
   The skip line darkens to oxblood like every other text-only control
   (.hold-control:hover, .since-link:hover); the filled Continue deepens the
   same way .primary-save's family does — a settle, not a glow. Shared by
   both walks, since both render this block's markup. */
#arrival-walk .arrival-skip:hover { color: var(--oxblood); }
#arrival-walk .arrival-continue:hover { background: #4a1a21; border-color: #4a1a21; }

/* The re-run door (Stephen's ruling 2026-08-13; moved beneath the sidebar
   foot at his 8/13 pm note — the last quiet thing in the rail, not a room
   among the rooms). One line, never wrapped: nowrap keeps "again" from
   orphaning on a narrow rail. Rendered only for live student identities
   with a resolved week (nav_walk_again), so demo and operator navs never
   carry it. */
.nav-walk-again {
  display: block; margin-top: 18px; text-decoration: none;
  font-family: var(--sans); font-size: 12.5px; color: var(--muted);
  white-space: nowrap;
}
.nav-walk-again .walk-again-mark {
  display: inline-block; width: 16px; height: 16px; line-height: 15px;
  text-align: center; border: 1px solid var(--hairline); border-radius: 50%;
  font-size: 11px; font-style: normal;
}
.nav-walk-again:hover { color: var(--oxblood); }
.nav-walk-again:hover .walk-again-mark { border-color: var(--oxblood); }
@media (max-width: 768px) {
  /* pinned above the fold's foot at narrow widths — the witness slide-over's
     own footprint discipline, reused. Centered by symmetric insets, NOT by
     transform: portal-reveal's fill-mode transform would override a
     translateX(-50%) and shove the card off-center (caught on the visual
     pass, 2026-08-12). */
  #arrival-walk .arrival-card {
    top: auto !important; bottom: 24px;
    left: 16px !important; right: 16px;
    width: auto; max-width: 420px; margin: 0 auto;
  }
}
@media (prefers-reduced-motion: no-preference) {
  #arrival-walk .arrival-scrim { transition: opacity var(--dur-quick) var(--ease-settle), visibility 0s linear var(--dur-quick); }
  #arrival-walk.is-on .arrival-scrim { transition: opacity var(--dur-quick) var(--ease-settle), visibility 0s linear 0s; }
  /* spotlight travel between the two stops: a panel changing its own
     footprint — --dur-calm, per the motion section's own reservation.
     Under reduced motion this block is simply absent: the spotlight appears
     already placed, no interpolated travel ("dim without spotlight travel"). */
  #arrival-walk .arrival-spotlight {
    transition: top var(--dur-calm) var(--ease-settle), left var(--dur-calm) var(--ease-settle),
                width var(--dur-calm) var(--ease-settle), height var(--dur-calm) var(--ease-settle),
                opacity var(--dur-quick) var(--ease-settle), visibility 0s linear 0s;
  }
  #arrival-walk .arrival-card:not([hidden]) { animation: portal-reveal 0.45s ease-out both; }
}

/* Walk finding #2 (2026-08-12) is CLOSED by a stronger answer, so its rule is
   gone rather than left dead: the holding page's one door was a hyperlink
   inside a sentence, whose faint rest underline read as plain italic text on
   touch. Strengthening that underline was the small fix; Kristine's own walk
   note the same day asked for the door itself (design §3), and the invitation
   is now a button (.intake-continue, above). Nothing on the page carries
   .doorway-link any more. */
