/* ============================================================
   RaisePortal — app.css
   One stylesheet for marketing, app, and investor room.
   No build step. Drop into /public/css/app.css.
   ============================================================ */

/* ---------- 1. Tokens ---------- */
/* Brand palette (public/media-kit/raiseportal-brand-guide.pdf, "Colour" page,
   v1.0): Evergreen #085041, Teal #1D9E75, Mint #5DCAA5 — "Evergreen carries
   the sober half of the product... Teal is the thing moving upward, and it
   is the only colour permitted to draw the eye." Neutrals: Ink #16211E
   ("a green-cast near-black, not pure black... never use #000000"), Slate
   #687A76 (secondary text/captions), Fog #F0F2F0 (cards, wells, dividers).
   Mapped onto the tokens already used everywhere below, not a parallel
   system: --indigo/-700/-50 (the site's one general-purpose accent, used for
   links, primary buttons and focus rings) becomes the teal/evergreen family;
   --ink/--ink-2/--muted/--paper/--paper-2/--line* become the ink/slate/fog
   neutral family; --bar-bg (the topbar, always dark regardless of light or
   dark mode) becomes Evergreen itself, exactly as the guide names it for
   "headers." --moss keeps its own semantic job (positive deltas, funded
   segments, done-steps) but is pulled from the same three-colour family
   rather than left as an unrelated green, per the guide's own "don't
   introduce a second accent hue" rule. --amber/--rust are deliberately left
   alone: the guide has no opinion on warning/danger status colours, and losing
   the near-universal red-for-error convention would hurt usability more than
   strict hue purity is worth. --indigo itself is not the raw brand Teal
   (#1D9E75) — Teal alone is only ~3.4:1 against white, short of the AA body
   text this file aims for (see below) — it's #0F6E56, the darker teal the
   real shipped wordmark (raiseportal-logo.svg) already uses for the word
   "Portal", which measures ~6.2:1 against white. */
:root {
  /* Ledger palette, brand-mapped */
  --ink:        #16211E; /* Ink — brand exact */
  --ink-2:      #37453F; /* derived: between Ink and Slate, for headings/labels one step lighter than body text */
  /* Slate #687A76 is the brand guide's exact value, but measured on real
     13px/400 helper text (e.g. the "Founder access…" line on /login) it is
     only 4.03:1 against --paper (#F0F2F0) and 3.68:1 against --paper-2
     (#E4E9E4) — short of the 4.5:1 AA floor for normal-size text (verifier
     round 4, 2026-08-03). #576663 is Slate darkened in place (same hue
     166.7° / same saturation 8% — only lightness moved, 44.3% → 37%) so it
     stays the brand's secondary colour, just legible: 5.35:1 on --paper,
     6.02:1 on --surface, 4.90:1 on --paper-2 — all real backgrounds this
     token actually sits on in the rendered UI. Dark mode's --muted
     (#8FA79E, below) already measured 7.40:1 and is untouched. */
  --muted:      #576663;
  --paper:      #F0F2F0; /* Fog — brand exact */
  --paper-2:    #E4E9E4; /* derived: a deeper Fog for the page's second surface tier */
  --surface:    #FFFFFF;
  --line:       #D7DED9; /* derived: light, hue-matched border on the ink/slate/fog family */
  --line-soft:  #E6EAE7; /* derived */

  --indigo:     #0F6E56; /* derived dark teal (see note above) — links, buttons, focus rings */
  --indigo-700: #085041; /* Evergreen — brand exact, hover/pressed state */
  --indigo-50:  #E1F1EA; /* derived light teal tint */
  --amber:      #E0A21C; /* unchanged — warning status, outside the brand's 3-colour accent system */
  --amber-50:   #FDF3DE;
  /* --amber itself is only ~2:1 on --paper/--surface — fine for a swatch
     (a badge/dot background, a bar segment) but never meant to carry text.
     A few templates use it as text anyway (views/app/feedback.ejs "Thinking"
     count, views/app/status.ejs "Soft-circled" figure, views/app/activity.ejs
     live-visitor avatar initials) to keep the same "amber = pending/soft/
     live" status language as the badges next to them. Rather than darken
     --amber itself (which would blunt its actual job — the live pulse dot,
     the soft-pledge bar segment), this names the darker brown this
     stylesheet already hand-picked for exactly that pairing elsewhere
     (.badge-live/.filetype.ppt/.feed-ico.enter's light-mode text, 5.0:1+ on
     their own amber-50 chip and 4.9–5.5:1 on plain --paper/--surface) so the
     three templates above can reference a real token instead of a second
     hand-picked hex each. */
  --amber-ink:  #8A6106;
  --moss:       #147A5C; /* derived: teal family, distinct from --indigo, ~5.3:1 on white (AA) */
  --moss-50:    #E2F2EC; /* derived */
  --rust:       #B4453A; /* unchanged — danger status, outside the brand's 3-colour accent system */
  --rust-50:    #FBEBE9;

  /* --accent-ink — the text colour to pair with --indigo/--moss when either is
     used as a *solid fill* (.avatar, .btn-primary, .step-num, the pressed
     "Looks great" pill), as opposed to their far more common job as text/
     link/ring colour on a plain surface. On a light paper #fff is correct and
     is what these four call sites hardcoded directly until this token
     replaced them (light mode: indigo #0F6E56/white 6.2:1, moss #147A5C/
     white 5.29:1 — both already AA). It only needs overriding somewhere a
     paper is dark enough that the same accent hue, picked to still read
     clearly *against* that dark paper as text/a ring, is by construction too
     light to also carry white text stacked *on top of* it — currently that
     is `.rp-chrome` dark mode (indigo #1D9E75/white measured 3.39:1, moss
     #3DB48D/white 2.59:1 — both fail AA) and the "midnight" room template
     (indigo #C2793F/white 3.44:1, moss #4C9A76/white 2.96:1 — same failure,
     public/css/themes/midnight.css). One token, overridden in exactly those
     two dark-paper places (both to their own `--paper`, already the
     darkest colour available in that context and never pure black per the
     brand rule) rather than four separate hand-picked hexes, so a future
     dark template only has to redeclare this once to stay correct — the
     same lesson the `.pill[aria-pressed="true"]` fix (§11) already forced. */
  --accent-ink: #fff;

  --bar-bg:     #085041; /* Evergreen — brand exact ("primary. fields, headers, app icon") */
  --bar-fg:     #FFFFFF;
  --surface-2:  #FBFCFE;
  --hover:      #FAFBFE;

  --r-sm: 6px;
  --r:    10px;
  --r-lg: 16px;

  --sh-1: 0 1px 2px rgba(19,26,43,.06), 0 1px 3px rgba(19,26,43,.04);
  --sh-2: 0 4px 12px rgba(19,26,43,.08), 0 1px 3px rgba(19,26,43,.05);
  --sh-3: 0 18px 44px rgba(19,26,43,.16);

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --display: "Bricolage Grotesque", "Inter", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --rail: 244px;
  --topbar: 60px;
  --maxw: 1240px;
}

/* ---------- 2. Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}
img, svg { max-width: 100%; display: block; }
a { color: var(--indigo); text-decoration: none; }
a:hover { text-decoration: underline; }
button, input, select, textarea { font: inherit; color: inherit; }
/* A <button type="submit"> inside a form that needs to read as an inline
   `a` — e.g. views/auth/check-email.ejs's "Send it again", which used to be
   a plain <a href> GET link and had to become a CSRF-protected POST form
   (a GET that sends mail is a CSRF vector) without looking like a form was
   dropped into the middle of a sentence. Mirrors the `a`/`a:hover` rule
   above exactly, with the browser's own button chrome reset to nothing. */
.btn-linklike {
  display: inline; background: none; border: none; padding: 0; margin: 0;
  font: inherit; color: var(--indigo); text-decoration: none; cursor: pointer;
}
.btn-linklike:hover { text-decoration: underline; }
.btn-linklike:focus-visible { outline: 2px solid var(--indigo); outline-offset: 2px; border-radius: 2px; }
h1, h2, h3, h4 { font-family: var(--display); font-weight: 600; letter-spacing: -.02em; margin: 0 0 .4em; line-height: 1.15; }
h1 { font-size: 30px; }
h2 { font-size: 22px; }
h3 { font-size: 17px; letter-spacing: -.01em; }
p  { margin: 0 0 1em; }
:focus-visible { outline: 2px solid var(--indigo); outline-offset: 2px; border-radius: 4px; }
::selection { background: var(--amber-50); }

/* ---------- 3. Utility ---------- */
.u-mono { font-family: var(--mono); }
.u-muted { color: var(--muted); }
.u-small { font-size: 13px; }
.u-tiny { font-size: 12px; }
.u-right { text-align: right; }
.u-center { text-align: center; }
.u-nowrap { white-space: nowrap; }
.u-hide { display: none !important; }
.u-row { display: flex; align-items: center; gap: 10px; }
.u-row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.u-wrap { flex-wrap: wrap; }
.u-grow { flex: 1 1 auto; min-width: 0; }
.u-stack > * + * { margin-top: 14px; }
.u-mt-0 { margin-top: 0; }
.u-mb-0 { margin-bottom: 0; }
.u-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.eyebrow {
  font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}

/* ---------- 4. App shell ---------- */
.shell { min-height: 100vh; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 40;
  height: var(--topbar);
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px;
  background: var(--bar-bg);
  color: var(--bar-fg);
}
/* `:where(.topbar)` rather than plain `.topbar` — this rule's whole job is
   to override the global `a { color: var(--indigo) }` default (§2) for
   ordinary links sitting inside the bar, not to out-rank a component's own
   opinion about its own colour. Plain `.topbar a` is (0,1,1), which beats
   any single-class component rule (.avatar is (0,1,0)) whenever that
   component happens to render as an `<a>` — exactly what silently reverted
   `.avatar`'s --accent-ink fix (§1) for the account avatar in
   partials/topbar.ejs, since every *other* avatar in the app is a `<span>`
   and never hit this. `:where()` contributes zero specificity, so this
   drops to (0,0,1) — still beats the global `a` default (a plain element
   selector, also (0,0,1), by source order, same as before) for ordinary
   links, but can never again beat ANY class-based component colour,
   regardless of source order, so this can't silently re-happen the next
   time a component is rendered as a link somewhere. Same reasoning applied
   to the other four container-anchor colour rules in this file (§9 footer,
   §14 marketing nav links, §17 tabbar/drawer links) — .footer's own
   `<span class="u-mono u-tiny u-muted">` version tag next to "Changelog"
   had the identical (unreported, no visible contrast failure, just the
   wrong token) collision, caught in the same sweep. */
:where(.topbar) a { color: var(--bar-fg); }
.brand { display: flex; align-items: center; gap: 9px; font-family: var(--display); font-weight: 700; font-size: 17px; letter-spacing: -.02em; text-decoration: none; }
.brand:hover { text-decoration: none; }
.brand-mark {
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  background: linear-gradient(160deg, var(--amber) 0%, #C3821A 55%, var(--indigo) 56%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.25);
}
/* The real RaisePortal mark (public/media-kit) — distinct from .brand-mark
   above, which stays a plain placeholder swatch for a *tenant's own* brand
   on investor-room pages (views/investor/*.ejs show `tenant.name` next to
   it, not RaisePortal's). Sized to match the old placeholder's footprint so
   swapping it in changes nothing about the surrounding layout. */
.brand-logo-mark { width: 26px; height: 26px; flex: none; object-fit: contain; }
.brand-logo { display: inline-flex; align-items: center; flex: none; }
.brand-logo img { width: 18px; height: 18px; }
/* Footer sits on `--surface`, which really does flip between modes (unlike
   the topbar's always-dark `--bar-bg`) — so, same pattern as the sun/moon
   toggle a few rules down, both variants render and CSS picks one. Default
   (no `.rp-chrome` at all — the investor room never opts into this
   founder-chrome toggle, SPEC §13.5) is the plain mark: the bare `:root`
   tokens in section 1 are already the light palette, so a room page's own
   footer is light unless its *own* template (public/css/themes/*.css)
   says otherwise, which this deliberately does not reach into. This CSS
   rule has no way to enforce "the investor room never opts into this" on
   its own — it can only select on the `.rp-chrome` class if the class is
   there, never test WHY it is there — so this held only as long as every
   router upstream that calls middleware/theme.js's `applyColorMode` scopes
   it to founder-only paths. One of them didn't for a while
   (routes/auth.js, docs/verification/20260805T212900Z.md T11/C3): see that
   middleware's own header for the full account. */
.brand-logo .bl-normal { display: block; }
.brand-logo .bl-reversed { display: none; }
.rp-chrome:not([data-mode="light"]) .brand-logo .bl-normal { display: none; }
.rp-chrome:not([data-mode="light"]) .brand-logo .bl-reversed { display: block; }
.tenant-chip {
  font-family: var(--mono); font-size: 12px;
  background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.18);
  padding: 4px 9px; border-radius: 999px; color: #D9E0F0;
}
.tenant-chip:hover { text-decoration: none; background: rgba(255,255,255,.16); }
.topbar-spacer { flex: 1 1 auto; }
.avatar {
  width: 32px; height: 32px; border-radius: 999px; flex: none;
  display: grid; place-items: center;
  background: var(--indigo); color: var(--accent-ink);
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.2);
}
.icon-btn {
  width: 36px; height: 36px; border-radius: var(--r-sm);
  display: grid; place-items: center; cursor: pointer;
  background: transparent; border: 1px solid transparent; color: inherit;
}
.icon-btn:hover { background: rgba(255,255,255,.12); }
.topbar .icon-btn.has-dot { position: relative; }
.topbar .icon-btn.has-dot::after {
  content: ""; position: absolute; top: 6px; right: 7px;
  width: 8px; height: 8px; border-radius: 999px;
  background: var(--amber); box-shadow: 0 0 0 2px var(--bar-bg);
}

.body { flex: 1 1 auto; display: flex; align-items: stretch; }

/* Left rail (desktop) */
.rail {
  width: var(--rail); flex: none;
  background: var(--surface);
  border-right: 1px solid var(--line);
  padding: 18px 12px 24px;
  position: sticky; top: var(--topbar); align-self: flex-start;
  height: calc(100vh - var(--topbar)); overflow-y: auto;
}
.rail-group + .rail-group { margin-top: 22px; }
.rail-label { padding: 0 10px 8px; }
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: var(--r-sm);
  color: var(--ink-2); font-size: 14px; font-weight: 500;
  text-decoration: none;
}
.nav-item:hover { background: var(--paper); text-decoration: none; color: var(--ink); }
.nav-item[aria-current="page"] { background: var(--indigo-50); color: var(--indigo-700); font-weight: 600; }
.nav-item[aria-current="page"] .nav-ico { color: var(--indigo); }
.nav-ico { width: 18px; height: 18px; flex: none; color: var(--muted); }
.nav-count {
  margin-left: auto; font-family: var(--mono); font-size: 11px;
  background: var(--paper-2); color: var(--ink-2); padding: 1px 6px; border-radius: 999px;
}

.main { flex: 1 1 auto; min-width: 0; padding: 22px 20px 96px; }
.main-wide { max-width: var(--maxw); margin: 0 auto; }

.page-head { margin-bottom: 20px; }
.page-head h1 { margin-bottom: 4px; }
.page-sub { color: var(--muted); margin: 0; font-size: 14px; }
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Mobile bottom nav + drawer */
.tabbar { display: none; }
.drawer-backdrop { display: none; }

/* ---------- 5. Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 15px; border-radius: var(--r-sm);
  font-size: 14px; font-weight: 600; line-height: 1.2;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background .14s ease, border-color .14s ease, transform .14s ease;
}
.btn:hover { text-decoration: none; background: var(--paper); }
.btn:active { transform: translateY(1px); }
.btn svg { width: 16px; height: 16px; }
.btn-primary { background: var(--indigo); border-color: var(--indigo); color: var(--accent-ink); }
.btn-primary:hover { background: var(--indigo-700); border-color: var(--indigo-700); }
.btn-dark { background: var(--bar-bg); border-color: var(--bar-bg); color: var(--bar-fg); }
.btn-dark:hover { filter: brightness(1.25); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn-ghost:hover { background: var(--paper-2); }
.btn-danger { color: var(--rust); border-color: var(--line); background: var(--surface); }
.btn-danger:hover { background: var(--rust-50); border-color: #E9C4BF; }
.btn-sm { padding: 6px 11px; font-size: 13px; }
.btn-lg { padding: 13px 22px; font-size: 15px; }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .5; pointer-events: none; }

/* ---------- 6. Cards & surfaces ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--sh-1);
}
.card-pad { padding: 18px; }
.card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 18px; border-bottom: 1px solid var(--line-soft);
}
.card-head h2, .card-head h3 { margin: 0; }
.card-foot { padding: 12px 18px; border-top: 1px solid var(--line-soft); background: var(--surface-2); border-radius: 0 0 var(--r) var(--r); }
.card-link-row {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 18px; border-bottom: 1px solid var(--line-soft);
}
.card-link-row:last-child { border-bottom: 0; }
.card-link-row:hover { background: var(--hover); }

.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-main { grid-template-columns: minmax(0, 1.6fr) minmax(320px, 1fr); align-items: start; }

/* Stat block */
.stat { padding: 15px 16px; }
.stat-label { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.stat-value { font-family: var(--display); font-size: 30px; font-weight: 600; letter-spacing: -.03em; margin-top: 4px; line-height: 1; }
.stat-delta { font-size: 12.5px; margin-top: 6px; color: var(--muted); }
.stat-delta.up { color: var(--moss); }

/* ---------- 7. Badges, pills, dots ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600; line-height: 1;
  padding: 4px 8px; border-radius: 999px;
  background: var(--paper-2); color: var(--ink-2);
}
.badge-live { background: var(--amber-50); color: #8A6106; }
.badge-ok { background: var(--moss-50); color: #1F5C42; }
.badge-info { background: var(--indigo-50); color: var(--indigo-700); }
.badge-danger { background: var(--rust-50); color: var(--rust); }
.badge-private { background: #F0E9F6; color: #6B3F91; }
/* Every current `.dot` in the views is actually `.dot.dot-live` (the amber,
   pulsing "someone's here now" indicator below) — the plain `.dot` rule
   is the idle/offline base state for a future case, not dead weight to
   delete. Confirmed safe as a *background*, unlike --ink-2/--muted
   elsewhere in this sweep: this is a small non-text swatch, not a fill
   behind foreground text, so the bar is WCAG 1.4.11's 3:1 (not 4.5:1) —
   --muted on --surface measures ~6:1 light, ~7.4:1 dark (§1's own note
   above), well clear either way. */
.dot { width: 7px; height: 7px; border-radius: 999px; background: var(--muted); flex: none; }
.dot-live { background: var(--amber); animation: pulse 1.8s ease-in-out infinite; }
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(224,162,28,.55); }
  70%      { box-shadow: 0 0 0 7px rgba(224,162,28,0); }
}

/* Signature: access-code stub (boarding-pass motif) */
/* `background: var(--surface-2)` — was a literal `#FCFDFF` (near-identical
   to light mode's own --surface-2, #FBFCFE, so this changes nothing visible
   in light mode) that never moved with dark mode. `.stub-code`'s `color:
   var(--ink)` right below already becomes near-white in dark mode
   (app.css §26, .rp-chrome:not([data-mode="light"])); paired with the old
   fixed near-white background that produced ~1.16:1 contrast — the access
   code a founder reads aloud or copies was nearly invisible. --surface-2
   already has its own correct dark-mode value (#0D1613, §26), so tokenizing
   this one property is the whole fix: ~16:1 in light mode, ~15.6:1 in dark
   (verifier, 2026-08-11). */
.code-stub {
  display: inline-flex; align-items: stretch; border-radius: var(--r-sm);
  border: 1px dashed var(--line); background: var(--surface-2); overflow: hidden;
}
.code-stub .stub-host {
  padding: 7px 10px; font-size: 12.5px; color: var(--muted);
  border-right: 1px dashed var(--line); display: flex; align-items: center;
  background: var(--paper); font-family: var(--mono);
}
.code-stub .stub-code {
  padding: 7px 12px; font-family: var(--mono); font-weight: 700;
  font-size: 14px; letter-spacing: .22em; text-transform: uppercase; color: var(--ink);
  display: flex; align-items: center;
}
.code-stub .stub-copy {
  border: 0; border-left: 1px dashed var(--line); background: var(--surface);
  padding: 0 10px; cursor: pointer; color: var(--muted); display: grid; place-items: center;
}
.code-stub .stub-copy:hover { background: var(--indigo-50); color: var(--indigo); }

/* ---------- 8. Forms ---------- */
.field { margin-bottom: 14px; }
.field > label, .label {
  display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px; color: var(--ink-2);
}
.req { color: var(--rust); font-weight: 400; }
.input, .select, .textarea {
  width: 100%; padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
  font-size: 14.5px; transition: border-color .14s ease, box-shadow .14s ease;
}
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--indigo); box-shadow: 0 0 0 3px var(--indigo-50);
}
.textarea { min-height: 96px; resize: vertical; }
.select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236C7893' d='M6 8 0 0h12z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 34px; }
.hint { font-size: 12.5px; color: var(--muted); margin-top: 5px; }
.field-error .input { border-color: var(--rust); }
.error-text { font-size: 12.5px; color: var(--rust); margin-top: 5px; }
.input-group { display: flex; }
.input-group .input { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.input-group .addon {
  display: flex; align-items: center; padding: 0 12px;
  background: var(--paper); border: 1px solid var(--line); border-left: 0;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-family: var(--mono); font-size: 13px; color: var(--muted); white-space: nowrap;
}
.check {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 12px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); cursor: pointer;
}
.check:hover { background: var(--hover); }
.check input { margin-top: 2px; width: 16px; height: 16px; accent-color: var(--indigo); flex: none; }
.check-title { font-weight: 600; font-size: 14px; }
/* `display: block` (not a plain inline span) so this always starts its own
   line under `.check-title` — every `.switch-row` caller already forces
   this with an inline `style="display:block"` override (settings.ejs,
   view-editor.ejs, account.ejs, document-detail.ejs), so making it the
   default here changes nothing for them. It DOES fix a real bug the
   inline-span default left in every plain `.check` (not `.switch-row`)
   usage that pairs a `.check-title` with a `.check-sub` — documents.ejs,
   login.ejs, cap-table.ejs and auth/signup.ejs — where the two ran
   together on one line with no space ("Keep private to meHidden from...").
   view-editor.ejs's `.check-title.u-row` (flex, already a block-level box)
   already forced its own `.check-sub` onto a new line regardless, so this
   is a no-op there too. */
.check-sub { display: block; font-size: 12.5px; color: var(--muted); }
.check.is-on { border-color: var(--indigo); background: var(--indigo-50); }
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 11px 0; border-bottom: 1px solid var(--line-soft); }
.switch-row:last-child { border-bottom: 0; }
.switch { position: relative; width: 42px; height: 24px; flex: none; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch span { position: absolute; inset: 0; background: var(--paper-2); border-radius: 999px; transition: background .16s ease; cursor: pointer; }
.switch span::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; background: #fff; border-radius: 999px; box-shadow: var(--sh-1); transition: transform .16s ease; }
.switch input:checked + span { background: var(--indigo); }
.switch input:checked + span::after { transform: translateX(18px); }

.segmented { display: inline-flex; background: var(--paper-2); border-radius: var(--r-sm); padding: 3px; gap: 3px; }
.segmented button {
  border: 0; background: transparent; padding: 6px 13px; border-radius: 5px;
  font-size: 13px; font-weight: 600; color: var(--muted); cursor: pointer;
}
.segmented button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--sh-1); }

/* ---------- 9. Tables (desktop) → cards (mobile) ---------- */
.table-wrap { overflow-x: auto; }
table.tbl { width: 100%; border-collapse: collapse; font-size: 14px; }
.tbl th {
  text-align: left; font-family: var(--mono); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); font-weight: 500;
  padding: 10px 14px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
.tbl td { padding: 13px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: var(--hover); }
.tbl .cell-title { font-weight: 600; }

/* attention heat bar — how long a doc was read, relative to the longest */
.heat { display: flex; align-items: center; gap: 9px; min-width: 130px; }
.heat-track { flex: 1 1 auto; height: 6px; border-radius: 999px; background: var(--paper-2); overflow: hidden; }
.heat-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--indigo) 0%, var(--amber) 100%); }
.heat-num { font-family: var(--mono); font-size: 12px; color: var(--ink-2); min-width: 44px; text-align: right; }

/* file / doc rows */
.filerow { display: flex; align-items: center; gap: 12px; }
.filetype {
  width: 34px; height: 34px; border-radius: 7px; flex: none; display: grid; place-items: center;
  font-family: var(--mono); font-size: 10px; font-weight: 700; letter-spacing: .04em;
  background: var(--indigo-50); color: var(--indigo-700);
}
.filetype.pdf { background: var(--rust-50); color: var(--rust); }
.filetype.xls { background: var(--moss-50); color: #1F5C42; }
.filetype.ppt { background: var(--amber-50); color: #8A6106; }
.filetype.doc { background: var(--indigo-50); color: var(--indigo-700); }
.filetype.img { background: #F0E9F6; color: #6B3F91; }

/* ---------- 10. Feed / timeline ---------- */
.feed { list-style: none; margin: 0; padding: 0; }
.feed li { display: flex; gap: 12px; padding: 13px 18px; border-bottom: 1px solid var(--line-soft); }
.feed li:last-child { border-bottom: 0; }
.feed-ico {
  width: 30px; height: 30px; border-radius: 999px; flex: none; display: grid; place-items: center;
  background: var(--paper-2); color: var(--ink-2);
}
.feed-ico svg { width: 15px; height: 15px; }
.feed-ico.enter { background: var(--amber-50); color: #8A6106; }
.feed-ico.doc { background: var(--indigo-50); color: var(--indigo-700); }
.feed-ico.say { background: var(--moss-50); color: #1F5C42; }
.feed-body { min-width: 0; flex: 1 1 auto; }
.feed-time { font-family: var(--mono); font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* ---------- 11. Feedback pills (investor) ---------- */
.pills { display: flex; gap: 8px; flex-wrap: wrap; }
.pill {
  border: 1px solid var(--line); background: var(--surface); border-radius: 999px;
  padding: 9px 15px; font-size: 14px; font-weight: 600; cursor: pointer; color: var(--ink-2);
}
.pill:hover { border-color: var(--indigo); color: var(--indigo-700); background: var(--indigo-50); }
/* The plain "Looks good" pill's pressed state used to be
   background:var(--ink);color:#fff — --ink is a *foreground* token (body
   text) that a room theme is free to redefine as a light colour when its
   own paper is dark (the "midnight" template sets --ink:#ECE7DE, near
   white), which put white text on a near-white pill. --bar-bg/--bar-fg is
   the pair every theme already keeps "always dark regardless of light or
   dark mode" (§14, used by .topbar and .btn-dark) — safe here for the same
   reason. */
.pill[aria-pressed="true"] { background: var(--bar-bg); border-color: var(--bar-bg); color: var(--bar-fg); }
/* .great doesn't set its own `color` here, so without this it falls back to
   the base `.pill[aria-pressed="true"]` rule above and inherits --bar-fg —
   which is meant to pair with the always-dark --bar-bg, not with --moss.
   Coincidence keeps that safe on three of four room templates (ledger,
   signal, studio all measure >=4.5:1), but "midnight" redefines both to a
   dark-paper palette and --moss there only measures 2.96:1 against
   --bar-fg's near-white. --accent-ink (§1) is the token actually meant for
   an --indigo/--moss solid fill and is what the plain "Looks good" case
   above would use too if it filled with an accent colour instead of
   --bar-bg. */
.pill.great[aria-pressed="true"] { background: var(--moss); border-color: var(--moss); color: var(--accent-ink); }
.pill.think[aria-pressed="true"] { background: var(--amber); border-color: var(--amber); color: #2A2000; }

.comment { padding: 14px 18px; border-bottom: 1px solid var(--line-soft); }
.comment:last-child { border-bottom: 0; }
.comment-meta { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.comment-reply { margin-top: 10px; margin-left: 16px; padding-left: 14px; border-left: 2px solid var(--indigo-50); }

/* ---------- 12. Empty state ---------- */
.empty { text-align: center; padding: 44px 22px; }
.empty-mark {
  width: 46px; height: 46px; border-radius: 12px; margin: 0 auto 12px;
  display: grid; place-items: center; background: var(--paper); border: 1px dashed var(--line); color: var(--muted);
}
.empty h3 { margin-bottom: 4px; }
.empty p { color: var(--muted); max-width: 380px; margin: 0 auto 16px; }

/* ---------- 13. Modal / drawer ---------- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(19,26,43,.45); z-index: 60;
  display: grid; place-items: center; padding: 18px;
}
/* documents.ejs's upload modal only ever exists in the DOM when it should be
   shown (server-rendered behind `<% if (showUploadModal) %>`), so it never
   carries `hidden`. The signup terms modal (views/auth/signup.ejs,
   public/js/app.js) is the first caller to JS-toggle the `hidden` attribute
   on `.modal`/`.modal-backdrop` instead — and without this, it silently
   doesn't work: `[hidden]`'s UA-stylesheet `display: none` and this file's
   own unconditional `display: grid`/(block default) have equal specificity,
   so source order alone decides, and an author stylesheet loaded after the
   UA one always wins. `[hidden]` combined with the class raises the
   specificity enough to win outright, closing that off for every current
   and future `hidden`-toggled `.modal`/`.modal-backdrop`. */
.modal-backdrop[hidden], .modal[hidden] { display: none; }
.modal {
  width: 100%; max-width: 520px; background: var(--surface);
  border-radius: var(--r-lg); box-shadow: var(--sh-3); overflow: hidden;
}
.modal-head { padding: 18px 20px 0; }
.modal-body { padding: 16px 20px; }
.modal-foot { padding: 14px 20px; background: var(--surface-2); border-top: 1px solid var(--line-soft); display: flex; justify-content: flex-end; gap: 8px; }
/* `.modal--lg` — a wider dialog for prose-length content (the signup form's
   terms modal, views/auth/signup.ejs) where the default 520px reads too
   cramped for paragraphs of legal text. `.modal-body--scroll` caps the body
   at a fraction of the viewport height and scrolls internally, so a long
   document never pushes the close button or the backdrop's own edges off
   either a 900px or an 844px-tall screen — the head (with its own close
   button) and, where present, the foot stay pinned in view throughout. */
.modal--lg { max-width: 680px; }
.modal-body--scroll { max-height: 55vh; overflow-y: auto; }

/* ---------- 14. Marketing ---------- */
.mk { background: var(--surface); }
.mk-wrap { max-width: 1100px; margin: 0 auto; padding: 0 20px; }
/* `.mk-nav` is a direct child of `.mk` (partials/mk-nav.ejs), not nested
   inside a `.mk-wrap` like the hero/body content — it needs a full-bleed
   background of its own (below) for the sticky bar to stay opaque over
   scrolling content, so the centered 1100px column lives in a `.mk-wrap`
   *inside* it instead. Sticky, same convention as `.topbar` (§4) — a plain
   background (not a blur) so it reads the same in both modes without an
   extra token; z-index 40, matching `.topbar`, and clear of the drawer
   scale below (45/46/50/55/60) since a page never shows both navs at once. */
.mk-nav {
  position: sticky; top: 0; z-index: 40;
  background: var(--surface); border-bottom: 1px solid var(--line-soft);
}
.mk-nav > .mk-wrap { height: 66px; display: flex; align-items: center; gap: 14px; }
.mk-nav .brand { color: var(--ink); }
.mk-nav-links { display: flex; gap: 22px; margin-left: 26px; font-size: 14px; }
/* :where() — same reason as :where(.topbar)/:where(.footer) above. No
   component currently renders inside these links, so this changes nothing
   today; it's here so the same "container rule silently outranks a
   component's own colour" bug can't start the moment one does. */
:where(.mk-nav-links) a { color: var(--ink-2); font-weight: 500; }
/* Same convention as `.nav-item[aria-current="page"]`/`.tabbar
   a[aria-current="page"]` above — the founder-app rail/tabbar's way of
   marking "you are here". Subtle on purpose: a colour + weight change, no
   background pill, so it doesn't fight the CTAs for attention on a bar this
   compact. */
.mk-nav-links a[aria-current="page"],
.mk-drawer-links a[aria-current="page"] { color: var(--indigo); font-weight: 700; }
/* Wraps the theme toggle + CTAs (partials/mk-nav.ejs) so they can be hidden
   as one unit at mobile widths (§17) — needs its own flex row, otherwise a
   plain `<div>` stacks its button/link children vertically instead of
   sitting inline like they did before this wrapper existed. */
.mk-nav-actions { display: flex; align-items: center; gap: 10px; }
/* In-page anchors (landing.ejs's #how/#tracking/#security, changelog's
   #v0.1.0 entries) would otherwise land straight under the now-sticky bar. */
html { scroll-padding-top: 74px; }
.hero { padding: 54px 0 20px; }
.hero h1 {
  font-size: clamp(34px, 5.4vw, 58px); line-height: 1.03; letter-spacing: -.035em;
  max-width: 15ch; margin-bottom: 16px;
}
.hero h1 em { font-style: normal; color: var(--indigo); }
.hero p.lede { font-size: 17px; color: var(--ink-2); max-width: 52ch; }

/* `.shell--prose` — a page-level modifier for the legal/help/contact/
   media-kit/changelog shape: nav + hero used to sit inside the *first*
   `.mk-wrap` (base 1100px, `.mk`'s default width, tuned for the landing
   page's wide multi-column layout), then a second, separate `.mk-wrap` held
   the body copy at its own one-off inline `style="max-width:760px"`. Two
   different widths, both centered independently, put the hero flush against
   a left edge ~170px further left than the body text below it — reported
   live on /privacy, screenshot docs/verification/shots/4-*.

   Narrowing *every* `.mk-wrap` under `.shell--prose`, nav included, fixed
   that but created a second, separately reported bug: the nav bar itself —
   and with it the brand mark — then sat 170px further right on every prose
   page (security/help/privacy/terms/changelog/media-kit/contact) than on
   the wide pages (landing/signup/404), so it visibly jumped left/right as
   you moved between a wide page and a prose one. A nav bar wider than the
   prose column beneath it is fine — it's a full-bleed element with its own
   background, not part of the reading column — but a nav bar that moves is
   not. `.mk .mk-wrap` (not the bare `.mk-wrap` this used to be) scopes the
   narrowing to wraps that are descendants of `.mk` — the hero and body
   content — and deliberately leaves `<nav class="mk-nav">`'s own `.mk-wrap`
   alone, since `.mk-nav` is a *sibling* of `.mk`, not nested inside it
   (sticky needs a containing block taller than itself, see mk-nav.ejs), so
   this selector can never reach it. Every public page's nav now measures
   1100px and lands the brand mark at the same x regardless of `bodyClass`;
   the hero and body inside `.mk` still share the narrower 760px measure and
   therefore still share a left edge with each other, which is also a
   reasonable ~70-character prose line length at this font size. Does not
   touch plain `.mk-wrap` (1100px) at all, so the landing page and pricing
   page — genuinely wide, multi-column, never mismatched — are unaffected. */
.shell--prose .mk .mk-wrap { max-width: 760px; }
/* The landing page's 15ch is a deliberate multi-line display treatment for
   its own big, short headline ("Send the deck. See who read it.") — wrong
   for a prose page's heading ("What we collect, and why"), which was
   wrapping after a third of the 1100px hero before this fix and now, at
   15ch again, would wrap after a third of the *760px* column instead of
   filling it. `none` lets it wrap at the column's own edge, same as every
   line of body copy below it. */
.shell--prose .hero h1 { max-width: none; }
.hero-cta { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 22px; }
.hero-note { font-size: 13px; color: var(--muted); margin-top: 12px; }
.hero-panel {
  margin-top: 40px; border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--paper); box-shadow: var(--sh-2); overflow: hidden;
}
.hero-panel-bar { display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: var(--bar-bg); }
.hero-panel-bar .tenant-chip { margin-left: auto; }
.hero-panel-body { padding: 16px; display: grid; gap: 12px; grid-template-columns: repeat(3, 1fr); }
.mk-section { padding: 56px 0; border-top: 1px solid var(--line-soft); }
.mk-section.alt { background: var(--paper); }
.mk-grid { display: grid; gap: 18px; grid-template-columns: repeat(3, 1fr); }
.mk-feature h3 { margin-bottom: 6px; }
.mk-feature p { color: var(--ink-2); font-size: 14.5px; margin: 0; }
.mk-feature .num {
  /* Was `color: var(--amber)` — --amber is a status/warning colour (live
     pulse, "soft pledged" segment), never contrast-checked for a *text*
     role because it was never meant for one. At 11px against --paper this
     measured 2.00:1 in light mode (verifier round 5), nowhere near AA's
     4.5:1 — and darkening --amber itself would blunt the one job it
     actually has elsewhere. --muted is the right token instead: this is a
     small label, the same job .eyebrow already does two rules up, and
     --muted already clears 4.5:1 in both modes (see section 1's note on
     the light-mode darkening two rounds ago). */
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  letter-spacing: .12em; display: block; margin-bottom: 10px;
}

/* ---------- 15. Investor room ---------- */
/* `--room-bar-reserve` — how much bottom space `.room-body` and
   `.room-foot` both reserve so the fixed `.feedback-bar` (below) can never
   overlap real content or the footer's own text. One custom property,
   redeclared once at the mobile breakpoint (§17), rather than the same
   magic number copy-pasted into both rules and drifting the next time
   either changes — the same lesson `.footer { padding-bottom: 84px }`
   already needed for the (shorter, single-row) tabbar. Measured against
   the real rendered bar on all four bar-carrying pages (room.ejs, doc.ejs,
   ask-ai.ejs, cap-table.ejs — each wraps its pills/input/button
   differently), not guessed: 1440×900 tallest real bar (room.ejs and
   doc.ejs, both three-pill layouts) is ~172px; a flat 190px covers that
   with headroom for a longer error line (`feedbackError`). `env(safe-area-
   inset-bottom)` is added on top at each use site, not folded in here,
   since it is itself already inside the bar's own measured height via its
   own padding (§ .feedback-bar) — this reserve is the room ABOVE that, not
   a duplicate of it. */
:root { --room-bar-reserve: 190px; }
.gate { min-height: 100vh; display: flex; flex-direction: column; background: var(--bar-bg); color: var(--bar-fg); }
.gate-main { flex: 1 1 auto; display: grid; place-items: center; padding: 28px 18px; }
/* Shares `.gate-foot`'s own reasoning below (§15, the `.gate-foot` rule's
   comment) — same always-dark `background: var(--bar-bg)`, same fixed
   lavender-grey literal replaced with the same token-tracking 70% white.
   A second small class rather than reusing `.gate-foot` itself: that class
   also carries padding/text-align/font-size tuned for the footer block, not
   an inline topbar label (views/investor/gate.ejs and closed.ejs's
   "Confidential" tag). */
.gate-confidential { color: rgba(255,255,255,.7); }
.gate-card {
  width: 100%; max-width: 400px; background: var(--surface); color: var(--ink);
  border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: 26px;
}
.gate-logo { width: 52px; height: 52px; border-radius: 13px; display: grid; place-items: center; background: var(--paper); border: 1px solid var(--line); font-family: var(--display); font-weight: 700; font-size: 20px; margin-bottom: 16px; }
.code-input {
  width: 100%; text-align: center; font-family: var(--mono); font-size: 26px;
  letter-spacing: .5em; text-indent: .5em; text-transform: uppercase;
  padding: 14px 10px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper);
}
/* `background: var(--surface)` — was a literal `#fff`. The gate is
   skinnable (`data-theme`, SPEC §13.5) and `.code-input`'s own text colour
   is inherited (§2's `input { color: inherit }`) from `.gate-card`'s
   `color: var(--ink)`. Three of the four room templates keep --ink dark, so
   a literal white focus background happened to still work — but "midnight"
   (public/css/themes/midnight.css) sets --ink to a near-white cream
   (#ECE7DE, for its own dark paper), and the literal white background does
   not know that: measured ~1.23:1 on focus, same failure as the code-stub
   above. --surface is already the token every skin pairs correctly with
   --ink (ledger #fff, midnight #14181E) so swapping to it fixes this
   without a per-skin override: ~14.5:1 on midnight (verifier, 2026-08-11). */
.code-input:focus { outline: none; border-color: var(--indigo); box-shadow: 0 0 0 3px var(--indigo-50); background: var(--surface); }
/* `color: rgba(255,255,255,.7)` — was a literal `#8D99B5` (a fixed
   lavender-grey) sitting on `.gate`'s `background: var(--bar-bg)`, which
   changes per room template (evergreen/near-black/cobalt/charcoal-olive,
   see public/css/themes/*.css) the same way `.tenant-chip` and `.icon-btn`
   already handle an always-dark bar with a translucent white rather than a
   hand-picked hex (§4). The old literal measured 3.29:1 on the default
   Ledger skin and 3.28:1 on Signal's cobalt bar — both below the 4.5:1 AA
   floor for this 12.5px text, not just a mismatched token. 70% white over
   the bar tracks --bar-fg (always near-white in every skin) and clears AA
   on all four: ledger 5.49:1, midnight 9.74:1, signal 5.07:1, studio 8.48:1
   (verifier, 2026-08-11). */
.gate-foot { padding: 16px; text-align: center; font-size: 12.5px; color: rgba(255,255,255,.7); }

.room-head { background: var(--surface); border-bottom: 1px solid var(--line); }
.room-head-inner { max-width: 900px; margin: 0 auto; padding: 22px 20px; }
/* `flex: 1 1 auto` — `.room-body` is a direct child of `.shell`
   (`min-height: 100vh; display: flex; flex-direction: column`, §4/partials/
   head.ejs+room-foot.ejs) alongside `.topbar`, `.room-head` and, below,
   `.room-foot`. None of those three has its own flex-grow, so without this
   a short room (few documents, no note) left `.shell`'s own leftover height
   sitting AFTER the last child instead of being absorbed by `.main` — which
   is exactly the reported bug: `.room-foot` rendered mid-page, followed by
   a blank gap of that leftover height, with the fixed `.feedback-bar`
   floating below it, visibly detached. Growing `.room-body` to fill that
   leftover space instead pushes `.room-foot` down flush to the true bottom
   of a short room (or has no effect at all on a long, already-scrolling
   one — flex-grow only ever consumes space that would otherwise go
   unused). */
/* `padding-bottom` was a flat `110px`, tuned for a bar shorter than the
   real one below (§ `--room-bar-reserve`'s own comment: measured ~172px
   at 1440×900, ~245px at 390×844) — a room scrolled to the very end could
   have its own last document row sit partly under the fixed bar, not just
   the footer past `</main>`. Uses the same reserve as `.room-foot` below so
   the two can never drift apart. */
.room-body { max-width: 900px; margin: 0 auto; padding: 20px 20px calc(var(--room-bar-reserve) + env(safe-area-inset-bottom)); flex: 1 1 auto; }
.room-cat { margin-bottom: 18px; }
.room-cat-head { display: flex; align-items: baseline; justify-content: space-between; padding: 0 2px 8px; }
.doc-item { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--line-soft); text-decoration: none; color: inherit; }
.doc-item:last-child { border-bottom: 0; }
.doc-item:hover { background: var(--hover); text-decoration: none; }
.doc-note { background: var(--amber-50); border: 1px solid #F0DFB4; border-radius: var(--r); padding: 14px 16px; }
.doc-note h3 { margin-bottom: 4px; }

.feedback-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  background: var(--surface); border-top: 1px solid var(--line);
  box-shadow: 0 -6px 20px rgba(19,26,43,.07);
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
}
.feedback-bar-inner { max-width: 900px; margin: 0 auto; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* The investor room's own footer (partials/room-foot.ejs) — replaces
   partials/foot.ejs's marketing footer on every `/room*` page (SPEC §8's
   "the room footer", see that partial's own header comment for why). Sits
   in normal flow, after `</main>`, same as the marketing footer used to;
   `.room-body`'s own `flex: 1 1 auto` immediately above is what keeps it
   from floating mid-page on a short room. `padding-bottom` uses the same
   `--room-bar-reserve` as `.room-body` above, so the fixed bar can never
   overlap the footer's own text either — the other half of this same bug.
   Bar-less pages (`view.allowFeedback` off, room.ejs/doc.ejs) just get a
   little unused blank space above the footer text instead of a second,
   conditional rule. */
.room-foot {
  max-width: 900px; margin: 0 auto;
  padding: 18px 20px calc(var(--room-bar-reserve) + env(safe-area-inset-bottom));
  text-align: center; font-size: 12px; color: var(--muted);
}
.room-foot p + p { margin-top: 2px; }

.viewer {
  background: #2A3142; border-radius: var(--r); min-height: 420px;
  display: grid; place-items: center; color: #A9B3CA; text-align: center; padding: 30px;
  background-image: repeating-linear-gradient(45deg, rgba(255,255,255,.02) 0 12px, transparent 12px 24px);
}
.viewer-page { background: #fff; color: var(--ink); width: min(100%, 420px); aspect-ratio: 4/3; border-radius: 4px; box-shadow: var(--sh-3); display: grid; place-items: center; }
.watermark { font-family: var(--mono); font-size: 11px; color: var(--muted); letter-spacing: .1em; }

/* ---------- 16. Footer ---------- */
.footer {
  border-top: 1px solid var(--line); background: var(--surface);
  padding: 22px 20px calc(22px + env(safe-area-inset-bottom));
  font-size: 13px; color: var(--muted);
}
.footer-inner { max-width: var(--maxw); margin: 0 auto; display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
/* :where() — same reason as :where(.topbar) above (§4): the "Changelog"
   link's own nested `<span class="u-mono u-tiny u-muted">v0.1.0</span>`
   version tag was silently getting `.footer a`'s --ink-2 instead of its own
   --muted (plain `.footer a` at (0,1,1) outranks `.u-muted` at (0,1,0)) —
   no contrast failure either token is fine here, but the wrong one was
   winning. Dropping this to element-level specificity fixes it the same
   structural way, not just for this one span. */
:where(.footer) a { color: var(--ink-2); }
.footer-links { display: flex; gap: 16px; flex-wrap: wrap; margin-left: auto; }
.footer-legal { width: 100%; font-size: 12px; color: var(--muted); }

/* ---------- 17. Responsive ---------- */
@media (max-width: 1020px) {
  .grid-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .grid-main { grid-template-columns: minmax(0,1fr); }
  .mk-grid, .hero-panel-body { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 780px) {
  h1 { font-size: 25px; }
  .main { padding: 16px 14px 92px; }
  .rail { display: none; }
  .rail.is-open {
    display: block; position: fixed; top: var(--topbar); bottom: 0; left: 0;
    width: 268px; z-index: 55; box-shadow: var(--sh-3); height: auto;
    animation: slideIn .18s ease-out;
  }
  @keyframes slideIn { from { transform: translateX(-100%); } to { transform: translateX(0); } }
  .drawer-backdrop.is-open { display: block; position: fixed; inset: var(--topbar) 0 0; background: rgba(19,26,43,.4); z-index: 50; }

  .grid-2, .grid-3, .grid-4, .mk-grid, .hero-panel-body { grid-template-columns: minmax(0, 1fr); }
  .page-head { display: block; }
  .page-actions { margin-top: 12px; }
  .page-actions .btn { flex: 1 1 auto; }

  /* table → stacked cards */
  .tbl, .tbl tbody, .tbl tr, .tbl td { display: block; width: 100%; }
  .tbl thead { display: none; }
  .tbl tr {
    border: 1px solid var(--line); border-radius: var(--r); margin-bottom: 10px;
    padding: 6px 2px; background: var(--surface);
  }
  .tbl td { border-bottom: 1px solid var(--line-soft); padding: 9px 14px; display: flex; gap: 12px; align-items: center; }
  .tbl td:last-child { border-bottom: 0; }
  .tbl td::before {
    content: attr(data-label);
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
    color: var(--muted); flex: 0 0 96px;
  }
  .tbl td[data-label=""]::before { content: none; }
  .tbl td.cell-wide { display: block; }
  .tbl td.cell-wide::before { display: block; margin-bottom: 6px; }

  /* bottom tab bar */
  .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 46;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    background: var(--surface); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .tabbar a {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 9px 4px 8px; font-size: 10.5px; font-weight: 600;
    color: var(--muted); text-decoration: none;
  }
  .tabbar a svg { width: 20px; height: 20px; }
  .tabbar a[aria-current="page"] { color: var(--indigo); }
  .footer { padding-bottom: 84px; }
  .mk-nav-links { display: none; }
  .hero { padding: 34px 0 10px; }
  .mk-section { padding: 40px 0; }
  .feedback-bar-inner { gap: 9px; }
  .feedback-bar .btn { width: 100%; }
  /* `--room-bar-reserve`'s desktop value (§15) assumes the feedback bar's
     one wide row; at this width every bar's own children stack onto
     several full-width rows instead (the two rules directly above), which
     measures noticeably taller in practice — real rendered heights at
     390×844 (verifier, 2026-08-11): room.ejs 216px, doc.ejs 245px (its
     extra "On this document:" label wraps its own line), ask-ai.ejs 143px.
     245px plus real headroom for a longer error line and a longer/wrapped
     comment placeholder is why 270 here, not a value closer to the actual
     measurement — redeclaring the one custom property updates both
     `.room-body` and `.room-foot` at once instead of two rules that can
     drift apart. */
  :root { --room-bar-reserve: 270px; }

  /* Public nav mobile menu (partials/mk-nav.ejs) — a separate panel+backdrop
     pair from the founder app's `.rail`/`.drawer-backdrop` above (untouched:
     different elements, different JS handlers, public.js/app.js §"mk-nav
     drawer" below), reusing the same conventions (backdrop click closes,
     Escape closes, z-index 50 for the backdrop matches the rail's own).
     `[hidden]` (toggled in JS, not a class) does the actual show/hide, so
     these rules only need to describe the *shown* geometry. Sits after
     `.mk-nav-actions` in the DOM (partials/mk-nav.ejs) — `.mk-nav-actions`
     is hidden at this width (below), so with `.topbar-spacer`'s `flex: 1 1
     auto` ahead of it, the trigger is the last item left in the row and
     lands flush against the wrap's right padding, brand mark alone on the
     left; `margin-right: -4px` (mirrors the old left-hugging offset this
     replaced) pulls the 44px box tight to that edge the same way the old
     left-side placement did against the left one. */
  .mk-nav [data-mk-menu-toggle] { width: 44px; height: 44px; margin-right: -4px; flex: none; }
  /* Measured overflow at 390px with all of hamburger + wordmark + theme
     toggle + "Sign in" + "Start free" in one row (~408px of content in
     350px of space) — without `flex: none` above, the hamburger (the only
     child without one) silently shrank to 14px instead of the page
     overflowing, which is worse: invisible instead of loud. Hiding
     `.mk-nav-actions` (mirrored in the drawer, see the partial) below is
     the actual fix; `flex: none` here is a second line of defence so a
     future addition to the bar fails as a visible overflow, not a
     shrunken, sub-44px tap target. */
  .mk-nav--has-menu .mk-nav-actions { display: none; }
  .mk-drawer-backdrop {
    position: fixed; inset: 66px 0 0; z-index: 50;
    background: rgba(19,26,43,.4);
  }
  .mk-drawer {
    position: fixed; left: 0; right: 0; top: 66px; z-index: 55;
    background: var(--surface); border-bottom: 1px solid var(--line);
    box-shadow: var(--sh-3);
    max-height: calc(100vh - 66px); overflow-y: auto;
    animation: mkDrawerIn .16s ease-out;
  }
  @keyframes mkDrawerIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
  .mk-drawer-links { display: flex; flex-direction: column; padding: 6px 20px; }
  .mk-drawer-links a {
    padding: 13px 2px; border-bottom: 1px solid var(--line-soft);
    color: var(--ink); font-weight: 500; font-size: 15px; text-decoration: none;
  }
  .mk-drawer-links a:last-child { border-bottom: 0; }
  .mk-drawer-foot {
    display: flex; align-items: center; gap: 10px;
    padding: 14px 20px calc(14px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line-soft);
  }
  .mk-drawer-foot .btn { flex: 1 1 auto; }
}
@media (min-width: 781px) {
  /* Belt and suspenders: the trigger that opens these is itself
     `.u-hide-desktop`, so this can never actually be reached on desktop —
     stated explicitly rather than relied on implicitly. */
  .mk-drawer, .mk-drawer-backdrop { display: none !important; }
}

@media (max-width: 420px) {
  .tenant-chip { display: none; }
  .pill { flex: 1 1 auto; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

@media print {
  .topbar, .rail, .tabbar, .feedback-bar, .footer { display: none !important; }
}

/* ---------- 18. Late additions ---------- */
.u-hide-desktop { display: none !important; }
@media (max-width: 780px) { .u-hide-desktop { display: grid !important; } }

/* ---------- 19. Auth screens ---------- */
.auth { flex: 1 1 auto; display: grid; place-items: center; padding: 34px 18px; }
.auth-card { width: 100%; max-width: 424px; }
.auth-card .card { padding: 26px; }
.auth-mark { width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center;
  background: var(--paper); border: 1px solid var(--line); font-family: var(--display); font-weight: 700; font-size: 18px; margin-bottom: 14px; }
.auth-alt { text-align: center; font-size: 13.5px; color: var(--muted); margin-top: 16px; }
.divider { display: flex; align-items: center; gap: 12px; color: var(--muted); font-size: 12px; margin: 16px 0; }
.divider::before, .divider::after { content: ""; height: 1px; background: var(--line); flex: 1 1 auto; }

/* ---------- 20. Onboarding checklist ---------- */
.steps { counter-reset: step; }
.step {
  display: flex; gap: 14px; padding: 16px 18px; border-bottom: 1px solid var(--line-soft);
}
.step:last-child { border-bottom: 0; }
.step-num {
  width: 28px; height: 28px; flex: none; border-radius: 999px; display: grid; place-items: center;
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  background: var(--paper-2); color: var(--ink-2);
}
.step.is-done .step-num { background: var(--moss); color: var(--accent-ink); }
.step.is-now .step-num { background: var(--indigo); color: var(--accent-ink); }
.step.is-now { background: var(--indigo-50); }
.step h3 { margin-bottom: 2px; }
.progress { height: 6px; border-radius: 999px; background: var(--paper-2); overflow: hidden; }
.progress span { display: block; height: 100%; background: var(--indigo); border-radius: 999px; }

/* ---------- 21. Template store / theme picker ---------- */
.themes { display: grid; gap: 14px; grid-template-columns: repeat(4, minmax(0,1fr)); }
.theme-card { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; background: var(--surface); position: relative; }
.theme-card.is-active { border-color: var(--indigo); box-shadow: 0 0 0 3px var(--indigo-50); }
.theme-swatch { height: 84px; display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; }
.theme-swatch i { display: block; }
.theme-meta { padding: 12px 14px; }
.theme-meta h3 { margin-bottom: 2px; }
.theme-foot { padding: 10px 14px; border-top: 1px solid var(--line-soft); background: var(--surface-2); display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.theme-lock { position: absolute; top: 10px; right: 10px; }

/* ---------- 22. Plans ---------- */
.plans { display: grid; gap: 16px; grid-template-columns: repeat(3, minmax(0,1fr)); align-items: start; }
.plan { border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); padding: 22px; }
.plan.is-current { border-color: var(--indigo); box-shadow: 0 0 0 3px var(--indigo-50); }
.plan-price { font-family: var(--display); font-size: 34px; letter-spacing: -.03em; line-height: 1; margin: 10px 0 4px; }
.plan ul { list-style: none; padding: 0; margin: 16px 0 0; }
.plan li { display: flex; gap: 9px; align-items: flex-start; padding: 6px 0; font-size: 14px; color: var(--ink-2); }
.plan li svg { width: 16px; height: 16px; flex: none; color: var(--moss); margin-top: 3px; }

/* ---------- 23. Detail pages ---------- */
.kv { display: grid; grid-template-columns: 130px 1fr; gap: 8px 14px; font-size: 14px; }
.kv dt { color: var(--muted); font-size: 12.5px; }
.kv dd { margin: 0; }
.rail-back { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }

@media (max-width: 1020px) { .themes { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 780px)  { .themes, .plans { grid-template-columns: minmax(0,1fr); } .kv { grid-template-columns: 1fr; gap: 2px 0; } .kv dd { margin-bottom: 10px; } }

/* ---------- 24. Cap table module ---------- */
:root {
  --c1: #2A3B8F;   /* common / founders   */
  --c2: #5A78D8;   /* common / others     */
  --c3: #0F7B63;   /* preferred           */
  --c4: #E0A21C;   /* SAFEs and notes     */
  --c5: #7E5FB5;   /* option pool         */
  --c6: #B4453A;   /* everything else     */
}
.ownbar {
  display: flex; width: 100%; height: 34px; border-radius: var(--r-sm);
  overflow: hidden; background: var(--paper-2); border: 1px solid var(--line);
}
.ownbar i {
  display: block; height: 100%; position: relative;
  border-right: 1px solid rgba(255,255,255,.55);
}
.ownbar i:last-child { border-right: 0; }
.ownbar.is-thin { height: 12px; }
.legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 12px; }
.legend span { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink-2); }
.legend b { font-family: var(--mono); font-weight: 500; color: var(--ink); }
.swatch-dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.num { font-family: var(--mono); font-size: 13.5px; text-align: right; white-space: nowrap; }
.tbl td.num, .tbl th.num { text-align: right; }
.delta { font-family: var(--mono); font-size: 12.5px; }
.delta.down { color: var(--rust); }
.delta.up { color: var(--moss); }
.scenario-panel[hidden] { display: none; }
.dilution-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line-soft); }
.dilution-row:last-child { border-bottom: 0; }
.pro-forma { background: var(--amber-50); border: 1px solid #F0DFB4; border-radius: var(--r); padding: 12px 14px; }
.stat-mini { padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
.stat-mini .stat-label { font-size: 10.5px; }
.stat-mini .stat-value { font-size: 22px; }
@media (max-width: 780px) {
  .ownbar { height: 26px; }
  .legend { gap: 6px 14px; }
}

/* ---------- 25. Raise goal and status board ---------- */
.goalbar {
  display: flex; width: 100%; height: 40px; border-radius: var(--r-sm);
  overflow: hidden; background: var(--paper-2); border: 1px solid var(--line);
}
.goalbar i { display: block; height: 100%; border-right: 1px solid rgba(255,255,255,.5); }
.goalbar i:last-child { border-right: 0; }
.goalbar.is-small { height: 10px; }
.seg-funded { background: var(--moss); }
.seg-signed { background: var(--indigo); }
.seg-soft   { background: var(--amber); }
.seg-pipe   { background: var(--paper-2); background-image: repeating-linear-gradient(45deg, var(--line-soft) 0 6px, transparent 6px 12px); }
.money { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.money-lg { font-family: var(--display); font-size: 34px; letter-spacing: -.03em; line-height: 1; }
.pace { border-left: 3px solid var(--indigo); padding-left: 14px; }
.pace.behind { border-left-color: var(--rust); }
.pace.ahead { border-left-color: var(--moss); }
.rank { width: 26px; height: 26px; border-radius: 999px; display: grid; place-items: center; flex: none;
  font-family: var(--mono); font-size: 11px; font-weight: 700; background: var(--paper-2); color: var(--ink-2); }
.rank.is-first { background: var(--amber); color: #2A2000; }
.minibar { width: 100%; max-width: 160px; height: 8px; border-radius: 999px; background: var(--paper-2); overflow: hidden; display: flex; }
.minibar i { height: 100%; display: block; }
.alloc-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.alloc-row:last-child { border-bottom: 0; }
.alloc-row .input { max-width: 170px; text-align: right; }
.remainder { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; border-radius: var(--r-sm); background: var(--paper); border: 1px solid var(--line); }
.remainder.is-over { background: var(--rust-50); border-color: #E9C4BF; }
.remainder.is-exact { background: var(--moss-50); border-color: #BFDCCC; }
.countdown { font-family: var(--mono); font-size: 12.5px; color: var(--muted); }
.countdown b { color: var(--ink); }
@media (max-width: 780px) {
  .goalbar { height: 30px; }
  .money-lg { font-size: 28px; }
  .alloc-row { flex-wrap: wrap; }
  .alloc-row .input { max-width: none; flex: 1 1 100%; text-align: left; }
}

/* ---------- 26. Colour mode (dark / light) — the whole app, not a room skin ----------
   This is a completely different axis from the investor-room templates in
   public/css/themes/*.css, which key off `data-theme` on <html> (SPEC §13.5).
   This section keys off a separate `data-mode` attribute and only ever
   applies inside an `.rp-chrome` scope, written by `partials/head.ejs` for
   marketing, auth and founder-app pages (`res.locals.themeChrome`). Investor
   room pages must never get `.rp-chrome`, so a founder's own dark/light
   choice never touches a pinned room template — Ledger stays Ledger,
   Midnight stays Midnight, no matter what the founder's own app looks like.

   THIS IS ENFORCED IN ROUTING, NOT HERE. This stylesheet has no way to know
   *why* `.rp-chrome` is or isn't present on a given page — it can only
   select on the class. The actual guarantee lives in which routers call
   middleware/theme.js's `applyColorMode` and on which paths (see that
   file's own header). It broke once already: routes/auth.js used to call
   `router.use(applyColorMode)` with no path scoping at all, and because
   app.js mounts that router unconditionally, ahead of the tenant/investor
   branch, `.rp-chrome` (and this whole section, including the dark-mode
   `--indigo` below) silently applied to every investor gate/room/document-
   viewer/AI-assistant page too — overwriting Midnight's copper, Studio's
   olive, Signal's cobalt and any brand-colour override with this section's
   own teal, for any investor in the default (no-cookie => dark) state
   (docs/verification/20260805T212900Z.md, T11/C3). If a future change ever
   makes `.rp-chrome` reachable from an investor-facing route again, this
   whole section reapplies there exactly as before — nothing in this file
   would stop it.

   Default is dark, unconditionally: a first-time visitor with no stored
   preference gets dark, full stop — not `prefers-color-scheme`, which is not
   consulted at all. Only an explicit `rp_theme=light` cookie (read
   server-side into `data-mode="light"`, so there is no flash either way)
   produces light. That is why the selector below matches "anything that
   is not explicitly light" rather than "explicitly dark" — it has to cover
   both the real no-cookie case and an explicit dark cookie with one rule.

   IMPORTANT: no `:where()` here. `:where()` zeroes the specificity of
   everything inside it, which previously left `:root`'s own `--paper` etc.
   (specificity 0,1,0) beating this block outright — dark mode never
   actually painted, regardless of the `data-mode` attribute or the cookie.
   A plain `.rp-chrome:not([data-mode="light"])` carries real specificity
   (a class plus an attribute selector, 0,2,0) and wins over `:root` on its
   own merits, which is the fix.

   That same (0,2,0) specificity is also HIGHER than every room skin's own
   `[data-theme="X"] { --indigo: ...; }` rule (0,1,0, public/css/themes/*.css)
   and higher than the inline brand-colour `<style>:root{--indigo:...}</style>`
   block partials/head.ejs writes per-page (also 0,1,0) — this rule beats
   both outright on specificity alone, source order irrelevant, if it is
   ever allowed to match on an investor page. That is exactly what T11/C3
   found (docs/verification/20260805T212900Z.md): every skin's accent and
   every brand-colour override was being silently replaced by this dark
   teal. The fix is NOT a specificity change here — deliberately: chasing
   this with `:where()` (see IMPORTANT above — that has already broken dark
   mode once by zeroing specificity where it was needed) or by dropping this
   rule's own specificity would just re-open the exact bug this comment
   warns about above; the fix has to be that `.rp-chrome` itself never
   reaches an investor page, enforced in routing (this section's own header
   comment, and middleware/theme.js). This specificity gap remains
   deliberately in place for `.rp-chrome` pages that legitimately want it
   (the founder app, always Ledger, always allowed to differ from a room
   skin) — it is a real, load-bearing, undisguised specificity fight
   between `.rp-chrome:not(...)` and `:root`, and it is meant to stay one.

   The brand guide (see section 1's note) only publishes a light scheme —
   Fog-and-white cards with Ink text — so the dark neutrals below are
   derived, not lifted from a page of the PDF: they extend Ink (#16211E,
   the guide's own "green-cast near-black... sits with the greens instead
   of fighting them") *downward* into an even darker page background,
   while Ink itself becomes this mode's `--surface` (a card sitting one
   step lighter than the page, same relationship light mode has between
   `--surface` and `--paper`). --indigo/-700 use the brand's Teal and Mint
   directly — Mint is captioned "reversed accent only" in the guide, i.e.
   built for exactly this, an accent *as text* on a dark background, so no
   darkening is needed the way light mode's --indigo required there.

   That reasoning does not extend to --indigo/--moss used the other way
   round — as the *background* of a solid fill carrying white text
   (.avatar, .btn-primary, .step-num, the pressed "Looks great" pill, §1's
   --accent-ink comment has the full measurements): a hue picked to still
   read clearly as text against this mode's near-black paper is by
   construction too light to also carry white text stacked on top of it
   (indigo 3.39:1, moss 2.59:1 — both fail AA). --accent-ink overridden to
   this mode's own --paper (already the darkest colour available here, and
   never pure black per the brand rule) is what those four call sites use
   instead of a hardcoded #fff, so this is the one place that needs to
   change for all of them. */
.rp-chrome:not([data-mode="light"]) {
  --ink:        #E7EEEA; /* derived: nearly-white, kept on the Ink/Slate hue rather than blue-white */
  --ink-2:      #B9CFC6; /* derived */
  --muted:      #8FA79E; /* derived: Slate brightened for legibility on a near-black page (brand Slate alone is ~4.3:1 here, short of AA) */
  --paper:      #0A120F; /* derived: darker than Ink, same green-cast-near-black family — never pure #000 (brand rule) */
  --paper-2:    #101C17;
  --surface:    #16211E; /* Ink — brand exact, one step lighter than --paper, same role --surface has in light mode */
  --surface-2:  #0D1613;
  --hover:      #1B2620;
  --line:       #26352E;
  --line-soft:  #1C2921;

  --indigo:     #1D9E75; /* Teal — brand exact */
  --indigo-700: #5DCAA5; /* Mint — brand exact ("reversed accent only"), brighter hover state */
  --indigo-50:  #12261F; /* derived dark teal tint */
  --amber:      #E7B84E; /* unchanged — see section 1 */
  --amber-50:   #2E2510;
  /* Dark mode's --amber is already bright enough to carry text on its own
     (8.2–10.3:1 on --paper/--surface/--amber-50 — measured, not assumed) —
     no separate darkened variant needed here, so --amber-ink is simply
     --amber again in this mode. */
  --amber-ink:  #E7B84E;
  --moss:       #3DB48D; /* derived: teal/mint family, distinct from --indigo, ~7.4:1 on --paper */
  --moss-50:    #123024;
  --rust:       #E2786C; /* unchanged — see section 1 */
  --rust-50:    #2E1613;
  --accent-ink: var(--paper); /* see the comment above this rule */

  /* --bar-bg deliberately not overridden here: :root's Evergreen (section 1)
     is correct in both modes — the topbar has always been a dark bar
     regardless of light/dark mode, brand or not, so there is nothing to
     switch. */
  --bar-fg:     #F3F5FB;

  --sh-1: 0 1px 2px rgba(0,0,0,.5), 0 1px 3px rgba(0,0,0,.35);
  --sh-2: 0 4px 14px rgba(0,0,0,.55), 0 1px 3px rgba(0,0,0,.4);
  --sh-3: 0 20px 48px rgba(0,0,0,.65);
}
/* Explicit light choice only — the un-scoped `:root` values from section 1
   already are this palette, so nothing needs re-declaring here; this rule
   exists purely so the selector above (which matches everything else) has
   a real opposite to exclude. */
.rp-chrome[data-mode="light"] { color-scheme: light; }
.rp-chrome:not([data-mode="light"]) { color-scheme: dark; }

/* A handful of rules above pair a token background with a literal text
   colour tuned for the *light* version of that token (e.g. `--amber-50`
   background with a hand-picked brown). Those need a dark-mode-specific
   correction so the pairing stays legible instead of turning to mud —
   the plain (light) versions of these rules are already in section 7/10. */
.rp-chrome:not([data-mode="light"]) .badge-live { color: var(--amber); }
.rp-chrome:not([data-mode="light"]) .badge-ok { color: var(--moss); }
.rp-chrome:not([data-mode="light"]) .badge-private { background: rgba(153,111,224,.18); color: #D3B9F2; }
.rp-chrome:not([data-mode="light"]) .filetype.xls { color: var(--moss); }
.rp-chrome:not([data-mode="light"]) .filetype.ppt { color: var(--amber); }
.rp-chrome:not([data-mode="light"]) .filetype.img { background: rgba(153,111,224,.18); color: #D3B9F2; }
.rp-chrome:not([data-mode="light"]) .feed-ico.enter { color: var(--amber); }
.rp-chrome:not([data-mode="light"]) .feed-ico.say { color: var(--moss); }
.rp-chrome:not([data-mode="light"]) .doc-note { border-color: #3A2E12; }
.rp-chrome:not([data-mode="light"]) .pro-forma { border-color: #3A2E12; }
.rp-chrome:not([data-mode="light"]) .remainder.is-over { border-color: #3A1F1C; }
.rp-chrome:not([data-mode="light"]) .remainder.is-exact { border-color: #1C3F2C; }
.rp-chrome:not([data-mode="light"]) .btn-danger:hover { border-color: #3A1F1C; }

/* ---------- 27. Colour-mode toggle ---------- */
.theme-toggle {
  width: 32px; height: 32px; border-radius: 999px; flex: none;
  display: grid; place-items: center; cursor: pointer;
  background: var(--paper-2); border: 1px solid var(--line); color: var(--ink-2);
}
.theme-toggle:hover { background: var(--hover); color: var(--ink); }
.theme-toggle svg { width: 16px; height: 16px; }
.topbar .theme-toggle { background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.18); color: var(--bar-fg); }
.topbar .theme-toggle:hover { background: rgba(255,255,255,.16); }
.theme-toggle .tt-sun, .theme-toggle .tt-moon { display: none; }
.rp-chrome:not([data-mode="light"]) .theme-toggle .tt-sun { display: block; }
.rp-chrome[data-mode="light"] .theme-toggle .tt-moon { display: block; }
