
.recent-block[data-v-ee10effd] {
  display: flex;
  flex-direction: column;
}
.recent-head[data-v-ee10effd] {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 0 0 12px;
}
.recent-title[data-v-ee10effd] {
  font-size: 13px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  margin: 0;
}
.recent-search-btn[data-v-ee10effd] {
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
}
.recent-search-btn[data-v-ee10effd]:hover {
  color: var(--text);
}
.recent-list[data-v-ee10effd] {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elev);
  overflow: hidden;
}
.recent-row[data-v-ee10effd] {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  cursor: pointer;
}
.recent-row[data-v-ee10effd]:last-child {
  border-bottom: 0;
}
.recent-row[data-v-ee10effd]:hover {
  background: var(--bg-hover);
}
.recent-row .hash-short[data-v-ee10effd] {
  width: 36px;
}
.recent-row .recent-name[data-v-ee10effd] {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.recent-row .recent-meta[data-v-ee10effd] {
  color: var(--text-muted);
  font-size: 12px;
}
.recent-empty[data-v-ee10effd] {
  padding: 24px;
  text-align: center;
  color: var(--text-faint);
  font-size: 13px;
}
.clear-history[data-v-ee10effd] {
  margin-top: var(--space-3);
  align-self: flex-end;
  color: var(--text-faint);
}
.clear-history[data-v-ee10effd]:hover {
  color: var(--danger);
}

.home-view[data-v-4970ae0d] {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--bg);
}
.home-scroll[data-v-4970ae0d] {
  height: 100%;
  overflow-y: auto;
  padding-top: 40px;
}
/* The generated prose centers itself (.prose: max-width 70ch, margin auto).
   Trim the document's huge comment-scroll padding so the recents sit close. */
.home-scroll[data-v-4970ae0d] .prose {
  padding-top: 8px;
  padding-bottom: 16px;
}
/* Match the recents/controls column to the prose reading column exactly: same
   70ch measure — sized at the prose font so the `ch` unit matches — and the
   same 56px gutters. The controls/recents then fill the full width of the
   content column and line up under the left-justified title. */
.home-slot[data-v-4970ae0d] {
  width: 100%;
  max-width: min(70ch, 100%);
  margin: 0 auto;
  padding: 8px 56px 96px;
  font-size: var(--prose-font-size);
  box-sizing: border-box;
}
/* Quick-start actions: three equal cards that mirror the + menu. */
.home-actions[data-v-4970ae0d] {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
.home-action[data-v-4970ae0d] {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 14px 16px;
  text-align: left;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.home-action[data-v-4970ae0d]:hover {
  background: var(--bg-hover);
  border-color: var(--text-faint);
}
.home-action-icon[data-v-4970ae0d] {
  font-size: 16px;
  color: var(--text-muted);
  line-height: 1;
}
.home-action-label[data-v-4970ae0d] {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
}
.home-action-hint[data-v-4970ae0d] {
  font-size: 12px;
  color: var(--text-muted);
}
/* Connect-your-agent banner: a full-width row under the quick-start cards.
   Accent-tinted so the install path reads as the primary next step on a
   first visit, not just another card. */
.home-install[data-v-4970ae0d] {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  margin-bottom: var(--space-6);
  padding: 14px 16px;
  text-align: left;
  background: color-mix(in oklab, var(--accent) 8%, var(--bg-elev));
  border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--border));
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.home-install[data-v-4970ae0d]:hover {
  background: color-mix(in oklab, var(--accent) 14%, var(--bg-elev));
  border-color: var(--accent);
}
.home-install-text[data-v-4970ae0d] {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Mobile: the 56px gutters + 3-up action grid don't fit a phone. Drop the
   gutters to match the prose (20px) and stack the quick-start cards. */
@media (max-width: 760px) {
.home-slot[data-v-4970ae0d] {
    padding: 8px 20px 96px;
}
.home-actions[data-v-4970ae0d] {
    grid-template-columns: 1fr;
}
}

.paste-modal[data-v-a4d37ddf] {
  position: absolute;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}
.paste-backdrop[data-v-a4d37ddf] {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.32);
  backdrop-filter: blur(2px);
}
:root[data-mode='dark'] {
  background: rgba(0, 0, 0, 0.55);
}
.paste-panel[data-v-a4d37ddf] {
  position: relative;
  width: min(640px, 100%);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg, 0 24px 48px rgba(0, 0, 0, 0.18));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.paste-header[data-v-a4d37ddf] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.paste-title[data-v-a4d37ddf] {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0;
  color: var(--text);
}
.paste-area[data-v-a4d37ddf] {
  width: 100%;
  min-height: 240px;
  resize: vertical;
  border: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--mono-font);
  font-size: 13px;
  line-height: 1.55;
  padding: var(--space-4) var(--space-5);
}
.paste-area[data-v-a4d37ddf]:focus {
  outline: none;
}
.paste-actions[data-v-a4d37ddf] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border);
}
.paste-hint[data-v-a4d37ddf] {
  font-size: var(--text-sm);
  color: var(--text-faint);
}
.paste-actions-buttons[data-v-a4d37ddf] {
  display: flex;
  gap: var(--space-2);
}

.install-modal[data-v-75fd30e2] {
  position: absolute;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}
.install-backdrop[data-v-75fd30e2] {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.32);
  backdrop-filter: blur(2px);
}
:root[data-mode='dark'] {
  background: rgba(0, 0, 0, 0.55);
}
.install-panel[data-v-75fd30e2] {
  position: relative;
  width: min(680px, 100%);
  max-height: 100%;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg, 0 24px 48px rgba(0, 0, 0, 0.18));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.install-header[data-v-75fd30e2] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.install-title[data-v-75fd30e2] {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0;
  color: var(--text);
}
.install-body[data-v-75fd30e2] {
  padding: var(--space-4) var(--space-5);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.install-blurb[data-v-75fd30e2] {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-md);
  line-height: 1.55;
}
.install-agents[data-v-75fd30e2] {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}
.install-agent[data-v-75fd30e2] {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg);
  text-align: left;
  cursor: pointer;
}
.install-agent[data-v-75fd30e2]:hover {
  background: var(--bg-hover);
}
.install-agent.is-active[data-v-75fd30e2] {
  border-color: var(--accent, var(--border-strong));
  background: var(--bg-hover);
}
.install-agent-name[data-v-75fd30e2] {
  font-weight: 600;
  color: var(--text);
  font-size: var(--text-md);
}
.install-agent-hint[data-v-75fd30e2] {
  color: var(--text-faint);
  font-size: var(--text-sm);
  line-height: 1.35;
}
.install-preview[data-v-75fd30e2] {
  margin: 0;
  padding: var(--space-4);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-family: var(--mono-font);
  font-size: 12px;
  line-height: 1.55;
  color: var(--text-muted);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.install-actions[data-v-75fd30e2] {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border);
}
.install-skill-link[data-v-75fd30e2] {
  font-size: var(--text-sm);
  color: var(--text-faint);
  text-decoration: none;
}
.install-skill-link[data-v-75fd30e2]:hover {
  color: var(--text);
  text-decoration: underline;
}
.install-actions-buttons[data-v-75fd30e2] {
  display: flex;
  gap: var(--space-2);
}
@media (max-width: 760px) {
.install-agents[data-v-75fd30e2] {
    grid-template-columns: 1fr;
}
}

.search-modal[data-v-b5fe8bde] {
  position: absolute;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh var(--space-6) var(--space-6);
}
.search-backdrop[data-v-b5fe8bde] {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.32);
  backdrop-filter: blur(2px);
}
:root[data-mode='dark'] {
  background: rgba(0, 0, 0, 0.55);
}
.search-panel[data-v-b5fe8bde] {
  position: relative;
  width: min(640px, 100%);
  max-height: 70vh;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg, 0 24px 48px rgba(0, 0, 0, 0.18));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.search-header[data-v-b5fe8bde] {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.search-input[data-v-b5fe8bde] {
  flex: 1;
  border: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--text-base);
  padding: var(--space-1) 0;
}
.search-input[data-v-b5fe8bde]:focus {
  outline: none;
}
.search-list[data-v-b5fe8bde] {
  flex: 1;
  overflow-y: auto;
}
.search-row[data-v-b5fe8bde] {
  display: grid;
  width: 100%;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  cursor: pointer;
}
.search-row[data-v-b5fe8bde]:last-child {
  border-bottom: 0;
}
.search-row[data-v-b5fe8bde]:hover {
  background: var(--bg-hover);
}
.search-row .hash-short[data-v-b5fe8bde] {
  width: 36px;
  color: var(--text-faint);
}
.search-name[data-v-b5fe8bde] {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.search-meta[data-v-b5fe8bde] {
  color: var(--text-muted);
  font-size: 12px;
}
.search-empty[data-v-b5fe8bde] {
  padding: 32px;
  text-align: center;
  color: var(--text-faint);
  font-size: 13px;
}
.search-foot[data-v-b5fe8bde] {
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--border);
  font-size: var(--text-sm);
  color: var(--text-faint);
}

.admin-page[data-v-cf33275d] { min-height: 100vh; overflow: auto; background: var(--bg); color: var(--text); padding: 48px 24px;
}
.admin-login[data-v-cf33275d] { width: min(420px, 100%); margin: 8vh auto 0; padding: 32px; background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-md);
}
.admin-brand[data-v-cf33275d] { display: inline-flex; gap: 8px; align-items: center; color: var(--text); text-decoration: none; font-weight: 650;
}
.admin-brand span[data-v-cf33275d] { color: var(--mark); font-size: 24px; line-height: 1;
}
h1[data-v-cf33275d] { margin: 24px 0 6px; font-size: 28px; line-height: 1.15;
}
h2[data-v-cf33275d] { margin: 0; font-size: 17px;
}
p[data-v-cf33275d] { color: var(--text-muted);
}
.admin-form[data-v-cf33275d] { display: grid; gap: 18px; margin-top: 28px;
}
.admin-form label[data-v-cf33275d] { display: grid; gap: 7px; font-size: var(--text-md); font-weight: 600;
}
input[data-v-cf33275d], select[data-v-cf33275d] { width: 100%; min-height: 42px; padding: 0 12px; color: var(--text); background: var(--bg); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); font: inherit;
}
input[data-v-cf33275d]:focus, select[data-v-cf33275d]:focus { outline: 2px solid var(--accent); outline-offset: 1px;
}
.admin-alert[data-v-cf33275d] { padding: 10px 12px; color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent); border-radius: var(--radius-sm);
}
.admin-dashboard[data-v-cf33275d] { width: min(1180px, 100%); margin: 0 auto;
}
.admin-header[data-v-cf33275d] { display: flex; justify-content: space-between; align-items: end; gap: 24px; margin-bottom: 28px;
}
.admin-header h1[data-v-cf33275d] { margin-top: 14px;
}
.admin-header p[data-v-cf33275d], .admin-panel p[data-v-cf33275d] { margin: 5px 0 0;
}
.admin-actions[data-v-cf33275d] { display: flex; align-items: center; gap: 10px;
}
.admin-actions select[data-v-cf33275d] { min-width: 150px; min-height: 32px;
}
.admin-cards[data-v-cf33275d] { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 20px;
}
.admin-card[data-v-cf33275d], .admin-panel[data-v-cf33275d] { background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius-md);
}
.admin-card[data-v-cf33275d] { padding: 18px;
}
.admin-card span[data-v-cf33275d], .admin-storage span[data-v-cf33275d] { display: block; color: var(--text-muted); font-size: var(--text-md);
}
.admin-card strong[data-v-cf33275d] { display: block; margin-top: 6px; font-size: 27px; font-variant-numeric: tabular-nums;
}
.admin-panel[data-v-cf33275d] { margin-top: 14px; padding: 20px;
}
.admin-panel-head[data-v-cf33275d] { margin-bottom: 16px;
}
.admin-table-wrap[data-v-cf33275d] { overflow-x: auto;
}
table[data-v-cf33275d] { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums;
}
th[data-v-cf33275d], td[data-v-cf33275d] { padding: 10px 12px; text-align: right; border-top: 1px solid var(--border); white-space: nowrap;
}
th[data-v-cf33275d] { color: var(--text-faint); font-size: var(--text-xs); font-weight: 600;
}
th[data-v-cf33275d]:first-child, td[data-v-cf33275d]:first-child { text-align: left;
}
.admin-storage[data-v-cf33275d] { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
}
.admin-storage div + div[data-v-cf33275d] { border-left: 1px solid var(--border); padding-left: 20px;
}
.admin-storage strong[data-v-cf33275d] { display: block; margin-top: 3px; font-size: 18px;
}
.admin-empty[data-v-cf33275d] { padding: 28px; text-align: center; color: var(--text-faint);
}
@media (max-width: 760px) {
.admin-page[data-v-cf33275d] { padding: 24px 14px;
}
.admin-login[data-v-cf33275d] { padding: 24px;
}
.admin-header[data-v-cf33275d] { align-items: stretch; flex-direction: column;
}
.admin-actions[data-v-cf33275d] { justify-content: space-between;
}
.admin-cards[data-v-cf33275d] { grid-template-columns: repeat(2, minmax(0, 1fr));
}
.admin-storage[data-v-cf33275d] { grid-template-columns: 1fr;
}
.admin-storage div + div[data-v-cf33275d] { border-left: 0; border-top: 1px solid var(--border); padding: 14px 0 0;
}
}
/* ============================================================
   redline.ink — design tokens + themes

   Brand system applied from the Redline design handoff
   (design_handoff_redline_brand). The handoff's generic contract
   (--c-bg, --c-text, --c-hi, …) is mapped onto our existing token
   names so components don't need renaming:

     --c-bg        → --bg            --c-text     → --text
     --c-bg2       → --bg-sunken     --c-text2    → --text-muted
     --c-surface   → --bg-elev       --c-text3    → --text-faint
     --c-line      → --border-strong --c-hi       → --highlight-active
     --c-line-soft → --border        --c-hi-soft  → --highlight
     --c-link      → --accent        --c-quote-bar→ --highlight-bd
     --c-red       → --mark          --c-quote    → --text-muted

   THE COLOR DISCIPLINE (three colors carry meaning, nothing else):
     • RED   (--red / --mark)  the caret mark ONLY.
     • YELLOW (--highlight, --highlight-active, --highlight-bd) highlights
       AND the comment tick + connector — shared on purpose.
     • BLUE  (--accent)        hyperlinks ONLY.

   Five themes, keyed by [data-theme]: paper (default light), ink
   (default dark), linen, slate, midnight. Values are the handoff's
   oklch() colors verbatim. The theme registry (id/name/mode/hljs)
   lives in src/composables/useTheme.ts.
   ============================================================ */

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/Geist-Variable-Bj2R_7yk.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/GeistMono-Variable-Dispecij.woff2') format('woff2');
}

:root {
  /* ---- brand accents: shared across every theme ---- */
  --red:        oklch(0.660 0.205 27);   /* the caret / mark (light bg) */
  --red-bright: oklch(0.715 0.200 30);   /* the mark on dark surfaces / hover */
  --link:       oklch(0.715 0.120 255);  /* hyperlink TEXT (light blue, reads on bg) */
  --accent-strong: oklch(0.530 0.160 255); /* primary-button FILL — deep enough that
                                              #fff text reads crisp (the link blue is
                                              too light to carry white text) */

  /* ---- fonts ---- */
  --prose-font: 'Geist', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono-font:  'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* ---- radius scale (handoff: 6 / 10 / 16) ---- */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  /* Full pill — floating chrome over content (FABs, chips, pager, status). */
  --radius-pill: 999px;

  /* ---- hover / active surfaces, derived from each theme's bg+text so
         they track automatically across every theme ---- */
  --bg-hover:  color-mix(in oklab, var(--bg), var(--text) 6%);
  --bg-active: color-mix(in oklab, var(--bg), var(--text) 11%);

  --prose-font-size: 20px;
  /* Single ratio that scales the surrounding chrome (top bar, comment-card
     buttons) alongside the prose. 1.0 = default prose 20px. Driven by the
     A+/A- controls via JS; multiply px-based sizes by var(--ui-scale) to
     opt them into scaling. */
  --ui-scale: 1;

  /* ---- spacing scale (4-px grid) ---- */
  --space-1:  4px;
  --space-2:  8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* ---- type scale ---- */
  --text-xs:   11px;
  --text-sm:   12px;
  --text-md:   13px;
  --text-base: 14px;
  --text-lg:   15px;
  --text-xl:   18px;
  --text-2xl:  22px;

  /* ---- control heights ---- */
  --ctrl-h-sm: 26px;
  --ctrl-h-md: 32px;
  --ctrl-h-lg: 36px;

  --tab-h:         36px;
  --reading-pct:   65%;
  --margin-pct:    35%;
}

/* ---------- LIGHT · PAPER (default light) ---------------------- */
:root,
:root[data-theme="paper"],
:root[data-theme="light"] {
  --bg:               oklch(0.991 0.005 85);
  --bg-sunken:        oklch(0.969 0.008 85);
  --bg-elev:          oklch(0.999 0.003 85);
  --border-strong:    oklch(0.886 0.010 85);
  --border:           oklch(0.920 0.008 85);
  --text:             oklch(0.225 0.010 70);
  --text-muted:       oklch(0.475 0.012 70);
  --text-faint:       oklch(0.610 0.012 70);
  --highlight:        oklch(0.905 0.155 102 / 0.5);
  --highlight-active: oklch(0.905 0.155 102);
  --highlight-bd:     oklch(0.845 0.180 100);
  --accent:           var(--link);
  --mark:             var(--red);
  --danger:           #b91c1c;
  /* "resolved/addressed" success green + soft-limit warning amber. Sanctioned
     4th/5th meaning-colors (the discipline's red/yellow/blue carry the rest);
     per-theme so they track light/dark like --danger does. */
  --success:          #2f9e63;
  --warning:          #d4a72c;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.06);
  --shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 8px 24px rgba(0,0,0,.08);
  --shadow-lg: 0 24px 48px rgba(0,0,0,.18);
}

/* ---------- LIGHT · LINEN (low-contrast cream) ---------------- */
:root[data-theme="linen"] {
  --bg:               oklch(0.974 0.013 88);
  --bg-sunken:        oklch(0.956 0.016 88);
  --bg-elev:          oklch(0.988 0.010 88);
  --border-strong:    oklch(0.890 0.020 88);
  --border:           oklch(0.918 0.016 88);
  --text:             oklch(0.315 0.024 64);
  --text-muted:       oklch(0.505 0.026 64);
  --text-faint:       oklch(0.625 0.026 64);
  --highlight:        oklch(0.880 0.150 96 / 0.5);
  --highlight-active: oklch(0.880 0.150 96);
  --highlight-bd:     oklch(0.840 0.175 95);
  --accent:           oklch(0.52 0.13 250);
  --mark:             var(--red);
  --danger:           #b3261e;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.04), 0 1px 3px rgba(0,0,0,.06);
  --shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 8px 24px rgba(0,0,0,.08);
}

/* ---------- DARK · INK (default dark · neutral charcoal) ------ */
:root[data-theme="ink"],
:root[data-theme="dark"] {
  --bg:               oklch(0.245 0.006 250);
  --bg-sunken:        oklch(0.278 0.007 250);
  --bg-elev:          oklch(0.312 0.008 250);
  --border-strong:    oklch(0.372 0.008 250);
  --border:           oklch(0.322 0.007 250);
  --text:             oklch(0.940 0.005 250);
  --text-muted:       oklch(0.772 0.007 250);
  --text-faint:       oklch(0.610 0.008 250);
  --highlight:        oklch(0.815 0.150 82 / 0.26);
  --highlight-active: oklch(0.815 0.150 82);
  --highlight-bd:     oklch(0.815 0.150 82);
  --accent:           var(--link);
  --mark:             var(--red-bright);
  --danger:           #f87171;
  --success:          #4ade80;
  --warning:          #fbbf24;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --shadow-md: 0 4px 8px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.45);
  --shadow-lg: 0 24px 48px rgba(0,0,0,.5);
}

/* ---------- DARK · SLATE (cool navy) ------------------------- */
:root[data-theme="slate"] {
  --bg:               oklch(0.215 0.017 262);
  --bg-sunken:        oklch(0.248 0.019 262);
  --bg-elev:          oklch(0.282 0.021 262);
  --border-strong:    oklch(0.350 0.022 262);
  --border:           oklch(0.300 0.020 262);
  --text:             oklch(0.928 0.012 262);
  --text-muted:       oklch(0.760 0.018 262);
  --text-faint:       oklch(0.600 0.022 262);
  --highlight:        oklch(0.815 0.150 82 / 0.26);
  --highlight-active: oklch(0.815 0.150 82);
  --highlight-bd:     oklch(0.815 0.150 82);
  --accent:           var(--link);
  --mark:             var(--red-bright);
  --danger:           #f87171;
  --success:          #4ade80;
  --warning:          #fbbf24;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --shadow-md: 0 4px 8px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.45);
  --shadow-lg: 0 24px 48px rgba(0,0,0,.5);
}

/* ---------- DARK · MIDNIGHT (rich navy) ---------------------- */
:root[data-theme="midnight"] {
  --bg:               oklch(0.205 0.034 264);
  --bg-sunken:        oklch(0.238 0.038 264);
  --bg-elev:          oklch(0.272 0.040 264);
  --border-strong:    oklch(0.332 0.040 264);
  --border:           oklch(0.286 0.036 264);
  --text:             oklch(0.920 0.020 264);
  --text-muted:       oklch(0.752 0.030 264);
  --text-faint:       oklch(0.598 0.036 264);
  --highlight:        oklch(0.815 0.150 82 / 0.26);
  --highlight-active: oklch(0.815 0.150 82);
  --highlight-bd:     oklch(0.815 0.150 82);
  --accent:           var(--link);
  --mark:             var(--red-bright);
  --danger:           #f87171;
  --success:          #4ade80;
  --warning:          #fbbf24;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --shadow-md: 0 4px 8px rgba(0,0,0,.3), 0 8px 24px rgba(0,0,0,.45);
  --shadow-lg: 0 24px 48px rgba(0,0,0,.5);
}

/* ---------- LIGHT · HOTDOG (Win95 "Hot Dog Stand" callback) ---
   The legendary eye-searing scheme: yellow page, red chrome, black
   ink, blue links. Breaks the 3-color discipline on purpose — it's a
   novelty. --accent-strong is overridden so primary buttons go red. */
:root[data-theme="hotdog"] {
  --bg:               oklch(0.945 0.155 102);
  --bg-sunken:        oklch(0.605 0.225 27);
  --bg-elev:          oklch(0.975 0.085 100);
  --border-strong:    oklch(0.560 0.215 28);
  --border:           oklch(0.760 0.180 60);
  --text:             oklch(0.205 0.030 55);
  --text-muted:       oklch(0.420 0.170 32);
  --text-faint:       oklch(0.530 0.150 42);
  --highlight:        oklch(0.620 0.240 28 / 0.32);
  --highlight-active: oklch(0.620 0.240 28);
  --highlight-bd:     oklch(0.555 0.225 28);
  --accent:           oklch(0.455 0.200 258);
  --accent-strong:    oklch(0.560 0.235 28);
  --mark:             oklch(0.580 0.245 28);
  --danger:           oklch(0.480 0.220 28);
  --shadow-sm: 0 1px 2px rgba(0,0,0,.10), 0 1px 3px rgba(0,0,0,.12);
  --shadow-md: 0 4px 8px rgba(0,0,0,.12), 0 8px 24px rgba(0,0,0,.16);
}
/* ============================================================
   redline.ink — application stylesheet
   Ported VERBATIM from prototype/reader.html <style> (the
   gold-standard visual spec), minus the :root design-token block
   and the 6 theme blocks (those live in tokens.css, imported
   first). Side-by-side parity with the prototype is the test gate,
   so these rules track the prototype exactly. Global (not scoped)
   because the prose subtree is rendered via v-html and managed
   imperatively (highlights, cards, connectors) just like the
   prototype — scoped-style attribute hashing would not reach it.
   ============================================================ */

/* ---------- reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }

/* Subtle, intentional scrollbars everywhere — the OS default bars looked
   grubby/heavy against the paper surfaces. Thin track-less thumb that only
   reads when you're actually scrolling. (The tab strip opts out entirely via
   its own more-specific rule — the ⌄N overflow menu replaces it.) */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 6px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
*::-webkit-scrollbar-corner { background: transparent; }

html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--prose-font);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}
#app { height: 100%; }
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
[hidden] { display: none !important; }

/* Text selection IS the highlight (same yellow + brand selection-ink) so
   selecting and marking read as one gesture. */
::selection { background: var(--highlight-active); color: #241c08; }
/* …and in the prose, the highlight a selection becomes is *my* author-tinted
   mark — so the drag itself reads in my color from the first pixel, not brand
   yellow. --my-author-color is kept in sync by useIdentity (it can shift as
   other reviewers join and the hues re-space); the same 52% mix as an active
   mark, with the theme's own text color (an author hue can't guarantee the
   brand selection-ink stays readable). Falls back to brand yellow pre-boot. */
.prose::selection,
.prose ::selection {
  background: color-mix(in srgb, var(--my-author-color, var(--highlight-active)) 52%, transparent);
  color: inherit;
}

/* ---------- brand: button component ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  font-family: inherit;
  font-weight: 500;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
  transition: background 120ms, color 120ms, border-color 120ms, opacity 120ms;
}
.btn:disabled { opacity: 0.55; pointer-events: none; }

/* sizes */
.btn-sm  { height: var(--ctrl-h-sm); padding: 0 var(--space-3); font-size: var(--text-sm); }
.btn-md  { height: var(--ctrl-h-md); padding: 0 var(--space-4); font-size: var(--text-md); }
.btn-lg  { height: var(--ctrl-h-lg); padding: 0 var(--space-5); font-size: var(--text-base); }
.btn-icon    { width: var(--ctrl-h-sm); height: var(--ctrl-h-sm); padding: 0; }
.btn-icon-md { width: var(--ctrl-h-md); height: var(--ctrl-h-md); padding: 0; }
.btn-icon-lg { width: var(--ctrl-h-lg); height: var(--ctrl-h-lg); padding: 0; }

/* variants */
.btn-primary  { background: var(--accent-strong); color: #fff; }
.btn-primary:hover  { opacity: 0.9; }
.btn-dark     { background: var(--text); color: var(--bg); }
.btn-dark:hover     { opacity: 0.9; }
.btn-default  { background: var(--bg-elev); color: var(--text); border-color: var(--border-strong); }
.btn-default:hover  { background: var(--bg-hover); border-color: var(--text-faint); }
.btn-ghost    { background: transparent; color: var(--text-muted); }
.btn-ghost:hover    { background: var(--bg-hover); color: var(--text); }
.btn-danger   { background: transparent; color: var(--text-faint); }
.btn-danger:hover   { background: var(--bg-hover); color: var(--danger); }

/* shadow modifier for floating/elevated buttons */
.btn-elev    { box-shadow: var(--shadow-md); }

/* ---------- app shell ---------- */
.app {
  display: grid;
  grid-template-rows: var(--tab-h) 1fr auto;
  /* Bound the single column to the viewport (minmax(0,1fr), not the default
     content-sized `auto`) so the tab strip can't grow the layout past the
     window — that's what lets the tab lane shrink + scroll instead of shoving
     the pinned controls off the right edge. */
  grid-template-columns: minmax(0, 1fr);
  height: 100vh;
}

/* ---------- tab bar ---------- */
.tab-bar-shell {
  display: flex;
  align-items: stretch;
  min-width: 0;
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--border);
}
.tab-bar {
  display: flex;
  align-items: stretch;
  /* Sized to the tabs when there's room (so + hugs the last one), shrinkable
     when crowded. The tabs themselves flex-shrink Chrome-style to share the
     space — no scrolling, so + / Docs never get pushed off-screen. */
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
}
/* tab-bar trailing controls (font size, theme) */
.tab-bar-ctrls {
  display: flex;
  align-items: stretch;
  height: 100%;
  flex: 0 0 auto;
}
.tab-ctrl {
  width: var(--tab-h);
  height: 100%;
  border-radius: 0;
  border-left: 1px solid var(--border);
  font-size: calc(var(--text-base) * var(--ui-scale));
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tab-ctrl:hover { background: var(--bg-hover); }
.tab-ctrl.is-text { font-weight: 500; font-variant-numeric: tabular-nums; }
/* settings dropdown (font size + theme, collapsed behind one gear) */
.settings-menu { position: relative; height: 100%; }
.tab-ctrl-settings {
  width: auto;
  gap: 6px;
  padding: 0 var(--space-3);
  /* match the tab labels (which scale with the font setting) */
  font-size: calc(13px * var(--ui-scale));
  font-weight: 500;
}
/* SVG gear sized to the text; the flex row centers its box on the dots' axis. */
.settings-cog { width: 1.05em; height: 1.05em; display: block; flex: 0 0 auto; }
/* Home / Docs lead with a glyph (same treatment as the settings cog) instead
   of a colored dot. */
.tab-doc-icon,
.tab-home-icon { width: 1.05em; height: 1.05em; display: block; flex: 0 0 auto; color: var(--text-faint); }
/* same popover shell as .plus-menu-popup / .theme-picker-menu */
.settings-menu-popup {
  position: absolute;
  top: 100%;
  right: 0;
  min-width: 220px;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-1) 0;
  z-index: 200;
  margin-top: 4px;
}
/* group header — quiet uppercase label, like .recent-title */
.settings-label {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--text-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.settings-label:not(:first-child) { margin-top: var(--space-1); }
/* text-size stepper — borderless controls sharing the menu's bg-hover idiom */
.settings-font {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3) var(--space-2);
}
.settings-font-btn,
.settings-font-reset {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--ctrl-h-sm);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
}
.settings-font-btn {
  width: var(--ctrl-h-sm);
  font-variant-numeric: tabular-nums;
}
.settings-font-reset { margin-left: auto; padding: 0 var(--space-2); font-size: var(--text-sm); }
.settings-font-btn:hover,
.settings-font-reset:hover { background: var(--bg-hover); color: var(--text); }
.settings-font-value {
  min-width: 40px;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.theme-option {
  display: flex;
  width: 100%;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-size: var(--text-base);
  color: var(--text);
  cursor: pointer;
}
.theme-option:hover { background: var(--bg-hover); }
.theme-option.is-active { background: var(--bg-sunken); font-weight: 500; }
.theme-swatch {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  position: relative;
  overflow: hidden;
}
.theme-swatch::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, var(--swatch-bg) 0 50%, var(--swatch-fg) 50% 100%);
}
.theme-option-check {
  margin-left: auto;
  color: var(--accent);
  font-size: var(--text-base);
  visibility: hidden;
}
.theme-option.is-active .theme-option-check { visibility: visible; }

/* Review toggles (EXPERIMENTAL): checkbox-style rows in the settings menu. */
.settings-toggle {
  display: flex;
  width: 100%;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-size: var(--text-base);
  color: var(--text);
  cursor: pointer;
}
.settings-toggle:hover { background: var(--bg-hover); }
.settings-toggle-box {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg);
  color: transparent;
  font-size: var(--text-sm);
  line-height: 1;
}
.settings-toggle-box.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  /* Chrome-style: each tab wants ~200px but shrinks toward nothing as more
     open, so the whole strip always fits. min-width:0 + overflow:hidden lets
     the label clip cleanly; the dot keeps each tab identifiable when narrow. */
  flex: 0 1 200px;
  min-width: 0;
  overflow: hidden;
  height: 100%;
  border-right: 1px solid var(--border);
  font-size: calc(13px * var(--ui-scale));
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
  position: relative;
  /* tracks the tab's own background so the close-button fade can match it */
  --tab-bg: var(--bg-sunken);
}
.tab:hover { background: var(--bg-hover); color: var(--text); --tab-bg: var(--bg-hover); }
.tab[aria-selected="true"] {
  background: var(--bg-elev);
  color: var(--text);
  font-weight: 500;
  --tab-bg: var(--bg-elev);
}
.hash-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--dot, var(--text-faint));
  flex-shrink: 0;
}
.hash-short {
  font-family: var(--mono-font);
  font-size: calc(11px * var(--ui-scale));
  color: var(--text-faint);
  letter-spacing: 0.02em;
}
.tab .tab-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
}
/* Close button overlays the tab's right edge (absolute, not in flow) so it's
   reachable at ANY tab width — never clipped by overflow:hidden the way an
   in-flow button is. A short gradient fade behind it lets the label recede
   underneath instead of colliding. Shown on hover; the active tab keeps it
   always (you can't lose the close affordance on the tab you're reading).
   Middle-click closes regardless, handled in TabBar.vue. */
.tab::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: 40px;
  background: linear-gradient(to right, transparent, var(--tab-bg) 55%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.1s ease;
  z-index: 0;
}
.tab:hover::after,
.tab[aria-selected="true"]::after { opacity: 1; }
.tab .tab-close {
  position: absolute;
  right: 5px;
  top: 50%;
  transform: translateY(-50%);
  width: 22px; height: 22px;
  border-radius: var(--radius-sm);
  color: var(--text-faint);
  display: none;
  align-items: center;
  justify-content: center;
  font-size: var(--text-base);
  line-height: 1;
  cursor: pointer;
  z-index: 1;
}
.tab:hover .tab-close,
.tab[aria-selected="true"] .tab-close { display: inline-flex; }
.tab .tab-close:hover { background: var(--bg-active); color: var(--text); }
/* Pinned tabs (Welcome.md / Docs.md) — always present, no close, sized to
   their label, with a muted fixed dot instead of a hash-colored one. */
.tab-pinned {
  min-width: auto;
  gap: 7px;
  flex: 0 0 auto;
  /* Content-sized and never compressed, so they opt out of the user-tab
     clipping: show the full label (no sub-pixel "Hom" clip) and skip the
     close-button fade (pinned tabs have no close button). */
  overflow: visible;
}
.tab-pinned::after { content: none; }
.tab-pinned .tab-name { flex: 0 0 auto; overflow: visible; }
/* Install + Docs are right-justified: Install carries the auto margin that
   pushes the trailing group (Install · Docs · Settings) to the edge. */
.tab-install {
  margin-left: auto;
  border-left: 1px solid var(--border);
  border-right: 0;
}
.tab-docs {
  border-left: 1px solid var(--border);
  border-right: 0;
}
.new-tab-btn {
  width: var(--tab-h);
  height: 100%;
  border-radius: 0;
  font-size: var(--text-xl);
}

/* + (new document) dropdown — mirrors the theme-picker popover */
.plus-menu { position: relative; height: 100%; flex: 0 0 auto; }
.plus-menu-popup {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 220px;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-1) 0;
  z-index: 200;
  margin-top: 4px;
}
.plus-option {
  display: flex;
  width: 100%;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-size: var(--text-base);
  color: var(--text);
  cursor: pointer;
}
.plus-option:hover { background: var(--bg-hover); }
.plus-option-icon {
  flex: 0 0 auto;
  width: 18px;
  text-align: center;
  color: var(--text-muted);
}

/* Mobile tab switcher — hidden on desktop (the full strip is shown there);
   the @media (max-width: 760px) block flips it on and collapses the strip. */
.tab-switcher { display: none; position: relative; height: 100%; flex: 0 0 auto; }
.tab-switcher-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 100%;
  padding: 0 10px;
  border-left: 1px solid var(--border);
  color: var(--text-muted);
  font-size: calc(13px * var(--ui-scale));
  font-variant-numeric: tabular-nums;
}
.tab-switcher-caret { width: 1em; height: 1em; display: block; flex: 0 0 auto; }
.tab-switcher-popup {
  position: absolute;
  top: 100%;
  right: 0;
  min-width: 240px;
  max-width: min(86vw, 360px);
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-1) 0;
  z-index: 200;
  margin-top: 4px;
}
.tab-switcher-row {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-size: var(--text-base);
  color: var(--text);
  cursor: pointer;
}
.tab-switcher-row:hover { background: var(--bg-hover); }
.tab-switcher-row.is-active { background: var(--bg-active); font-weight: 500; }
.tab-switcher-name {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tab-switcher-close {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  color: var(--text-faint);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-lg);
  line-height: 1;
}
.tab-switcher-close:hover { background: var(--bg-active); color: var(--text); }

/* ---------- main ---------- */
.main {
  position: relative;
  overflow: hidden;
  background: var(--bg);
}

/* The empty state is now a rendered "home" document — see HomeView.vue
   (prose layout reuses .prose) and RecentList.vue (self-contained recents
   widget with its own scoped styles). No splash-card CSS lives here. */

/* ---------- doc view ---------- */
.doc-view {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.doc-view[hidden] { display: none !important; }
.doc-scroll {
  height: 100%;
  overflow-y: auto;
  scroll-padding-top: 24px;
}
.doc-meta-wrap {
  grid-column: 1;
  grid-row: 1;
  /* Mirror .prose exactly so the header + hairline span the content width:
     same max-width basis AND same font so `70ch` resolves to the same px. */
  max-width: min(70ch, 100%);
  width: 100%;
  margin: 0 auto;
  padding: 24px 56px 0;
  box-sizing: border-box;
  font-size: var(--prose-font-size);
}
/* Quiet byline under a hairline: one small, muted, dot-separated line of
   provenance + review facts that recedes behind the H1 (no card, no chips).
   The document name is omitted here — it's already the tab label and the H1. */
.doc-meta {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.doc-meta-byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-faint);
}
.doc-meta-byline time {
  font-variant-numeric: tabular-nums;
}
/* the recency signal — what people actually scan for */
.doc-meta-age {
  color: var(--text-muted);
  font-weight: 500;
}
/* review activity present — draw a touch of the eye */
.doc-meta-active {
  color: var(--accent);
  font-weight: 500;
}
.doc-meta-sep {
  flex: 0 0 auto;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--border-strong);
}
.prose-grid {
  display: grid;
  grid-template-columns: var(--reading-pct) var(--margin-pct);
  grid-template-rows: auto 1fr;
  align-items: start;
  position: relative;
  min-height: 100%;
}
.prose {
  grid-column: 1;
  grid-row: 2;
  /* width:100% (not shrink-to-fit) keeps the reading column a stable 70ch
     measure regardless of content length, so the doc-meta header + hairline
     above it always span the same width. */
  width: 100%;
  max-width: min(70ch, 100%);
  /* Grid items default to min-width:auto, so a long <pre> line (which can't
     wrap) forces this column wider than its track and blows out the whole
     layout — paragraphs then wrap to the inflated width and the page scrolls
     sideways. min-width:0 lets the column hold its track and the <pre> scroll
     internally via its own overflow-x:auto. */
  min-width: 0;
  margin: 0 auto;
  box-sizing: border-box;
  padding: 16px 56px 200px;
  font-family: var(--prose-font);
  font-size: var(--prose-font-size);
  line-height: 1.62;
  color: var(--text);
}
.prose-grid > .margin-area { grid-column: 2; grid-row: 2; }
/* reading-only doc (Docs.md): no comment gutter — single column, centered prose,
   and no review byline (it's reference text, not a reviewed plan) */
.doc-view.is-plain .prose-grid { grid-template-columns: 1fr; }
.doc-view.is-plain .margin-area { display: none; }
.doc-view.is-plain .doc-meta-wrap { display: none; }
.prose .md-block { /* per-block wrapper from renderMarkdownWithLineInfo */ }
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
  line-height: 1.25;
  margin: 1.6em 0 0.6em;
  font-weight: 600;
}
/* Type scale from the brand handoff: tight tracking on the big cuts. */
.prose h1 {
  font-size: 2.25em;     /* ~45px at the 20px body */
  line-height: 1.04;
  font-weight: 650;
  letter-spacing: -0.03em;
  margin: 0 0 0.55em;    /* doc title hugs the meta byline above it */
}
/* Inline brand mark in a heading (the Welcome doc): sized to the cap height,
   nudged to sit on the text baseline rather than overhang it. */
.prose h1 img {
  height: 0.82em;
  width: auto;
  vertical-align: -0.06em;
  margin-right: 0.34em;
}
.prose h2 { font-size: 1.4em;  letter-spacing: -0.02em; }
.prose h3 { font-size: 1.15em; letter-spacing: -0.01em; }
.prose h4 { font-size: 1em; }
.prose p { margin: 0 0 1.05em; }
.prose ul, .prose ol { margin: 0.8em 0; padding-left: 1.6em; }
.prose li { margin: 0.25em 0; }
.prose blockquote {
  margin: 0.8em 0;
  padding: 0.2em 0 0.2em 1em;
  border-left: 3px solid var(--border-strong);
  color: var(--text-muted);
}
.prose code {
  font-family: var(--mono-font);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  background: var(--bg-sunken);
  border-radius: var(--radius-sm);
}
.prose pre {
  font-family: var(--mono-font);
  font-size: 0.9em;
  padding: 12px 14px;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow-x: auto;
}
.prose pre code { background: none; padding: 0; }
.prose hr { border: 0; border-top: 1px solid var(--border); margin: 2em 0; }
.prose a { color: var(--accent); }
.prose table { border-collapse: collapse; margin: 0.8em 0; }
.prose th, .prose td { border: 1px solid var(--border); padding: 6px 10px; }
.prose mark {
  background: var(--highlight);
  color: inherit;
  padding: 0.05em 0;
  border-radius: 2px;
  cursor: pointer;
  transition: outline 120ms;
}
/* No outlines on marks — they fragment badly across inline code and line
   wraps. Resting = soft highlight; hover/active = the full highlight, with the
   brand's dark selection-ink on the active mark. (Theme tokens carry the
   light/dark values, so no per-mode overrides needed.) */
.prose mark.is-hover  { background: var(--highlight-active); }
.prose mark.is-active { background: var(--highlight-active); color: #241c08; }
/* After a desktop selection becomes a draft we keep the native selection alive
   over the new marks so Cmd/Ctrl+C copies the quote. Hide the native overlay so
   the mark's own author tint reads clean instead of the colors doubling up. */
.prose mark::selection { background: transparent; color: inherit; }

/* When a highlight crosses inline code, swap the code's gray bg for the
   highlight color so the band reads as one continuous strip. */
.prose code:has(mark)              { background: var(--highlight); }
.prose pre code:has(mark)          { background: none; }
.prose code:has(mark.is-hover)     { background: var(--highlight-active); }
.prose code:has(mark.is-active)    { background: var(--highlight-active); }

/* Multi-author highlights: a current-review mark is tinted with its author's
   stable color (set inline as --author-color) so prose marks group by writer,
   the same way the cards do. Overrides the default yellow; hover/active deepen
   the same hue. */
.prose mark.has-author          { background: color-mix(in srgb, var(--author-color) 32%, transparent); }
.prose mark.has-author.is-hover,
.prose mark.has-author.is-active { background: color-mix(in srgb, var(--author-color) 52%, transparent); color: inherit; }
.prose code:has(mark.has-author) { background: color-mix(in srgb, var(--author-color) 32%, transparent); }

/* Agent-authored highlights. The reviewer's own notes own the solid yellow
   fill (the actionable layer); agent questions read in the accent blue (a
   machine annotation pointing at a decision it wants judged). */
.prose mark[data-origin="agent"] {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
}
.prose mark[data-origin="agent"].is-hover,
.prose mark[data-origin="agent"].is-active {
  background: color-mix(in srgb, var(--accent) 42%, transparent);
  color: inherit;
}

/* A pref-muted layer reads as plain prose until it's switched back on. */
.prose mark.is-pref-hidden {
  background: transparent !important;
  border-bottom: 0 !important;
  cursor: text;
  pointer-events: none;
}

/* Quick review filter (EXPERIMENTAL): floats at the top of the comment gutter;
   one chip per present layer, struck through when that layer is muted. */
.review-filter {
  position: absolute;
  top: 10px;
  right: 16px;
  z-index: 25;
  display: flex;
  gap: var(--space-1);
}
.review-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: var(--ctrl-h-sm);
  padding: 0 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: color-mix(in srgb, var(--bg-elev) 90%, transparent);
  backdrop-filter: blur(6px);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition: opacity 120ms, border-color 120ms, color 120ms;
}
.review-filter-chip:hover { border-color: var(--border-strong); color: var(--text); }
.review-filter-chip.is-off { opacity: 0.5; text-decoration: line-through; }
.review-filter-chip .rf-glyph { line-height: 1; }
.review-filter-chip .rf-agent { color: var(--accent); }
.review-filter-chip .rf-resolved { color: var(--success); }
.review-filter-chip.is-off .rf-glyph { color: var(--text-faint); }

.margin-area {
  position: relative;
  border-left: 1px solid var(--border);
  min-height: 100%;
  padding: 48px 0 200px;
}
/* The bottom-sheet grab handle and dismiss scrim are mobile-only. */
.sheet-handle { display: none; }
.sheet-scrim { display: none; }
.connectors {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  width: 100%;
  height: 100%;
}
.margin-card {
  position: absolute;
  left: 16px;
  right: 16px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 12px 8px;
  box-shadow: var(--shadow-sm);
  transition: top 200ms cubic-bezier(.2,.7,.2,1), box-shadow 120ms, border-color 120ms;
}
.margin-card.is-hover { box-shadow: var(--shadow-md); }
.margin-card.is-active {
  /* The active outline follows the comment's own accent (its highlight hue),
     not a fixed yellow. --card-accent is set inline per card; see commentAccent. */
  border-color: var(--card-accent, var(--highlight-bd));
  box-shadow: var(--shadow-md);
}
.margin-card.is-draft {
  /* Follow the comment's own accent (the writer's hue) rather than a fixed blue,
     so a draft reads in your colour the moment it appears. --card-accent is set
     inline per card; see commentAccent. */
  border-color: var(--card-accent, var(--accent));
  box-shadow: 0 4px 16px color-mix(in oklab, var(--card-accent, var(--accent)) 15%, transparent),
    var(--shadow-md);
}
/* Primary actions in draft/edit modes (Leave comment, Save) push to the right. */
.margin-card .card-meta .btn-primary { margin-left: auto; }

/* View mode: note renders as static text instead of a textarea. */
.margin-card .card-note-view {
  font-size: var(--text-md);
  line-height: 1.5;
  color: var(--text);
  padding: var(--space-1) 0;
  white-space: pre-wrap;
  word-break: break-word;
  cursor: text;
  border-radius: var(--radius-sm);
}
.margin-card .card-note-view:hover { background: var(--bg-hover); }
/* Keyboard focus on the role=button note view (and the mobile sheet handle) —
   both are tabbable with Enter/Space activation but had no visible focus ring. */
.margin-card .card-note-view:focus-visible,
.sheet-handle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.margin-card .card-note-empty {
  color: var(--text-faint);
  font-style: italic;
}
/* Right-side action group in committed view: edit + delete icons. */
.margin-card .card-actions {
  display: inline-flex;
  gap: 2px;
  align-items: center;
  margin-left: auto;
}
/* Editing state matches draft outline so the input mode is recognizable. */
.margin-card.is-editing {
  border-color: var(--accent);
  box-shadow: 0 4px 16px color-mix(in oklab, var(--accent) 15%, transparent), var(--shadow-md);
}

/* ---- revision-aware comment origins (EXPERIMENTAL) ----
   A left rail + origin badge distinguishes the three kinds of card, each tied to
   its highlight treatment: agent notations (yellow rail ↔ dashed-yellow mark),
   carried-forward prior comments (gray rail ↔ dotted-gray mark, dimmed when
   addressed), and the current reviewer's own comments (no rail, solid yellow). */
.margin-card.origin-agent   { border-left: 3px solid var(--accent); }
/* (Carried-card rail styling retired with the thread model — resolved threads
   take the green rail via .margin-card.is-resolved below.) */
.card-origin {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: calc(var(--prose-font-size) * 0.64);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  margin-bottom: 6px;
}
/* Multi-author: tint the card's left rail with the writer's stable color
   (--author-color, set inline) and show a color-dot + name + time byline. */
.margin-card.has-author { border-left: 3px solid var(--author-color); }
.card-author {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: calc(var(--prose-font-size) * 0.64);
  margin-bottom: 6px;
  color: var(--text-muted);
}
.card-author-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--author-color);
  flex: none;
}
.card-author-name { font-weight: 600; color: var(--text); }
.card-author-time { color: var(--text-faint); }

.card-origin.origin-agent { color: var(--accent); }
.card-origin.is-resolved { color: var(--success); }
.card-origin-label { color: inherit; }
.card-origin-glyph { font-size: 1.05em; line-height: 1; color: var(--text-faint); }
.card-origin.origin-agent .card-origin-glyph { color: var(--accent); }
.card-origin.is-resolved .card-origin-glyph { color: var(--success); }

/* ---------- threads: one comment model ---------- */
/* A resolved thread reads settled: green rail, dimmed. (Any thread can be
   resolved — by the agent or a reviewer — so this keys off status, not a
   record type.) The author rail color yields to the resolved green. */
.margin-card.is-resolved { border-left-color: var(--success) !important; opacity: 0.78; }
/* Agent byline: ✦ glyph in the accent instead of a color dot. */
.card-author.is-agent .card-author-name { color: var(--accent); }
.card-author-glyph {
  font-size: 1.05em;
  line-height: 1;
  color: var(--accent);
  flex: none;
}
/* A reply: indented under the opening message with a quiet thread rail. */
.card-reply {
  margin-top: var(--space-2);
  padding-left: 10px;
  border-left: 2px solid color-mix(in srgb, var(--text-faint) 35%, transparent);
}
.card-reply .card-author { margin-bottom: 2px; }
.card-reply .card-note-readonly { padding-top: 0; }
/* Thread actions: quiet inline row (Reply · Resolve / Re-open). */
.card-thread-actions {
  display: flex;
  gap: var(--space-1);
  margin-top: var(--space-1);
}
.card-thread-actions .btn { font-size: calc(var(--prose-font-size) * 0.68); }
.card-reply-compose { margin-top: var(--space-2); }
.margin-card.is-collapsed .card-thread-actions,
.margin-card.is-collapsed .card-reply,
.margin-card.is-collapsed .card-author,
.margin-card.is-collapsed .card-reply-compose { display: none; }

/* Addressed carried cards collapse to just the badge row; a chevron toggles. */
.card-collapse {
  margin-left: 4px;
  border: 0;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  font-size: 0.9em;
  line-height: 1;
  padding: 2px 4px;
  border-radius: var(--radius-sm);
}
.card-collapse:hover { color: var(--text-muted); background: var(--bg-hover); }
.margin-card.is-collapsible .card-origin { cursor: pointer; margin-bottom: 0; }
.margin-card.is-collapsed .card-quote,
.margin-card.is-collapsed .card-note-readonly,
.margin-card.is-collapsed .card-meta { display: none; }
.margin-card.is-collapsed { padding-bottom: 10px; }
/* Orphaned (quote-gone) cards keep their quote when collapsed: with no
   highlight left in the prose, the quote is the only cue to what it referenced.
   The quote bar reads quieter here since it points nowhere. */
.margin-card.is-collapsed.is-orphaned .card-quote { display: block; opacity: 0.7; }
/* Anchor-lost: an explicitly-open carried comment whose passage isn't in this
   revision. It stays Open (never auto-resolved); the quote reads quieter since
   it points at no live words, and a warning line says why there's no highlight. */
.margin-card.is-anchor-lost .card-quote { opacity: 0.7; }
.card-anchor-lost {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: calc(var(--prose-font-size) * 0.64);
  font-weight: 600;
  color: var(--warning);
  margin-bottom: 6px;
}
/* A filtered-out layer is pulled from the column entirely (relayout skips it). */
.margin-card.is-pref-hidden { display: none; }
/* Read-only note body for foreign (agent / carried) cards — no edit affordance. */
.margin-card .card-note-readonly {
  font-size: calc(var(--prose-font-size) * 0.8);
  line-height: 1.5;
  color: var(--text);
  padding: var(--space-1) 0;
  white-space: pre-wrap;
  word-break: break-word;
}
.margin-card .card-quote {
  font-size: calc(var(--prose-font-size) * 0.7);
  color: var(--text-muted);
  /* The quote bar follows the comment's accent so the side color, connector, and
     active outline all read as one hue per comment (was always yellow). */
  border-left: 2px solid var(--card-accent, var(--highlight-bd));
  padding-left: 8px;
  margin-bottom: 8px;
  white-space: pre-wrap;
  word-break: break-word;
  cursor: text;
}
.margin-card .card-note,
.margin-card .card-note-view {
  font-size: calc(var(--prose-font-size) * 0.8);
}
.margin-card .card-note {
  width: 100%;
  min-height: 44px;
  resize: none; /* height is managed by autoGrowNote() */
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  padding: 6px 8px;
}
.margin-card .card-note:focus { outline: 1px solid var(--card-accent, var(--accent)); }
.margin-card .card-meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-faint);
}
/* Buttons inside a comment card scale with the same ratio as the prose. */
.margin-card .btn-sm {
  height: calc(var(--ctrl-h-sm) * var(--ui-scale));
  padding: 0 calc(var(--space-3) * var(--ui-scale));
  font-size: calc(var(--text-sm) * var(--ui-scale));
}

/* selection floating button */
.selection-btn {
  position: absolute;
  z-index: 50;
  background: var(--text);
  color: var(--bg);
  font-size: 12px;
  padding: 5px 9px;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  display: none;
  gap: 4px;
  align-items: center;
  transform: translate(-50%, 0);
}
.selection-btn.is-visible { display: inline-flex; }
.selection-btn:hover { opacity: 0.92; }

/* ---------- floating buttons (inside .main, bottom-right) ---------- */
.floating-actions {
  position: absolute;
  bottom: var(--space-6);
  right: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  z-index: 10;
}
/* Send feedback is the primary "send it back" action. It spends NO brand color (blue
   stays for links, red for the mark, yellow for highlights): a neutral high-
   emphasis pill via btn-dark (--text fill / --bg ink), which flips correctly on
   every theme. Elevation comes from btn-elev (--shadow-md) — no bespoke glow. */
.floating-actions .btn {
  min-width: 96px;
  gap: 8px;
  border-radius: var(--radius-pill);
}
.floating-actions .btn:active { transform: translateY(0.5px); }
.handoff-fab-icon { font-size: 16px; line-height: 1; font-weight: 700; }
.handoff-fab-label { font-weight: 600; }

/* ---------- reviewer identity chip (bottom-right, above Send feedback) ---------- */
.identity-chip {
  position: absolute;
  bottom: calc(var(--space-6) + 52px);
  right: var(--space-6);
  z-index: 10;
}
.identity-chip-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px 6px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-elev);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  color: var(--text-muted);
  transition: background 120ms, border-color 120ms;
}
.identity-chip-btn:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.identity-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--me-color);
  flex: none;
}
.identity-name { color: var(--text); font-weight: 600; }
.identity-input {
  padding: 6px 12px;
  border: 1px solid var(--me-color);
  border-radius: 999px;
  background: var(--bg-elev);
  box-shadow: var(--shadow-sm);
  font: inherit;
  font-size: 13px;
  color: var(--text);
  width: 180px;
  outline: none;
}

/* ---------- document outline (inside .main, bottom-left) ---------- */
.outline-dock {
  position: absolute;
  bottom: var(--space-6);
  left: var(--space-6);
  z-index: 10;
}
.outline-fab { position: relative; display: inline-flex; align-items: center; gap: 7px; border-radius: var(--radius-pill); }
.outline-fab.is-active {
  background: var(--bg-elev);
  border-color: var(--border-strong);
  color: var(--text);
}
.outline-fab-icon { font-size: 15px; line-height: 1; }
/* The cap-height of the label sits high in its line box (font ascent/descent
   asymmetry), so the text reads a touch above center — nudge it down a smidge. */
.outline-fab-label { font-weight: 600; transform: translateY(1px); }

.outline-panel {
  position: absolute;
  bottom: calc(100% + var(--space-2));
  left: 0;
  width: 280px;
  max-height: min(60vh, 480px);
  display: flex;
  flex-direction: column;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.outline-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
}
.outline-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.outline-close {
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
  border-radius: var(--radius-sm);
}
.outline-close:hover { color: var(--text); }
.outline-empty {
  padding: var(--space-3);
  font-size: 13px;
  color: var(--text-faint);
}
.outline-list {
  list-style: none;
  margin: 0;
  padding: var(--space-1) 0;
  overflow-y: auto;
}
.outline-item {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: 13px;
  line-height: 1.35;
  padding: 5px 12px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.outline-item:hover { background: var(--bg-hover); color: var(--text); }
.outline-item.is-l1 { font-weight: 600; color: var(--text); }
.outline-item.is-l2 { font-weight: 500; }

/* ---------- shared-plan load error (failed /plan/<slug> decrypt) ----------
   Fills the surface (like .plan-loading): a failed load leaves the
   Home/Welcome surface active beneath, and those are absolutely-positioned
   opaque layers (.home-view, .doc-view: inset 0). A full-surface error state
   reads as intentional and never collides with the content behind it. */
.plan-error {
  position: absolute;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: var(--bg);
}
.plan-error-card {
  width: min(420px, 100%);
  box-sizing: border-box;
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
.plan-error-icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin-bottom: var(--space-1);
  border-radius: 50%;
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}
.plan-error-icon svg { width: 26px; height: 26px; }
.plan-error-title {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text);
}
.plan-error-msg {
  margin: 0;
  max-width: 34ch;
  color: var(--text-muted);
  font-size: var(--text-base);
  line-height: 1.55;
}
.plan-error-actions {
  margin-top: var(--space-3);
  display: flex;
  gap: var(--space-2);
}

/* ---------- shared-plan loading (deep-link, pre-render) ---------- */
/* Fills the surface so the Welcome/Home screen never flashes before the
   fetched ciphertext is decrypted. Quiet by design — usually on screen for
   only a beat. */
.plan-loading {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  background: var(--bg);
  color: var(--text-muted);
  font-size: var(--text-md);
}
.plan-loading-spinner {
  width: 16px;
  height: 16px;
  border: 2px solid var(--border-strong);
  border-top-color: transparent;
  border-radius: 50%;
  animation: plan-loading-spin 0.7s linear infinite;
}
@keyframes plan-loading-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  /* The loading spinner is a genuine progress indicator — keep it spinning
     (slowed), but suppress every other transition/animation and smooth scroll
     for vestibular comfort. (JS scrollIntoView/scrollTo also honor this via a
     matchMedia check in DocView — CSS scroll-behavior doesn't cover them.) */
  .plan-loading-spinner {
    animation-duration: 1.6s;
  }
  *:not(.plan-loading-spinner),
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- handoff modal (scoped to main area, not viewport) ---------- */
.handoff {
  position: absolute;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}
.handoff[hidden] { display: none; }
.handoff-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.32);
  backdrop-filter: blur(2px);
}
:root[data-mode="dark"] .handoff-backdrop { background: rgba(0, 0, 0, 0.55); }
.handoff-panel {
  position: relative;
  width: min(1040px, 100%);
  height: min(640px, 100%);
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg, 0 24px 48px rgba(0,0,0,0.18));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.handoff-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}

/* Cohesive section labeling used across share / general comments / payload. */
.handoff-section-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.handoff-section-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
}
.handoff-section-meta {
  font-size: var(--text-xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.handoff-title-group {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex: 1;
  min-width: 0;
}
.handoff-title {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0;
  color: var(--text);
}
.handoff-subtitle {
  font-size: var(--text-sm);
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.handoff-header-actions { display: flex; align-items: center; gap: var(--space-2); }
.handoff-body {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: 0;
  flex: 1;
  min-height: 0;
}
.handoff-edit {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  border-right: 1px solid var(--border);
  overflow: hidden;
  min-height: 0;
}
.handoff-field { display: flex; flex-direction: column; gap: var(--space-2); }
.handoff-field-grow { flex: 1; min-height: 0; }
.handoff-edit input,
.handoff-edit textarea {
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  padding: var(--space-3);
}
.handoff-edit input { font-size: var(--text-base); }
.handoff-edit textarea {
  flex: 1;
  resize: none;
  font-size: var(--text-base);
  line-height: 1.55;
  min-height: 120px;
}
.handoff-field-grow > textarea { height: 100%; }
.handoff-edit input:focus,
.handoff-edit textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
}
.handoff-preview {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--bg-sunken);
}
.handoff-preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  background: var(--bg-elev);
}
.handoff-preview-body {
  flex: 1;
  margin: 0;
  padding: var(--space-4);
  overflow: auto;
  font-family: var(--mono-font);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}
.handoff-preview-body.is-empty {
  color: var(--text-faint);
  font-style: italic;
}
@media (max-width: 760px) {
  .handoff-body { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
  .handoff-edit { border-right: 0; border-bottom: 1px solid var(--border); }
}

/* the bigger close glyph reads better at the larger icon-lg footprint */
.handoff-close { font-size: 22px; line-height: 1; }

/* ---------- feedback footer: export + copy actions ---------- */
.handoff-footer {
  flex: none;
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border);
  background: var(--bg-elev);
}
.handoff-modes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
}
.handoff-hint {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text-muted);
}
/* ---------- drop overlay ---------- */
.drop-overlay {
  position: fixed;
  inset: 0;
  background: rgba(37, 99, 235, 0.08);
  border: 3px dashed var(--accent);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 100;
  pointer-events: none;
}
.drop-overlay.is-visible { display: flex; }
.drop-overlay .drop-msg {
  background: var(--bg-elev);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 16px 24px;
  box-shadow: var(--shadow-md);
  font-size: 15px;
  color: var(--text);
}

/* ---------- toasts ---------- */
.toasts {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 200;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 8px 14px;
  background: var(--text);
  color: var(--bg);
  border-radius: var(--radius-md);
  font-size: 13px;
  box-shadow: var(--shadow-md);
  animation: toast-in 180ms ease-out;
}
.toast.is-error { background: var(--danger); color: white; }
.toast-action {
  pointer-events: auto;
  flex: none;
  margin: -2px -4px -2px 0;
  padding: 2px 8px;
  border: 0;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--bg) 22%, transparent);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.toast-action:hover { background: color-mix(in oklab, var(--bg) 36%, transparent); }
.toast-action:focus-visible { outline: 2px solid var(--bg); outline-offset: 1px; }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* hljs renders inside our .prose pre; reset its default padding/background
   so the prose's own pre styling controls layout, and the hljs theme only
   controls token colors. (Themes vendored locally, loaded via useTheme.) */
.prose pre code.hljs { padding: 0; background: transparent; }
.prose code.hljs { padding: 0.1em 0.35em; }

/* ============================================================
   Mobile / touch
   Phase 1: single-column reading + touch hygiene. The comment gutter
   and its SVG connectors are a desktop affordance; on a phone the
   reading column goes full-width and comments live in a bottom sheet
   (see CommentSheet, mounted below 760px).
   ============================================================ */

/* Stop iOS from auto-inflating body text in landscape. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

@media (max-width: 760px) {
  /* iOS Safari auto-zooms when you focus a text control whose font-size is
     < 16px. Floor every text-entry control at 16px so focusing — or pasting
     into — any input/textarea never triggers that zoom. !important so it
     wins over component-scoped rules (paste area, search box, handoff notes).
     None of our inputs are intentionally larger than 16px, so this only ever
     bumps up. We keep pinch-zoom available (no maximum-scale hack). */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
  textarea,
  select {
    font-size: 16px !important;
  }

  /* --- mobile tab management ---
     A phone can't show a Chrome-style strip of tabs. Collapse the user-tab
     strip to just the active tab (full width), hang every open doc in the
     switcher dropdown, and trim the pinned/settings chrome to icons so the
     active tab gets the room. */
  .tab-bar .tab { display: none; }
  .tab-bar .tab[aria-selected="true"] { display: inline-flex; flex: 1 1 auto; }
  .tab-switcher { display: inline-flex; }
  /* Pinned Home/Docs and the settings control go icon-only. */
  .tab-pinned .tab-name { display: none; }
  .tab-pinned { padding: 0 12px; }
  .tab-ctrl-settings > span { display: none; }
  .tab-ctrl-settings { padding: 0 12px; gap: 0; }

  /* Collapse the reading/margin grid to one full-width column. */
  .prose-grid { grid-template-columns: 1fr; }
  /* Empty gutter (no comments) just disappears. With comments it becomes a
     fixed bottom sheet — see the .has-comments rules below. */
  .prose-grid > .margin-area { display: none; }

  /* --- mobile comment sheet --- */
  /* Tap-to-dismiss scrim behind an open sheet. Deliberately light: a hint of
     modality without hiding the document the comments refer to. */
  .doc-view:not(.is-plain) .sheet-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 39;
    background: rgba(0, 0, 0, 0.14);
  }
  /* A draggable, frosted bottom sheet. It's always rendered at full height and
     positioned by transform: collapsed pushes it down to the peek (just the
     handle), expanded eases to translateY(0). The handle drag (see DocView)
     moves it live; .is-dragging kills the transition so it tracks the finger. */
  .doc-view:not(.is-plain) .margin-area.has-comments {
    /* Peek = handle height: grip + the Prev / count / Next row. */
    --sheet-peek: calc(64px + env(safe-area-inset-bottom));
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    height: min(72vh, 560px);
    min-height: 0;
    padding: 0 0 calc(20px + env(safe-area-inset-bottom));
    border-left: none;
    border-top: 1px solid color-mix(in oklab, var(--border) 70%, transparent);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    /* Frosted + translucent so the document still reads through behind it. */
    background: color-mix(in oklab, var(--bg-elev) 82%, transparent);
    backdrop-filter: blur(20px) saturate(1.4);
    -webkit-backdrop-filter: blur(20px) saturate(1.4);
    box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.22), var(--shadow-md);
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    transform: translateY(calc(100% - var(--sheet-peek)));
    transition: transform 280ms cubic-bezier(0.22, 0.71, 0.18, 1);
    will-change: transform;
  }
  .doc-view:not(.is-plain) .margin-area.has-comments.is-expanded {
    transform: translateY(0);
  }
  .doc-view:not(.is-plain) .margin-area.has-comments.is-dragging {
    transition: none;
  }
  /* The handle sticks to the top of the sheet while the cards scroll under it.
     touch-action:none hands the vertical drag gesture to our pointer handlers
     instead of the browser trying to scroll. */
  .margin-area.has-comments .sheet-handle {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 2;
    width: 100%;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    height: var(--sheet-peek);
    padding: 8px 18px env(safe-area-inset-bottom);
    box-sizing: border-box;
    /* Near-opaque so cards scrolling under the sticky handle don't bleed through. */
    background: color-mix(in oklab, var(--bg-elev) 90%, transparent);
    border: none;
    border-bottom: 1px solid transparent;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    cursor: grab;
    touch-action: none;
    font: inherit;
    color: var(--text);
  }
  .margin-area.has-comments.is-expanded .sheet-handle,
  .margin-area.has-comments.is-focused .sheet-handle { border-bottom-color: var(--border); }
  .margin-area.has-comments.is-dragging .sheet-handle { cursor: grabbing; }
  /* The grip is the drag affordance — a fat, obvious pill, centered. */
  .sheet-grip {
    width: 40px;
    height: 5px;
    border-radius: 999px;
    background: color-mix(in oklab, var(--text-muted) 55%, transparent);
  }
  /* Prev / count / Next pager row: step through comments in reading order. */
  .sheet-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
  }
  .sheet-label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--text-muted);
    min-width: 78px;
    text-align: center;
  }
  .sheet-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 60px;
    height: 32px;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg);
    color: var(--text);
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
    touch-action: manipulation;
  }
  .sheet-nav-btn:active:not(:disabled) { background: var(--bg-sunken); }
  .sheet-nav-btn:disabled { opacity: 0.38; cursor: default; }
  /* Cards stack naturally inside the sheet — drop the absolute layout and the
     connector elbows (which only make sense beside the prose). */
  .margin-area.has-comments .margin-card {
    position: static;
    left: auto;
    right: auto;
    top: auto !important;
    margin: 2px 14px 14px;
    transition: box-shadow 120ms, border-color 120ms;
  }
  .margin-area.has-comments .margin-card:first-of-type { margin-top: 8px; }
  .margin-area.has-comments .connectors { display: none; }

  /* Focused mode: only the tapped comment shows, so the sheet can shrink to it.
     The whole card reads as a tap target — selecting a comment shouldn't dump
     you straight into editing. */
  .margin-area.has-comments.is-focused .margin-card:not(.is-active) { display: none; }
  .margin-area.has-comments.is-focused .margin-card.is-active { margin-top: 8px; }
  .margin-area.has-comments .margin-card { cursor: pointer; }
  .margin-area.has-comments .card-note-view { cursor: pointer; }

  /* Bigger, easier edit/delete targets on touch — a comfortable 40px each,
     spaced apart, with larger glyphs. */
  .margin-area.has-comments .card-actions { gap: 6px; }
  .margin-area.has-comments .card-actions .btn-icon {
    width: 40px;
    height: 40px;
    font-size: 19px;
  }
  /* (card-note no-zoom handled by the global 16px floor at the top of this
     block.) */

  /* Floating "+ Comment" affordance over a live selection (touch). Sits at the
     bottom, clear of the OS callout which anchors to the selection itself. */
  .comment-pill {
    position: fixed;
    left: 50%;
    /* Clear the 56px comment-sheet peek (and the home indicator) so the pill
       never sits on top of the handle bar. */
    bottom: calc(72px + env(safe-area-inset-bottom));
    transform: translateX(-50%);
    z-index: 60;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 11px 20px;
    border: none;
    border-radius: 999px;
    background: var(--accent-strong);
    color: #fff;
    font: inherit;
    font-weight: 600;
    font-size: var(--text-md);
    box-shadow: 0 6px 20px color-mix(in oklab, var(--accent-strong) 35%, transparent), var(--shadow-md);
    cursor: pointer;
  }

  .prose {
    max-width: 100%;
    /* Tighter side padding; bottom padding clears the collapsed comment
       sheet peek so the last lines aren't hidden under it. */
    padding: 12px 20px 140px;
  }
  /* Keep the byline + hairline locked to the prose measure (it's designed to
     mirror .prose exactly) — otherwise it stays inset 56px over 20px body. */
  .doc-meta-wrap {
    padding-left: 20px;
    padding-right: 20px;
  }
  /* Slightly smaller display heading so it doesn't eat the first screen. */
  .prose h1 { font-size: 1.9em; }

  /* Floating chrome sits ABOVE the 64px comment-sheet peek so it's never
     occluded when a doc has comments (the common case). They keep z-index:10,
     so when the sheet is dragged open it slides over them — you read comments
     without FABs floating on top. With no comments there's no peek and they
     just rest a little higher; harmless. */
  .floating-actions {
    right: calc(var(--space-4) + env(safe-area-inset-right));
    bottom: calc(64px + var(--space-3) + env(safe-area-inset-bottom));
  }
  .identity-chip {
    right: calc(var(--space-4) + env(safe-area-inset-right));
    bottom: calc(64px + var(--space-3) + env(safe-area-inset-bottom) + 52px);
  }
  .outline-dock {
    left: calc(var(--space-4) + env(safe-area-inset-left));
    bottom: calc(64px + var(--space-3) + env(safe-area-inset-bottom));
  }
  /* The outline popover is a fixed 280px — let it fit a narrow screen. */
  .outline-panel { width: min(280px, calc(100vw - var(--space-6))); }
}

/* Coarse-pointer hygiene (independent of width). */
@media (hover: none) {
  /* Kill the gray tap-flash on interactive elements. */
  a, button, .tab, mark, .outline-item, .plus-option, .theme-option {
    -webkit-tap-highlight-color: transparent;
  }
}
