/* library.css — the reading room's stylesheet, shared by every page
   (page.html and the login turnstile alike). Loads after chroma.css and
   before the operator theme (/theme/site.css, DEMARKUS_THEME_CSS), so a
   theme overrides anything here; the :root tokens below are the intended
   surface. */

/* The Tufte pane (ADR 0005, R1): ~60ch reading column + working margin.
   Off-white/off-black; color is reserved for signal (the status badge).
   R2 stacks the same pane on the trail canvas. */
:root {
  color-scheme: light dark;
  --margin-w: 250px;
  --gutter: 2.5rem;
  --paper: light-dark(#fffff8, #151515);
  --ink: light-dark(#111111, #dddddd);
  --faint: light-dark(#11111120, #dddddd28);
  --muted: light-dark(#11111199, #dddddd99);
  /* Type + signal tokens: every font and signal color routes through
     these so an operator's /theme/site.css (DEMARKUS_THEME_CSS) can
     rebrand the room by overriding :root alone. */
  --font-prose: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, Georgia, serif;
  --font-ui: system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  --ok: light-dark(#1a7f37, #3fb950);
  --warn: light-dark(#9a6700, #d29922);
  --danger: light-dark(#cf222e, #f85149);
  --info: light-dark(#0969da, #4493f8);
  --accent: light-dark(#8250df, #ab7df8);
}
body { margin: 0 auto; padding: 1.5rem 1.25rem 4rem;
       max-width: calc(60ch + var(--gutter) + var(--margin-w));
       background: var(--paper); color: var(--ink);
       font: 17px/1.55 var(--font-prose); }
nav { display: flex; gap: 1rem; align-items: baseline; margin-bottom: 2rem;
      padding-bottom: .6rem; border-bottom: 1px solid var(--faint); }
nav a.brand { font-weight: 600; text-decoration: none; white-space: nowrap; color: inherit; }
nav a.brand img { height: 1.15em; vertical-align: -.2em; margin-right: .45rem; }
nav .world { font-size: .8rem; color: var(--muted); flex: 1; }
nav .user { font-size: .78rem; color: var(--muted); font-family: var(--font-ui);
            white-space: nowrap; }
nav form { margin: 0; }
button.signout { font: inherit; font-size: .8rem; padding: .3rem .7rem;
      border: 1px solid var(--faint); border-radius: 6px; background: transparent;
      color: inherit; cursor: pointer; white-space: nowrap; }
button.signout:hover { background: var(--faint); }

/* ── The pane: body column + margin ─────────────────────────────── */
.doc-pane { display: grid; grid-template-columns: minmax(0, 60ch) var(--margin-w);
            column-gap: var(--gutter); }
.doc-body { grid-column: 1; counter-reset: sidenote; }
.doc-margin { grid-column: 2; font-family: var(--font-ui); }
header.doc .meta { font-size: .8rem; color: var(--muted); }
header.doc .meta a, .doc-margin a { color: inherit; }
h1 { font-size: 1.7rem; font-weight: 500; line-height: 1.2; margin: 0 0 1rem; }
h2 { font-size: 1.3rem; font-weight: 500; font-style: italic; margin: 1.8rem 0 .6rem; }
h3 { font-size: 1.05rem; font-weight: 600; font-style: italic; margin: 1.4rem 0 .5rem; }
a { text-underline-offset: 2px; text-decoration-color: var(--muted); }

/* ── Pinned metadata block: the trust signals, sticky at margin top.
      Solid paper background + z-index so floated sidenotes slide under
      it rather than colliding when it is stuck mid-scroll. ─────── */
.doc-meta { position: sticky; top: 1rem; font-size: .75rem; line-height: 1.5;
            background: var(--paper); z-index: 1;
            border-top: 2px solid var(--ink); padding-top: .6rem; }
.doc-meta > * { margin: 0 0 .7rem; }
.status { display: inline-block; font-size: .65rem; font-weight: 650;
          letter-spacing: .09em; text-transform: uppercase;
          padding: .25rem .55rem; border: 1px solid var(--muted);
          border-radius: 4px; color: var(--muted); }
.status-wip      { color: var(--warn); border-color: currentColor; }
.status-accepted { color: var(--ok); border-color: currentColor; }
.status-archived { color: var(--muted); border-style: dashed; text-decoration: line-through; }
ul.tags { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: .3rem .6rem; }
ul.tags a { color: var(--muted); }
.doc-meta dl { display: grid; grid-template-columns: max-content 1fr; gap: .1rem .6rem; }
.doc-meta dt { color: var(--muted); }
.doc-meta dd { margin: 0; overflow-wrap: anywhere; }
.doc-meta .meta-head { font-size: .6rem; font-weight: 650; letter-spacing: .1em;
                       text-transform: uppercase; color: var(--muted); margin: 0 0 .35rem; }
.doc-meta .okf > * { margin: 0 0 .5rem; }
.doc-meta .okf > :last-child { margin-bottom: 0; }
/* The catalog-record fold: a <details> in the meta-head register. Closed
   it is one quiet line; open, the groups keep their internal rhythm. */
.doc-meta .meta-fold summary { cursor: pointer; user-select: none;
                       list-style-position: outside; }
.doc-meta .meta-fold summary:hover { color: var(--ink); }
.doc-meta .meta-fold[open] summary { margin-bottom: .5rem; }
.doc-meta .meta-fold > :not(summary) { margin: 0 0 .6rem; }
.doc-meta .meta-fold > :last-child { margin-bottom: 0; }
.doc-meta .meta-extra { border-top: 1px solid var(--faint); padding-top: .55rem; }
.doc-meta .meta-extra dt { font-variant-numeric: tabular-nums; }
.doc-meta .meta-extra dd { font-variant-numeric: tabular-nums; }
.doc-meta .properties { border-top: 1px solid var(--faint); padding-top: .55rem; }
.doc-meta .escape { border-top: 1px solid var(--faint); padding-top: .55rem;
                    color: var(--muted); }
.doc-meta .escape code { font-size: .7rem; display: block; overflow-wrap: anywhere;
                         margin-bottom: .25rem; }

/* ── Sidenotes: footnote syntax, floated into the margin. CSS counters
      number the in-text anchor and the note identically. ────────── */
.sidenote-number { counter-increment: sidenote; }
.sidenote-number::after { content: counter(sidenote); font-size: .75em; }
.sidenote { float: right; clear: right; width: var(--margin-w);
            margin-right: calc(-1 * (var(--margin-w) + var(--gutter)));
            margin-top: .15rem; margin-bottom: .8rem;
            font-family: var(--font-ui); font-size: .74rem; line-height: 1.5;
            color: var(--muted); }
.sidenote::before { content: counter(sidenote) " "; font-size: .85em;
                    vertical-align: super; }

/* ── Booktabs tables: heavy top/bottom rules, light rule under the
      header, no verticals, no row rules. ─────────────────────────── */
table { border-collapse: collapse; margin: 1.5rem 0; font-size: .92em;
        border-top: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
thead th { border-bottom: 1px solid var(--ink); }
th, td { text-align: left; padding: .35rem .8rem .35rem 0; border: none; }

pre { background: var(--faint); padding: .75rem; overflow-x: auto; border-radius: 6px;
      font-size: .82rem; line-height: 1.45; }
code { font-family: var(--font-mono); }
/* GFM alerts (markdown adapter emits .callout structure; colors are
   GitHub's palette via light-dark(), tracking color-scheme above). */
.callout { border-left: 3px solid var(--faint); padding: .1rem 1rem; margin: 1rem 0; }
.callout-title { display: flex; gap: .5rem; align-items: center; font-weight: 600; }
.callout-title p { margin: .5rem 0; }
.callout-body > :first-child { margin-top: .25rem; }
.callout-note      { border-color: var(--info); }
.callout-note      .callout-title { color: var(--info); }
.callout-tip       { border-color: var(--ok); }
.callout-tip       .callout-title { color: var(--ok); }
.callout-important { border-color: var(--accent); }
.callout-important .callout-title { color: var(--accent); }
.callout-warning   { border-color: var(--warn); }
.callout-warning   .callout-title { color: var(--warn); }
.callout-caution   { border-color: var(--danger); }
.callout-caution   .callout-title { color: var(--danger); }
.htmx-request#main { opacity: .55; transition: opacity .15s; }

/* ── The trail canvas (ADR 0005, R2): panes laid out left→right; the
      canvas scrolls, panes never resize to fit. ──────────────────── */
body.canvas-page { max-width: none; padding: 1.5rem 1.5rem 2rem; }
main.canvas { display: flex; align-items: flex-start; gap: 1.25rem;
              overflow-x: auto; overscroll-behavior-x: contain;
              min-height: 85vh; }
.pane { flex: 0 0 auto; }
.pane.focused { width: calc(60ch + var(--gutter) + var(--margin-w)); }
.pane.body { width: 60ch; }
.pane.focused .pane-cols { display: grid;
              grid-template-columns: minmax(0, 60ch) var(--margin-w);
              column-gap: var(--gutter); }
/* The margin is an attribute of attention: a body-only parent hides
   its sidenotes (their float would bleed into the neighboring pane). */
.pane.body .sidenote { display: none; }
.pane-head { font-family: var(--font-ui); font-size: .72rem;
             color: var(--muted); margin-bottom: .8rem;
             border-bottom: 1px solid var(--faint); padding-bottom: .35rem; }
/* The attention marker: the active pane gets the room's 2px ink rule
   (the same register as the margin's top rule) and a full-ink address;
   context panes keep the faint hairline. Color stays reserved for
   status. Three sources of attention, one cue at a time: the URL focus
   (server truth, works without JS), a click-engaged pane, and the pane
   holding keyboard focus (the ask bar). When a reader engages a pane,
   the bare URL-focus marker steps back so exactly one pane reads as
   active. */
.pane.focused > .pane-head, .pane:focus-within > .pane-head,
.pane.engaged > .pane-head {
             border-bottom: 2px solid var(--ink); color: var(--ink); }
.canvas:has(.pane.engaged) .pane.focused:not(.engaged):not(:focus-within) > .pane-head,
.canvas:has(.pane:focus-within) .pane.focused.engaged:not(:focus-within) > .pane-head,
.canvas:has(.pane:focus-within) .pane.focused:not(.engaged):not(:focus-within) > .pane-head {
             border-bottom: 1px solid var(--faint); color: var(--muted); }
.pane-head a { color: inherit; text-decoration: none; }
/* The head's status chip (ADR 0007): the marginless room's at-rest trust
   signal — smaller than the margin badge, same color register. */
.pane-head .status { font-size: .52rem; padding: .12rem .35rem;
             margin-left: .3rem; vertical-align: middle; }
.pane.spine { display: flex; flex-direction: column; align-items: center;
              gap: .7rem; width: 2.4rem; min-height: 22rem;
              padding: .8rem .3rem; border: 1px solid var(--faint);
              border-radius: 6px; text-decoration: none; color: var(--muted);
              background: transparent; }
.pane.spine:hover { border-color: var(--muted); }
.pane.spine .spine-title { writing-mode: vertical-rl; font-family: var(--font-ui);
              font-size: .8rem; max-height: 15rem; overflow: hidden;
              text-overflow: ellipsis; white-space: nowrap; }
.pane.spine .status { writing-mode: vertical-rl; font-size: .55rem;
              letter-spacing: .06em; padding: .3rem .15rem; }
.pane.spine.gone { opacity: .55; border-style: dashed; }
/* Active-link highlight: the open links ARE the breadcrumb. */
a.active-link { background: var(--faint); border-radius: 3px;
                padding: 0 .2rem; text-decoration-thickness: 2px; }

/* ── The pane-scroll room (ADR 0007 — the default; DEMARKUS_PANE_SCROLL
      is the opt-out): the sliding-panes model. The room is a fixed
      viewport — nav on top, dock on the bottom, the canvas filling the
      rest — and each pane scrolls internally, so reading deep into one
      document no longer drags its neighbors (and the dock/nav never
      leave). Presentation only: same markup, same URLs, same no-JS
      behavior. ─────────────────────────────────────────────────────── */
body.pane-scroll { height: 100dvh; overflow: hidden; box-sizing: border-box;
                   display: flex; flex-direction: column; }
body.pane-scroll nav { flex: 0 0 auto; }
body.pane-scroll main.canvas { flex: 1 1 auto; min-height: 0;
                   align-items: stretch; }
body.pane-scroll .pane { overflow-y: auto; overscroll-behavior-y: contain;
                   /* Independent scrollers must whisper: thin, muted,
                      and only while the pane is under the pointer. */
                   scrollbar-width: thin;
                   scrollbar-color: transparent transparent; }
body.pane-scroll .pane:hover { scrollbar-color: light-dark(#11111138, #dddddd38) transparent; }
/* Column rules: with every pane a full-height scroller, adjacent text
   columns need a boundary the gap alone no longer provides. Interior
   edges only; spines carry their own border box. */
body.pane-scroll .pane:not(.spine):not(:first-child) {
                   border-left: 1px solid var(--faint); padding-left: 1.25rem; }
/* The address rail stays readable while the pane's content scrolls under
   it — same paper-backed sticky register as the margin's doc-meta. */
body.pane-scroll .pane-head { position: sticky; top: 0; z-index: 2;
                   background: var(--paper); margin-bottom: 0;
                   padding-bottom: .35rem; }
body.pane-scroll .pane-head + .pane-cols { margin-top: .8rem; }
/* Spines stretch to the room's full height — a floor-to-ceiling book
   edge, not a floating chip. (The librarian transcript keeps its own
   bounded scroller: that is what pins the view to the newest exchange
   while tokens stream and keeps the ask form in reach.) */
body.pane-scroll .pane.spine { min-height: 0; }
/* Marginless, wider panes: in this room the catalog record is summoned
   (the head's "meta" affordance → the ?meta= overlay), so the reading
   column takes the freed width. Panes grow to share the viewport;
   measure stays bounded so long lines never sprawl. Sidenotes follow
   the margin out (still available in the reader overlay). */
body.pane-scroll .pane.focused, body.pane-scroll .pane.body {
                   width: auto; flex: 1 1 60ch; min-width: 46ch; max-width: 82ch; }
body.pane-scroll .pane .pane-cols { display: block; }
body.pane-scroll .pane .doc-margin { display: none; }
body.pane-scroll .pane .sidenote { display: none; }
/* The metadata overlay: the record as a card, not a page. */
.meta-panel { width: 34rem; }
.meta-panel .meta-title { margin: 0 0 .8rem; font-family: var(--font-ui);
                   font-size: .75rem; color: var(--muted); overflow-wrap: anywhere; }
.meta-panel .doc-meta { position: static; } /* sticky is a margin behavior */

/* ── The floor: SSR-SVG universe map (pane zero). Trust signals ride
      the nodes — status as stroke color, importance as radius. ──── */
svg.floor { max-width: 100%; height: auto; }
svg.floor text { fill: var(--muted); font-family: var(--font-ui); }
.floor-world-label { font-size: 15px; font-weight: 600; }
.floor-doc-label { font-size: 10.5px; }
.floor-world { fill: var(--faint); stroke: var(--muted); stroke-width: 1.5; }
.floor-doc { fill: var(--paper); stroke: var(--muted); stroke-width: 1.5; }
.floor-doc.status-accepted { stroke: var(--ok); }
.floor-doc.status-wip      { stroke: var(--warn); }
.floor-doc.status-archived { stroke-dasharray: 3 2; opacity: .6; }
.floor-system.gone circle { stroke-dasharray: 4 3; opacity: .5; }
svg.floor a:hover circle { stroke-width: 3; }
.floor-empty { color: var(--muted); font-style: italic; }
/* World-level edges (hub graph ∪ observed map) + portal nodes for
   externally-linked hosts (the extensional universe). */
.floor-edge { stroke: var(--faint); stroke-width: 1.5; }
.floor-portal-node { fill: var(--paper); stroke: var(--muted);
                     stroke-width: 1.5; stroke-dasharray: 3 2; }
/* World map (zoom level 2), reference-only (ADR 0006 §5): linked docs in a
   connected ring (reuse .floor-doc + .graph-edge), orphans floated in a band
   below — dashed + dimmed so "unconnected" reads at a glance. */
.world-map .floor-doc-label { fill: var(--ink); }
.world-map-caption { font-family: var(--font-ui); font-size: 11px; fill: var(--muted); }
.world-map-band { font-family: var(--font-ui); font-size: 10px; fill: var(--muted);
                  text-transform: uppercase; letter-spacing: .05em; }
.floor-doc.world-map-orphan { stroke-dasharray: 3 2; opacity: .7; }
.world-map-new { margin-top: .6rem; font-family: var(--font-ui); font-size: .85rem; }

/* ── Backlinks: the observed-links map made visible (R3). Last margin
      block; empty ⇒ absent (decision 8). ────────────────────────── */
.doc-meta .backlinks { border-top: 1px solid var(--faint); padding-top: .55rem; }
.backlinks-head { color: var(--muted); letter-spacing: .03em; margin-bottom: .35rem; }
.backlinks ul { list-style: none; padding: 0; margin: 0;
                display: flex; flex-direction: column; gap: .3rem; }
.backlinks a { color: inherit; }

/* ── Hover preview cards (R3): pure htmx + CSS. The card is fetched on
      hover into .preview-card and revealed only once it has content, so
      there is no empty flash and no JS. The card opens UPWARD: scrollable
      overflow only grows toward the block/inline END, so a downward card
      near a bottom edge grew the page/canvas scroll area (and re-anchored
      the librarian's bottom-pinned transcript) — visible jitter on every
      hover. Upward/startward overflow is clipped instead of grown: worst
      case a card near a pane top is cut, never a layout shift. ──── */
.preview-host { position: relative; }
.preview-card { display: none; position: absolute; bottom: calc(100% + .3em);
                left: 0; z-index: 10; width: max-content; max-width: 22rem;
                background: var(--paper); color: var(--ink);
                border: 1px solid var(--muted); border-radius: 6px;
                padding: .6rem .7rem; box-shadow: 0 4px 14px #00000028;
                font-family: var(--font-ui); font-size: .76rem;
                line-height: 1.45; white-space: normal; }
.preview-host:hover .preview-card:not(:empty) { display: block; }
/* Margin cards (backlinks) open leftward: the margin is the pane's right
   edge, so a left-anchored card would overhang into the neighboring pane
   (or grow the canvas scroll). Right-anchored, it opens over the gutter
   and body — always inside its own pane. */
.doc-margin .preview-card { left: auto; right: 0; }
/* Anchor positioning (progressive enhancement): position:absolute cannot
   escape a scroll container, so a card near a pane's top edge is clipped —
   constant in the pane-scroll room, where every pane is an overflow
   scroller. Where supported, the card goes position:fixed pinned to its
   link, which escapes every clip. The anchor names are per-pair and
   inline (previewize / the preview-link template mint them): a SHARED
   anchor-name resolves to the last element in the whole document, which
   parked every card on the final link. Unsupported browsers keep the
   absolute card and its documented worst case. */
@supports (anchor-name: --p) {
  .preview-card, .doc-margin .preview-card {
    position: fixed; /* position-anchor rides inline on each card */
    bottom: anchor(top); left: anchor(left); right: auto;
    margin-bottom: .3em;
    /* No room above → flip below; no room rightward (margin cards sit
       near the viewport edge) → flip leftward; corners get both. */
    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
  }
}
.preview-inner { display: flex; flex-direction: column; gap: .35rem; }
.preview-title a { font-weight: 600; color: inherit; }
.preview-snippet { color: var(--muted); }
.preview-mark { font-size: .68rem; color: var(--muted); overflow-wrap: anywhere; }

/* ── Graph neighborhood pane (R3): SSR SVG, same family as the floor.
      Backlinks left, outbound right, center in the middle. ──────── */
svg.graph { max-width: 100%; height: auto; }
svg.graph text { fill: var(--muted); font-family: var(--font-ui); }
.graph-edge { stroke: var(--faint); stroke-width: 1.5; }
/* A typed relation (rel-<predicate>) draws dashed; its predicate is the
   <title> tooltip on the line. */
.graph-edge.edge-rel { stroke-dasharray: 5 3; }
.edge-arrow { fill: var(--muted); }
/* Node hover (islands.js) lights up the incident edges and the nodes they
   connect, so the relationship reads. */
.edge-arrow-hot { fill: var(--ok); }
.graph-edge.edge-hot { stroke: var(--ok); stroke-width: 2.5;
        marker-end: url(#arrow-hot); }
.node-hot circle, circle.node-hot { stroke: var(--ok);
        stroke-width: 3; }
.graph-center { fill: var(--ink); stroke: var(--ink); }
.graph-center-label { font-size: 13px; font-weight: 600; fill: var(--ink); }
.graph-node { fill: var(--paper); stroke: var(--muted); stroke-width: 1.5; }
.graph-node.graph-in  { stroke: var(--info); }
.graph-node.graph-out { stroke: var(--ok); }
/* A neighbor already on your trail (ADR 0006 §4): filled green = walked. */
.graph-node.graph-walked { fill: var(--ok);
        stroke: var(--ok); stroke-width: 3; }
.graph-node-label { font-size: 11px; }
.graph-more { font-size: 11px; font-style: italic; }
svg.graph a:hover circle { stroke-width: 3; }
.graph-empty { color: var(--muted); font-style: italic; }

/* ── Cataloging desk (Phase 3): the edit form. Metadata as dedicated
      fields; textarea + live server preview, no JS editor. The desk
      needs elbow room the reading column doesn't allow: like the
      canvas, its body lifts the 60ch cap so the editor/preview split
      is genuinely side by side (markdown left, rendered right). ──── */
body.edit-page { max-width: none; padding: 1.5rem 1.5rem 2rem; }
.edit-pane { max-width: 1500px; margin: 0 auto; }
.edit-error { background: light-dark(#fff0f0, #3a1e1e); border: 1px solid
              light-dark(#d1242f, #f85149); color: light-dark(#82071e, #ff9492);
              padding: .6rem .8rem; border-radius: 4px; font-size: .85rem; }
.edit-notice { background: light-dark(#fff8e6, #2e2611); border: 1px solid
              var(--warn); color: light-dark(#7a5200, #e0b341);
              padding: .6rem .8rem; border-radius: 4px; font-size: .85rem; }
.edit-meta { display: flex; flex-wrap: wrap; gap: .8rem 1.2rem; margin: 1rem 0; }
.edit-meta label { display: flex; flex-direction: column; gap: .2rem;
                   font-size: .72rem; color: var(--muted); }
.edit-meta input, .edit-meta select { font: inherit; font-size: .85rem;
                   padding: .3rem .4rem; border: 1px solid var(--faint);
                   border-radius: 3px; background: var(--paper); color: var(--ink); }
.edit-meta input[name="title"] { min-width: 22rem; }
.edit-meta input[name="tags"] { min-width: 20rem; }
.edit-split { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem;
              align-items: start; }
/* Pin the panes: the preview reuses .doc-body for document styling, and
      .doc-body's reading-pane rule (grid-column: 1) would otherwise drag
      it under the editor instead of beside it. */
.edit-split .edit-body { grid-column: 1; grid-row: 1; }
.edit-split .edit-preview { grid-column: 2; grid-row: 1; }
.edit-body { font-family: var(--font-mono);
             font-size: .82rem; line-height: 1.5; width: 100%; resize: vertical;
             padding: .7rem; border: 1px solid var(--faint); border-radius: 4px;
             background: var(--paper); color: var(--ink); tab-size: 2; }
/* The preview is a pane, not a tail: sticky beside the editor and
      scrolling within itself, so a long document never pushes the
      rendered view below the markdown. */
.edit-preview { border: 1px solid var(--faint); border-radius: 4px;
                padding: .7rem 1rem; overflow-wrap: anywhere; min-height: 8rem;
                position: sticky; top: 1rem;
                max-height: calc(100vh - 2rem); overflow-y: auto; }
/* The desk's scrollers whisper like the pane-scroll room's: thin, muted,
      visible only under the pointer. */
.edit-body, .edit-preview { scrollbar-width: thin;
                scrollbar-color: transparent transparent; }
.edit-body:hover, .edit-preview:hover {
                scrollbar-color: light-dark(#11111138, #dddddd38) transparent; }
/* Actions sit in the metadata flex row, baseline-aligned with the
      inputs beside them (the row's labels stack caption-over-field). */
.edit-actions { display: flex; gap: 1rem; align-items: center;
                align-self: flex-end; }
.edit-meta .edit-append-note { margin: 0; align-self: center; }
.edit-save { font: inherit; padding: .4rem 1.2rem; cursor: pointer;
             border: 1px solid var(--muted); border-radius: 4px;
             background: var(--ink); color: var(--paper); }
.edit-cancel { color: var(--muted); }
.edit-link { font-weight: 600; }
@media (max-width: 900px) {
  .edit-split { grid-template-columns: 1fr; }
  .edit-split .edit-preview { grid-column: 1; grid-row: auto;
              position: static; max-height: none; }
}

/* ── Reader mode (R4): a focus lens over the dimmed trail. URL state
      (?reader=i); SSR + CSS — the only JS is the Esc-close handler in
      islands.js. The trail stays visible behind, deliberately dimmed:
      context preserved, attention zoomed. ───────────────────────── */
.pane-head .reader-link { color: var(--muted); text-decoration: none;
              font-size: .9em; margin-left: .5rem; }
.pane-head .reader-link:hover { color: var(--ink); text-decoration: underline; }
.reader-backdrop { position: fixed; inset: 0; z-index: 50;
              background: light-dark(#11111155, #00000099);
              display: flex; justify-content: center;
              overflow-y: auto; padding: 2vh 2vw; }
/* The scrim is a backdrop-filling link back to the bare trail: a click
   anywhere outside the panel closes the overlay. It sits behind the
   panel (DOM order), so panel clicks never reach it. */
.reader-scrim { position: absolute; inset: 0; }
/* Near-fullscreen immersion: the panel fills the backdrop (a sliver of
      dimmed trail keeps the context visible), and the content column
      expands to fit it — the reader trades the 60ch measure for room. */
.reader-panel { position: relative; height: max-content;
              width: 100%; min-height: 96vh;
              max-width: 100%; background: var(--paper); color: var(--ink);
              border: 1px solid var(--faint); border-radius: 8px;
              box-shadow: 0 8px 40px #00000055; padding: 2.5rem 2rem 3rem; }
.reader-panel .pane-cols { display: grid;
              grid-template-columns: minmax(0, 1fr) var(--margin-w);
              column-gap: var(--gutter); }
.reader-close { position: absolute; top: .7rem; right: 1rem; z-index: 1;
              font-size: 1.1rem; line-height: 1; padding: .2rem .45rem;
              text-decoration: none; color: var(--muted); }
.reader-close:hover { color: var(--ink); }
@media (max-width: 900px) {
  .reader-panel { padding: 2.2rem 1.2rem 2.5rem; }
  .reader-panel .pane-cols { grid-template-columns: minmax(0, 1fr); }
}

/* ── Command palette (⌃K), ADR 0006 §3: htmx active-search overlay. The
      markup is server-rendered (templates/palette.html); islands.js only
      toggles [hidden] and moves the .sel selection. System-ui sans —
      chrome, not prose. ─────────────────────────────────────────────── */
.nav-search { font-size: .8rem; color: var(--muted); text-decoration: none;
              border: 1px solid var(--faint); border-radius: 6px;
              padding: .25rem .6rem; white-space: nowrap; font-family: var(--font-ui); }
.nav-search:hover { color: var(--ink); }
.palette-backdrop { position: fixed; inset: 0; z-index: 100;
              background: light-dark(#11111144, #00000099);
              display: flex; justify-content: center; align-items: flex-start;
              padding: 12vh 1rem 1rem; font-family: var(--font-ui); }
.palette-backdrop[hidden] { display: none; }
.palette-panel { width: 640px; max-width: 100%; background: var(--paper);
              color: var(--ink); border: 1px solid var(--faint);
              border-radius: 10px; box-shadow: 0 12px 48px #00000066;
              overflow: hidden; display: flex; flex-direction: column;
              max-height: 70vh; }
.palette-head { margin: 0; padding: .5rem .8rem; border-bottom: 1px solid var(--faint); }
.palette-input { width: 100%; box-sizing: border-box; font: inherit; font-size: 1rem;
              border: 0; background: transparent; color: inherit; outline: none; padding: .35rem 0; }
.palette-results { overflow-y: auto; }
.palette-list { list-style: none; margin: 0; padding: .3rem; }
.palette-empty { padding: 1rem .8rem; color: var(--muted); font-size: .85rem; }
.palette-row a { display: flex; align-items: baseline; gap: .6rem;
              padding: .45rem .6rem; border-radius: 6px; text-decoration: none; color: inherit; }
.palette-row a:hover, .palette-row a.sel { background: var(--faint); }
.palette-title { font-size: .95rem; white-space: nowrap; overflow: hidden;
              text-overflow: ellipsis; }
.palette-loc { font-family: var(--font-mono);
              font-size: .72rem; color: var(--muted); flex: 1;
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
              text-align: right; }
.palette-badge { font-size: .65rem; text-transform: uppercase; letter-spacing: .04em;
              color: var(--muted); border: 1px solid var(--faint);
              border-radius: 4px; padding: 0 .3rem; }
.palette-hint { padding: .4rem .8rem; border-top: 1px solid var(--faint);
              font-size: .7rem; color: var(--muted); }
@media (max-width: 640px) { .palette-loc { display: none; } }

/* ── Graph overlay (ADR 0006 §4): on-demand pull-up of the focused doc's
      reference neighborhood. Hidden until summoned by `g`. ───────────── */
.graph-backdrop { position: fixed; inset: 0; z-index: 90;
        background: light-dark(#11111155, #00000099);
        display: flex; justify-content: center; align-items: center;
        padding: 4vh 2vw; }
.graph-backdrop[hidden] { display: none; }
.graph-panel { background: var(--paper); color: var(--ink);
        border: 1px solid var(--faint); border-radius: 10px;
        box-shadow: 0 12px 48px #00000066;
        /* The summoned map/graph is near-fullscreen — the content fills
           the overlay rather than shrinking to the SVG's intrinsic ~760px. */
        width: 99vw; height: 97vh; max-width: 99vw; max-height: 97vh;
        overflow: hidden; display: flex; flex-direction: column; }
.graph-head { display: flex; justify-content: space-between; align-items: baseline;
        gap: 1rem; padding: .5rem .9rem; border-bottom: 1px solid var(--faint);
        font-family: var(--font-ui); }
.graph-title { font-weight: 600; font-size: .9rem; }
.graph-hint { font-size: .68rem; color: var(--muted); }
/* Canvas fills the panel below the head; its SVG scales up to the box
   (viewBox + preserveAspectRatio keeps the layout centered, not stretched). */
.graph-canvas { padding: .15rem; flex: 1 1 auto; min-height: 0;
        display: flex; flex-direction: column; align-items: stretch;
        justify-content: center; overflow: auto; }
.graph-canvas svg { width: 100%; height: 100%; max-width: 100%; min-height: 0;
        flex: 1 1 auto; }
.graph-canvas .world-map-new { flex: 0 0 auto; margin-top: .4rem; }

/* ── World index (ADR 0006 §5): the universe lists worlds as doors, never
      loose documents. A card carries door affordances (glyph, chevron) a
      document row never has; federated worlds read as a remote server. ── */
.floor-view { font-family: var(--font-ui); font-size: .75rem;
        color: var(--muted); text-decoration: none; }
.floor-view:hover { color: var(--ink); }
.worlds { list-style: none; margin: 1rem 0 0; padding: 0;
        display: grid; gap: .6rem; }
.world-card { display: flex; align-items: center; gap: .7rem;
        padding: .7rem .9rem; border: 1px solid var(--faint); border-radius: 8px;
        text-decoration: none; color: inherit; font-family: var(--font-ui); }
.world-card:hover { border-color: var(--muted); background: var(--faint); }
.world-glyph { font-size: 1.1rem; color: var(--muted); }
.world-name { font-weight: 600; }
.world-chev { margin-left: auto; color: var(--muted); }
.world-tag { margin-left: auto; font-size: .65rem; text-transform: uppercase;
        letter-spacing: .04em; color: var(--muted); border: 1px solid var(--faint);
        border-radius: 4px; padding: 0 .35rem; }
.world-card.federated { border-style: dashed; }
.world-card.federated .world-chev { margin-left: .4rem; }
.world-card.gone { opacity: .55; }
/* Rich directory index (ADR 0006 §5): listing rows lead with the title; the
   *.md filename is mono secondary, with a status badge and orphan tag. */
.idx-file { font-family: var(--font-mono);
        font-size: .76rem; color: var(--muted); margin-left: .5rem; }
.idx-orphan { font-family: var(--font-ui); font-size: .62rem;
        text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
        border: 1px solid var(--faint); border-radius: 4px; padding: 0 .3rem; margin-left: .4rem; }

/* ── Trail dock (ADR 0006 §2): bottom orientation strip. Fixed; <details>
      gives zero-JS minimize. System-ui sans — an instrument, not prose. ── */
body.canvas-page { padding-bottom: 4.5rem; }
.dock { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
        background: var(--paper); border-top: 1px solid var(--faint);
        font-family: var(--font-ui); font-size: .8rem; }
.dock-head { cursor: pointer; padding: .25rem 1rem; color: var(--muted);
        font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
        user-select: none; }
.dock-body { display: flex; align-items: center; gap: 1rem;
        padding: .35rem 1rem .55rem; overflow-x: auto; overscroll-behavior-x: contain; }
.dock-trail { display: flex; align-items: center; gap: .5rem;
        list-style: none; margin: 0; padding: 0; white-space: nowrap; }
.dock-entry { display: inline-flex; align-items: center; gap: .4rem; }
/* Connector into each entry: solid arrow for a walked edge, dashed for a jump. */
.dock-entry.walked::before { content: "→"; color: var(--muted); }
.dock-entry.jumped::before { content: "⇢"; color: var(--muted); opacity: .55; }
.dock-world { color: var(--muted); font-size: .62rem; text-transform: uppercase;
        letter-spacing: .04em; border: 1px solid var(--faint); border-radius: 4px;
        padding: 0 .3rem; }
.dock-link { color: inherit; text-decoration: none; }
.dock-link:hover { text-decoration: underline; }
.dock-entry.active .dock-link { font-weight: 600;
        text-decoration: underline; text-decoration-thickness: 2px; }
.dock-chips { display: flex; align-items: center; gap: .4rem;
        margin-left: auto; white-space: nowrap; }
.dock-from { color: var(--muted); font-size: .68rem; }
.dock-chip { color: var(--muted); text-decoration: none; font-size: .75rem;
        border: 1px solid var(--faint); border-radius: 999px; padding: .1rem .55rem; }
.dock-chip:hover { color: var(--ink); border-color: var(--muted); }

/* The librarian pane (Phase 4): transcript exchanges + the ask form.
   Questions read as marginalia headers; the trace is the same muted
   monospace register as the dock; streamed text keeps its paragraph
   breaks until the rendered answer swaps in. */
/* Bottom-anchored chat scroll: column-reverse with a single child pins
   the view to the newest exchange without JS; the bounded height keeps a
   long conversation from stretching the whole canvas. */
#librarian-transcript { display: flex; flex-direction: column-reverse;
        overflow-y: auto; max-height: 62vh; overflow-anchor: none; }
.exchange { margin: 1.2rem 0; }
.ask-q { font-weight: 600; margin: 0 0 .3rem; }
.ask-q::before { content: "? "; color: var(--muted); }
.ask-trace { color: var(--muted); font-size: .78rem; font-family: var(--font-mono);
        white-space: pre-wrap; }
.ask-stream { white-space: pre-wrap; }
.ask-notice { color: var(--muted); font-style: italic; }
.ask-form { display: flex; gap: .5rem; margin-top: 1.2rem; }
.ask-input { flex: 1; font: inherit; color: inherit; background: transparent;
        border: 1px solid var(--faint); border-radius: 4px; padding: .35rem .6rem; }
.ask-form button { font: inherit; color: inherit; background: transparent;
        border: 1px solid var(--muted); border-radius: 4px; padding: .35rem .8rem;
        cursor: pointer; }
