/* COSIGNA desktop initiator — brand v0.1.1 ink/paper/gold
   Desktop-first (1024px+), degrades to mobile single-column.
   Semantic tokens: canonical COSIGNA palette.                              */

/* ── Tokens ──────────────────────────────────────────────────────────────── */
/* WP2 complete: tokens.css (linked in <head> before this file) supplies every
   canonical token AND the legacy-alias block, so this file's plain :root
   forwarders are gone. The only per-file token override that remains is the
   dark-mode --grad below (a lighter panel gradient, NOT the canonical header
   gradient). */

/* ── Dark mode ───────────────────────────────────────────────────────────── */
/* Only --grad is overridden per-file: a lighter panel gradient (surface-ivory →
   border-default) that deliberately differs from tokens.css's canonical header
   gradient (--gradient-header). All other dark tokens come from tokens.css. */
[data-theme="dark"] {
  --grad:      linear-gradient(135deg, var(--surface-ivory, #1d3147), var(--border-default, #27405e));
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --grad:      linear-gradient(135deg, var(--surface-ivory, #1d3147), var(--border-default, #27405e));
  }
}

/* DEL-1 (v0.548.0): the type-chooser disclosure's certify link. --brand-gold-dark alone (light,
   AA-tuned) fails dark; --brand-gold alone (its own dark-mode brightening) fails light — see "THE
   TWO GOLDS" in tokens.css. Neither single custom property covers both registers, so this needs
   its own tiny per-scheme rule (76-contrast-guard caught the gap: 2.45:1 / 1.53:1, both need 4.5:1). */
#type-chooser-disclosure-certify { color: var(--brand-gold-dark, #7e6314); font-weight: 600; }
[data-theme="dark"] #type-chooser-disclosure-certify { color: var(--brand-gold, #d8b864); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #type-chooser-disclosure-certify { color: var(--brand-gold, #d8b864); }
}
/* 2026-08-07 finding 3: /notarize's mirror link — same contrast-guard-safe treatment as
   #type-chooser-disclosure-certify above (76-contrast-guard applies identically here). */
#type-chooser-disclosure-multi-link { color: var(--brand-gold-dark, #7e6314); font-weight: 600; }
[data-theme="dark"] #type-chooser-disclosure-multi-link { color: var(--brand-gold, #d8b864); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #type-chooser-disclosure-multi-link { color: var(--brand-gold, #d8b864); }
}

/* OQ-9 (v0.548.0): shared link style for the trust-narrative dedup — every app-surface note that
   used to re-explain "the server never sees it" now keeps one short orienting sentence and links
   here instead. Same two-golds AA split as the rule above (76-contrast-guard precedent). */
.security-link { display: block; margin-top: 2px; font-size: 12px; color: var(--brand-gold-dark, #7e6314); font-weight: 600; text-decoration: none; }
.security-link:hover, .security-link:focus-visible { text-decoration: underline; }
[data-theme="dark"] .security-link { color: var(--brand-gold, #d8b864); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .security-link { color: var(--brand-gold, #d8b864); }
}

/* ── Reset ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; }
body {
  font-family: var(--font);
  /* v0.532.0 D-5b WP4: aurora ABOVE base --bg; WP5: --material-body-bg paper shields body text from the aurora (AA contrast) */
  background: var(--material-body-bg), var(--material-aurora), var(--bg);
  background-repeat: no-repeat;
  background-attachment: fixed;
  color: var(--text);
  min-height: 100dvh;
}

/* ── Topbar ──────────────────────────────────────────────────────────────── */
/* W2-A A2: header SKIN properties deleted — topbar.css/.cg-topbar--app now owns
   background, color, padding, layout, border-bottom.
   .topbar CLASS is RETAINED: .topbar .btn--ghost (initiator.css:129-133) and
   descendant rules for ☰/theme buttons depend on the class.
   v0.121.x iOS-Safari fix is now covered by topbar.css (same one-hop+literal pattern). */
.topbar {
  /* skin intentionally empty — governed by topbar.css .cg-topbar--app */
}

.topbar__brand {
  display: flex; align-items: center; gap: var(--s3);
  font-size: 18px; font-weight: 800; letter-spacing: -0.3px;
  color: var(--on-ink, #FAF8F3);
}
.topbar__brand svg { flex-shrink: 0; }
/* W2-A A2: bare .brand-lockup deleted — governed by topbar.css .cg-topbar--app .brand-lockup
   (44px @desktop; was 56px here). The class is still on the SVG markup. */
/* W2-A A2: bare .lang-flag / .lang-flag__btn / .lang-flag__caret / .lang-flag__menu deleted —
   all governed by topbar.css .cg-topbar--app .lang-flag* (higher-specificity scoped rules). */
/* v0.503.0 S-F1 (design call #12): .topbar__status small-pill override REMOVED.
   The element now carries .cg-topbar__title (governed by topbar.css) — large, bold, centered.
   The old pill (13px, rounded bg, absolute positioning) was preventing the title-block style
   from rendering. topbar.css handles all centering + font-size via .cg-topbar__title-block. */
.topbar__actions { display: flex; gap: var(--s2); align-items: center; }

/* v0.14.0 UX FIX: the topbar is ALWAYS dark (--ink) in both themes, but .btn--ghost uses
   color:var(--text) — which is navy in LIGHT mode → the ☰ menu + ☽ theme toggle were navy-on-navy
   (invisible). Scope topbar ghost buttons to paper-on-ink so the icons read in both themes. */
.topbar .btn--ghost {
  color: var(--on-ink, #FAF8F3);
  border-color: rgba(250, 248, 243, 0.35);
}
.topbar .btn--ghost:hover { background: rgba(250, 248, 243, 0.12); }

/* ── Layout ──────────────────────────────────────────────────────────────── */
.workspace {
  display: grid;
  grid-template-columns: 420px 1fr;
  grid-template-rows: 1fr;
  min-height: calc(100dvh - 52px);
  gap: 0;
}
.panel-left {
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: var(--s6);
  display: flex; flex-direction: column; gap: var(--s5);
}
.panel-right {
  padding: var(--s6);
  overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--s5);
}

/* v0.578.1 REVIEW-FIX-1 (2026-08-23 Gemini 3.7 + 3.1 Pro convergent finding, pre-existing since
   D1 2e4d751d): the roster-integrity banners (#roster-mismatch-warning / #roster-integrity-warning
   / #roster-integrity-unavailable) are insertBefore'd as the FIRST CHILD of .workspace — a
   2-column grid. Without an explicit placement they auto-flow into row 1 col 1 (the 420px track),
   pushing .panel-left into col 2 and .panel-right (the ring) into a crushed row-2 col-1 cell.
   Span every .workspace-mounted role=alert banner across both columns instead — one shared rule
   that also covers any future banner reusing the same mount pattern. (Verified 2026-08-23: these
   three banners are the ONLY direct .workspace children carrying role=alert; every other
   role=alert element mounts on document.body, a .state-panel, or nested panel markup, so the
   child combinator cannot over-match.) align-self:start keeps the alert box content-sized if the
   1fr row ever has spare height to distribute (short panels on a tall screen) instead of
   stretching the banner. */
.workspace > [role="alert"] {
  grid-column: 1 / -1;
  align-self: start;
}

@media (max-width: 1023px) {
  .workspace { grid-template-columns: 1fr; }
  .panel-left { border-right: none; border-bottom: 1px solid var(--border); }
  /* v0.595 S-W6-50 (SOLUTIONS §B/§G, entry 50): on one column the monitor used to inherit raw DOM order —
     every links/signers/audit card first, Ceremony Progress ~1000 px down. The monitor now reads Progress
     first, then the COSignees list (the "who signed" tap target), then the links. Scoped to the MONITOR:
     the close screen keeps its action card first. CSS order (visual only) because .panel-right is also
     the desktop right column; its DOM stays after .panel-left (keyboard order unchanged — noted). */
  [data-state="monitor"] .panel-right { order: -1; border-bottom: 1px solid var(--border); }
  [data-state="monitor"] .panel-left { border-bottom: none; }
  [data-state="monitor"] .state-panel--monitor > .monitor-card--signers { order: -1; }
  /* v0.601 S-PK4-8 (R-18 a'): while nobody has signed and at least one COSignee is still pending, the only action of
     the screen is passing the links on, so the links card goes first. initiator.js sets the links-first flag (a data
     attribute on body) on every poll, and only when the ceremony is not terminal (EXPIRED / ABORTED / CLOSED keep the order above).
     Every rule carries the monitor-state prefix, so the flag is inert on any other screen. Consequence, stated: in
     this state the phone order is links, COSignees, audit log, and THEN Ceremony Progress (the whole left column
     goes first). Visual order equals DOM order here. The role=alert banners mounted at the top of .workspace keep
     their place above everything. */
  [data-state="monitor"][data-links-first="1"] .panel-left { order: -2; }
  [data-state="monitor"][data-links-first="1"] .state-panel--monitor > .monitor-card--links { order: -2; }
  [data-state="monitor"][data-links-first="1"] .workspace > [role="alert"] { order: -3; }
  /* each COSignee row is a >= 48 px target on a phone (the face pills are ~30 px) */
  #participant-table tr[data-signer-id] > td { height: 48px; }
}

/* ── MB-1 (2026-08-05, finding 11b, screenshot-CONFIRMED) ────────────────────
   In MONITOR the shared topbar (UX/libs/topbar.css) is a 3-column grid whose
   right-column occupants (language flag + #ceremony-nickname-display + menu +
   #btn-abort, the last only revealed from OPEN onward) can't shrink
   (flex-shrink:0), while the centre title column has no min-content floor
   (min-width:0) — so the title's word-break:break-word wraps at the character
   under the combined squeeze. #ceremony-nickname-display is the only ELASTIC
   occupant of that cluster (its content — the ceremony nickname — is already
   shown in the page body, this is a convenience echo, not the only copy), so
   hiding it is the cheapest fix that buys back the collapse without touching
   the shared topbar.css component (which fleet-wide would need a whole-set
   baseline re-seed — see the plan's §1.1 "shared-component hazard" note).
   640px matches topbar.css's own responsive breakpoint (:163-166), not an
   arbitrary one — the nickname should hide exactly when the topbar's own
   3-column layout is under the pressure that causes the collapse. */
@media (max-width: 640px) {
  #ceremony-nickname-display { display: none !important; }
}

/* ── v0.44 SETUP-only full-width layout ──────────────────────────────────────
   During setup the ring/photo furniture is hidden (data-state gating), so the right
   half was empty wasted space while the intro + Document + COSignees were cramped in
   the narrow 420px left column. In setup ONLY, collapse the workspace to a single
   full-width column, drop the left-panel border, and lay the title + Document/COSignees
   across the reclaimed width. Monitor/open/close/done keep the original two-column ring
   layout untouched (these rules are scoped to [data-state="setup"]). */
[data-state="setup"] .workspace { grid-template-columns: 1fr; }
[data-state="setup"] .panel-left { border-right: none; }

/* v0.93.0 S-UX (#8): the OPEN consent-capture is a single-block page — collapse the workspace to ONE
   full-width column (like setup) and drop the left border, so the capture card can centre on the whole
   screen (the camera sits at the CENTRE on a computer, not the left). The ring/photo furniture is already
   gated out of open. */
[data-state="open"] .workspace { grid-template-columns: 1fr; max-width: var(--container-content, 720px); margin-inline: auto; }
[data-state="open"] .panel-left { border-right: none; }
[data-state="open"] .panel-right { display: none; }

/* v0.93.0 S-UX (#13): on wide desktop the DONE screen (and its #done-onboard alpha-invite panel) was
   cramped into the 420px left column while the ring cockpit sprawled on the right — buttons clipped and
   the panel read awkwardly. Collapse done to ONE centred full-width column (the closed-ring "seal" SVG
   already crowns the done screen, so the right cockpit is redundant here) and drop the left border. */
[data-state="done"] .workspace { grid-template-columns: 1fr; max-width: var(--container-content, 720px); margin-inline: auto; }
[data-state="done"] .panel-left { border-right: none; }
[data-state="done"] .panel-right { display: none; }

/* v0.58.0: top band — title (left) beside the invite-mode doc-picker (right). The aside collapses
   when empty (normal mode), so the title spans full width and the grid rises either way. */
.setup-header { display: flex; gap: var(--s6); align-items: flex-start; margin-bottom: var(--s4); }
.setup-header .setup-title { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
.setup-aside { flex: 0 0 290px; }
.setup-aside:empty { display: none; }
.setup-aside #referral-banner {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--ag-radius-lg); padding: var(--s4);
}
.setup-aside #dogfood-doc-pick { max-width: 100%; width: 100%; }
@media (max-width: 860px) {
  .setup-header { flex-direction: column; gap: var(--s3); }
  .setup-aside { flex-basis: auto; width: 100%; }
}

/* Full-width page title above the work area. */
.setup-title { margin-bottom: var(--s2); }
.setup-title__h {
  font-size: 30px; font-weight: 800; letter-spacing: -0.5px; line-height: 1.15;
  color: var(--text); margin-bottom: var(--s2);
}
.setup-title__p {
  font-size: 16px; color: var(--text-muted); line-height: 1.5; max-width: 70ch;
}
/* v0.53.0: the SignCredits reward expectation (invite mode) — a quiet gold note under the title. */
.setup-reward {
  margin-top: var(--s2); font-size: 14.5px; font-weight: 600; line-height: 1.45;
  color: var(--text); max-width: 70ch;
  background: rgba(195,161,75,.10); border-left: 3px solid var(--gold);
  border-radius: var(--ag-radius-sm, 6px); padding: 8px 12px;
}

/* Document + COSignees as two equal columns spanning the reclaimed full width;
   stack to one column on narrow screens.
   v0.93.0 S-UX (#1): align-items:stretch so the Document(1) and COSignees(2) cards are the SAME
   HEIGHT on wide screens (they share a row); .setup-card stretches to fill its grid cell. */
.setup-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5); align-items: stretch;
}
.setup-grid > .setup-card { height: 100%; }
/* M-3 (internal notes / P-9, reflection §4.4): the Document column's
   post-upload content (drop-zone + hash + QR/download) is genuinely shorter than its equal-height
   sibling once COSignees/Options grow taller — the card's OUTER box does stretch to match (that
   part already worked), but the plain block content just sits at the top, leaving visible blank
   card background beneath the QR/download block. Turn the Document card into a flex column and
   pin its trailing QR/download row to the bottom with an auto top margin, so any extra row height
   is absorbed there instead of showing as an empty gap; padding-top keeps its normal spacing when
   there's no slack to absorb (i.e. when Document is itself the tallest column). */
.setup-grid > .setup-card:first-child { display: flex; flex-direction: column; }
.setup-grid > .setup-card:first-child > .qr-doc-row { margin-top: auto; padding-top: var(--s3); }
@media (max-width: 860px) {
  .setup-grid { grid-template-columns: 1fr; }
  .setup-title__h { font-size: 24px; }
}

/* P0(E3) (internal notes §2): the equal-height machinery above (:224-227)
   makes card 1 as tall as its tallest sibling; M-3 (:236-237) sends that slack to the bottom-pinned
   .qr-doc-row. Pre-pick there IS no qr-doc-row content, so the slack showed as dead card space.
   Give it to the drop-zone instead — desktop only (<861px the cards stack: no shared-row slack
   exists). justify-content:center is required: .drop-zone (governed, see below) sets
   flex-direction:column with no main-axis alignment, so a grown box would strand its content at
   the top. Selector anchored to `.setup-grid > .setup-card:first-child >` so the four other
   surfaces that define `.drop-zone` (mobile-signer, notary, notary-act, verifier) are untouched —
   `.drop-zone` is a governed contract (UX/governed/drop-zone.css, atom input-drop-zone-pdf).
   :not(.drop-zone--done) keeps the post-pick and alpha-staged states byte-identical. */
@media (min-width: 861px) {
  .setup-grid > .setup-card:first-child > #pdf-drop:not(.drop-zone--done) {
    flex: 1 1 auto; min-height: 220px; justify-content: center;
  }
}

/* v0.93.0 S-UX (#4): when Options(3) is revealed, the setup grid becomes THREE columns on desktop —
   Document(1) | COSignees(2) | Options(3) — and the Launch seal + plain button span all columns.
   .setup-grid--3col is toggled by revealOptions(); collapses to one column on narrow screens. */
@media (min-width: 1024px) {
  /* P0(E1) (internal notes §2): plain `1fr` is `minmax(auto, 1fr)` — a
     column never shrinks below its content's min-content, so column 3's inner drawer grid (whose
     min-content floor measured ~507px live) silently defeated the equal split: 342.5 | 342.6 |
     506.9px instead of ~397px each. `minmax(0, 1fr)` removes that floor so the three pills are
     genuinely equal; verified live via a static mirror of this page (2026-09-11): post-fix each
     column measured 397.3px. */
  .setup-grid--3col { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* VP6: in the 3-column layout the right column (#setup-col3) is ONE grid cell — the options, then the launch block at
     its foot. (The old full-span rules for #launch-seal-mount / #btn-launch here were dead after VP6: those elements are
     no longer direct grid children — removed by the VP6 follow-up.) */
  .setup-grid--3col > .setup-col3 { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; }
}
/* v0.595 S-L-Q4 (register block "drift-sweep founder questions, batch 1 of 2", Q4 "(a) Hide block 2 in solo mode"):
   initiator.js updateCosigneesTitle() hides the empty "You" card (#cosignees-card) in solo mode and sets this class.
   Below 1024 px the grid stacks (no half-width hole beside the document); from 1024 px the document and the launch
   column share the row in two halves (no empty third column). */
.setup-grid.setup-grid--no-cosignees { grid-template-columns: 1fr; }
@media (min-width: 1024px) {
  .setup-grid--3col.setup-grid--no-cosignees { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* item 15: the 3 tier-drawers (#options-drawers-wrap's children) sit beside/under the posture
   card in a row at desktop width, mirroring .setup-grid--3col's own breakpoint. Deliberately NOT
   reusing .setup-grid/.setup-grid--3col directly: those carry a `.setup-grid > .setup-card:first-
   child { display:flex; flex-direction:column }` rule (+ .qr-doc-row margin) meant for the OUTER
   Document/COSignees/Options grid, which would also match these drawers (they share .setup-card)
   and alter their internal layout unpredictably — a dedicated rule avoids that collision. */
#options-drawers-wrap { display: flex; flex-direction: column; gap: var(--s3); }
/* 2026-08-13 (founder, live feedback): the 3 tier-drawers read as "stacked vertically" well below
   the outer .setup-grid--3col breakpoint (1024px) — likely a HiDPI screenshot (physical pixels
   look wide; the actual CSS viewport is much narrower). Lowered so ordinary desktop/tablet windows
   get the 3-column row; phones (~<640px CSS width) still stack, since a collapsed drawer's title
   alone is workable at ~200px but an EXPANDED drawer's full checkbox rows (e.g. "Sécurité") are not. */
@media (min-width: 640px) {
  /* P0(E2)/D-i (internal notes §2/§3): E1 removes the outer grid's
     obligation to honour this inner grid's ~507px min-content floor — it does NOT make the inner
     grid fit inside the now-397px column. Left at a fixed `1fr 1fr 1fr` this overflows the card
     (verified live via a static mirror of this page, 2026-09-11: setup-grid scrollWidth 1342 vs
     1232 with E1 alone). `repeat(auto-fit, minmax(150px, 1fr))` wraps to 2-up + 1 instead of
     forcing 3 cramped ~124px tracks — wider per drawer than the pre-fix 155-172px collapsed
     widths, and a 2+1 wrap reads as three drawers, not the 1-up stack the founder objected to
     on 2026-08-13 (:258-262 above). */
  #options-drawers-wrap { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s3); }
}
/* v0.594 VP6 (founder 2026-09-23 14:56, V6c): the launch step header + seal + button are no longer grid children
   spanning the full width below the three columns — they sit at the foot of the right column, inside #setup-col3,
   directly under the options whatever the columns' heights (tests/e2e/specs/158-initiate-launch-column.spec.js). */
/* VP6 follow-up (close-out refutation review, 2026-09-23): OUTSIDE the 3-column layout — the 2-column band 861-1023 px and
   one column below — the wrapper dissolves (display: contents) so its children are grid items again, exactly as before VP6,
   and the launch block spans the whole row (it had collapsed into one half-width cell: 416 of 852 px at 900 px). */
.setup-col3 { display: contents; }
.setup-col3 > #launch-step-head,
.setup-col3 > #launch-seal-mount,
.setup-col3 > #btn-launch { grid-column: 1 / -1; }
/* V6a: "+ Add COSignee" reads as the card's action, not as one more full-width input box. */
.btn--card-action { align-self: flex-start; border-color: var(--text); font-weight: 700; }

/* v0.95.0: the "3 · Seal & start" step header. Greyed (aria-disabled) until the setup is ready, then
   it brightens and the gate hint gives way to the hold-to-seal — same gating idiom as step 2. */
.launch-step-head {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  justify-content: center; margin-top: var(--s5);
}
/* v0.601 S-PK3-S2: the "Start signing ceremony" title rule is deleted with its element and key. */
.launch-step-head[aria-disabled="true"] { opacity: .55; filter: saturate(.6); }
.launch-step-head .launch-gate-hint { display: none; }
.launch-step-head[aria-disabled="true"] .launch-gate-hint {
  display: inline-block; flex-basis: 100%; text-align: center;
  font-size: 13px; font-weight: 600; color: var(--text-muted);
  background: var(--surface-2); border: 1px dashed var(--border);
  border-radius: var(--ag-radius-md); padding: 8px 12px; margin-top: var(--s2);
}
/* v0.97.0 — the high-contrast "do this next" prompt shown above a hold-to-seal once it's the action
   (launch / open). Full-width, accent-coloured, with a gentle attention pulse. Replaces the situation
   where the seal sat below the fold with no signpost ("had to scroll down to find it"). */
/* v0.553 X-5 (internal notes, A1): gold is CEREMONIAL
   ONLY — closure, seal, anchored receipt. An "act now" prompt is a state-of-progress signal, not a
   closure, so the tint / border / pulse move to INK at full weight. `color: var(--ink)` was already
   correct and is unchanged. The three ink values are carried on custom properties so the dark
   register can invert them in ONE place — @keyframes cannot be theme-forked, but a custom property
   read from inside a keyframe resolves against the animated element, which is what makes the pulse
   follow the scheme. */
.action-cta {
  flex-basis: 100%; display: block; text-align: center;
  font-size: 15px; font-weight: 800; letter-spacing: .01em; line-height: 1.4;
  --cta-tint:  rgba(20,37,61,.06);
  --cta-line:  var(--brand-ink);
  --cta-pulse: rgba(20,37,61,.16);
  color: var(--ink); background: var(--cta-tint);
  border: 1.5px solid var(--cta-line); border-radius: var(--ag-radius-lg);
  padding: 10px 14px; margin: var(--s3) auto 2px; max-width: 460px;
  animation: ctaPulse 2.2s ease-in-out infinite;
}
@keyframes ctaPulse {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50%      { box-shadow: 0 0 0 6px var(--cta-pulse, rgba(20,37,61,.16)); }
}
@media (prefers-reduced-motion: reduce) { .action-cta { animation: none; } }
/* v0.601 S-PK3-S2 (PK-3 11b A-H1, founder 2026-09-29 18:05 "Keep on desktop only"): the desktop ready-state prompt #launch-cta stays,
   but from 860 px down the phone launch bar (#setup-launch-bar) owns the signpost, so the prompt is hidden there. !important because JS
   toggles the element's inline display (revealActionGesture sets it to '' when ready; the inline default is display:none). */
@media (max-width: 860px) { #launch-cta { display: none !important; } }
/* v0.601 S-PK3-S3 (PK-3 11b A-M4): the phone launch bar. position:fixed, NOT sticky: .panel-left { overflow-y: auto } makes the setup column a
   scroll container with no height cap, so a sticky bar would stick to that box, not to the viewport (T1). The author display rule below is written
   with :not([hidden]) so the [hidden] the JS sets (launch area on screen) still wins (T2). No vw, no gold (gold is ceremonial only, v0.553 X-5).
   >= 24 px is spec 77's TARGET_MIN; 44 px is the design target. Rendered only in the setup state (the parent panel is display:none after launch). */
.setup-launch-bar { display: none; }
@media (max-width: 860px) {
  [data-state="setup"] .setup-launch-bar:not([hidden]) {
    display: flex; align-items: center; justify-content: center; text-align: center;
    position: fixed; left: 12px; right: 12px; bottom: calc(var(--motto-bar-h) + 8px); z-index: 50;
    min-height: 44px; padding: 12px 16px;
    font: inherit; font-size: 14px; font-weight: 600; line-height: 1.3; cursor: pointer;
    color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--ag-radius-lg);
    box-shadow: 0 6px 18px -8px rgba(20,37,61,.35);
  }
  [data-state="setup"] .setup-launch-bar:focus-visible { outline: 3px solid var(--focus-ring, #2563eb); outline-offset: 2px; }
}
/* v0.601 S-PK3-S2: the R-13 fold under the anchor checkbox (indented to align with the label text; a >= 24 px target, spec 77 TARGET_MIN). */
#anchor-more { margin: 2px 0 0 23px; }
#anchor-more > summary {
  cursor: pointer; font-size: 12px; color: var(--text-muted); line-height: 1.4;
  min-height: 24px; display: flex; align-items: center; list-style: none;
}
#anchor-more > summary::-webkit-details-marker { display: none; }
#anchor-more > summary::before { content: "\25B8"; font-size: 11px; margin-right: 6px; }
#anchor-more[open] > summary::before { content: "\25BE"; }
#anchor-more > summary:focus-visible { outline: 3px solid var(--focus-ring, #2563eb); outline-offset: 2px; border-radius: 4px; }
/* v0.553 X-5 A1 — the dark register, as ONE treatment (no competing second rule).
   NB the shape change: the prior rule paired `[data-theme="dark"]` with a BARE
   `:root:not([data-theme="light"])`, which also matches a LIGHT OS with no data-theme set. That was
   harmless while the rule only set `color: var(--text)` (a var that flips by itself), and it is
   actively wrong the moment scheme-specific literals live here. So the second selector is now
   wrapped in its @media, matching this file's own #type-chooser-disclosure-certify / .security-link
   precedent. Light-mode output is byte-identical: --text and --ink both resolve to #14253D there. */
[data-theme="dark"] .action-cta {
  color: var(--text);
  --cta-tint:  rgba(250,248,243,.10);
  --cta-line:  var(--text);
  --cta-pulse: rgba(250,248,243,.16);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .action-cta {
    color: var(--text);
    --cta-tint:  rgba(250,248,243,.10);
    --cta-line:  var(--text);
    --cta-pulse: rgba(250,248,243,.16);
  }
}

/* v0.93.0 S-UX (#2): numbered order pills on the card titles (1 = Document, 2 = COSignees, 3 = Options)
   so the flow reads visually left→right. Optional variant (3) is muted/outline to signal it's not a gate. */
.order-pill {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: var(--ag-radius-full);
  background: var(--ink); color: var(--on-ink, #FAF8F3);
  font-size: 14px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums;
}
.order-pill--optional { background: transparent; color: var(--text-muted); border: 1.5px solid var(--border); }
.order-pill__opt { font-size: 12px; font-weight: 600; color: var(--text-muted); }

/* §4.2 posture card (internal notes, OQ-10 ruled (a)): 3 labeled
   radio-cards, not a segmented control — same visual family as #ceremony-type-chooser's button-cards
   (question title + labeled options + auto-fit grid), but native <input type="radio"> so mutual
   exclusion is structural, not JS-juggled. :has() styles the selected card (already used elsewhere
   in this codebase, e.g. admin.css's .wrap:has(...)). */
.posture-radio-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--s2); margin-top: var(--s3);
}
/* MP-1 (internal notes): auto-fit wraps 2+1 at 390px (card content box is
   ~330px, so only two 140px-min columns fit) — force 3-up and shrink the label to keep it feasible. */
@media (max-width: 480px) {
  .posture-radio-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .posture-radio-card { padding: var(--s2) 4px; font-size: 12px; gap: 4px; }
}
.posture-radio-card {
  display: flex; align-items: center; justify-content: center; text-align: center;
  gap: 6px; padding: var(--s3) var(--s2); border: 1.5px solid var(--border); border-radius: var(--ag-radius-md, 8px);
  cursor: pointer; font-size: 14px; font-weight: 600; color: var(--text);
  background: var(--surface); transition: border-color .15s ease, background .15s ease;
}
.posture-radio-card:has(input:checked) {
  border-color: var(--brand-gold, #C3A14B); background: rgba(195, 161, 75, .08);
}
.posture-radio-card:has(input:focus-visible) { outline: 2px solid var(--brand-gold, #C3A14B); outline-offset: 2px; }
.posture-radio-card input[type="radio"] { flex: 0 0 auto; margin: 0; }
.posture-radio-card__label { line-height: 1.3; }

/* v0.93.0 S-UX (#3): gate step 2 on the document — until a PDF is picked, the COSignees card is
   greyed out + non-interactive (best-practice disabled affordance). #cosignees-card carries
   aria-disabled="true" until handlePDF success / a sample is loaded; checkSetupReady() clears it. */
#cosignees-card[aria-disabled="true"] {
  opacity: .5; pointer-events: none; filter: saturate(.6);
}

/* 2026-08-07 finding 4 CORRECTION: #posture-card now gates too (OQ-10(a)'s "always-visible, NOT
   gated" clause reversed, founder-confirmed) — same treatment as #cosignees-card above. */
#posture-card[aria-disabled="true"] {
  opacity: .5; pointer-events: none; filter: saturate(.6);
}

/* MP-2 (internal notes, taxonomy §2.3 RULED): the three options drawers
   gate on the document existing, same idiom as #cosignees-card above. #posture-card stays
   deliberately OUTSIDE this wrapper — OQ-10(a) rules it always-visible, never gated. */
#options-drawers-wrap[aria-disabled="true"] {
  opacity: .5; pointer-events: none; filter: saturate(.6);
}

/* v0.93.0 S-UX (#5): the launch hold-to-seal logo gesture on the paper bg — ink C+Signa. */
#launch-seal-mount .cg-logo { --logo-c: #14253D; }
.launch-seal-mount { display: flex; justify-content: center; margin-top: var(--s5); }

/* Larger fonts in the setup cards (a clear notch up vs the rest of the UI). */
.setup-grid .card__title { font-size: 19px; }
.setup-grid label.field-label { font-size: 14px; }
.setup-grid .drop-zone__label { font-size: 17px; }
.setup-grid .drop-zone__sub { font-size: 14px; }
.setup-grid .hash-pill { font-size: 14px; }
.setup-grid .roster-item__name { font-size: 16px; }
.setup-grid .roster-item__channel { font-size: 14px; }

/* P0(E5) (internal notes §2): scale the drop-zone's lead type up on
   desktop, now that E1-E3 give it real room to be the card's dominant element — smallest, most
   reversible part of the bundle. Deliberately placed AFTER the unscoped base rule at :430 (not
   with E1/E3 near :246): both selectors tie on specificity, so at a tie the later-in-source rule
   wins — putting a `min-width:861px` override BEFORE :430 would lose the cascade to that later
   unscoped rule at desktop widths instead of overriding it. */
@media (min-width: 861px) {
  .setup-grid .drop-zone__icon { font-size: 40px; }
  .setup-grid .drop-zone__label { font-size: 19px; }
}

/* v0.3.2: shared widgets (Ceremony Progress + Consent photograph chain) stack by default. */
.shared-widgets { display: flex; flex-direction: column; gap: var(--s5); }

/* v0.34.0: the ring + consent-photo chain are "ceremony in progress" furniture — keep them OUT of the
   setup/open screens (they're empty there and clutter the step-by-step flow). They appear once the
   COSignMaster has opened the ring + taken their consent photo, i.e. from the monitor state on. */
[data-state="setup"] .shared-widgets,
[data-state="open"]  .shared-widgets { display: none; }

/* v0.3.2 wide-screen (≥1280px): make the right panel a 3rd+2nd column pair.
   Setup: Invitation preview | Security Mode side by side; Launch + What's new full width.
   Shared widgets: Ceremony Progress | Consent photograph chain side by side.
   (Document + Signers stay stacked in the 420px left panel = visual column 1.) */
/* v0.93.0 S-UX: the old right-panel setup 2-col grid is gone (Options+Launch moved into #setup-grid).
   Keep only the shared-widgets side-by-side treatment for the monitor cockpit. */
@media (min-width: 1280px) {
  .shared-widgets {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); align-items: start;
  }
}

/* ── v0.74.0 bugfix: SETUP layout + Launch button ─────────────────────────────
   The founder's report: on a computer the setup blocks sprawl edge-to-edge and the
   "Launch ceremony" control renders as a full-bleed dark BAR (not a button).
   Cause: in setup the workspace collapses to one column (panel-right spans the full
   viewport); at ≥1280 the panel-right state-panel is a 2-col grid whose 2nd column is
   empty (the ring/photo furniture is hidden until first signature), and #btn-launch
   spans grid-column 1/-1 → as an inline-flex grid item it stretches the whole width.
   Fix (scoped to setup only — cockpit states untouched): cap + centre the setup column,
   make the panel-right setup panel a single centred column, and let the Launch button
   size to its content as an obvious, centred primary button. Placed AFTER the 1280 grid
   block so source order wins the cascade (equal specificity). */
/* v0.93.0 S-UX (#4): the setup workspace was capped at 1040px (a 2-col-friendly width). With Options(3)
   now a third column, widen the cap so three cards breathe on desktop. */
[data-state="setup"] .workspace {
  max-width: var(--container-app, 1280px);
  margin-inline: auto;
}
/* v0.93.0 S-UX: Options(3) + Launch moved into #setup-grid, so the right-panel setup slot is empty —
   collapse it so it adds no stray gap. */
[data-state="setup"] .panel-right .state-panel--setup { display: none; }
/* v0.93.0 S-UX: #btn-launch is now hidden (the hold-to-seal logo replaces it). #btn-continue-options
   is an optional ghost link inside the COSignees card — left-aligned, content-sized (not a big CTA). */
[data-state="setup"] #btn-continue-options {
  align-self: flex-start; width: auto;
}
/* G1 fix: launch button + seal mount are always rendered (visible-disabled affordance).
   When aria-disabled="true" they appear at reduced opacity with a not-allowed cursor so the
   user knows the control exists and understands it needs steps 1–2 first. */
#btn-launch[aria-disabled="true"],
#launch-seal-mount[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; pointer-events: none;
}
#btn-launch[aria-disabled="true"] { filter: saturate(0.4); }

/* v0.3.2: roster drag-handle — makes reordering discoverable. */
.roster-item__handle {
  cursor: grab; color: var(--text-muted); font-size: 16px; line-height: 1;
  user-select: none; padding-right: 2px; flex-shrink: 0;
}
.roster-item.dragging { opacity: 0.5; }

/* ── State visibility — js adds data-state to <body> ──────────────────── */
[data-state] .state-panel { display: none; }
[data-state="setup"]   .state-panel--setup   { display: flex; flex-direction: column; gap: var(--s4); }
[data-state="open"]    .state-panel--open    { display: flex; flex-direction: column; gap: var(--s4); }
[data-state="monitor"] .state-panel--monitor { display: flex; flex-direction: column; gap: var(--s4); }
[data-state="close"]   .state-panel--close   { display: flex; flex-direction: column; gap: var(--s4); }
[data-state="done"]    .state-panel--done    { display: flex; flex-direction: column; gap: var(--s4); }

/* ── Cards ───────────────────────────────────────────────────────────────── */
.card {
  /* v0.532.0 D-5b WP4: material card glass (setup/monitor/done cards) */
  background: var(--material-card-bg, linear-gradient(180deg, rgba(255,255,255,.86), rgba(255,255,255,.52)));
  border: 1px solid var(--material-card-border, rgba(255,255,255,.72));
  border-radius: var(--material-card-radius, 14px); padding: var(--s5);
  backdrop-filter: var(--material-card-blur, blur(16px) saturate(1.45));
  -webkit-backdrop-filter: var(--material-card-blur, blur(16px) saturate(1.45));
  box-shadow: var(--material-card-rim, inset 0 1px 0 rgba(255,255,255,.95), 0 8px 22px -12px rgba(20,37,61,.30));
  animation: agFadeIn .3s ease;
}
.card__title {
  font-size: 17px; font-weight: 700;   /* v0.34.0 type bump: 15→17 */
  display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s4);
  color: var(--text);
}
.card__title .icon { font-size: 18px; }

/* ── Form elements ───────────────────────────────────────────────────────── */
label.field-label {
  display: block; font-size: 13px; font-weight: 600;   /* v0.34.0 type bump: 12→13 */
  /* v0.74.0 bugfix: drop uppercase — it mangled the brand casing ("Your name (COSignMaster)"
     → "YOUR NAME (COSIGNMASTER)"). Weight + muted colour carry the label affordance;
     sentence-case preserves brand names (COSignMaster / COSignee / COSigna). */
  text-transform: none; letter-spacing: .01em;
  color: var(--text-muted); margin-bottom: var(--s1);
}
input[type="text"], input[type="email"], select {
  width: 100%; padding: var(--s3) var(--s4);
  border: 1.5px solid var(--border); border-radius: var(--ag-radius-md);
  font-family: var(--font); font-size: 16px;   /* v0.34.0 type bump: 14→16 (also stops iOS zoom-on-focus) */
  background: var(--field); color: var(--text);
  transition: border-color .2s;
}
input:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(20,37,61,.12);
}

/* ── Drop zone ───────────────────────────────────────────────────────────── */
.drop-zone {
  border: 2px dashed var(--border); border-radius: var(--ag-radius-xl);
  padding: var(--s8) var(--s6); text-align: center;
  cursor: pointer; transition: border-color .2s, background .2s;
  display: flex; flex-direction: column; align-items: center; gap: var(--s3);
}
.drop-zone:hover, .drop-zone.drag-over {
  border-color: var(--accent); background: rgba(195,161,75,.06);
}
.drop-zone--done { border-color: var(--success); background: rgba(46,125,91,.05); }
.drop-zone__icon { font-size: 32px; }
.drop-zone__label { font-size: 15px; font-weight: 600; }   /* v0.34.0: 14→15 */
.drop-zone__sub { font-size: 13px; color: var(--text-muted); }   /* v0.34.0: 12→13 */

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: 12px var(--s6); border: none; border-radius: var(--ag-radius-lg);
  font-family: var(--font); font-size: 15px; font-weight: 600; cursor: pointer;   /* v0.34.0 type bump: 14→15 */
  min-height: 44px; transition: transform .15s, box-shadow .15s, opacity .2s;
  text-decoration: none; white-space: nowrap;
}
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn--primary {
  background: var(--grad); color: var(--on-ink, #FAF8F3);
  box-shadow: 0 2px 12px rgba(20,37,61,.18);
}
.btn--primary:not(:disabled):hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 18px rgba(20,37,61,.28);
}
.btn--ghost { background: transparent; border: 1.5px solid var(--border); color: var(--text); }
.btn--ghost:hover { background: var(--bg); }
.btn--danger { background: var(--danger); color: white; }
.btn--success { background: var(--success); color: white; }
/* internal notes P-7: #opt-add ("Add a COSignee") once its own single-add sub-form
   (#invited-signers-block) is open — its job (opening the form) is done, so it no longer looks like
   a still-actionable duplicate of the sub-form's own "+ Add COSignee" submit button (#btn-add-signer).
   Visual-only de-emphasis: no pointer-events change, so it stays clickable/idempotent and never
   affects existing #opt-add click-driven e2e specs. */
.btn.opt-done { opacity: .55; }
.btn.opt-done:hover { transform: none; box-shadow: none; }
/* Gold ceremonial close button */
.btn--seal {
  background: linear-gradient(135deg, var(--gold), var(--brand-gold-dark, #a88435));
  color: #fff;  /* white text on gold bg — intentional */
  box-shadow: 0 2px 12px rgba(195,161,75,.35);
}
.btn--seal:not(:disabled):hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 20px rgba(195,161,75,.5);
}
.btn--sm { padding: 8px var(--s4); font-size: 13px; min-height: 34px; }
.btn-row { display: flex; gap: var(--s3); flex-wrap: wrap; }

/* v0.52.0: QR fingerprint + Preview/Download on ONE row, led by a prominent "download & send" CTA.
   The COSignee must receive the document out-of-band (the server is blind) — so this is a real step. */
.qr-doc-row { display: flex; gap: var(--s4); align-items: flex-start; flex-wrap: wrap; margin-top: var(--s3); }
.qr-doc-row__actions { flex: 1 1 200px; min-width: 180px; }
/* v0.56.1: preview eye + the single "download & send" banner on one row (banner IS the download). */
.doc-send-row { display: flex; gap: var(--s2); align-items: stretch; margin-bottom: var(--s3); }
.doc-preview-icon {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  padding: 0 12px; font-size: 18px; line-height: 1; cursor: pointer; color: var(--text);
  background: transparent; border: 1px solid var(--border); border-radius: var(--ag-radius-md);
}
.doc-preview-icon:hover { background: rgba(195,161,75,.10); border-color: var(--gold); }
.doc-send-cta {
  flex: 1 1 auto; font-size: 15px; font-weight: 700; line-height: 1.4; color: var(--text);
  background: rgba(195,161,75,.12); border: 1px solid var(--gold); cursor: pointer;
  border-radius: var(--ag-radius-md); padding: 10px 12px; text-decoration: none;
  display: flex; align-items: center;
}
.doc-send-cta:hover { background: rgba(195,161,75,.18); }
.setup-grid .doc-send-cta { font-size: 16px; }   /* a notch larger in the side-by-side setup layout */
/* internal notes: the mobile doc card renders TWO send CTAs at once
   (this <a download> + #btn-mobile-send). The general/alpha mobile flow keeps only the share
   sheet + eye-preview; the <a download> stays for SOLO/COAutoCert (index.html:364-368 — that
   leg needs byte-identical bytes on a second device) and for every desktop flow.
   Same [hidden]-vs-author-display trap as .mobile-doc-note .btn above (v0.565.1): an author
   `display` rule beats the UA [hidden]{display:none} rule, so el.hidden=true alone is a silent
   no-op here without this override. */
.doc-send-cta[hidden] { display: none; }

/* ── Badges ──────────────────────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px var(--s3); border-radius: var(--ag-radius-full);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
}
/* v0.62.0: status chips match the C-ring — Signed = gold (no more green), Withdrawn = danger,
   Pending = neutral, Skipped = neutral. Keeps the whole monitor on the brand palette. */
.badge--success { background:rgba(195,161,75,.20); color:#7a5e16; }
.badge--warning { background:#fde8e8; color:#B91C1C; }
.badge--danger  { background:#fde8e8; color:#B91C1C; }
.badge--info    { background:var(--surface-2); color:var(--text-muted); }
.badge--neutral { background:var(--surface-2); color:var(--text-muted); }
[data-theme="dark"] .badge--success { background:#3a2f12; color:#d8b864; }
[data-theme="dark"] .badge--warning { background:#3a1414; color:#f0a3a3; }
[data-theme="dark"] .badge--danger  { background:#3a1414; color:#f0a3a3; }
[data-theme="dark"] .badge--info    { background:#27405e; color:var(--text-muted); }
[data-theme="dark"] .badge--neutral { background:#27405e; color:var(--text-muted); }

/* ── Hash ─────────────────────────────────────────────────────────────────── */
.hash-pill {
  font-family: var(--font-mono); font-size: 12px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--ag-radius-md); padding: var(--s2) var(--s3);
  word-break: break-all; cursor: pointer; transition: color .2s;
}
.hash-pill:hover { color: var(--accent); }

/* ── Roster ──────────────────────────────────────────────────────────────── */
.roster-item {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); border: 1px solid var(--border);
  border-radius: var(--ag-radius-lg); background: var(--bg);
}
.roster-item__avatar {
  width: 36px; height: 36px; border-radius: var(--ag-radius-full);
  background: var(--grad); color: var(--on-ink, #FAF8F3); display: flex; align-items: center;
  justify-content: center; font-weight: 700; font-size: 14px; flex-shrink: 0;
}
.roster-item__name { font-weight: 600; font-size: 15px; }   /* v0.34.0: 14→15 */
/* 2026-08-13: the alpha-prefilled "COSigna" row's avatar is the brand mark, not a letter — a
   light plate so the mark (which assumes a pale background, same as the header lockup) reads
   clearly instead of sitting on the dark avatar gradient meant for a letter's white text. */
.roster-item__avatar--mark { background: var(--surface-2, #F4F0E6); }
.roster-item__avatar--mark img { width: 20px; height: 20px; display: block; }
/* Hover/tap explainer on the "COSigna" row's name — dotted underline signals it's interactive
   (native title covers hover; the tap popover below covers touch, which title does not reach). */
.roster-item__name--tip { cursor: help; text-decoration: underline dotted; text-underline-offset: 3px; }
.roster-founder-tip {
  margin-top: 4px; font-size: 12.5px; color: var(--text-muted); line-height: 1.4;
  max-width: 260px; background: var(--surface-2, #F4F0E6); border: 1px solid var(--border);
  border-radius: var(--ag-radius-md); padding: 6px 10px;
}
.roster-item__channel { font-size: 13px; color: var(--text-muted); }   /* v0.34.0: 12→13 */
.roster-item__remove { margin-left: auto; color: var(--text-muted); cursor: pointer; font-size: 18px; line-height: 1; }
.roster-item__remove:hover { color: var(--danger); }

/* ── Signer Links ────────────────────────────────────────────────────────── */
.signer-links { display: flex; flex-direction: column; gap: var(--s2); font-size: 14px; }   /* v0.34.0: 13→14 */
.signer-links__note { color: var(--text-muted); margin-bottom: var(--s2); line-height: 1.5; font-size: 12px; }
.signer-link-row { display: flex; flex-direction: column; padding: var(--s3); background: var(--bg); border-radius: var(--ag-radius-lg); border: 1px solid var(--border); }
.signer-link-row__name { font-weight: 600; margin-bottom: 4px; }
/* W2-C: display:block moved from inline style → governed CSS class; hidden attr still suppresses until "Show link" is clicked. */
.signer-link-row__url { display: block; margin-top: 6px; font-family: var(--font-mono); font-size: 11px; word-break: break-all; color: var(--accent); text-decoration: none; }
.signer-link-row__url:hover { text-decoration: underline; }
/* internal notes P-11: per-row locality caption, attached to the send-button row itself
   (reinforces the panel-level init.invite.you_send disclosure at the moment of the click). */
.signer-link-row__locality { margin-top: 6px; font-size: 11px; color: var(--text-muted); line-height: 1.35; }
/* v0.93.0 S-UX (#14): the Notarize / self-2nd-device row narrows to JUST the "📱 Scan to open" QR
   (Email/Message/Copy dropped — you ARE the other device, so a QR is the only relevant action). */
.signer-link-row--self { max-width: 340px; }
.signer-link-row--self .signer-link-row__actions { justify-content: flex-start; }

/* ── Message preview ─────────────────────────────────────────────────────── */
.msg-preview {
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--ag-radius-lg); padding: var(--s4);
  font-size: 13px; line-height: 1.6; color: var(--text-muted);
  font-style: italic;
}

/* v0.93.0 S-UX (#8): centre the OPEN consent-capture card horizontally so on a computer the user
   (and the camera) look at the CENTRE of the screen, not the left edge (mirrors the FaceCapture fix).
   The viewfinder already centres via margin:auto; this centres the whole card + caps its width. */
.state-panel--open { align-items: center; }
.state-panel--open__card {
  width: 100%; max-width: 560px; margin-inline: auto; text-align: center;
}
.state-panel--open__card .card__title { justify-content: center; }
.state-panel--open__card .btn-row { justify-content: center; }
/* v0.93.0 S-UX (#9): the OPEN hold-to-seal logo gesture on paper — ink C+Signa, centred. */
#open-seal-mount .cg-logo { --logo-c: #14253D; }
.open-seal-mount { display: flex; justify-content: center; margin-top: var(--s4); }

/* ── Camera ──────────────────────────────────────────────────────────────── */
.viewfinder-wrap {
  position: relative; width: 100%; max-width: 480px; margin: 1rem auto;
  border-radius: var(--ag-radius-xl); overflow: hidden; aspect-ratio: 4/3;
  /* Calm near-black viewfinder so the moment before the feed loads (or if permission is
     denied) reads as a dark camera, never a flash of white/green. */
  background: #0c1726;  /* camera dark bg — intentional; skin literal */
}
/* C (2026-08-15, Fazal Majid finding): this OPEN-capture screen is reachable from BOTH a desktop
   webcam and a mobile front camera (same markup, no separate desktop-only page). Desktop webcams
   are natively landscape — the 3/4 portrait crop forced on every device made desktop framing look
   phone-shaped. §3-review finding: a `@media (pointer: coarse)` selector here would NOT actually
   mirror initiator.js's isMobileDevice() (that function also matches via a UA regex when the
   pointer check is false — e.g. an Android tablet with a mouse attached), letting the requested
   camera stream and this box disagree. startOpenCamera() sets this class from the SAME
   isMobileDevice() call it uses to pick the getUserMedia constraints, so both always agree. */
.viewfinder-wrap--portrait { aspect-ratio: 3/4; max-width: 360px; }
/* v0.595 S-L2b: the closing camera (v0.590.0) never got this rule — its video rendered at native size in the
   overflow:hidden box (960 px in a 329 px viewfinder, spec 134 T8), unmirrored, and Center Stage kept moving the
   face into the hidden part. Both feeds now fill the box and mirror, the same way. */
#open-camera-feed,
#close-camera-feed {
  width: 100%; height: 100%; object-fit: cover; display: block; transform: scaleX(-1);
}
.qr-corner {
  position: absolute; bottom: var(--s3); right: var(--s3);
  width: 72px; height: 72px; background: white;
  border-radius: var(--ag-radius-md); padding: 4px;
  box-shadow: 0 2px 8px rgba(0,0,0,.4);
  pointer-events: none; z-index: 7;   /* v0.604.0: the tap goes to the capture overlay beneath */
}
.qr-corner img { width: 100%; height: 100%; display: block; }
.face-guide-dt {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none;
}
.face-oval {
  /* Delicate gold ring outline (thin, not a solid block) — the framing guide. */
  width: 160px; height: 200px; border: 2px solid var(--gold);
  border-radius: 50%/40%; transition: border-color .3s; opacity: .85;
}
.face-oval--ok { border-color: var(--success); border-style: solid; }
.cam-hint {
  position: absolute; top: var(--s3); left: var(--s3); right: var(--s3); text-align: center;
  font-size: 12px; color: white; font-weight: 600; text-shadow: 0 1px 3px rgba(0,0,0,.8);
}
/* A14 fix: post-capture framing-check prompt, shown alongside the review preview. */
.preview-hint { margin-top: var(--s2); font-size: 12px; color: var(--text-muted); text-align: center; }

/* ── Ring visualization ──────────────────────────────────────────────────── */
/* v0.2.3 S-uxrevamp: circle on the LEFT, legend stacked vertically on the RIGHT
   (row layout); wraps back to a stack on narrow widths so nothing clips. */
/* v0.99.1 (founder bug): the abort confirmation is a CENTERED fixed modal (was inserted into the topbar
   and overflowed the top-right on mobile). Pinned just under the sticky topbar; width-capped to the viewport. */
.abort-confirm {
  position: fixed; top: calc(var(--topbar-h, 56px) + 12px); left: 50%; transform: translateX(-50%);
  z-index: 200; width: min(360px, 92vw); box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; gap: var(--s2);
  padding: var(--s4); background: var(--surface); border: 1.5px solid var(--danger, #c0392b);
  border-radius: var(--ag-radius-lg); box-shadow: 0 14px 44px rgba(0,0,0,.30);
}
.abort-confirm__msg { font-weight: 700; font-size: 14px; text-align: center; line-height: 1.45; color: var(--text); }
.abort-confirm__cancel { margin-top: 2px; }

/* v0.98.0 (founder): explicit monitor wayfinding — "Waiting for your COSignees to sign…" while collecting. */
/* v0.553 X-5 (internal notes, A2): "Waiting for your COSignees to
   sign…" is literally the in-progress indicator the ruling names — a state of progress, not a
   closure. Tint / border / pulsing dot move to ink, with the same custom-property inversion for
   the dark register as A1 above. */
.monitor-wait {
  display: flex; align-items: center; gap: 10px; justify-content: center;
  font-size: 13.5px; font-weight: 600; color: var(--text); line-height: 1.45;
  --wait-tint: rgba(20,37,61,.06);
  --wait-line: var(--brand-ink);
  background: var(--wait-tint); border: 1px solid var(--wait-line);
  border-radius: var(--ag-radius-md); padding: 10px 14px; margin: 0 0 var(--s4); text-align: left;
}
.monitor-wait__pulse {
  flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%; background: var(--wait-line);
  animation: waitPulse 1.6s ease-in-out infinite;
}
/* A4 fix: .monitor-wait's own `display:flex` is an AUTHOR rule and defeats the `hidden` attribute
   (author CSS beats the UA `[hidden]{display:none}`), same bug class as .signer-qr above — so
   `wait.hidden = true` (initiator.js) never actually hid it, and it stayed visible simultaneously
   with the ready-to-close state that replaces it. Honor `hidden` explicitly. */
.monitor-wait[hidden] { display: none; }
[data-theme="dark"] .monitor-wait {
  --wait-tint: rgba(250,248,243,.10);
  --wait-line: var(--text);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .monitor-wait {
    --wait-tint: rgba(250,248,243,.10);
    --wait-line: var(--text);
  }
}
@keyframes waitPulse { 0%,100% { opacity: .35; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.1); } }
@media (prefers-reduced-motion: reduce) { .monitor-wait__pulse { animation: none; } }

.ring-wrap { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--s6); }
.ring-svg { max-width: 240px; width: 100%; flex: 0 1 240px; }
/* P1-B4/B7 (internal notes): mouth doc-icon's HTML filename caption. .ring-wrap's
   existing flex-wrap already puts this under the ring below ~480px effective width — no extra
   breakpoint rule needed. */
.ring-doc-caption { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; color: var(--text-muted); display: inline-block; cursor: pointer; }
.ring-doc-caption.expanded { white-space: normal; max-width: 100%; word-break: break-word; }
/* v0.573 S-ring-visual (WP-F, PLAN §R2.7): Signed/Pending on one line. `column` was the whole
   defect. F2 (recommended, not the F1 minimal): make the intent explicit — a full-width row BELOW
   the ring, deterministic in EN/FR/DE (the arithmetic shows a side-by-side row can't fit the 133px
   left over beside a 240px ring in a 397px card, worst case DE "Signiert / Ausstehend" ~168px). */
.ring-legend {
  display: flex; flex-direction: row; flex-wrap: nowrap; gap: var(--s2);
  justify-content: center; align-items: center; flex-basis: 100%; font-size: 13px;
}
.ring-legend-item { display: flex; align-items: center; gap: 6px; }
/* A8 fix (zero-count legend entry): #ring-legend-pending is hidden once nobody is left PENDING —
   an author `display` rule always beats the UA `[hidden]{display:none}` (same class as .signer-qr
   / .monitor-wait above), so this override is required for the JS toggle in renderMonitor to work. */
.ring-legend-item[hidden] { display: none; }
/* v0.573 S-ring-visual (WP-D1, PLAN §R2.6): click-to-view affordance caption. */
.ring-click-hint { display: block; text-align: center; font-size: 12px; color: var(--text-muted); margin-top: var(--s1); }
/* v0.595 S-W6-50: the monitor points at the COSignee list; the close screen (no list) keeps the face hint. */
.ring-click-hint--face { display: none; }
[data-state="close"] .ring-click-hint--face { display: block; }
[data-state="close"] .ring-click-hint--list { display: none; }
/* v0.595 S-W6-50: the whole COSignee row opens that COSignee's chip on the ring (initiator.js
   _focusRingFaceFor); the name is a real <button> for keyboard users, styled as the plain name. */
#participant-table tr[data-signer-id] { cursor: pointer; }
.ptable-name { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
.ptable-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* v0.595 S-W6-9a: a frozen links panel stays visible (no concealment), visibly inert, with its reason. */
.signer-links--frozen .signer-link-row,
.signer-links--frozen .alpha-handback { opacity: .55; }
/* v0.595 S-L-31b (CRUMBLES row "…self-cosign nudge still shows on an ABORTED ceremony"): the "Just testing?" nudge asks
   the organizer to sign through a link — on a terminal (frozen) panel nobody can, so it is not shown at all. */
.signer-links--frozen .selfcosign-note { display: none; }
.signer-links__frozen { font-weight: 600; }
.ring-legend-dot {
  width: 12px; height: 12px; border-radius: var(--ag-radius-full);
}

/* ── Photo strip ─────────────────────────────────────────────────────────── */
.photo-strip {
  display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center;
}
.photo-strip__item {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.photo-strip__face {
  width: 56px; height: 56px; border-radius: var(--ag-radius-full);
  object-fit: cover; border: 2.5px solid var(--accent);
  background: var(--bg);
}
.photo-strip__face--placeholder {
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; border-color: var(--border);
}
/* A8 fix (over-truncated names): 60px was only 4px wider than the 56px face circle above it, so
   most real names ellipsized almost immediately. .photo-strip wraps (flex-wrap:wrap) and items
   size to content, so a wider name simply claims more of its own row without breaking layout. */
.photo-strip__name { font-size: 11px; color: var(--text-muted); max-width: 92px; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* v0.99.0 (founder): tap a consent-photo pill to enlarge it (esp. on mobile); tap again to shrink. */
.photo-strip__face { cursor: zoom-in; transition: transform .18s ease, box-shadow .18s ease; }
.photo-strip__face.enlarged {
  transform: scale(2.8); position: relative; z-index: 30; cursor: zoom-out;
  box-shadow: 0 10px 30px rgba(20,37,61,.35); border-color: var(--gold);
}
@media (prefers-reduced-motion: reduce) { .photo-strip__face { transition: none; } }

/* v0.402.0 S-signpad: per-COSignee self-declared-signature cell in the monitor participant table.
   Honest label always; thumbnail swaps in when CK decrypts the sealed commitment. Never identity. */
.ptable-signpad { display: inline-flex; align-items: center; gap: 6px; }
.ptable-signpad__label { font-size: 11px; color: var(--text-muted); white-space: nowrap; }
.ptable-signpad__img {
  height: 28px; max-width: 110px; object-fit: contain;
  background: #fff; border: 1px solid var(--border, #ddd); border-radius: 4px; padding: 2px;
  cursor: zoom-in;
}

/* v0.99.0 (founder): mobile-only "send this document to your other device" note (2-device seal). */
.mobile-doc-note {
  margin-top: var(--s4); padding: var(--s3) var(--s4);
  background: rgba(195,161,75,.10); border: 1px solid var(--gold); border-radius: var(--ag-radius-lg);
}
.mobile-doc-note p { margin: 0 0 var(--s3); font-size: 13px; line-height: 1.5; color: var(--text); }
.mobile-doc-note .btn { width: 100%; }
/* v0.565.1 S-ALPHA-MOBILE-1: .btn sets `display:inline-flex`, and an author `display` rule always
   beats the UA `[hidden]{display:none}` — so setting .hidden on this button did NOTHING. Exactly the
   bug class already fixed for .signer-qr, .monitor-wait and .ring-legend-item above; same remedy.
   Load-bearing for the alpha branch of updateMobileDocNote(), which hides this CTA. */
.mobile-doc-note .btn[hidden] { display: none; }
.photo-strip__arrow { font-size: 16px; color: var(--text-muted); margin-bottom: 16px; }

/* ── Audit log ───────────────────────────────────────────────────────────── */
.audit-log { display: flex; flex-direction: column; gap: var(--s2); max-height: 220px; overflow-y: auto; }
.audit-entry { display: flex; gap: var(--s3); align-items: flex-start; font-size: 13.5px; }   /* v0.34.0: 13→13.5 */
.audit-entry__time { color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; white-space: nowrap; }
.audit-entry__action { font-weight: 600; }
.audit-entry__detail { color: var(--text-muted); }

/* ── Dispatch preview ────────────────────────────────────────────────────── */
.dispatch-preview-table { width: 100%; font-size: 13px; border-collapse: collapse; }
.dispatch-preview-table td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--border); vertical-align: top; }
.dispatch-preview-table td:first-child { font-weight: 600; white-space: nowrap; width: 1%; }

/* ── Confirm affirm ──────────────────────────────────────────────────────── */
.affirm-box {
  border: 1.5px solid var(--border); border-radius: var(--ag-radius-lg);
  padding: var(--s4); display: flex; gap: var(--s3); align-items: flex-start; cursor: pointer;
}
.affirm-box:has(input:checked) { border-color: var(--success); background: rgba(46,125,91,.05); }
.affirm-box input { width: 20px; height: 20px; margin-top: 2px; flex-shrink: 0; accent-color: var(--success); }
.affirm-box label { font-size: 14px; line-height: 1.5; cursor: pointer; }

/* ── Close predecessor panel ─────────────────────────────────────────────── */
.close-pred { display: flex; gap: var(--s4); align-items: center; padding: var(--s4); background: var(--bg); border-radius: var(--ag-radius-xl); }
.close-pred__face {
  width: 80px; height: 80px; border-radius: var(--ag-radius-full);
  object-fit: cover; border: 3px solid var(--gold); flex-shrink: 0; background: var(--bg);
}
.close-pred__info { flex: 1; }
.close-pred__name { font-size: 18px; font-weight: 700; }
.close-pred__role { font-size: 13px; color: var(--text-muted); }

/* ── Done screen ──────────────────────────────────────────────────────────── */
/* v0.93.0 S-UX (#13): on wide desktop the done screen (and its #done-onboard alpha-invite panel) read
   awkwardly edge-to-edge. Centre the whole screen, cap its width, and lay action buttons in a tidy
   centred column so it reads cleanly. */
.state-panel--done { align-items: center; }
.done-screen {
  text-align: center; padding: var(--s12) var(--s8); animation: agFadeIn .4s ease;
  width: 100%; max-width: 560px; margin-inline: auto;
  display: flex; flex-direction: column; align-items: center;
}
.done-screen > a.btn, .done-screen > button.btn { align-self: stretch; }
/* #done-onboard alpha-invite panel: constrain + centre so it no longer sprawls on wide screens. */
#done-onboard {
  width: 100%; max-width: 520px; margin-inline: auto; box-sizing: border-box;
}
#done-onboard .btn { width: 100%; }
.done-screen__icon { font-size: 80px; }
/* Closed ring "cryptographic lock" — soft gold glow signifying the sealed loop. */
.done-screen__seal {
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: var(--s2);
  filter: drop-shadow(0 0 14px rgba(195,161,75,.55)) drop-shadow(0 0 4px rgba(15,118,110,.45));
  animation: sealGlow 2.4s ease-in-out infinite;
}
@keyframes sealGlow {
  0%, 100% { filter: drop-shadow(0 0 10px rgba(195,161,75,.40)) drop-shadow(0 0 4px rgba(15,118,110,.35)); }
  50%      { filter: drop-shadow(0 0 18px rgba(195,161,75,.65)) drop-shadow(0 0 6px rgba(15,118,110,.50)); }
}
@media (prefers-reduced-motion: reduce) { .done-screen__seal { animation: none; } }
/* v0.96.0 S-DESIGN: the SEALED lockup as the DONE-screen hero mark — ink C+wordmark (currentColor),
   gold o/S/padlock; responsive width so the padlock stays legible without overflowing on a phone. */
.done-screen__seal .sealed-lockup { width: min(260px, 78vw); height: auto; color: var(--text); }
.done-screen__title { font-size: 28px; font-weight: 800; margin: var(--s4) 0 var(--s3); }
.done-screen__body { font-size: 15px; color: var(--text-muted); line-height: 1.6; max-width: 480px; margin: 0 auto var(--s6); }
/* v0.595 S-W4-76: the anchor-bound lead (S2) — the dot uses the PROTECTED anchor tokens (tokens.css, read-only here):
   complete = --anchor-complete, pending = --anchor-pending, failed/unknown = --verdict-pending (never ok-teal);
   no dot when the ceremony is not anchored at all (nothing to colour). */
.done-lead {
  display: flex; gap: 10px; align-items: flex-start; text-align: left;
  width: 100%; max-width: 480px; margin: 0 auto var(--s4);
  font-size: 15px; line-height: 1.6; color: var(--text-muted);
}
.done-lead__dot { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%; margin-top: .5em; background: var(--anchor-pending); }
.done-lead[data-anchor-state="complete"] .done-lead__dot { background: var(--anchor-complete); }
.done-lead[data-anchor-state="failed"] .done-lead__dot,
.done-lead[data-anchor-state="unknown"] .done-lead__dot { background: var(--verdict-pending); }
.done-lead[data-anchor-state="none"] .done-lead__dot { display: none; }
/* v0.595 S-W4-r2: the S4 SAVE and S5 SHARE slots. S5's two secondaries sit side by side and wrap under each other
   when a language's labels do not fit (every .btn keeps white-space:nowrap). */
.done-slot { display: flex; flex-direction: column; align-items: stretch; gap: var(--s2); width: 100%; max-width: 480px; margin: 0 auto var(--s4); }
.done-slot--row { flex-direction: row; flex-wrap: wrap; }
.done-slot--row > .btn { flex: 1 1 0; }
.done-slot > .btn { margin: 0; }
/* v0.595 S-W4-72: the S4 save button may wrap its label — scoped to this slot, the global .btn nowrap is untouched.
   Measured at 390 px (spec 180): the shortened labels fit on one line, except the .zip variant (Safari/Firefox, i.e.
   every iPhone) in FR (+13 px) and DE (+9 px); a wrap keeps it inside the button in every language and font. */
#done-save > .btn { white-space: normal; text-wrap: balance; }
/* v0.595 S-W4-32: "Prepare an email — attach the saved folder yourself" is a text link (Claude Design), not a button. */
.done-mail-link { display: inline-flex; align-items: center; justify-content: center; gap: 6px; margin-top: 0; font-size: 13px; line-height: 1.4; }
/* v0.595 S-W4-r2: S6 RECORD keeps the body's full width (it no longer holds the long lead that used to stretch it —
   in the centred flex column it would shrink-wrap and squeeze the label/value rows together). */
.done-record { width: 100%; }
/* v0.595 S-W4-r2: the S6 signing-order line (document name · "Host → Guest"). */
.done-record__order { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 13px; }
.done-record__order #done-ring-legend { text-align: right; }

/* ── Enrollment nudge (post-close viral CTA) ─────────────────────────────── */
.enrollment-nudge {
  margin-top: var(--s8);
  border-top: 1px solid var(--border);
  padding-top: var(--s8);
  text-align: center;
  max-width: 480px;
  margin-left: auto;
  margin-right: auto;
}
.enrollment-nudge__heading {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: var(--s3);
}
.enrollment-nudge__body {
  font-size: 14px;
  color: var(--text-muted);
  line-height: 1.7;
  margin-bottom: var(--s5);
}
.enrollment-nudge__actions {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
  justify-content: center;
}

/* ── Spinner ─────────────────────────────────────────────────────────────── */
.spinner { width: 36px; height: 36px; border: 3px solid var(--border); border-top-color: var(--ink); border-radius: var(--ag-radius-full); animation: agSpin .8s linear infinite; margin: 0 auto; }

/* ── Animations ──────────────────────────────────────────────────────────── */
@keyframes agFadeIn { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }
@keyframes agSpin   { to { transform:rotate(360deg); } }

/* v0.1.4 — Center Stage guidance note + live version chip */
.camera-help {
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
  margin-top: var(--s3);
  line-height: 1.45;
}
.camera-help strong { color: var(--text); }
.app-version {
  position: fixed;
  right: 8px;
  bottom: 6px;
  font-size: 11px;
  font-family: var(--font-mono);
  color: var(--text-muted);
  opacity: .65;
  pointer-events: none;
  z-index: 5;
}

/* v0.89.0 Test-Mode: "try a COSigna sample" prompt under the drop-zone (1-page / 350-page).
   Hidden once a document is loaded (#pdf-drop gains .drop-zone--done). */
.sample-try{ margin-top:var(--s2); display:flex; flex-direction:column; gap:6px; }
.sample-try__head{ font-size:12.5px; color:var(--text-muted); line-height:1.4; }
.sample-try__btns{ display:flex; gap:8px; flex-wrap:wrap; }
.sample-try__btns .btn{ text-decoration:none; }
#pdf-drop.drop-zone--done ~ .sample-try{ display:none; }

/* v0.90.0 Test-Mode: cross-device self-cosign nudge atop the COSignee links panel. */
.selfcosign-note{ background:var(--surface-2); border:1px solid var(--accent); border-radius:var(--ag-radius-lg);
  padding:var(--s3); margin-bottom:var(--s3); font-size:13px; line-height:1.45; color:var(--text); }
.selfcosign-note strong{ color:var(--accent); }

/* WP-A (2026-08-16, Fazal Majid Finding A / R4+R5): the hand-back moment — promoted above the
   selfcosign note (rendered BEFORE it in the panel) since it is the more urgent action in alpha. */
.alpha-handback{ background:var(--surface-2); border:1px solid var(--brand-gold,#C3A14B); border-radius:var(--ag-radius-lg);
  padding:var(--s3); margin-bottom:var(--s3); font-size:13px; line-height:1.45; color:var(--text); }
.alpha-handback__title{ display:block; font-size:15px; color:var(--brand-gold,#C3A14B); margin-bottom:4px; }
.alpha-handback__lead{ margin:4px 0; }
.alpha-handback__how{ margin:4px 0 var(--s3); color:var(--text-muted); font-size:12px; }
#alpha-handback-cta{ margin-top:4px; }

/* v0.91.0: scan-to-open QR under a COSignee/notarize link (built client-side; preserves zero-trust). */
.signer-qr{ margin-top:8px; padding:8px; background:#fff; border:1px solid var(--border); border-radius:var(--ag-radius-md); display:inline-block; }
/* The `display:inline-block` above is an AUTHOR rule and defeats the `hidden` attribute (author CSS beats
   the UA `[hidden]{display:none}`), so the per-COSignee QR box was ALWAYS visible as an empty white
   rectangle and cosignaShowQR()'s `box.hidden = true` toggle could not hide it. Honor `hidden` explicitly.
   (signer-qr--auto on the self/notarize row carries no `hidden` attr + an inline display:block → unaffected.) */
.signer-qr[hidden]{ display:none; }
.signer-qr svg{ display:block; }
.signer-qr__cap{ margin-top:6px; font-size:11px; color:var(--text-muted); max-width:180px; line-height:1.35; }
/* v0.304.0 D: the auto-render self/notarize QR is click-to-enlarge. */
.signer-qr--auto{ cursor:zoom-in; }
/* v0.304.0 D: big-by-default QR overlay — centered, NON-fullscreen, z-index 150 (below the abort modal z-200,
   above the topbar z-10). Tap anywhere (card or backdrop) to shrink. SVG scales losslessly to ~72vh. */
/* Positioned BELOW the sticky topbar (var(--topbar-h)) so the Abort control in the header stays visible
   and clickable while the QR is enlarged (§3: the abort must never be occluded). Auto-collapses on seal. */
.self-qr-overlay{ position:fixed; top:var(--topbar-h,56px); left:0; right:0; bottom:0; z-index:150;
  display:flex; align-items:center; justify-content:center; background:rgba(20,37,61,.45); padding:var(--s3,16px); cursor:zoom-out; }
.self-qr-overlay[hidden]{ display:none; }
.self-qr-overlay--invite{ background:#fff; }   /* v0.604.1 8b: the invite overlay is fully opaque (the self-sign one keeps its translucent backdrop) */
.self-qr-overlay__card{ background:#fff; border-radius:var(--ag-radius-lg,16px); padding:var(--s4,24px);
  box-sizing:border-box; max-width:92vw; text-align:center; box-shadow:0 12px 48px rgba(0,0,0,.30); cursor:zoom-out; }
/* v0.402.0 S-qr (founder, mobile): the QR + caption must FIT the card content box, not a bare viewport
   width. Before, height:min(72vh,86vw) + width:auto made the SVG ~86vw wide while the card content box
   is only (92vw − padding), so the SVG OVERFLOWED the card → both the card and the QR read left-aligned
   on a portrait phone. max-width:100% caps the SVG to the card → margin:0 auto centers it → the card
   stays ≤92vw and the flex overlay centers the card; margin:auto centres the caution/cap blocks too. */
.self-qr-overlay__qr{ text-align:center; }
.self-qr-overlay__qr svg{ width:auto; height:auto; max-width:100%; max-height:min(72vh,80vw); display:block; margin:0 auto; }
.self-qr-overlay__who{ font-size:17px; font-weight:600; color:var(--text); margin:0 auto 2px; max-width:min(72vh,86vw); overflow-wrap:anywhere; }   /* v0.604.1 8b */
.self-qr-overlay__ident{ font-size:13px; color:var(--text-muted); margin:0 auto 10px; max-width:min(72vh,86vw); overflow-wrap:anywhere; }
.self-qr-overlay__who[hidden], .self-qr-overlay__ident[hidden]{ display:none; }
.self-qr-overlay__caution{ font-size:13px; color:var(--brand-gold,#C3A14B); margin:0 auto 10px; max-width:min(72vh,86vw); line-height:1.4; }
.self-qr-overlay__cap{ font-size:13px; color:var(--text-muted); margin:10px auto 0; max-width:min(72vh,86vw); line-height:1.4; }
@media (prefers-reduced-motion: no-preference){ .self-qr-overlay__card{ animation:self-qr-pop .18s ease-out; } }
@keyframes self-qr-pop{ from{ transform:scale(.86); opacity:0; } to{ transform:scale(1); opacity:1; } }

/* v0.545.1 S-a11y-T1 (2026-08-03): WCAG 2.4.7 focus indicator for the boxes/radios on /initiate.
   Supersedes the v0.535.0 single-selector rule that lived here (`#allow-attachments-check:focus`).

   THE REAL DEFECT: the global `input:focus{outline:none}` (l.375) strips the native ring from EVERY
   input on this page. Its compensation — `border-color: var(--accent)` + `box-shadow: 0 0 0 3px
   rgba(20,37,61,.12)` — is a 12%-alpha navy shadow: measurable in the DOM, but nowhere near the 3:1
   project bar. So a keyboard user tabbing through the option checkboxes/radios got a cue that is
   effectively invisible. `outline: 2px solid var(--text)` clears 3:1 on both the light and the dark
   card by construction (--text IS the body-copy colour on that surface), and a solid outline (not a
   box-shadow) also survives forced-colors mode.

   MEASURED, 2026-08-03 — the v0.535.0 comment this replaces asserted two things the browser
   contradicts, so both are corrected here rather than carried forward (workspace CLAUDE.md,
   documented-invariant discipline):
     1. It claimed a native checkbox "shows neither" border nor box-shadow, "leaving zero measurable
        focus delta". FALSE: with the drawer OPEN, every non-disabled checkbox on /initiate reports
        `box-shadow: rgba(20,37,61,0.12) 0px 0px 0px 3px` on focus — a real delta. The gap is the
        indicator's CONTRAST, not its absence.
     2. It credited the rule with fixing the spec-77 finding. It cannot. All of these controls live
        inside a COLLAPSED `<details class="tier-drawer">`, and Chromium refuses focus to collapsed-
        <details> content: `element.focus()` is a no-op, `document.activeElement` stays `<body>`, so
        NO `:focus` rule of any kind can match in the state the guard audits. That is why the finding
        is ratcheted (D-25, 77-accessibility-dom-guard.spec.js) and not "fixed" by this CSS.
   In other words: this rule is the fix for REAL USERS (drawer open, focus lands, cue is visible);
   the ratchet entry is the honest record of the harness artifact. Neither substitutes for the other.

   NB: `:focus`, not `:focus-visible` — kept from v0.535.0. A programmatic element.focus() does not
   reliably match :focus-visible (browser modality heuristic), so `:focus` is what an automated
   audit can observe deterministically. */
input[type="checkbox"]:focus,
input[type="radio"]:focus {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* Survey item 22 D1 (founder ruling 2026-09-02, Option A): same WCAG 2.4.7 focus-indicator gap as
   S-a11y-T1 just above — these three controls (renderAlphaSignedOutNotice, initiator.js) carry only
   inline text-decoration:underline styling, no outline, so the browser default focus ring is what
   the spec-77 guard measured at 2.15:1 against the gold #alpha-status-notice banner in dark mode —
   under the project's 3:1 bar. Same fix, same reasoning: outline:2px solid var(--text) clears 3:1 in
   both light and dark by construction (--text IS the body-copy colour on that banner). `:focus`, not
   `:focus-visible`, for the same reason as above — the automated audit's element.focus() must match. */
#alpha-signedout-signin:focus,
#alpha-signedout-waitlist:focus,
#alpha-signedout-skip:focus {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* v0.604.0 (founder, 2026-09-30): the capture button IS the viewfinder. A full-size overlay button inside .viewfinder-wrap (same id and handlers as
   before) saying "Tap the picture to take it" (touch) or "Click the picture to take it" (mouse); the decorations (QR, face guide, hints) never
   steal the tap. A brief white flash confirms a capture (UX/libs/tap-capture.js). */
.cg-tap{ position:absolute; inset:0; z-index:5; display:flex; align-items:flex-end; justify-content:center; padding:0 12px 14px; box-sizing:border-box;
  background:transparent; border:0; border-radius:inherit; color:#fff; font:800 15px/1.25 var(--font,inherit); text-align:center;
  text-shadow:0 1px 4px rgba(0,0,0,.85); cursor:pointer; -webkit-tap-highlight-color:transparent; }
.cg-tap:disabled{ cursor:default; color:transparent; text-shadow:none; }
.cg-tap:focus-visible{ outline:3px solid var(--gold,#C3A14B); outline-offset:-3px; }
/* the instruction sits LEFT of the QR that shares the viewfinder's bottom edge (Codex review, P2) */
.cg-tap--qr{ padding-right:96px; }
.cg-tap__touch{ display:none; } .cg-tap__mouse{ display:inline; }
@media (pointer:coarse){ .cg-tap__touch{ display:inline; } .cg-tap__mouse{ display:none; } }
.cg-flash::after{ content:""; position:absolute; inset:0; z-index:6; background:#fff; opacity:.75; pointer-events:none; animation:cgflash .2s ease-out forwards; }
@keyframes cgflash{ to{ opacity:0; } }
@media (prefers-reduced-motion:reduce){ .cg-flash::after{ animation:none; opacity:0; } }
