/* vault.css — /u/<slug> SPA chrome. Inherits tokens from site.css.
   Three-pane layout: rail-left (tree) | stage (wiki|map) | rail-right (meta).
   Rails are collapsible. cmd/ctrl+\ toggles the menu. */

html, body { overflow: hidden; }   /* SPA: panes scroll independently */

/* iter-260: brand-matched selection — default Chrome blue jars on the
   warm-paper aesthetic. Translucent accent against ink. */
::selection { background: rgba(184,138,61,0.28); color: var(--ink); }
::-moz-selection { background: rgba(184,138,61,0.28); color: var(--ink); }

.vault {
  position: relative; z-index: 1;
  height: calc(100dvh - var(--bar-h));
  /* Flexbox (not grid): grid `auto` columns size to content even with
     width:0 on the item, so collapsing a rail didn't shrink its track.
     Flexbox `flex: 0 0 0` actually collapses. (Operator caught
     2026-05-08: rails wouldn't collapse + toggle disappeared.) */
  display: flex;
  border-top: 1px solid var(--rule);
}
.stage { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }

/* ─── rails ──────────────────────────────────────────────────────────── */
.rail {
  position: relative;
  background: var(--paper);
  /* No transition on width / flex-basis — Chrome 148 wouldn't animate
     across an inline-!important value change, leaving the rail visually
     stuck at the pre-collapse width. Snap to the new size instead. */
}
/* site.css inherited a `.rail--left { width: 280px }` rule from the
   prototype (legacy grid era). In flexbox, width acts as flex-basis,
   so my `flex: 0 0 0 !important` for collapsed didn't override it
   (browser keeps the width-derived basis). Use longhand on both
   states to win the cascade unambiguously. (Operator caught
   2026-05-08: rail width stayed 280 even with collapsed class.) */
/* Default rail widths — no !important here so the JS click handler's
   inline style (set on toggle) can WIN the cascade. Site.css has its
   own legacy `.rail--left { width: 280px }` from the prototype but
   inline style + flex-basis from JS overrides any non-important rule. */
.rail--left  { border-right: 1px solid var(--rule); width: 280px; flex: 0 0 280px; min-width: 0; }
.rail--right { border-left:  1px solid var(--rule); width: 300px; flex: 0 0 300px; min-width: 0; }
.rail--collapsed { overflow: visible; }
.rail--collapsed .rail__inner { display: none; }
.rail__inner {
  width: 100%;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
}
.rail__toggle {
  position: absolute;
  top: 14px;
  width: 22px; height: 44px;
  border: 1px solid var(--rule);
  background: var(--paper-2);
  color: var(--ink-2);
  cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 12px;
  z-index: 4;
  transition: background .12s, border-color .12s;
}
@media (hover: hover) {
  .rail__toggle:hover { background: var(--paper-3); color: var(--ink); }
}
.rail--left  .rail__toggle { right: -11px; border-radius: 0 6px 6px 0; }
.rail--right .rail__toggle { left:  -11px; border-radius: 6px 0 0 6px; }
/* When collapsed (rail width = 0), flip the toggle from "peek into stage"
   to "stick at the visible edge" so it stays clickable. */
.rail--left.rail--collapsed  .rail__toggle { right: auto; left: 0; }
.rail--right.rail--collapsed .rail__toggle { left:  auto; right: 0; }

/* ─── tree ───────────────────────────────────────────────────────────── */
.tree__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 22px 12px;
  border-bottom: 1px solid var(--rule-2);
}
.tree__h {
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--faint);
}
.tree__count {
  font-family: var(--mono); font-size: 10.5px; color: var(--accent);
}
.tree ul { list-style: none; margin: 0; padding: 0; }
.tree li { margin: 0; }
.tree-folder {
  padding: 6px 22px;
  font-family: var(--mono); font-size: 12.5px; color: var(--ink);
  font-weight: 500; letter-spacing: -0.01em;
  cursor: pointer;
  display: flex; align-items: center; gap: 8px;
  user-select: none;
}
.tree-folder .ch {
  color: var(--faint);
  transition: transform .15s;
  display: inline-block;
}
.tree-folder.is-collapsed .ch { transform: rotate(-90deg); }
/* iter-282: match mobile chevron pattern. Drop the trailing "/" on
   folder labels; add a " …" cue on collapsed folders so the "more
   inside" affordance lives there instead of in a static slash.
   Operator's mobile-side comment: "u had seam and dot dot dot
   making it ugly, just use one (dot dot dot)". Same here. */
.tree-folder.is-collapsed .label::after {
  content: " …";
  color: var(--faint);
}
.tree-folder.is-collapsed + ul { display: none; }
.tree-folder ul { padding-left: 16px; }   /* nested indent */
/* iter-282: no transform needed when no chevron — root folders
   render their label only, no glyph to rotate. Nested folders get
   `+`/`−` rendered directly so transform stays no-op there too. */

.tree-leaf {
  padding: 5px 22px 5px 32px;
  font-family: var(--mono); font-size: 12.5px;
  color: var(--ink-2);
  cursor: pointer;
  display: flex; align-items: center; gap: 8px;
  border-left: 2px solid transparent;
  transition: background .12s, color .12s;
  /* Browser-native virtualization: skip layout + paint for leaves
     scrolled off-screen. `contain-intrinsic-size` is the placeholder
     height so the scrollbar stays accurate before any leaf has been
     measured. Falls back gracefully on browsers without
     content-visibility (Safari < 18) — they just render everything.
     Net effect at 10000 leaves: initial paint drops dramatically
     since only the ~30 visible rows actually lay out. */
  content-visibility: auto;
  contain-intrinsic-size: 0 24px;
}
@media (hover: hover) {
  .tree-leaf:hover { background: var(--paper-2); color: var(--ink); }
}
.tree-leaf.is-active {
  background: var(--paper-2);
  color: var(--ink);
  font-weight: 500;
  border-left-color: var(--accent);
}
.tree-leaf .glyph { color: var(--faint); width: 12px; flex-shrink: 0; }
.tree-leaf.is-active .glyph { color: var(--accent); }
/* Let long page titles wrap to a 2nd line instead of truncating with
   ellipsis. Chapter titles like "Ch07 The Photograph Without A Date"
   used to become "...Photograph Withou..." — readable only on hover.
   Two lines is fine; line-height keeps the density tight. (issue #161) */
.tree-leaf .name { flex: 1; min-width: 0; line-height: 1.3; word-break: break-word; }

.tree-empty {
  padding: 30px 22px;
  font-family: var(--serif); font-size: 14px; color: var(--muted);
  font-style: italic;
}

/* ─── stage ──────────────────────────────────────────────────────────── */
.stage {
  display: flex; flex-direction: column;
  min-width: 0;
  background: var(--paper);
}
.stage__bar {
  display: flex; align-items: center; gap: 12px;
  padding: 12px clamp(20px, 3vw, 36px);
  border-bottom: 1px solid var(--rule-2);
  /* Keep bar height stable when .seg is yanked out of flow (see below). */
  min-height: 48px;
}
.stage__crumbs {
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  letter-spacing: 0.04em;
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stage__crumbs a, .stage__crumbs span { color: var(--ink-2); text-decoration: none; }
.stage__crumbs .sep { color: var(--rule); margin: 0 6px; }
.stage__crumbs b { color: var(--ink); font-weight: 500; }
.stage__crumb-seg {
  appearance: none; background: transparent; border: 0;
  padding: 0; margin: 0;
  font: inherit; color: var(--ink-2);
  cursor: pointer;
  transition: color .12s;
}
@media (hover: hover) {
  .stage__crumb-seg:hover { color: var(--accent); }
}

.seg {
  display: inline-flex;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 3px;
  gap: 2px;
  /* Sits at the right end of the stage bar in flow. An earlier iter
     pinned it to viewport-50% (`position: absolute; left: 50%`) so
     it stayed put when the sidebars collapsed — but the absolute
     placement floated it over the breadcrumb text, which the
     operator caught on 2026-05-11. Right-aligned via margin-left
     auto keeps the toggle in a consistent place (always rightmost
     in the stage bar) without clipping the crumb. */
  margin-left: auto;
  flex-shrink: 0;
}
.seg button {
  /* iter-291: min-height 38 → 44 (HIG floor). Wiki/mindmap toggle
     is a primary view switcher — sub-44 forced the user to aim. */
  background: transparent; border: 0;
  min-height: 44px;
  padding: 6px 14px;
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  color: var(--ink-2); cursor: pointer;
  border-radius: 6px;
  display: inline-flex; align-items: center; gap: 6px;
  transition: background .12s, color .12s;
}
@media (hover: hover) {
  .seg button:hover { color: var(--ink); }
}
.seg button.is-active { background: var(--ink); color: var(--paper); }
/* r20 iter-142: respect the `hidden` attribute. vault.html toggles
   `mapBtn.hidden = !hasPages` (iter-119) to hide the mindmap toggle
   on empty wikis, but the `.seg button { display: inline-flex }` rule
   above overrides the UA `[hidden] { display: none }` default — so
   the toggle painted anyway and tapping it did nothing. */
.seg button[hidden] { display: none; }
.seg .glyph { font-size: 13px; }

.view { display: none; min-height: 0; min-width: 0; flex: 1; }
.view.is-active { display: flex; }

/* ─── doc ────────────────────────────────────────────────────────────── */
.doc {
  flex: 1; min-height: 0;
  overflow-y: auto;
  /* iter-363f / UXW-008: top padding clamp(28,4vh,48) read as
     ~130px dead zone above "RECENTLY EDITED" at desktop 1280.
     Halved the top contribution; horizontal reading-column padding
     and bottom unchanged. */
  padding: clamp(16px, 2.6vh, 32px) clamp(28px, 5vw, 56px) clamp(28px, 4vh, 48px);
}
/* The article content centers in the stage via a max-width inner wrapper.
   Reading column ≈ 65–75 chars at our type sizes. */
.doc__inner {
  max-width: 760px;
  margin: 0 auto;
}
.doc h1 {
  /* iter-260: line-height 1.14 so wrapped h1s breathe — at 375px the
     36px serif title wraps for any page name >16 chars, and at lh 1.02
     descenders ("p"/"g") on the top line nearly touched cap-tops on
     the bottom line. Desktop 52px wraps less but also reads tight at
     1.02. text-wrap: balance keeps the wrap point sensible. */
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(36px, 4vw, 52px); line-height: 1.14;
  letter-spacing: -0.025em; color: var(--ink);
  margin: 0 0 6px;
  text-wrap: balance;
}
.doc h1 em { font-style: italic; color: var(--accent); font-weight: 500; }
.doc__meta {
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  letter-spacing: 0.04em; margin-bottom: 22px;
  display: flex; gap: 14px; flex-wrap: wrap;
}
.doc__meta b { color: var(--ink-2); font-weight: 500; }

.doc__chip {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: 0.06em;
}
.doc__chip--public  { background: rgba(42, 111, 77, 0.10); color: #2a6f4d; }
.doc__chip--private { background: rgba(31, 26, 18, 0.06); color: var(--ink-2); }

.doc h2 {
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(22px, 1.9vw, 28px); line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--ink); margin: 28px 0 12px;
}
.doc h2 em { font-style: italic; color: var(--accent); }
.doc h3 {
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(18px, 1.4vw, 21px); letter-spacing: -0.01em;
  color: var(--ink); margin: 22px 0 10px;
}
.doc h3 em { font-style: italic; color: var(--accent); }
/* iter-292: h4-h6 + hr + img were unstyled — fell through to UA
   defaults (bold sans, hard charcoal line, 100%-of-natural images
   overflowing the column). Carry the same serif voice down the
   heading tier and tame hr/img to brand surfaces. */
.doc h4 {
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(16px, 1.2vw, 18px); letter-spacing: -0.005em;
  color: var(--ink); margin: 18px 0 8px;
}
.doc h5, .doc h6 {
  font-family: var(--mono); font-weight: 500;
  font-size: 12.5px; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2); margin: 16px 0 6px;
}
.doc hr {
  border: 0; height: 1px;
  background: var(--rule-2);
  margin: 22px 0;
}
.doc img {
  max-width: 100%; height: auto;
  border-radius: 4px;
  margin: 12px 0;
}
.doc p {
  font-family: var(--serif); font-size: clamp(15.5px, 1.15vw, 17px);
  line-height: 1.6; color: var(--ink-2);
  margin: 0 0 14px;
  max-width: 64ch;
  text-wrap: pretty;
}
.doc p em { font-style: italic; color: var(--ink); font-weight: 500; }
.doc strong, .doc b { color: var(--ink); font-weight: 600; }

.doc a.wikilink {
  color: var(--accent); text-decoration: none;
  border-bottom: 1px dotted var(--accent);
  padding-bottom: 1px;
  cursor: pointer;
  /* r20 iter-192: wikilinks are the most-touched element on a wiki —
     the reader's hand on the page. Pre-fix the hover state snapped
     (color flipped, border changed dotted→solid instantly). Adding a
     transition + a barely-there accent-soft underline-fill makes the
     link feel like it WAKES UP under the cursor instead of toggling.
     Uses the iter-182 motion tokens hoisted into site.css :root so
     wikilinks ride the same beat as the drawer + first-keep toast. */
  background-image: linear-gradient(var(--accent-soft), var(--accent-soft));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  background-position: 0 0;
  transition: color var(--dur-tap) var(--ease-ios),
              border-bottom-color var(--dur-tap) var(--ease-ios),
              background-size var(--dur-push) var(--ease-ios);
}
@media (hover: hover) {
  .doc a.wikilink:hover {
    color: var(--accent-2);
    border-bottom-style: solid;
    /* Soft brass wash sweeps left→right under the link as it wakes. */
    background-size: 100% 100%;
  }
}
.doc a:not(.wikilink) {
  color: var(--ink-2);
  text-decoration: underline; text-underline-offset: 3px;
}

.doc ul, .doc ol {
  margin: 0 0 14px; padding-left: 22px;
  font-family: var(--serif); font-size: 16px; line-height: 1.5;
  color: var(--ink-2);
}
.doc ul li, .doc ol li { margin-bottom: 4px; }

.doc blockquote {
  margin: 18px 0; padding: 4px 0 4px 18px;
  border-left: 2px solid var(--accent);
  font-family: var(--serif); font-style: italic;
  font-size: 16px; color: var(--ink); line-height: 1.45;
}

/* Admonition callouts. Source can be either pymdown's `!!! type body`
   or GitHub's `> [!type] body` — the api/vault preprocessor rewrites
   the latter into the former before rendering. Both end up as
   <div class="admonition <type>"><p class="admonition-title">…</p>
   <p>…</p></div>. */
.doc .admonition {
  margin: 18px 0; padding: 12px 16px 14px;
  border-left: 3px solid var(--accent);
  background: rgba(184,138,61,0.07);
  border-radius: 0 4px 4px 0;
  font-family: var(--serif);
  font-size: 15.5px; line-height: 1.5;
  color: var(--ink);
}
.doc .admonition > :first-child { margin-top: 0; }
.doc .admonition > :last-child  { margin-bottom: 0; }
.doc .admonition .admonition-title {
  font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 6px;
  font-weight: 500;
}
/* Type-specific accents — pymdown auto-generates the title from the
   class, so a `!!! agent` gets `<p class="admonition-title">Agent</p>`
   without us writing per-type rules. Color the rail per type so the
   eye can scan a long page. Unmatched types fall back to the default
   accent. */
.doc .admonition.note,
.doc .admonition.info,
.doc .admonition.tip       { border-left-color: #2A6F4D; background: rgba(42,111,77,0.06); }
.doc .admonition.note .admonition-title,
.doc .admonition.info .admonition-title,
.doc .admonition.tip .admonition-title { color: #2A6F4D; }
.doc .admonition.warning,
.doc .admonition.caution   { border-left-color: #B85F40; background: rgba(184,95,64,0.07); }
.doc .admonition.warning .admonition-title,
.doc .admonition.caution .admonition-title { color: #B85F40; }
.doc .admonition.danger    { border-left-color: #8A2B1F; background: rgba(138,43,31,0.07); }
.doc .admonition.danger .admonition-title { color: #8A2B1F; }

.doc code {
  font-family: var(--mono); font-size: 0.86em;
  background: var(--paper-2); padding: 1px 6px; border-radius: 4px;
  color: var(--ink-2);
}
.doc pre {
  background: var(--paper-2); border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  padding: 12px 14px; overflow-x: auto;
  margin: 16px 0;
}
.doc pre code { background: transparent; padding: 0; font-size: 13px; }

/* Markdown tables. api/vault.py wraps every <table> in .md-table so
   wide tables get horizontal scroll instead of blowing out the doc
   column. */
.doc .md-table {
  overflow-x: auto;
  max-width: 100%;
  margin: 16px 0;
  border: 1px solid var(--rule-2);
  border-radius: 6px;
}
.doc .md-table table {
  border-collapse: collapse;
  font-family: var(--sans, system-ui, sans-serif);
  font-style: normal;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  min-width: 100%;
}
.doc .md-table th,
.doc .md-table td {
  border-right: 1px solid var(--rule-2);
  border-bottom: 1px solid var(--rule-2);
  padding: 8px 12px;
  text-align: left;
  vertical-align: top;
}
.doc .md-table th:last-child,
.doc .md-table td:last-child { border-right: none; }
.doc .md-table tr:last-child td { border-bottom: none; }
.doc .md-table th {
  background: var(--paper-2);
  font-weight: 500;
  color: var(--ink);
}
.doc .md-table td p { margin: 0; font-style: normal; font-family: inherit; }

.doc__loading, .doc__empty {
  font-family: var(--serif); font-size: 16px;
  color: var(--muted); padding: 40px 0;
  font-style: italic;
}
/* r20 iter-184: owner-mode empty-state on /u/<own-slug> when the wiki
   has zero pages — directive headline + primary CTA to /me/install.
   Stacks the prompt + action so the next ritual move is the natural
   next click. */
.doc__empty--owner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
}
.doc__empty--owner p {
  margin: 0;
  font-size: clamp(24px, 2.6vw, 32px);
  font-family: var(--serif); font-style: italic; font-weight: 500;
  color: var(--ink); line-height: 1.2;
}

/* "recently edited" default pane — shown when no page is selected.
   Calm, no boxes/borders/icons. Italic-serif titles, tabular mono
   dates aligned right. Designed to feel like a magazine contents
   page rather than a tool surface. */
.recent-pane { padding: 8px 0; }
.recent__list {
  list-style: none; padding: 0; margin: 14px 0 22px;
  display: flex; flex-direction: column;
}
.recent__list li { margin: 0; }
.recent__link {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--rule-2);
  text-decoration: none; color: inherit;
}
.recent__list li:last-child .recent__link { border-bottom: 0; }
.recent__main {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.recent__title {
  font-family: var(--serif); font-style: italic; font-weight: 500;
  font-size: 17px; line-height: 1.3; color: var(--ink);
  /* allow 2 lines so disambiguating suffixes aren't truncated. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.recent__path {
  font-family: var(--mono); font-size: 11px;
  color: var(--faint); letter-spacing: 0.04em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recent__when {
  flex: 0 0 auto;
  font-family: var(--mono); font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--faint); letter-spacing: 0.04em;
  padding-top: 4px;
}
@media (hover: hover) {
  .recent__link:hover .recent__title { color: var(--accent); }
}
.recent__hint {
  font-family: var(--mono); font-size: 11px;
  color: var(--faint); letter-spacing: 0.04em;
  margin: 4px 0 0;
}

/* ─── map ────────────────────────────────────────────────────────────── */
/* Flat paper. No radial gradient, no grid overlay — label pills are
   paper-coloured and they MUST match the canvas exactly, or the
   label-on-canvas seam reads as sloppy. */
.map {
  flex: 1; min-height: 0;
  position: relative;
  overflow: hidden;
  background: var(--paper);
}
/* Cytoscape canvas — replaces the SVG renderer (operator-caught
   scale ceiling: server-positioned SVG hairballs past ~200 nodes).
   Cytoscape draws into its own <canvas> children inside this div, so
   we just need a sized box with a transparent background (the .map
   gradient + grid still show through). touch-action: none lets
   cytoscape own pinch + pan — without it the browser handles pinch
   as a viewport zoom and tap coords drift from the rendered canvas. */
.map__cy {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: transparent;
  touch-action: none;
  overflow: hidden;
}
.map__legend {
  position: absolute; right: 18px; top: 18px;
  background: var(--paper-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 10px 12px;
  display: flex; flex-direction: column; gap: 5px;
  z-index: 3;
  font-family: var(--mono); font-size: 10.5px; color: var(--ink-2);
}
.map__legend .row {
  display: flex; align-items: center; gap: 8px;
  appearance: none; background: transparent; border: 0;
  padding: 3px 8px; margin: 0;
  font: inherit; color: inherit;
  border-radius: 4px;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.map__legend .row[hidden] { display: none; }
.map__legend .row__label { flex: 1; min-width: 0; text-align: left; }
.map__legend .row__x {
  font-family: var(--serif); font-size: 13px; line-height: 1;
  margin-left: 4px;
  opacity: 0;
  transition: opacity .15s ease;
}
@media (hover: hover) {
  .map__legend .row:hover { background: rgba(0,0,0,0.04); }
}
.map__legend .row.is-active {
  background: var(--ink);
  color: var(--paper);
}
.map__legend .row.is-active .row__x { opacity: 0.85; }
.map__legend .row.is-active .sw { border-color: var(--paper); }
.map__legend .sw {
  width: 9px; height: 9px; border-radius: 50%;
  border: 1px solid var(--rule);
  flex-shrink: 0;
}
/* "showing 300 of 10,000 · most recent" under the section rows.
   Sits inside .map__legend so it shares the panel; muted italic
   so it reads as context, not a control.

   order:999 pins it to the bottom of the flex column. The legend
   appends dynamically-discovered sections AFTER the hardcoded rows
   in syncLegendVisibility, so without an explicit order the
   indicator gets sandwiched between hardcoded + dynamic rows
   (visible mid-list, ugly). r20 iter-77. */
.map__legend__cap {
  order: 999;
  margin-top: 4px;
  padding: 5px 8px 1px;
  border-top: 1px solid var(--rule);
  font-family: var(--serif); font-style: italic; font-size: 11px;
  color: var(--muted);
  line-height: 1.3;
}
/* Slim indeterminate progress bar — always shown during map render,
   fades out on layoutstop. No node-count branching: 8 nodes and
   10000 nodes get the same loading affordance. */
.map__progress {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: var(--paper-3);
  overflow: hidden;
  z-index: 5;
  opacity: 1;
  transition: opacity 240ms ease-out;
  pointer-events: none;
}
.map__progress.is-done {
  opacity: 0;
}
.map__progress::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: -30%;
  width: 30%;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: map-progress 1.2s ease-in-out infinite;
}
@keyframes map-progress {
  0%   { transform: translateX(0); }
  100% { transform: translateX(433%); }
}

/* Section colors + node/edge appearance are now driven by the
   cytoscape style block inside vault.html (cytoscape can't read CSS
   custom properties, so palette values are pulled from
   getComputedStyle at init time and applied via JS). The
   .map__legend swatches in HTML still use these hex values inline. */

/* ─── meta panel (right rail) ────────────────────────────────────────── */
.meta {
  padding: 22px 22px 32px;
  display: flex; flex-direction: column; gap: 22px;
}
.meta__sec {
  border-top: 1px dashed var(--rule-2);
  padding-top: 16px;
}
.meta__sec:first-child { border-top: 0; padding-top: 0; }
.meta__title {
  font-family: var(--mono); font-size: 10.5px;
  color: var(--accent); letter-spacing: 0.18em; text-transform: uppercase;
  margin: 0 0 10px;
}
.meta__list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.meta__list a {
  font-family: var(--mono); font-size: 12px;
  color: var(--ink-2); text-decoration: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 0;
  border-bottom: 1px solid var(--rule-2);
  cursor: pointer;
}
@media (hover: hover) {
  .meta__list a:hover { color: var(--accent); }
}
.meta__list .ct { color: var(--faint); font-size: 10.5px; }
.meta__list .empty {
  font-family: var(--serif); font-style: italic; font-size: 13px;
  color: var(--muted);
  padding: 6px 0;
}

.frontmatter {
  font-family: var(--mono); font-size: 11.5px;
  background: var(--paper-2); border: 1px solid var(--rule-2);
  border-radius: var(--radius);
  padding: 12px 14px;
  color: var(--ink-2);
  line-height: 1.55;
  white-space: pre-wrap; word-break: break-word;
}
.frontmatter .k { color: var(--accent); }

/* Right-pane action surfaces — quiet, ink-on-paper. NOT the warm-yellow
   pending-card style (.prow__btn) — that belongs to /me's pending stack. */
.meta__sub {
  font-family: var(--serif); font-size: 13.5px; line-height: 1.45;
  color: var(--muted);
  margin: 6px 0 0;
}
.meta__sub em { font-style: italic; color: var(--ink-2); font-weight: 500; }
.meta__action {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 8px;
  background: transparent; border: 0; padding: 0;
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  color: var(--ink-2);
  cursor: pointer;
  transition: color .12s;
}
@media (hover: hover) {
  .meta__action:hover { color: var(--accent); }
}
.meta__action[disabled] { color: var(--faint); cursor: default; }
.meta__action .ar { color: var(--accent); transition: transform .12s; }
@media (hover: hover) {
  .meta__action:hover .ar { transform: translateX(2px); }
}

/* r20 iter-187 / taste r30 P1: visitor-only "whose mind is this" block.
   Sits at the top of the right rail on /u/<slug> when the viewer is
   NOT the owner. Replaces what was previously a visibility chip ("public ·
   anyone with the link can read this") that read as ops-vocab leaking
   into the reading surface. */
.meta__sec--visitor-intro {
  padding-bottom: 14px;
}
.meta__sec--visitor-intro .meta__title {
  font-family: var(--serif);
  font-style: normal;
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.3;
  margin-bottom: 6px;
}
.meta__sec--visitor-intro .meta__sub {
  color: var(--ink-2);
  margin-bottom: 10px;
}
.meta__action--cta {
  text-decoration: none;
}

.meta__btn-row { display: flex; gap: 10px; margin-top: 10px; }
.meta__btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px;
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
@media (hover: hover) {
  .meta__btn:hover { background: var(--paper-2); border-color: var(--ink-2); color: var(--ink); }
}
.meta__btn--primary {
  background: var(--ink); color: var(--paper); border-color: var(--ink);
}
@media (hover: hover) {
  .meta__btn--primary:hover { background: #2c241a; color: var(--paper); }
}

/* ─── responsive ─────────────────────────────────────────────────────── */
/* Removed the 1100px auto-hide on .rail--right — that pane now carries
   visibility-toggle / approve / timeline (operator's "right pane should
   be prioritized"), too important to hide silently. The user can still
   collapse it manually via the toggle. Only mobile (760px) collapses it. */
/* Right-rail hide kicks in earlier than the full mobile-fallback
   breakpoint. With both rails visible the chrome is 280 + 300 = 580px,
   so a 780px desktop window leaves only 200px for the stage — the
   recent-edits stack truncates titles mid-word. Hide the right rail
   first so the stage stays readable. Below 760px the left rail also
   drops out and the layout stacks vertically (see next block). Real
   mobile UAs render mobile/vault.html and never hit either rule. */
@media (max-width: 980px) {
  .rail--right { display: none; }
}

@media (max-width: 760px) {
  html, body { overflow: auto; }
  .vault { grid-template-columns: 1fr; height: auto; }
  .rail--left { display: none; }
  .map { height: 60vh; }
  .stage__bar { padding: 12px 18px; }
  .doc { padding: clamp(20px, 3vh, 32px) 22px; }
}

/* ─── In-page TOC (3+ H2 jump menu) ──────────────────────────────────── */
.page-toc {
  margin: 20px 0 26px;
  border: 1px solid var(--rule-2);
  border-radius: 6px;
  background: var(--paper-2);
}
.page-toc__summary:focus-visible {
  /* iter-363e Lane H: keyboard nav needs a visible focus indicator.
     `outline: none` (line below) kills the default; replace with a
     subtle accent ring for tab-stops only (not mouse clicks). */
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px;
}
.page-toc__summary {
  cursor: pointer;
  padding: 10px 16px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  list-style: none;
  outline: none;
  user-select: none;
}
.page-toc__summary::-webkit-details-marker { display: none; }
.page-toc__summary::before {
  content: "▸"; display: inline-block;
  margin-right: 10px;
  transition: transform .18s ease;
  font-family: var(--serif); font-size: 13px;
  color: var(--accent);
}
.page-toc[open] .page-toc__summary::before { transform: rotate(90deg); }
.page-toc[open] .page-toc__summary { border-bottom: 1px solid var(--rule-2); }
.page-toc__list {
  list-style: none; padding: 10px 16px 14px; margin: 0;
}
.page-toc__list li { margin: 2px 0; }
.page-toc__link {
  display: block;
  font-family: var(--serif); font-style: italic;
  font-size: 15px; color: var(--ink-2);
  text-decoration: none;
  padding: 4px 0;
}
@media (hover: hover) {
  .page-toc__link:hover { color: var(--accent); }
}

/* ─── Inline backlinks + related (page foot) ─────────────────────────── */
.page-foot {
  display: block;
  margin: 36px 0 0;
  padding: 22px 0 0;
  border-top: 1px solid var(--rule-2);
}
.page-foot__sec { margin: 0 0 26px; }
.page-foot__sec:last-child { margin-bottom: 0; }
.page-foot__head {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 12px;
  font-weight: 500;
}
.page-foot__list { list-style: none; padding: 0; margin: 0; }
/* `.doc a.page-foot__link` (0,1,1) — bare `.page-foot__link` (0,1,0)
   lost to `.doc a:not(.wikilink)` (0,1,1) which set text-decoration:
   underline. Net effect was double underline: the body-link
   underline on top of the dotted separator border, on every related-
   page row at the bottom of an article. */
.doc a.page-foot__link {
  display: block;
  padding: 10px 0;
  border-bottom: 1px dotted var(--rule-2);
  text-decoration: none; color: inherit;
}
.page-foot__list li:last-child .page-foot__link { border-bottom: 0; }
.page-foot__title {
  font-family: var(--serif);
  font-size: 16px; font-style: italic;
  color: var(--ink);
  line-height: 1.3;
}
@media (hover: hover) {
  .page-foot__link:hover .page-foot__title { color: var(--accent); }
}
.page-foot__sub {
  display: flex; align-items: baseline; gap: 10px;
  margin-top: 4px;
}
.page-foot__path {
  flex: 1; min-width: 0;
  font-family: var(--mono); font-size: 11px;
  color: var(--muted); letter-spacing: 0.04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.page-foot__kind {
  flex-shrink: 0;
  font-family: var(--mono); font-size: 10px;
  color: var(--accent); letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 1px 6px; border-radius: 3px;
  background: rgba(184,138,61,0.10);
}

/* ─── Search modal ───────────────────────────────────────────────────── */
/* `.bar .bar-link--btn` (not bare `.bar-link--btn`) — the search chip
   also carries the `.bar-link` class, and site.css's `.bar .bar-link`
   has higher specificity. Without scoping, the chip inherited bar-link
   padding (10px 10px) and margin-right (-10px), so it ballooned and
   crashed into "sign in →" on the right edge. */
.bar .bar-link--btn {
  background: transparent; border: 1px solid var(--rule-2);
  font-family: var(--mono); font-size: 12px;
  color: var(--ink-2); padding: 4px 9px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  margin-right: 12px;
  min-height: 0; min-width: 0;
}
@media (hover: hover) {
  .bar .bar-link--btn:hover { background: var(--paper-2); color: var(--ink); }
}
.bar-link__kbd {
  font-size: 10px; color: var(--faint);
  border: 1px solid var(--rule-2);
  padding: 0 5px; border-radius: 3px;
  background: var(--paper);
}
/* iter-348 / r171 P3: keyboard-shortcut chips ("/" + "⌘K") are
   cosmetic noise on touch devices — there's no keyboard. Hide
   them on hover-less inputs. The search-trigger button itself
   stays visible + tappable; only the kbd hint suffixes drop. */
@media (hover: none) {
  .bar-link__kbd { display: none; }
}

.search-modal { position: fixed; inset: 0; z-index: 200; display: none; }
.search-modal.is-open { display: block; }
.search-modal__scrim {
  position: absolute; inset: 0;
  background: rgba(20,18,12,0.42);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.search-modal__card {
  position: absolute; left: 50%; top: 12vh;
  transform: translateX(-50%);
  width: min(560px, calc(100vw - 32px));
  max-height: min(70vh, 600px);
  display: flex; flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 18px 56px rgba(20,18,12,0.22);
}
.search-modal__input {
  appearance: none;
  font-family: var(--serif);
  font-size: 19px;
  padding: 18px 20px;
  border: 0; border-bottom: 1px solid var(--rule-2);
  background: transparent;
  color: var(--ink);
  outline: none;
}
/* iter-363e Lane H: focus on keyboard nav only (Cmd+K → search is
   keyboard-primary). Border-bottom is the visual; bump it to accent
   on focus so the input "wakes up" but no harsh ring. */
.search-modal__input:focus-visible { border-bottom-color: var(--accent); border-bottom-width: 2px; }
.search-modal__input::placeholder { color: var(--faint); font-style: italic; }
.search-modal__results {
  flex: 1; min-height: 0;
  overflow-y: auto;
  padding: 6px 0;
}
.search-modal__hint, .search-modal__empty {
  font-family: var(--mono);
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--faint);
  padding: 20px;
  text-align: center;
}
.search-result {
  display: block; padding: 10px 20px;
  border-left: 2px solid transparent;
  cursor: pointer;
  text-decoration: none; color: inherit;
}
@media (hover: hover) {
  .search-result:hover { background: var(--paper-2); }
}
.search-result.is-active {
  background: var(--paper-2);
  border-left-color: var(--accent);
}
.search-result__title {
  font-family: var(--serif); font-size: 16px;
  color: var(--ink); font-style: italic;
  line-height: 1.25;
}
.search-result__meta {
  font-family: var(--mono); font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.06em;
  margin-top: 4px;
}
.search-result__type {
  display: inline-block;
  margin-right: 6px; padding: 1px 6px;
  border-radius: 3px;
  background: var(--paper-2);
  color: var(--ink-2);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Vault three-pane: shrink to make room for chrome on top/bottom.
   Shared chrome styles for .visitor-foot and .preview-banner live in
   site.css (they're used by mobile too). */
body.has-visitor-foot .visitor-foot {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 6;
}
body.has-visitor-foot .vault {
  height: calc(100dvh - var(--bar-h) - var(--visitor-foot-h));
}
body.is-owner-previewing .vault {
  height: calc(100dvh - var(--bar-h) - var(--preview-banner-h));
}

/* ─── owner: "preview as visitor" pill in the right rail ──────────── */
.meta__owner-tools {
  padding: 14px 20px 18px;
  border-top: 1px dashed var(--rule);
  margin-top: auto;
}
.meta__pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: 11.5px;
  letter-spacing: 0.02em;
  color: var(--ink-2);
  text-decoration: none;
  padding: 6px 10px;
  border: 1px solid var(--rule-2);
  border-radius: 999px;
  transition: background .12s, color .12s, border-color .12s;
}
@media (hover: hover) {
  .meta__pill:hover {
    background: var(--paper-2);
    color: var(--accent);
    border-color: var(--accent);
  }
}
/* Push the owner-tools to the bottom of the right rail. */
.rail--right .rail__inner {
  display: flex; flex-direction: column;
}
.rail--right .meta { flex: 1; min-height: 0; overflow-y: auto; }

/* ─── print ───────────────────────────────────────────────────────────
   Wiki pages should be printable. The SPA chrome (top bar, drawer,
   left/right rails, mindmap, modals, stage bar/crumbs) is interactive
   navigation that wastes ink on paper. Hide all of that and let just
   the article body flow. Reset the body overflow so multi-page
   articles aren't clipped to the viewport.
   (uiux audit r3, 2026-05-18) */
@media print {
  html, body {
    overflow: visible !important;
    height: auto !important;
    background: #FFF !important;
    color: #000 !important;
  }
  body::before { display: none !important; }
  .bar, .drawer, .drawer-scrim, .rail, .map, .vmodal, .agent-modal,
  .stage__bar, .stage__crumbs, .public-foot, .visitor-foot,
  .preview-banner {
    display: none !important;
  }
  .vault, .stage, .stage__body {
    display: block !important;
    overflow: visible !important;
    height: auto !important;
    min-height: 0 !important;
    border: 0 !important;
  }
  .doc {
    max-width: 100% !important;
    padding: 0 !important;
    color: #000 !important;
  }
  a { color: #000 !important; text-decoration: underline; }
}
