/* semgit — layer 1: shell.

   Press: near-monochrome grotesk, hairline rules, tight tracking. Colour is
   held back almost entirely so it still means something when the semantic layer
   finally spends it.

   Dark mode is section 2: the same ramp inverted, honouring the OS and
   overridable with [data-theme] on <html>.

   Fonts are OS stacks, so nothing is downloaded and nothing is pinned. If Inter
   proper is wanted over the Segoe/system fallback it gets self-hosted as woff2
   in this directory — no CDN, no build step — and a DECISIONS.md entry, per the
   house rule on new dependencies. */

/* ---------------------------------------------------------------- 1. tokens */

:root {
  /* Both, so light-dark() below can resolve either way and the OS decides
     by default. [data-theme] on <html> forces one. */
  color-scheme: light dark;

  /* Neutrals. A warm grey ramp — the same lightnesses as a neutral one with the
     hue pushed off cold, so the desk reads as paper stock rather than a screen.
     --ink..--ink-3 carry text and each clears 4.5:1 on --surface in BOTH
     schemes; --line and --line-2 are hairlines only.

     light-dark() carries both values in one declaration. The previous version
     wrote the dark ramp out twice — once under prefers-color-scheme, once under
     [data-theme="dark"] — and the two promptly disagreed the first time the
     palette was edited: the media block was warmed and the attribute block was
     left cold. One declaration cannot drift from itself. */
  --ink:         light-dark(#14130f, #ebe9e4);
  --ink-2:       light-dark(#474440, #b8b4ad);
  --ink-3:       light-dark(#75706a, #8b867f);
  --line:        light-dark(#e0ddd7, #302e2a);
  --line-2:      light-dark(#efedea, #24221f);
  --surface:     light-dark(#fffefc, #1a1917);
  --canvas:      light-dark(#f7f5f1, #111110);
  --sunken:      light-dark(#f0eee9, #262420);

  /* "Accent" is near-black on purpose: action reads as weight, not hue. Dark
     inverts that to near-white; nothing gains a colour it did not have. */
  --accent:      light-dark(#14130f, #ebe9e4);
  --accent-2:    light-dark(#000000, #ffffff);
  --accent-ink:  light-dark(#fffefc, #14130f);
  --accent-wash: light-dark(#efedea, #2a2823);

  --danger:      light-dark(#b3261e, #ef8a84);
  --danger-wash: light-dark(#fdeceb, #3a2220);
  --ok:          light-dark(#2e7d5b, #5cbb8b);
  --ok-wash:     light-dark(#e9f4ee, #1f3128);
  --warn:        light-dark(#b7791f, #e2b25c);
  --warn-wash:   light-dark(#fbf2df, #3a2f18);

  /* Space — 4pt scale, and the shell uses the small end of it. */
  --s1: 4px;  --s2: 6px;  --s3: 10px; --s4: 14px;
  --s5: 20px; --s6: 28px; --s7: 40px; --s8: 56px;

  --r1: 2px; --r2: 3px; --r3: 5px;

  /* Two heights: sitting on the desk, and floating over it. */
  --e1: 0 1px 2px rgba(16, 18, 21, .05);
  --e2: 0 1px 2px rgba(16, 18, 21, .06), 0 4px 12px rgba(16, 18, 21, .05);

  --ease: cubic-bezier(.2, .6, .3, 1);
  --fast: 90ms; --mid: 160ms;

  --topbar-h: 40px;

  /* The dock's height, so the history panel can start level with the paper
     rather than level with the dock floating above it. */
  --dock-h: 38px;
  --ribbon-h: 38px;   /* the Edit mode's formatting bar (#68) */

  /* The panel's fold button, and the size of the square the panel clips down to.
     It also sets the panel's text gutter: the header label and every commit
     title start at --handle, so the two columns line up by arithmetic rather
     than by two hand-tuned paddings drifting apart. */
  --handle: 32px;

  /* The panel is a fixed size, like the sheet is. Deriving it from whatever
     margin the paper left over meant it was a different width on every window
     and never looked settled. The window accommodates it; below --flow-at the
     window cannot, and the panel joins the flow and pushes the paper instead.
     --flow-at = --sheet-w + 2 * (--rail-w + --s4 + --channel)
               = 794 + 2 * (240 + 14 + 16) = 1334px.
     Media queries cannot read custom properties, so that literal is in the
     @media below and this is the arithmetic behind it. CHANGE --rail-w AND YOU
     MUST MOVE THAT NUMBER: every 100px on the panel is 200px on the
     breakpoint. */
  --rail-w: 240px;
  --channel: 16px;

  /* Annotations in the right margin. Not a panel — each card floats beside the
     sentence it is about, the way a margin note does. */
  --note-w: 260px;
  --note-gap: 8px;

  /* THE PAGE IS A PAGE. A4 width, written in millimetres so it is not a number
     anybody is tempted to tune: the browser resolves 210mm to 793.7px, and the
     same value drives the PDF (SPEC G.5, CSS Paged Media).

     INVARIANT: the sheet's dimensions never change. Not for a panel, not for
     the annotation gutter, not for the window. A manuscript whose line breaks
     move when you open a sidebar is not a manuscript. Everything else yields
     first — the annotations give up their column, then the desk scrolls. */
  --sheet-w: 210mm;
  --spread-w: 1080px;

  --t-xs: 11px; --t-sm: 12px; --t-md: 13px; --t-lg: 15px; --t-xl: 19px;
  --ui-tracking: -.006em;
  --rail-weight: 500;

  --ui:    "Inter", "Inter Tight", "Segoe UI Variable Text", "Segoe UI",
           ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", sans-serif;
  --prose: "Inter", "Segoe UI Variable Text", ui-sans-serif, system-ui, sans-serif;
  --mono:  "Cascadia Mono", ui-monospace, Consolas, "SF Mono", monospace;
}

/* ----------------------------------------------------------- 2. scheme ---

   The palette itself is in the tokens above. Only two things are left here:
   the explicit override, and the shadows — light-dark() is a colour function
   and a box-shadow is not a colour. */

:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* Shadow does little on a dark ground; there, the borders carry separation. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --e1: 0 1px 2px rgba(0, 0, 0, .5);
    --e2: 0 1px 3px rgba(0, 0, 0, .6), 0 6px 18px rgba(0, 0, 0, .45);
  }
}
:root[data-theme="dark"] {
  --e1: 0 1px 2px rgba(0, 0, 0, .5);
  --e2: 0 1px 3px rgba(0, 0, 0, .6), 0 6px 18px rgba(0, 0, 0, .45);
}

/* ----------------------------------------------------------------- 3. reset */

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

html, body { height: 100%; }

body {
  margin: 0;
  font: var(--t-md)/1.5 var(--ui);
  letter-spacing: var(--ui-tracking);
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
[hidden] { display: none !important; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r1); }
:focus:not(:focus-visible) { outline: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.mono { font-family: var(--mono); font-size: .93em; }
.prose { font-family: var(--prose); }

/* ------------------------------------------ 4. the workspace: desk + sheet */

/* The document is a sheet of paper lying on a desk. The desk is the full width
   of the window and nothing permanently occupies part of it — the rail floats
   above it and folds away, so opening the panel never narrows the page you are
   reading. That is the whole reason the rail is not a grid column. */

.shell { display: grid; grid-template-rows: var(--topbar-h) 1fr; height: 100dvh; }

/* The paper is centred on the desk and stays there. The history is anchored to
   the top-left of the page, out of the flow, and opens rightwards into the empty
   margin beside the document — so expanding it costs the reader nothing: the
   document does not move, it just stops being alone on the desk. */
.desk {
  position: relative;
  min-height: 0;
  overflow: auto;
  background: var(--canvas);
  /* Edit mode has a bar under the topbar (#68); everything on the desk
     that is anchored to its top — the padding, the dock, the rail — moves
     down by it, through this one variable. */
  --ribbon-on: 0px;
  padding: calc(var(--s4) + var(--ribbon-on)) var(--s4) var(--s8);
  transition: padding-top var(--mid) var(--ease);
}
.desk:has(.editing[data-mode="edit"]) { --ribbon-on: var(--ribbon-h); }

/* Dock and paper are one column, so the flow layout below the breakpoint has
   exactly two children to lay out: history, then this. Relative because the
   annotation margin is positioned against it. */
.paper { min-width: 0; position: relative; }

/* width, not max-width, and flex:none so a flex parent cannot shrink it either.
   See the invariant on --sheet-w.

   Square corners, and nothing on it that a printer would not put there. Paper
   does not have rounded edges, and a sha in the top corner is the application
   talking on a surface that belongs to the manuscript. Chrome goes on the desk;
   the sheet carries the document and only the document. */
/* --zoom scales the page. It does not resize it: at any zoom the sheet is still
   210mm of paper and the line breaks are where they were. `zoom` rather than a
   transform because it participates in layout, so the desk's scrollbars and the
   annotation positions follow it without being told. */
.sheet {
  zoom: var(--zoom, 1);
  /* The sentence markers are drawn into the page's own margin, positioned
     against this. */
  position: relative;
  width: var(--sheet-w);
  flex: none;
  margin: 0 auto;
  padding: var(--s7) var(--s7) var(--s8);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--e2);
}

/* The manuscript as pages (pages.js): the sheet becomes a transparent stack
   and each .page is what the sheet used to be — 210mm by 297mm of paper with
   the same margins, border and shadow. Rounded corners no more than before.
   A block that will not cut and is taller than a page gets `height: auto`
   from the script, so nothing is ever clipped. */
.sheet.is-paged {
  display: grid;
  row-gap: var(--s5);
  padding: 0;
  background: none;
  border: 0;
  box-shadow: none;
}
.sheet.is-paged > .page {
  box-sizing: border-box;
  height: calc(297mm - 2px);
  padding: var(--s7) var(--s7) var(--s8);
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--e2);
}
/* A paragraph continued from the previous page starts at the page's top. */
.sheet.is-paged > .page > [data-cont] { margin-top: 0; }

/* Everything the application knows about the document, on the desk beneath it
   rather than printed onto it: the sha it is showing, who has touched it. */
.pagefoot {
  display: flex; align-items: center; gap: var(--s3);
  max-width: var(--sheet-w);
  margin: var(--s3) auto 0;
  padding: 0 var(--s1);
  font-size: var(--t-xs);
  color: var(--ink-3);
}
.pagefoot .avatar { width: 16px; height: 16px; font-size: 8px; }

/* The document page's frame (#58): the editor's root and form wrap the dock
   and #mode; #mode wraps a mode's sheet, pagefoot and margin. All of them are
   display:contents so the paper's layout is untouched: the dock stays sticky
   against the desk and the margin positioned against the paper. A parked mode
   (the editor, kept alive while another mode shows) is hidden outright. */
.editing, .editing-form, .mode { display: contents; }

/* Why the editor is in exact source mode, when it is. On the desk, above the
   page: the sheet carries the document and only the document. */
.editing-notice {
  max-width: var(--sheet-w);
  margin: calc(-1 * var(--s2)) auto var(--s3);
  padding: var(--s1) var(--s3);
  font-size: var(--t-xs);
  color: var(--ink-2);
  background: var(--accent-wash);
  border: 1px solid var(--line);
  border-radius: var(--r2);
}

/* Lorenz's Edit chrome (#33, #68): his ribbon and his Submit dock, his
   markup and his rules. The rules are scoped under .edit-preview (section 9)
   so each piece sits in a wrapper carrying that class, and the sheet stays
   outside them. The ribbon is a bar under the topbar, as on his page; the
   desk moves down by it (--ribbon-on, above). His rule places the ribbon in
   a flex column; here it is fixed, so the selector has to outweigh his. */
.mode > .edit-preview { display: contents; }
.mode .edit-preview .ep-ribbon {
  position: fixed;
  top: var(--topbar-h); left: 0; right: 0;
  height: var(--ribbon-h);
  z-index: 3;   /* over the dock and the rail, under the topbar */
}
/* Arriving with the mode, it comes down from under the topbar. */
.mode .edit-preview .ep-ribbon.is-arriving { opacity: 1; transform: translateY(-100%); }
/* His paper is a page tall even while the draft is short. */
.editing[data-mode="edit"] .sheet { min-height: 297mm; }

/* The editor's content box is the page. editor.css gives it a serif and a
   minimum height for the harness; on the sheet the page's own type rules
   apply, so Read and Edit set the same words in the same places. */
.sheet [data-editor-host] .semgit-editor-content {
  min-height: 0;
  color: var(--ink);
  font: 16.5px/1.7 var(--prose);
  /* Above the marks layer, so the bars notes.js draws for the anchored
     sentences sit under the text like a highlight, not over it. */
  position: relative;
  z-index: 1;
}

/* The manuscript as the server sent it. Nothing here is the shell's; this is
   markdown-it's output, given a measure and a rhythm. Sizes are absolute, not
   relative to the UI type, because this is the page and the UI is around it. */
/* One set of type rules whether the page is rendered or being edited: the
   rules nest under the sheet and under the editor's content box inside it.
   Switching Read and Edit must not move a word (#58). */
.sheet, .sheet > .page, .sheet .semgit-editor-content {
  & > h1, & > h2, & > h3, & > p, & > ul, & > ol, & > blockquote, & > pre, & > table {
    font-family: var(--prose);
    color: var(--ink);
  }
  & > h1 { font-size: 26px; font-weight: 600; letter-spacing: -.015em; line-height: 1.25; margin: 0 0 var(--s6); }
  & > h2 { font-size: 19px; font-weight: 600; line-height: 1.3; margin: var(--s6) 0 var(--s3); }
  & > h3 { font-size: 16.5px; font-weight: 600; margin: var(--s5) 0 var(--s2); }
  & > p, & > ul, & > ol, & > blockquote { font-size: 16.5px; line-height: 1.7; margin: 0 0 var(--s4); }
  & > blockquote { padding-left: var(--s4); border-left: 2px solid var(--line); color: var(--ink-2); }
  & > pre { font-family: var(--mono); font-size: 13px; line-height: 1.55; padding: var(--s3) var(--s4); background: var(--sunken); overflow-x: auto; margin: 0 0 var(--s4); }
  & > table { border-collapse: collapse; font-size: 14.5px; margin: 0 0 var(--s4); }
  & > table th, & > table td { padding: var(--s1) var(--s3); border-bottom: 1px solid var(--line); text-align: left; }
}
.sheet .prose { font-family: var(--prose); color: var(--ink); }
.sheet code { font-family: var(--mono); font-size: .92em; }
.sheet a { text-decoration: underline; text-underline-offset: 2px; }
/* Raw view: the source, as a page. */
.sheet > .raw { white-space: pre-wrap; }

/* A sentence, wrapped by notes.js so an anchor has something to point at. No
   style at all: it must not cost the text a pixel. */
.sent {}

/* The page a fresh repo shows: the desk, the paper, one thing to do on it. */
.empty { display: grid; gap: var(--s4); justify-items: start; padding: var(--s6) 0; }
.empty h1 { font-family: var(--prose); font-size: 22px; font-weight: 600; }
.empty p { color: var(--ink-2); }
.rail-empty { padding: var(--s3); font-size: var(--t-sm); color: var(--ink-3); }

/* Screens that are lists, not documents, want the desk without the paper. */
.spread { max-width: var(--spread-w); margin: 0 auto; }

/* ------------------------------------------------------------ 5. the topbar */

/* The stack, in one place, because this is where it goes wrong:
     1  history panel      6  menus and the search results
     2  dock, notes head   7  selection bar
     10 topbar
   The topbar is a grid item with a z-index, so it makes a stacking context and
   everything inside it — the breadcrumb menus, the search results — is clamped
   to the topbar's own level. At 2 it tied with the dock and lost on DOM order,
   which put the search results underneath the Read/Edit pill. */
.topbar {
  display: flex; align-items: center; gap: var(--s3);
  padding: 0 var(--s3) 0 var(--s4);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  z-index: 10;
}

/* A request in flight. htmx puts .htmx-request on whatever hx-indicator names,
   and <body> names this, so every request shows here unless the element that
   made it points somewhere quieter. A hairline that travels, not a spinner: it
   says "working" without claiming any part of the page. */
.busy {
  position: absolute;
  top: var(--topbar-h); left: 0; right: 0;
  height: 2px;
  z-index: 11;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--fast) var(--ease) 120ms; /* not for the quick ones */
}
.busy::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: 30%;
  background: var(--ink);
  animation: travel 1s var(--ease) infinite;
}
.busy.htmx-request { opacity: 1; }
@keyframes travel { from { left: -30%; } to { left: 100%; } }

/* Applied for one frame on load so remembered state (a pinned panel) is
   restored in place rather than animated open from its fold. */
.no-motion * { transition: none !important; animation: none !important; }

.brand {
  display: flex; align-items: center; gap: var(--s2);
  font-family: var(--ui);
  font-weight: 650; font-size: var(--t-md);
  letter-spacing: -.01em;
  color: var(--ink);
}
.brand:hover { text-decoration: none; }
.brand-mark {
  width: 13px; height: 13px; border-radius: var(--r1);
  background: var(--accent);
}

#crumb { display: contents; }
.crumb {
  display: flex; align-items: center; gap: var(--s2);
  min-width: 0;
  font-family: var(--mono);
  font-size: var(--t-sm);
  color: var(--ink-3);
}
.crumb a { color: var(--ink-2); }
.crumb-sep { color: var(--line); }
.crumb strong {
  font-weight: 550; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The document name in the breadcrumb is how you reach the other documents, so
   it looks pressable rather than like the path text around it. */
.crumb-doc {
  display: flex; align-items: center; gap: var(--s1);
  padding: 3px var(--s2);
  border-radius: var(--r1);
  font-weight: 550;
  color: var(--ink);
  cursor: pointer;
}
.crumb-doc:hover { background: var(--sunken); text-decoration: none; }
.crumb-doc svg { color: var(--ink-3); }

/* A menu in the breadcrumb — which document, which branch — and the View menu
   at the other end of the topbar. <details> rather than script: it opens on
   click and on Enter without anyone writing that. Click-away and Escape are the
   two dismissals <details> does NOT give you; shell.js adds them. */
.menu { position: relative; }
.menu > summary {
  display: flex; align-items: center; gap: var(--s1);
  padding: 3px var(--s2);
  border-radius: var(--r1);
  font-weight: 550;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary:hover { background: var(--sunken); }
.menu > summary svg { color: var(--ink-3); }
.menu[open] > summary { background: var(--sunken); }

.menu-pop {
  position: absolute;
  top: calc(100% + var(--s2)); left: 0;
  z-index: 6;
  min-width: 260px;
  padding: var(--s1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  box-shadow: var(--e2);
}
.menu-item {
  display: flex; align-items: baseline; gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  font-family: var(--ui);
  font-size: var(--t-sm);
  color: var(--ink-2);
  white-space: nowrap;
}
.menu-item:hover { background: var(--sunken); color: var(--ink); text-decoration: none; }
.menu-item[aria-current="true"] { background: var(--sunken); color: var(--ink); font-weight: 600; }
.menu-item .mono { color: var(--ink-3); }
.menu-item .spacer { min-width: var(--s5); }
.menu-note {
  display: flex; align-items: center;
  padding: var(--s2) var(--s3) var(--s1);
  color: var(--ink-3);
  font-size: var(--t-xs); font-weight: 650;
  letter-spacing: .07em; text-transform: uppercase;
}

/* A menu at the right-hand end of the topbar opens leftwards, or it hangs off
   the window. */
.menu.right .menu-pop { left: auto; right: 0; }

.menu-sep { height: 1px; margin: var(--s1) var(--s2); background: var(--line); }

/* A setting that is on shows a tick where a destination shows nothing, so the
   rows stay one list and the state is still readable at a glance. */
.menu-item .tick { width: 12px; color: var(--ink); visibility: hidden; }
.menu-item[aria-pressed="true"] .tick { visibility: visible; }

button.menu-item {
  align-items: center;
  width: 100%;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
}
button.menu-item[aria-pressed="true"] { color: var(--ink); font-weight: 600; }

kbd {
  padding: 1px 5px;
  border: 1px solid var(--line);
  border-radius: var(--r1);
  background: var(--sunken);
  font-family: var(--mono); font-size: 10px;
  color: var(--ink-3);
}

.spacer { flex: 1; }

/* Search. A button's width until it is wanted, so it does not crowd a topbar
   that already carries a path and four links. */
.search { position: relative; display: flex; align-items: center; }
.search input {
  width: 132px;
  padding: var(--s1) var(--s2) var(--s1) 26px;
  border: 1px solid var(--line);
  border-radius: var(--r2);
  background: var(--surface);
  font-size: var(--t-sm);
  color: var(--ink);
  transition: width var(--mid) var(--ease), background var(--fast) var(--ease);
}
.search input::placeholder { color: var(--ink-3); }
.search input:focus { width: 280px; background: var(--surface); }
.search svg { position: absolute; left: var(--s2); color: var(--ink-3); pointer-events: none; }

.search-pop {
  position: absolute;
  top: calc(100% + var(--s2)); right: 0;
  z-index: 6;
  width: 380px;
  max-height: 60vh; overflow-y: auto;
  padding: var(--s1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  box-shadow: var(--e2);
}
.search:not(:focus-within) .search-pop { display: none; }

.result {
  display: grid; gap: 1px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  color: inherit;
}
.result:hover { background: var(--sunken); text-decoration: none; }
.result-head {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-xs); color: var(--ink-3);
}
.result-title { font-size: var(--t-sm); color: var(--ink); }
.result mark { background: var(--accent-wash); color: inherit; border-radius: 2px; }

/* Leaving the document: repo-level places, quiet until wanted. */
.topnav { display: flex; align-items: center; gap: 2px; }
.topnav a {
  padding: var(--s1) var(--s2);
  border-radius: var(--r1);
  font-size: var(--t-sm);
  color: var(--ink-3);
}
.topnav a:hover { background: var(--sunken); color: var(--ink); text-decoration: none; }
.topnav a[aria-current="page"] { color: var(--ink); font-weight: 550; }

.topbar-sep { width: 1px; height: 18px; margin: 0 var(--s2); background: var(--line); }

/* Three states, not two: "follow the OS" is the default and has to stay
   reachable, or choosing once means choosing forever. Three rows in the View
   menu rather than one button that cycles: there is room to show all three, and
   no reason to make anybody guess what the next press does. */
.theme { display: block; }

/* Whether the page is still hearing from the server (G.4 "live updates"). Silent
   when it is — a green tick for the normal case is noise — and only speaks up
   when the answer is no. */
.live {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s2);
  border-radius: var(--r1);
  font-size: var(--t-xs);
  color: var(--ink-3);
}
.live::before {
  content: "";
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ink-3);
}
.live-label { display: none; }
.live[data-state="live"]::before { background: var(--ok); }
.live[data-state="waiting"]::before { background: var(--ink-3); animation: pulse 1.4s var(--ease) infinite; }
.live[data-state="off"]::before { background: var(--danger); }
.live[data-state="none"] { display: none; }
.live[data-state="waiting"] .live-label,
.live[data-state="off"] .live-label { display: inline; }
@keyframes pulse { 50% { opacity: .25; } }

/* Something arrived while you were reading. It is announced, never inserted:
   the shell does not move text under a reader's eye. Clicking takes it. */
.newpill {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  width: 100%;
  margin: var(--s1) 0 var(--s2);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--ink);
  border-radius: var(--r2);
  background: var(--surface);
  font-size: var(--t-xs); font-weight: 550;
  color: var(--ink);
  cursor: pointer;
  animation: toast-in 160ms var(--ease);
}
.newpill:hover { background: var(--sunken); text-decoration: none; }
.newpill[hidden] { display: none; }

/* A count that just changed says so once, then stops. */
@keyframes bump { 40% { transform: scale(1.28); } }
.dock-count.is-new, .rail-count.is-new { animation: bump 320ms var(--ease); }
.dock-count.is-new { background: var(--ink); color: var(--surface); }

.avatar {
  display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent-2);
  font-family: var(--ui);
  font-size: 10px; font-weight: 650; letter-spacing: .02em;
}
/* Provenance by shape, not colour (SPEC G.4): an agent is never a round face. */
.avatar[data-kind="agent"] {
  border-radius: var(--r1);
  background: var(--sunken);
  color: var(--ink-2);
  font-family: var(--mono);
}

/* ----------------------------------------------------------- 5b. the dock ---

   Two kinds of navigation, in two places, and the split is the point:

   - Leaving the document — other documents, rules, settings, the repo — lives
     in the topbar. That is chrome belonging to the app.
   - Acting on the document in front of you — read it, edit it, review what is
     proposed for it — lives here, floating over the paper. That is chrome
     belonging to the document.

   Sticky, so the modes stay reachable however far down you have read. */

.dock {
  position: sticky;
  /* From the desk's content edge, so the padding the ribbon adds (#68)
     already keeps the pill under it. */
  top: 0;
  z-index: 2;
  display: flex;
  justify-content: center;
  max-width: var(--sheet-w);
  margin: 0 auto var(--s4);
  /* Only the pill takes the pointer; the desk shows through either side of it. */
  pointer-events: none;
}

.dock-bar {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--s1);
  height: var(--dock-h);
  padding: 0 var(--s1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: calc(var(--r3) + var(--s1));
  box-shadow: var(--e2);
}

.dock-tab {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink-3);
  white-space: nowrap;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.dock-tab:hover { background: var(--sunken); color: var(--ink); text-decoration: none; }
.dock-tab[aria-current="page"] {
  background: var(--sunken); color: var(--ink); font-weight: 600;
}

/* A count on a mode is the reason to go there. */
.dock-count {
  min-width: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--accent-wash);
  color: var(--ink-2);
  font-family: var(--mono);
  font-size: 10px; line-height: 16px; text-align: center;
}
.dock-tab[aria-current="page"] .dock-count { background: var(--surface); }

/* The draft is alive and unwritten: the tab that leads back to it says so. */
.dock-tab[data-unsaved]::after {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

/* ---------------------------------------------- 6. the rail — a floating panel

   Not a column. It sits on the desk, folds left out of the way, and takes the
   sheet's width with it either way (the sheet is centred in the full desk, so
   it does not move when the panel opens). Toggled by a checkbox — no JS. */

/* Sticky beside the document, never over it. No shadow — it lives in the page,
   so its border is the whole separation.

   Folding does not hide the panel: it clips it down to its own top-left 26px,
   which is exactly the button. So the button is not a separate object that the
   panel slides out from behind — it is the panel, collapsed. Nothing appears,
   disappears, or gains a border at the end of the animation. */
.rail {
  position: fixed;
  /* Level with the paper, not with the dock floating above it. */
  top: calc(var(--topbar-h) + var(--ribbon-on) + var(--s4) + var(--dock-h) + var(--s4));
  left: var(--s4);
  width: var(--rail-w);
  z-index: 1;
  max-height: calc(100dvh - var(--topbar-h) - var(--ribbon-on) - var(--s4) - var(--s4));
  /* No top padding: the header shares the handle's 26px band so the chevron and
     the word "History" sit on one line. No flex gap either — every child below
     carries its own spacing (commits their padding, the footer its rule), and a
     gap on top of that opened a hole under the header. */
  display: flex; flex-direction: column; gap: 0;
  padding: 0 var(--s2) var(--s2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  overflow: hidden;
  clip-path: inset(0 round var(--r3));
  transition: clip-path 200ms var(--ease), border-radius 200ms var(--ease),
              margin-right 200ms var(--ease), top var(--mid) var(--ease);
}

/* The toggle lives in the shell so a plain sibling selector reaches the panel;
   `:has()` is not needed and support is not a question. */
.rail-toggle { position: absolute; opacity: 0; pointer-events: none; }

/* Folded: clipped to its own top-left button. Only that square remains
   hoverable — clip-path clips hit-testing too — so `.rail:hover` is "the button"
   while folded and "the panel" while open. One selector, no dead ground. */
.rail-toggle:not(:checked) ~ .desk .rail {
  clip-path: inset(0 calc(100% - var(--handle)) calc(100% - var(--handle)) 0 round var(--r2));
  border-radius: var(--r2);
  /* Closing is quicker than opening, and waits only long enough that clipping a
     corner on the way to the list does not shut it. */
  transition-duration: 130ms;
  transition-delay: 70ms;
}

.rail-toggle:not(:checked) ~ .desk .rail:hover,
.rail-toggle:not(:checked) ~ .desk .rail:focus-within {
  clip-path: inset(0 round var(--r3));
  border-radius: var(--r3);
  transition-duration: 200ms;
  transition-delay: 0ms;
}

/* The contents wait for the box to have somewhere to put them. */
.rail-head, .history, .rail-foot { transition: opacity 130ms var(--ease) 80ms; }
.rail-toggle:not(:checked) ~ .desk .rail .rail-head,
.rail-toggle:not(:checked) ~ .desk .rail .history,
.rail-toggle:not(:checked) ~ .desk .rail .rail-foot {
  opacity: 0;
  transition-delay: 0ms;
}
.rail-toggle:not(:checked) ~ .desk .rail:hover .rail-head,
.rail-toggle:not(:checked) ~ .desk .rail:hover .history,
.rail-toggle:not(:checked) ~ .desk .rail:hover .rail-foot,
.rail-toggle:not(:checked) ~ .desk .rail:focus-within .rail-head,
.rail-toggle:not(:checked) ~ .desk .rail:focus-within .history,
.rail-toggle:not(:checked) ~ .desk .rail:focus-within .rail-foot {
  opacity: 1;
  transition-delay: 80ms;
}

/* One control in one place, all the time: hamburger means "pin open", chevron
   means "unpin". It sits above the panel so a peek can always be clicked into
   a pin without chasing a moving target. */
/* The handle lives inside the panel, at its own top-left, and carries no border
   or shadow of its own — the panel's border is what you see when it is clipped
   down to this square. That is why nothing pops in at the end of a fold. */
.rail-handle {
  position: absolute;
  top: 0; left: 0;
  z-index: 5;
  display: grid; place-items: center;
  width: var(--handle); height: var(--handle);
  background: var(--surface);
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--fast) var(--ease);
}
.rail-handle:hover { color: var(--ink); }
/* Inside the fold's clip, so the ring goes inward or it is cut off. */
.rail-handle:focus-visible { outline-offset: -3px; border-radius: var(--r2); }

.rail-handle .i-pin { display: block; }
.rail-handle .i-unpin { display: none; }
.rail-toggle:checked ~ .desk .rail-handle .i-pin { display: none; }
.rail-toggle:checked ~ .desk .rail-handle .i-unpin { display: block; }

/* Same band as the handle, so the two are level; and the label starts at the
   panel's text gutter, which is where the commit titles start too. */
.rail-head {
  display: flex; align-items: center; gap: var(--s2);
  min-height: var(--handle);
  padding-left: calc(var(--handle) - var(--s2));
}

.rail-group { display: flex; flex-direction: column; gap: 1px; }

/* ----------------------------------------------- 6b. the history itself ---

   The panel holds the document's history and nothing else — it is not
   navigation. Each entry is a commit: a node on a line, a title, who and when.
   The line is drawn with a pseudo-element rather than a border so the first and
   last nodes can cap it instead of the line running past them. */

/* The panel itself does not scroll — the header and the network button stay
   put, and only the list moves. */
.history {
  display: flex; flex-direction: column;
  min-height: 0;
  overflow-y: auto;
}

.commit {
  position: relative;
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  gap: var(--s2);
  padding: var(--s2) var(--s2) var(--s2) var(--s1);
  border-radius: var(--r1);
  color: inherit;
}
.commit:hover { background: var(--sunken); text-decoration: none; }

/* The graph: a node, and a line reaching to the next one.

   align-content, not align-items: the cell still stretches the full height of
   the row — the connector below is measured off its bottom edge — but the dot
   packs to the top so it sits on the title's first line. Centring it put the dot
   somewhere between the title and the sha whenever a title wrapped to two lines.
   3px is half the difference between the 16px title line box and the 10px dot. */
.commit-node {
  position: relative;
  display: grid;
  align-content: start;
  justify-items: center;
  padding-top: 3px;
}
.commit-node::before {
  content: "";
  width: 7px; height: 7px;
  border: 1.5px solid var(--ink-3);
  border-radius: 50%;
  background: var(--surface);
  z-index: 1;
}
.commit-node::after {
  content: "";
  position: absolute;
  top: 13px; bottom: calc(-1 * var(--s2) - 3px);
  width: 1px;
  background: var(--line);
}
.commit:last-child .commit-node::after { display: none; }

/* A merge that changed meaning is the one thing in the list worth marking.
   Filled node, not a colour swap — the palette stays monochrome until the
   semantic layer earns it. */
.commit[data-kind="semantic"] .commit-node::before {
  background: var(--ink);
  border-color: var(--ink);
}

.commit-body { display: grid; gap: 2px; min-width: 0; }

.commit-title {
  font-size: var(--t-sm);
  line-height: 1.35;
  color: var(--ink);
  overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}

.commit-meta {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-xs);
  color: var(--ink-3);
}
.commit-meta .mono { font-size: 10.5px; }

/* Agent authorship is visible in the list, not only on the commit page (G.4). */
.commit-agent {
  padding: 0 3px;
  border: 1px solid var(--line);
  border-radius: 2px;
  font-family: var(--mono); font-size: 9.5px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3);
}

/* Pinned to the bottom of the panel: the way out to the full graph. A link, not
   a button — it goes somewhere, it does not do something. */
.rail-foot {
  margin-top: auto;
  padding-top: var(--s2);
  border-top: 1px solid var(--line-2);
}
.rail-foot a {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--r1);
  font-size: var(--t-sm);
  color: var(--ink-2);
}
.rail-foot a:hover { background: var(--sunken); color: var(--ink); text-decoration: none; }
.rail-foot svg { color: var(--ink-3); flex: none; }
.rail-foot a:hover svg { color: var(--ink-2); }

.rail-label {
  padding: 0 var(--s3) var(--s1);
  font-family: var(--ui);
  font-size: var(--t-xs); font-weight: 650;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3);
}

.rail-item {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s3);
  min-height: 24px;
  border-radius: var(--r1);
  font-size: var(--t-sm);
  font-weight: var(--rail-weight);
  color: var(--ink-2);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.rail-item:hover { background: var(--sunken); color: var(--ink); text-decoration: none; }
.rail-item[aria-current="page"] {
  background: var(--accent-wash); color: var(--accent-2); font-weight: 600;
}
.rail-count {
  margin-left: auto;
  font-family: var(--mono); font-size: var(--t-xs);
  color: var(--ink-3);
}

/* ------------------------------------------- 6c. annotations in the margin ---

   Not a panel. Git has no such thing as a comment on a line — `git notes`
   attaches text to a whole commit and that is the extent of it — so anchored
   discussion is semgit's own layer, kept in SQLite while git keeps the content
   (SPEC A.4). The anchor is {para, sent, hash} per G.2, and the hash is how a
   note finds out its sentence changed underneath it.

   Because these are *about a sentence*, they are drawn beside that sentence.
   notes.js sets each card's `top` from its anchor's position and pushes
   overlapping cards down the stack; CSS owns everything else. */

/* The gutter is reserved, not hoped for. Earlier this waited for the paper to
   happen to have space beside it, which on a 1440px window it does not — the
   cards were simply never drawn. Now a document that has annotations keeps a
   column for them and the sheet centres in what is left. */
.paper.has-notes { padding-right: calc(var(--note-w) + var(--channel)); }

.margin {
  position: absolute;
  top: 0;
  /* Fallback for no-JS; notes.js sets this from the sheet's real right edge. */
  left: calc(50% + var(--sheet-w) / 2 + var(--channel));
  width: var(--note-w);
  /* Cards take the pointer; the space between them is still the desk. */
  pointer-events: none;
}

/* Marginalia, not cards. A boxed card with a coloured strip down its left edge
   is the house style of every web app written this decade; a manuscript's
   margin has numbered notes in it, which is also what G.5 specifies for the
   annotated PDF, so the screen and the paper agree.

   No box, no shadow, no strip. A hanging number, a rule above it, and text. */
.note {
  position: absolute;
  left: 0;
  width: var(--note-w);
  pointer-events: auto;
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  column-gap: var(--s2);
  row-gap: 2px;
  padding: var(--s2) var(--s2) var(--s3) 0;
  border-top: 1px solid var(--line);
  color: inherit;
  transition: top var(--mid) var(--ease), background var(--fast) var(--ease),
              border-color var(--fast) var(--ease);
}
.note:hover, .note.is-active {
  background: var(--sunken);
  border-top-color: var(--ink-3);
  text-decoration: none;
}

/* The number is the whole marker: filled when it blocks, ringed when it is a
   flag, bare when it is only a note. Severity is a shape, not a hue — and the
   subkind is spelled out beside it regardless (G.4 wants a non-colour cue). */
/* The number owns column 1 for the note's whole height; everything else is
   column 2. Stated explicitly rather than left to auto-placement, which put the
   footer in the 22px number column as soon as a fourth row appeared. */
.note > *:not(.note-n) { grid-column: 2; }

.note-n {
  grid-column: 1;
  grid-row: 1 / -1;
  justify-self: center;
  display: grid; place-items: center;
  width: 18px; height: 18px;
  margin-top: 1px;
  border-radius: 50%;
  font-family: var(--mono);
  font-size: 10px; font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.note[data-severity="block"] .note-n { background: var(--danger); color: var(--surface); }
.note[data-severity="flag"]  .note-n { box-shadow: inset 0 0 0 1.5px var(--ink-2); }
.note[data-severity="note"]  .note-n { color: var(--ink-3); }

.note[data-state="resolved"] { opacity: .45; }
.note[data-state="resolved"]:hover { opacity: 1; }
.note[data-state="resolved"] .note-title { text-decoration: line-through; }

/* An issue about the document rather than a sentence has no position to sit at,
   so it pins above the anchored ones. Its marker is a dash, not a number:
   there is no place in the text for a number to refer to. */
.note.is-pinned .note-n { font-size: 12px; color: var(--ink-3); box-shadow: none; }

/* Small caps and letterspacing rather than a filled pill — a badge with a tinted
   background is the thing that made these read as generic cards. */
.note-kind {
  font-family: var(--ui);
  font-size: 9.5px; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-3);
}
.note[data-severity="block"] .note-kind { color: var(--danger); }

.note-scope { font-style: italic; letter-spacing: 0; text-transform: none; font-weight: 400; }

.note-head {
  display: flex; align-items: baseline; gap: var(--s2);
  font-size: var(--t-xs); color: var(--ink-3);
}
.note-head .mono, .note-head > span:last-child {
  font-family: var(--mono); font-size: 10px;
}

.note-title { font-size: var(--t-sm); line-height: 1.35; color: var(--ink); }
.note-body {
  font-size: var(--t-xs); line-height: 1.5; color: var(--ink-3);
  overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
/* One line, always. The handle is the only part allowed to give way, because
   the age and the comment count are two or three characters and wrapping them
   costs a whole line for nothing. */
.note-foot {
  display: flex; align-items: center; gap: var(--s2);
  margin-top: 2px;
  font-size: 10px; color: var(--ink-3);
  white-space: nowrap;
}
.note-foot .avatar { width: 14px; height: 14px; font-size: 7px; flex: none; }
.note-who { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* The anchored sentence, marked at rest. A finding you have to go looking for is
   a finding that gets missed, so the underline is always on and carries the
   severity; hovering only deepens what is already there.

   The mark is an underline rather than a full highlight because several
   anchored sentences in one paragraph should still read as prose. */
.anchor {
  padding-bottom: 1px;
  border-bottom: 2px solid var(--ink-3);
  cursor: pointer;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.anchor[data-severity="block"] { border-bottom-color: var(--danger); }
.anchor[data-severity="flag"]  { border-bottom-color: var(--ink-2); }
.anchor[data-severity="note"]  { border-bottom-style: dotted; border-bottom-color: var(--ink-3); }

.anchor:hover, .anchor.is-active { background: var(--accent-wash); }
.anchor[data-severity="block"]:hover,
.anchor[data-severity="block"].is-active { background: var(--danger-wash); }

/* Changes mode (#58, §G.5 `changes`): a changed sentence is marked in the
   text by what kind of change it is, with a shape as well as a colour (G.4):
   added is washed green and underlined, a meaning change is underlined
   solid, a surface change dotted. Background and underline only — nothing
   that could move a word. A removed sentence has no text here; its note
   says so. A flag on the change colours the text by what it is: red where
   it blocks, amber where it asks. */
.sent[data-change="added"]   { background: var(--ok-wash); border-bottom: 2px solid var(--ok); }
.sent[data-change="meaning"] { border-bottom: 2px solid var(--accent); }
.sent[data-change="surface"] { border-bottom: 2px dotted var(--ink-3); }
.sent[data-change="removed"] { border-bottom: 2px dashed var(--ink-3); }
.sent[data-change]:hover, .sent[data-change].is-active { background: var(--accent-wash); }
.sent[data-flag="flag"] { background: var(--warn-wash); border-bottom-color: var(--warn); }
.sent[data-flag="block"] { background: var(--danger-wash); border-bottom-color: var(--danger); }
.sent[data-flag="flag"]:hover, .sent[data-flag="flag"].is-active { background: var(--warn-wash); filter: brightness(.96); }
.sent[data-flag="block"]:hover, .sent[data-flag="block"].is-active { background: var(--danger-wash); filter: brightness(.96); }
.note.change[data-flag="block"] { border-top-color: var(--danger); }
.note.change[data-flag="flag"] { border-top-color: var(--warn); }
.note-flag[data-severity="flag"] .note-kind { color: var(--warn); }

/* In the editor the sentence cannot carry the underline itself (nothing may
   be written into ProseMirror's DOM), so notes.js draws it into the marks
   layer: one bar per line the sentence covers, styled like the anchor. */
.mark-line {
  position: absolute;
  box-sizing: content-box;
  border-bottom: 2px solid var(--ink-3);
  transition: background var(--fast) var(--ease);
}
.mark-line[data-severity="block"] { border-bottom-color: var(--danger); }
.mark-line[data-severity="flag"]  { border-bottom-color: var(--ink-2); }
.mark-line[data-severity="note"]  { border-bottom-style: dotted; border-bottom-color: var(--ink-3); }
.mark-line.is-active { background: var(--accent-wash); }
.mark-line[data-severity="block"].is-active { background: var(--danger-wash); }

/* The change note: kind and intent in the head, the wording that changed as
   the body — struck where it went, underlined where it came. A flag on the
   unit hangs under it as its own link. */
.note.change { cursor: default; }
.note-intent { font-size: var(--t-xs); color: var(--ink-2); }
.note-diff {
  font-family: var(--prose);
  font-size: var(--t-sm); line-height: 1.45;
  color: var(--ink-2);
}
.note-diff del { color: var(--ink-3); text-decoration: line-through; text-decoration-color: var(--ink-3); }
.note-diff ins { color: var(--ink); text-decoration: underline; text-decoration-color: var(--accent); text-underline-offset: 2px; }
.note-diff del + ins { margin-left: 3px; }
.note[data-change="removed"] .note-diff del { color: var(--ink-2); }
.note-flag {
  display: grid; row-gap: 2px;
  margin-top: var(--s1);
  padding-top: var(--s1);
  border-top: 1px dotted var(--line);
  color: inherit;
}
.note-flag:hover { text-decoration: none; }
.note-flag:hover .note-title { text-decoration: underline; }
.note-flag[data-severity="block"] .note-kind { color: var(--danger); }

/* Moving between modes (#58): the sheet you were reading slides to where
   the next mode puts it, and what is new fades in. modes.js sets the
   transform for one frame and then lets it go; the transition does the rest. */
.is-sliding { transition: transform 360ms var(--ease), opacity 280ms var(--ease); }
.is-arriving { opacity: 0; }
.margin.is-sliding, .submissions.is-sliding { transition-delay: 120ms; }

/* Two documents on the desk (Changes, #58): main on the left as it stands,
   the branch on the right as proposed, and the verdicts in the channel
   between them. The channel is the notes column, narrowed; notes.js puts it
   off main's right edge and numbers both sheets. The branch sheet is first
   in the DOM — it is the document — and main is placed left by order. Each
   sheet is still 210mm; when two do not fit the window, one Fit zooms them
   together (shell.js), and the channel stays unzoomed like the margin. */
.compare {
  --note-w: 200px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  column-gap: calc(var(--note-w) + 2 * var(--channel));
}
.compare .sheet { margin: 0; }
.compare .sheet.base { order: -1; }
.paper:has(.compare) { --note-w: 200px; }
/* The column is between the pages, so no gutter is reserved to the right. */
.paper.has-notes:has(.compare) { padding-right: 0; }

/* The submissions, always in view: the paper becomes two columns, the list
   and the spread, and the floating history panel steps aside — a review
   surface does not fold its list away. The dock, the pages and the pagefoot
   take the second column; the margin and the gutter are positioned, so the
   grid does not touch them. */
.paper:has(.compare) {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  column-gap: var(--s4);
  align-items: start;
}
.paper:has(.compare) > *, .paper:has(.compare) .mode > * { grid-column: 2; }
/* Rows stated, not left to auto-placement: the dock is a child of the
   editor's form (display: contents), so it would otherwise take the first
   free cell — the list's column. */
.paper:has(.compare) .dock { grid-column: 2; grid-row: 1; }
.paper:has(.compare) .compare { grid-row: 2; }
.paper:has(.compare) .compare-foot { grid-row: 3; }
/* Positioned children take their grid area as containing block; these two
   are positioned against the whole paper, so they get no area. */
.paper:has(.compare) .margin, .paper:has(.compare) .gutter { grid-column: auto; grid-row: auto; }
.submissions {
  grid-column: 1 !important;
  grid-row: 2 / span 3;
  position: sticky;
  top: calc(var(--dock-h) + var(--s4));
  display: flex; flex-direction: column;
  padding: 0 var(--s2) var(--s2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
}
.submissions .rail-head { padding: var(--s2) var(--s2) 0; }
.submissions .commit[aria-current="page"] { background: var(--sunken); }
.desk:has(.compare) .rail { display: none; }
/* The channel is where the dock floats, so the column starts under the dock
   rather than level with it. No head and no fold: the column between two
   pages is the comparison, and the count is in the pagefoot. */
.paper:has(.compare) .margin { top: calc(var(--dock-h) + var(--s4)); }
.paper:has(.compare) .gutter { display: none; }
.compare-foot {
  max-width: none;
  width: calc(2 * var(--sheet-w) * var(--zoom, 1) + var(--note-w) + 2 * var(--channel));
}

/* On main the change is what is about to go: a removed sentence struck
   through where it still exists, in red, a changed one underlined where it
   was. */
.sheet.base .sent[data-change="removed"] {
  text-decoration: line-through;
  text-decoration-color: var(--danger);
  background: var(--danger-wash);
  border-bottom: 0;
  color: var(--ink-2);
}
.sheet.base .sent[data-change="added"] { background: none; border-bottom: 0; }
.sheet.base .sent[data-change="meaning"],
.sheet.base .sent[data-change="surface"] { border-bottom-style: dotted; border-bottom-color: var(--ink-3); }

/* ------------------------------------------------------- note density ---

   Zooming out shrinks the page but a 260px note does not shrink with it, so at
   50% the annotations are two thirds as wide as the manuscript. Scaling them
   with the page is the obvious fix and the wrong one: the text goes to 6px and
   stops being readable, which is the only thing a note is for.

   Instead the notes change what they are. Zooming out is asking for an
   overview, so they become one — first losing their body, then everything but
   the numbered marker, until the margin is a density map of where the argument
   is in the document (G.4's "Patchwork minibar for locating changes in long
   documents"). Hovering any of them brings the whole note back.

   shell.js sets data-density from the zoom; the three states live here. */

body[data-density="compact"] { --note-w: 190px; }
body[data-density="marks"]   { --note-w: 26px; }

[data-density="compact"] .note-body,
[data-density="compact"] .note-foot { display: none; }
[data-density="compact"] .note-title { -webkit-line-clamp: 2; }

[data-density="marks"] .note {
  grid-template-columns: minmax(0, 1fr);
  padding: 1px 0;
  border-top: 0;
}
[data-density="marks"] .note-head,
[data-density="marks"] .note-title,
[data-density="marks"] .note-body,
[data-density="marks"] .note-foot { display: none; }
[data-density="marks"] .note-n { grid-row: auto; grid-column: 1; margin: 0; }

/* Whatever the density, one note at a time can be read in full. */
[data-density="compact"] .note:hover,
[data-density="compact"] .note.is-active,
[data-density="marks"] .note:hover,
[data-density="marks"] .note.is-active {
  z-index: 2;
  width: 260px;
  grid-template-columns: 22px minmax(0, 1fr);
  padding: var(--s2) var(--s2) var(--s3) var(--s2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r2);
  box-shadow: var(--e2);
}
[data-density="marks"] .note:hover .note-n,
[data-density="marks"] .note.is-active .note-n { grid-row: 1 / -1; }

[data-density] .note:hover .note-head,   [data-density] .note.is-active .note-head,
[data-density] .note:hover .note-foot,   [data-density] .note.is-active .note-foot { display: flex; }
[data-density] .note:hover .note-title,  [data-density] .note.is-active .note-title { display: block; }
[data-density] .note:hover .note-body,   [data-density] .note.is-active .note-body { display: -webkit-box; }

/* The column's head: what is in the margin, and the way to put it away.

   Not a pill in the dock's row. The dock is what you are DOING to the document
   — read, edit, changes, review — and all four still happen whether or not the
   notes are showing, so a switch among them said the wrong thing and carried the
   wrong weight. This is a property of the column, so it sits on the column, at
   its head, and looks like the marginalia it belongs to: a hairline and a label,
   no box.

   Sticky, so the way back is reachable however far down you have read. The
   wrapper runs the full height of the page to make that possible; notes.js gives
   it the notes' own left edge. */
.gutter {
  position: absolute;
  top: 0; bottom: 0;
  z-index: 2;
  /* Fallback for no-JS; notes.js sets this the same way .margin's is set. */
  left: calc(50% + var(--sheet-w) / 2 + var(--channel));
  pointer-events: none;
}
/* No column to head: the window is too narrow for one, or the page is zoomed
   past the desk. notes.js says so, and the head pins to the window's corner
   instead, since a fold whose column has gone still has to be findable. */
.gutter.is-loose {
  position: fixed;
  top: calc(var(--topbar-h) + var(--s4));
  bottom: auto;
  left: auto; right: var(--s4);
  z-index: 8;
}

.margin-head {
  position: sticky;
  top: 0;
  pointer-events: auto;
  box-sizing: border-box;
  display: flex; align-items: center; gap: var(--s2);
  /* --dock-h, so the head sits level with the dock pill across the desk.
     min-width holds it usable once --note-w drops to a marker strip. */
  width: var(--note-w);
  min-width: 46px;
  height: var(--dock-h);
  padding: 0 var(--s1) 0 0;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: var(--canvas);
  font-family: var(--ui);
  font-size: var(--t-xs); font-weight: 650;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.margin-head:hover { color: var(--ink); }
.margin-head svg { flex: none; }
.margin-n {
  font-family: var(--mono); font-size: var(--t-sm); font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-2);
}
.margin-head .i-unfold { display: none; }

/* Folded, or with no column to head: a tab carrying the count. A hairline rule
   is the head of a column; with no column under it, it is a canvas-coloured bar
   lying across the manuscript. So it stops being a rule and becomes an object. */
[data-notes="off"] .margin-head,
.gutter.is-loose .margin-head {
  width: auto; min-width: 0;
  gap: var(--s2);
  padding: 0 var(--s3);
  border: 1px solid var(--line);
  /* The dock pill's height and radius: the two of them are one row across the
     desk, and a tab 6px shorter and squarer than the pill beside it reads as a
     mistake rather than a difference. */
  height: var(--dock-h);
  border-radius: calc(var(--r3) + var(--s1));
  background: var(--surface);
  box-shadow: var(--e1);
}
[data-notes="off"] .margin-head .spacer,
.gutter.is-loose .margin-head .spacer { display: none; }

[data-notes="off"] .margin-head .i-fold { display: none; }
[data-notes="off"] .margin-head .i-unfold { display: block; order: -1; }

/* The head narrows with the column it heads. Zoomed out, the notes are down to
   a marker each; a word of chrome beside them would be the largest thing on
   that side of the desk. */
[data-notes="on"][data-density="compact"] .margin-word,
[data-notes="on"][data-density="marks"] .margin-word { display: none; }

/* ------------------------------------------------- the notes, put away ---

   Two questions, not one setting with three values:

     the fold    put the notes away and give the page its width back. Frequent,
                 so it belongs to the column — the fold is in the column's own
                 head, where the thing being hidden actually is.
     clean       the manuscript with nothing of semgit on it: no notes, no
                 numbers, no underlines. Rare, and a different way of reading
                 rather than a third position of the same switch, so it lives in
                 the View menu with the other decisions a reader makes once.

   The old three-way switch named the middle state "marks" and gave it a button.
   Nobody asks for that state: it is what the column degrades to when there is no
   room, and it still is — folded, the sentences keep their inline count. A
   fallback with a name and a button was a permanent control for a decision made
   twice a session, sized like the dock beside it and claiming the same weight.
*/

/* Folded, the notes go and their markers stay: the numbers are what is left
   saying where the discussion is. They cost the text nothing either way. */
/* Neither state reaches the Changes spread: there, the notes and the marks
   are the comparison, and two pages with nothing between them say nothing. */
[data-notes="off"] .paper:not(:has(.compare)) .note { display: none; }

[data-clean] .paper:not(:has(.compare)) .anchor {
  border-bottom: 0;
  padding-bottom: 0;
  cursor: auto;
}
[data-clean] .paper:not(:has(.compare)) .anchor:hover { background: none; }
[data-clean] .paper:not(:has(.compare)) .marks { display: none; }
/* Nothing of semgit on the page, the head included: the way back is the same
   menu the reader came in by. */
[data-clean] .paper:not(:has(.compare)) .note,
[data-clean] .paper:not(:has(.compare)) .gutter { display: none; }

/* When the paper has no room beside it, the notes collapse to a count on the
   sentence. notes.js decides that by measuring, not a media query: whether
   there is room depends on the window AND on whether the history panel is
   open, which a breakpoint cannot see. */
.no-margin .note { display: none; }

/* Select a sentence and the ways to say something about it come to the
   selection, rather than the reader going to find a form. §G.4 requires opening
   an issue from a text selection; this is where that starts. */
.selbar {
  position: absolute;
  z-index: 7;
  display: none;
  align-items: center; gap: var(--s1);
  padding: var(--s1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: calc(var(--r3) + 2px);
  box-shadow: var(--e2);
  /* Sits above the text it is about, and points at nothing while hidden. */
  transform: translate(-50%, calc(-100% - var(--s2)));
}
.selbar.is-open { display: flex; }
.selbar button {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s3);
  border: 0;
  border-radius: var(--r2);
  background: transparent;
  font-size: var(--t-sm);
  color: var(--ink-2);
  white-space: nowrap;
  cursor: pointer;
}
.selbar button:hover { background: var(--sunken); color: var(--ink); }
.selbar .sep { width: 1px; align-self: stretch; margin: 2px 0; background: var(--line); }

/* The marker on the sentence, in the page's own margin.

   INVARIANT, and the sibling of the one on --sheet-w: the markup never moves a
   word. Turning it on or off changes what is drawn over the manuscript and
   nothing about the manuscript — not a line break, not a line count, not the
   height of the page.

   This used to be a <sup> and a count set inside the sentence, and an inline box
   is part of the line: switching to Clean reading re-wrapped the paragraph and
   the page lost a whole line, 691.70px to 663.66px. Nothing inside the text can
   be optional. So the marker leaves the text — absolutely positioned in the
   40px band the page already keeps clear on its right, level with the line the
   sentence ends on. Out of the flow, it cannot perturb it, and the band is empty
   whether it is drawn or not.

   The underline stays inline: vertical padding and borders on an inline box do
   not affect the line box, so it costs the text nothing. Measured, not assumed. */
.marks {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.mark {
  position: absolute;
  display: flex; align-items: center;
  /* Inside the page's right margin: clear of the text column, clear of the
     paper's edge. notes.js sets top, height and any offset for a collision. */
  right: var(--s3);
  font-family: var(--mono); font-size: 10px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--ink-3);
}
.mark[data-severity="block"] { color: var(--danger); }
.mark.is-active { color: var(--ink); font-weight: 600; }

/* ------------------------------------------------- 6d. zoom, toasts, skip ---

   Zoom exists because the page does not stretch. With --sheet-w fixed at A4, a
   narrow window scrolls sideways and the reader has no way out; scaling the
   page is the only escape that keeps the invariant, since reflowing it is the
   very thing we forbade. It scales — the line breaks do not move.

   It lives in the View menu rather than floating on the desk. Zoom is set once
   and left; Ctrl +, Ctrl - and Ctrl 0 serve the reader who does move it often,
   and cost no pixels. The menu does not close when a zoom row is used, so
   stepping twice is two clicks and the reading stays put. */

.zoom { display: block; }
.zoom output {
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--ink-3);
}

/* An action that changed something on the server says so, once, and goes. The
   error box is for things that stay wrong; this is for things that went right. */
.toasts {
  position: fixed;
  left: 50%; bottom: var(--s5);
  z-index: 9;
  display: flex; flex-direction: column; gap: var(--s2);
  align-items: center;
  transform: translateX(-50%);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--s2);
  max-width: 60ch;
  padding: var(--s2) var(--s4);
  background: var(--ink);
  border-radius: calc(var(--r3) + 2px);
  box-shadow: var(--e2);
  font-size: var(--t-sm);
  color: var(--surface);
  animation: toast-in 160ms var(--ease);
}
.toast[data-kind="error"] { background: var(--danger); }
.toast.is-going { opacity: 0; transition: opacity 200ms var(--ease); }
.toast .mono { color: inherit; opacity: .7; }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Keyboard operability (G.4). Off-screen until focused, which is the first Tab
   on the page. */
.skip {
  position: absolute;
  left: var(--s4); top: var(--s4);
  z-index: 20;
  padding: var(--s2) var(--s3);
  background: var(--surface);
  border: 1px solid var(--ink);
  border-radius: var(--r2);
  font-size: var(--t-sm);
  color: var(--ink);
  transform: translateY(-200%);
  transition: transform var(--fast) var(--ease);
}
.skip:focus { transform: none; text-decoration: none; }

/* ----------------------------------------------------------------- 7. buttons */

.btn {
  display: inline-flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s3);
  min-height: 26px;
  border: 1px solid var(--line);
  border-radius: var(--r2);
  background: var(--surface);
  color: var(--ink-2);
  font-family: var(--ui);
  font-size: var(--t-sm); font-weight: 500;
  cursor: pointer; white-space: nowrap;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              color var(--fast) var(--ease);
}
.btn:hover { background: var(--sunken); color: var(--ink); text-decoration: none; }

.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn.primary:hover { background: var(--accent-2); border-color: var(--accent-2); color: var(--accent-ink); }

.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover { background: var(--sunken); }

.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }

.inline { display: inline; }

/* ------------------------------------------------- 8. states the frame owns */

.loading {
  position: fixed; inset: 0 0 auto; height: 2px;
  background: var(--accent); transform-origin: 0 50%;
  animation: loadbar 1.1s var(--ease) infinite; z-index: 9;
}
@keyframes loadbar {
  0%   { transform: scaleX(0);   opacity: 1; }
  60%  { transform: scaleX(.85); opacity: 1; }
  100% { transform: scaleX(1);   opacity: 0; }
}
[aria-busy="true"] { cursor: progress; }

.error {
  padding: var(--s2) var(--s3);
  border: 1px solid var(--danger);
  border-radius: var(--r2);
  background: var(--danger-wash);
  color: var(--danger);
  font-size: var(--t-sm);
}

/* An empty state always carries the way out (SPEC G.4). */
.empty {
  display: grid; justify-items: center; gap: var(--s3);
  max-width: 380px; margin: var(--s7) auto;
  padding: var(--s5) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  background: var(--surface);
  text-align: center;
}
.empty h2 { font-family: var(--prose); font-size: var(--t-lg); font-weight: 600; }
.empty p { font-size: var(--t-sm); color: var(--ink-3); }

/* ------------------------------------------------------------ 9. responsive */

/* The panel may sit in the margin only while the margin can hold it:
   794 + 2 * (240 + 14 + 16) = 1334px. Below that it rejoins the flow and pushes
   the paper across rather than lying on top of it, and shrinks with the window
   so the document keeps a usable measure. Folding pulls its footprint back to
   the 32px button, so the paper gets the room straight back either way. */
@media (max-width: 1334px) {
  /* The panel keeps its 400px here too. It used to shrink with the window
     (26vw), which meant the history was a different width on every screen and
     jittered while resizing — the panel is a fixed thing, like the sheet, and
     what gives instead is the paper: the sheet shrinks, and below 560px of
     prose the annotations collapse to inline counts and hand their column back.
     Folding takes the panel to 32px and returns nearly all of it. */

  /* Same channel as the fixed layout, so the spacing does not change when the
     panel crosses the breakpoint into the flow. */
  .desk { display: flex; align-items: flex-start; gap: var(--channel); }
  .rail {
    position: sticky;
    top: 0; left: auto;
    flex: 0 0 var(--rail-w);
    width: auto;
    /* Same offset as the fixed layout: level with the paper, not the dock. */
    margin-top: calc(var(--dock-h) + var(--s4));
  }
  .paper { flex: 1 1 auto; }
  .rail-toggle:not(:checked) ~ .desk .rail {
    margin-right: calc(-1 * (var(--rail-w) - var(--handle)));
  }
  .rail-toggle:not(:checked) ~ .desk .rail:hover,
  .rail-toggle:not(:checked) ~ .desk .rail:focus-within { margin-right: 0; }

}

/* Laptop and below. The sheet is deliberately NOT touched here — it used to go
   full-bleed and drop its margins, which is exactly the stretching the invariant
   forbids. A narrow window scrolls to the page instead of reshaping it. */
@media (max-width: 900px) {
  .spread { padding: 0 var(--s3); }
  .rail { max-width: calc(100vw - var(--s4) * 2); }
  .dock { margin-bottom: var(--s3); }
}

@media (max-width: 600px) { .crumb a, .crumb-sep { display: none; } }

/* ------------------------------------------------------------- print ---

   Ctrl+P gives the manuscript and only the manuscript — Clean reading, on
   paper. The desk, the chrome, the discussion and its markers all stay on the
   screen. (The PDF export is a different path: render.py and print.css, §G.5,
   which owns page numbers and running heads. This is just the browser.) */
@media print {
  @page { size: A4; margin: 0; }
  .topbar, .busy, .rail, .rail-toggle, .dock, .margin, .gutter, .marks,
  .selbar, .toasts, .skip, .pagefoot { display: none !important; }
  html, body, .shell { height: auto; }
  .shell { display: block; }
  .desk { overflow: visible; padding: 0; background: none; }
  .paper { padding: 0 !important; }
  .sheet {
    zoom: 1;
    margin: 0;
    border: 0;
    box-shadow: none;
  }
  /* The browser paginates for print; the script's pages become its pages. */
  .sheet.is-paged { display: block; }
  .sheet.is-paged > .page { height: auto; border: 0; box-shadow: none; break-after: page; }
  .anchor { border-bottom: 0; padding-bottom: 0; background: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}

/* edit surface preview — issue #33 */
/* Standalone, unmounted surface. Every rule is scoped to the preview root. */
.edit-preview {
  --ep-ink: #14130f;
  --ep-ink-2: #474440;
  --ep-ink-3: #75706a;
  --ep-line: #e0ddd7;
  --ep-line-2: #efedea;
  --ep-surface: #fffefc;
  --ep-canvas: #f7f5f1;
  --ep-sunken: #f0eee9;
  --ep-danger: #b3261e;
  --ep-danger-wash: #fdeceb;
  --ep-ok: #2e7d5b;
  --ep-ok-wash: #e9f4ee;
  --ep-amber: #b7791f;
  --ep-amber-wash: #fbf2df;
  --ep-collaborator: #7895af;
  --ep-collaborator-wash: #dce6ee;
  --ep-sheet-w: 210mm;
  --ep-sheet-h: 297mm;
  --ep-ui: "Inter", "Inter Tight", "Segoe UI Variable Text", "Segoe UI",
    ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", sans-serif;
  --ep-mono: "Cascadia Mono", ui-monospace, Consolas, "SF Mono", monospace;
  --ep-shadow: 0 1px 2px rgba(16, 18, 21, .06), 0 4px 12px rgba(16, 18, 21, .05);
  height: 100%;
  overflow: hidden;
  color: var(--ep-ink);
  background: var(--ep-canvas);
  font: 13px/1.5 var(--ep-ui);
  letter-spacing: -.006em;
  color-scheme: light;
}

.edit-preview *,
.edit-preview *::before,
.edit-preview *::after { box-sizing: border-box; }

.edit-preview [hidden] { display: none !important; }
.edit-preview button,
.edit-preview select { font: inherit; letter-spacing: inherit; }
.edit-preview button { color: inherit; }
.edit-preview a { color: var(--ep-ink-2); }
.edit-preview a:hover { color: var(--ep-ink); text-decoration: none; }
.edit-preview ::selection { color: var(--ep-ink); background: #dce5eb; }

.edit-preview :focus-visible {
  outline: 2px solid var(--ep-ink);
  outline-offset: 2px;
}

.edit-preview .ep-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.edit-preview .ep-skip {
  position: fixed;
  top: 8px;
  left: 10px;
  z-index: 80;
  padding: 5px 10px;
  color: var(--ep-ink);
  background: var(--ep-surface);
  border: 1px solid var(--ep-ink);
  border-radius: 3px;
  transform: translateY(-180%);
  transition: transform 90ms ease;
}
.edit-preview .ep-skip:focus { transform: none; }

.edit-preview .ep-shell {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100dvh;
  min-height: 0;
}

.edit-preview .ep-topbar {
  position: relative;
  z-index: 30;
  display: flex;
  flex: 0 0 40px;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 0 14px;
  background: var(--ep-surface);
  border-bottom: 1px solid var(--ep-line);
}

.edit-preview .ep-brand {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 6px;
  color: var(--ep-ink);
  font-weight: 650;
  letter-spacing: -.01em;
}

.edit-preview .ep-brand-mark {
  width: 13px;
  height: 13px;
  background: var(--ep-ink);
  border-radius: 2px;
}

.edit-preview .ep-crumb {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  color: var(--ep-ink-3);
  font: 12px/1.4 var(--ep-mono);
  white-space: nowrap;
}
.edit-preview .ep-crumb a,
.edit-preview .ep-crumb strong,
.edit-preview .ep-crumb span { flex: none; }
.edit-preview .ep-crumb strong {
  max-width: 250px;
  overflow: hidden;
  color: var(--ep-ink);
  font-weight: 600;
  text-overflow: ellipsis;
}
.edit-preview .ep-crumb-sep { color: #b0aca5; }
.edit-preview .ep-spacer { flex: 1; }
.edit-preview .ep-mono { font-family: var(--ep-mono); font-size: .93em; }

.edit-preview .ep-mode-switch {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  background: var(--ep-canvas);
  border-radius: 5px;
}
.edit-preview .ep-mode-switch button,
.edit-preview .ep-reset {
  min-height: 25px;
  padding: 3px 8px;
  color: var(--ep-ink-3);
  background: transparent;
  border: 0;
  border-radius: 3px;
  cursor: pointer;
}
.edit-preview .ep-mode-switch button {
  font-size: 12px;
  text-transform: lowercase;
}
.edit-preview .ep-mode-switch button[aria-pressed="true"] {
  color: var(--ep-ink);
  background: var(--ep-surface);
  box-shadow: 0 1px 2px rgba(16, 18, 21, .08);
}
.edit-preview .ep-mode-switch button:hover,
.edit-preview .ep-reset:hover { color: var(--ep-ink); background: var(--ep-sunken); }
.edit-preview .ep-reset { width: 26px; padding: 0; }
.edit-preview .ep-topbar-sep { width: 1px; height: 18px; background: var(--ep-line); }

.edit-preview .ep-top-actions { display: flex; align-items: center; gap: 4px; }
.edit-preview .ep-button {
  display: inline-flex;
  min-height: 27px;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 4px 11px;
  color: var(--ep-ink-2);
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  border-radius: 3px;
  cursor: pointer;
  white-space: nowrap;
}
.edit-preview .ep-button:hover { color: var(--ep-ink); background: var(--ep-sunken); }
.edit-preview .ep-button-primary {
  color: var(--ep-surface);
  background: var(--ep-ink);
  border-color: var(--ep-ink);
  font-weight: 600;
}
.edit-preview .ep-button-primary:hover { color: #fff; background: #000; }
.edit-preview .ep-button-ghost { background: transparent; border-color: transparent; }
.edit-preview .ep-button:disabled,
.edit-preview .ep-button[aria-disabled="true"] {
  color: var(--ep-ink-3);
  background: var(--ep-sunken);
  border-color: var(--ep-line);
  cursor: not-allowed;
  opacity: .62;
}

.edit-preview .ep-avatar,
.edit-preview .ep-mini-avatar {
  display: inline-grid;
  flex: none;
  place-items: center;
  border-radius: 50%;
  color: var(--ep-ink-2);
  background: var(--ep-sunken);
  font-weight: 650;
  letter-spacing: .02em;
}
.edit-preview .ep-avatar { width: 22px; height: 22px; font-size: 9px; }
.edit-preview .ep-avatar-small { width: 20px; height: 20px; font-size: 8px; }
.edit-preview .ep-mini-avatar { width: 14px; height: 14px; font-size: 6px; }
.edit-preview .ep-avatar-collaborator,
.edit-preview .ep-mini-avatar:has(+ span) {
  color: #355069;
  background: var(--ep-collaborator-wash);
}
.edit-preview .ep-avatar[data-kind="agent"],
.edit-preview .ep-mini-avatar[data-kind="agent"] {
  border-radius: 2px;
  font-family: var(--ep-mono);
}

.edit-preview .ep-ribbon {
  position: relative;
  z-index: 20;
  display: flex;
  flex: 0 0 38px;
  align-items: center;
  gap: 2px;
  min-width: 0;
  padding: 0 14px;
  overflow-x: auto;
  overflow-y: hidden;
  color: var(--ep-ink-2);
  background: var(--ep-surface);
  border-bottom: 1px solid var(--ep-line);
  scrollbar-width: thin;
}
.edit-preview .ep-ribbon button,
.edit-preview .ep-style-select {
  flex: none;
  height: 27px;
  min-width: 29px;
  padding: 3px 8px;
  color: var(--ep-ink-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  cursor: pointer;
}
.edit-preview .ep-ribbon button {
  display: inline-grid;
  place-items: center;
}
.edit-preview .ep-ribbon button:hover,
.edit-preview .ep-style-select:hover { color: var(--ep-ink); background: var(--ep-sunken); }
.edit-preview .ep-style-select { min-width: 92px; }
.edit-preview .ep-underlined { text-decoration: underline; text-underline-offset: 2px; }
.edit-preview .ep-ribbon-rule { flex: none; width: 1px; height: 16px; margin: 0 4px; background: var(--ep-line); }
.edit-preview .ep-ribbon-spacer { flex: 1 1 auto; min-width: 16px; }
.edit-preview .ep-ribbon-status {
  flex: none;
  padding: 3px 7px;
  color: var(--ep-ink-2);
  background: var(--ep-sunken);
  border: 1px solid var(--ep-line);
  border-radius: 999px;
}
.edit-preview .ep-editor-notice {
  flex: none;
  margin: 0;
  padding: 7px 14px;
  color: var(--ep-ink-2);
  background: var(--ep-accent-wash);
  border-bottom: 1px solid var(--ep-line);
}

.edit-preview .ep-workspace {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: var(--ep-canvas);
}

.edit-preview .ep-history-rail {
  display: flex;
  flex: 0 0 240px;
  flex-direction: column;
  width: 240px;
  min-width: 0;
  height: calc(100% - 28px);
  margin: 14px 0 14px 14px;
  overflow: hidden;
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  border-radius: 5px;
  transition: width 160ms ease, flex-basis 160ms ease;
}
.edit-preview .ep-history-rail.is-collapsed { flex-basis: 34px; width: 34px; }

.edit-preview .ep-history-head {
  display: flex;
  flex: 0 0 32px;
  align-items: center;
  gap: 6px;
  padding: 0 4px 0 10px;
  border-bottom: 1px solid var(--ep-line-2);
}
.edit-preview .ep-history-head h2 {
  margin: 0;
  overflow: hidden;
  color: var(--ep-ink-3);
  font-size: 11px;
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
}
.edit-preview .ep-history-head button {
  display: grid;
  flex: none;
  width: 25px;
  height: 25px;
  margin-left: auto;
  padding: 0;
  place-items: center;
  color: var(--ep-ink-3);
  background: transparent;
  border: 0;
  border-radius: 3px;
  cursor: pointer;
}
.edit-preview .ep-history-head button:hover { color: var(--ep-ink); background: var(--ep-sunken); }
.edit-preview .ep-history-rail.is-collapsed .ep-history-head { padding: 3px; border-bottom: 0; }
.edit-preview .ep-history-rail.is-collapsed .ep-history-head h2,
.edit-preview .ep-history-rail.is-collapsed .ep-history-list,
.edit-preview .ep-history-rail.is-collapsed .ep-history-foot { opacity: 0; pointer-events: none; }

.edit-preview .ep-history-list {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  padding: 5px;
  overflow-y: auto;
  list-style: none;
  transition: opacity 90ms ease;
}
.edit-preview .ep-history-item {
  --ep-lane-x: 11px;
  position: relative;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: 4px;
  min-height: 48px;
  padding: 6px 5px;
  border-radius: 3px;
}
.edit-preview .ep-history-item:hover { background: var(--ep-canvas); }
.edit-preview .ep-history-item[data-lane="mine"] { --ep-lane-x: 5px; }
.edit-preview .ep-history-item[data-lane="collaborator"] { --ep-lane-x: 18px; }
.edit-preview .ep-history-node { position: relative; min-height: 32px; }
.edit-preview .ep-history-node::before {
  content: "";
  position: absolute;
  top: 4px;
  left: var(--ep-lane-x);
  z-index: 1;
  width: 8px;
  height: 8px;
  background: var(--ep-surface);
  border: 1.5px solid var(--ep-ink-3);
  border-radius: 50%;
  transform: translateX(-50%);
}
.edit-preview .ep-history-node::after {
  content: "";
  position: absolute;
  top: 16px;
  bottom: -12px;
  left: var(--ep-lane-x);
  width: 1px;
  background: var(--ep-line);
}
.edit-preview .ep-history-item[data-lane="mine"] .ep-history-node::before {
  background: var(--ep-ink);
  border-color: var(--ep-ink);
}
.edit-preview .ep-history-item[data-lane="mine"] .ep-history-node::after {
  box-shadow: 13px 0 0 var(--ep-line);
}
.edit-preview .ep-history-item[data-lane="collaborator"] .ep-history-node::before {
  background: var(--ep-collaborator-wash);
  border-color: var(--ep-collaborator);
}
.edit-preview .ep-history-item[data-lane="shared"] .ep-history-node::before {
  background: var(--ep-sunken);
  border-color: var(--ep-line);
}
.edit-preview .ep-history-item:last-child .ep-history-node::after { display: none; }
.edit-preview[data-decision-state="open"] .ep-history-pending .ep-history-node::before {
  background: var(--ep-danger-wash);
  border-color: var(--ep-danger);
}
.edit-preview:not([data-decision-state="open"]) .ep-history-pending .ep-history-node::before {
  background: var(--ep-ok);
  border-color: var(--ep-ok);
}
.edit-preview .ep-history-copy { display: grid; align-content: start; gap: 3px; min-width: 0; }
.edit-preview .ep-history-copy > strong {
  overflow: hidden;
  color: var(--ep-ink);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  text-overflow: ellipsis;
}
.edit-preview .ep-history-copy > span:not(.ep-history-meta) { color: var(--ep-ink-3); font-size: 11px; }
.edit-preview .ep-history-meta {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  color: var(--ep-ink-3);
  font-size: 10.5px;
}
.edit-preview .ep-history-foot {
  display: flex;
  flex: 0 0 36px;
  align-items: center;
  gap: 6px;
  margin: 0 6px;
  padding: 0 8px;
  color: var(--ep-ink-2);
  border-top: 1px solid var(--ep-line-2);
  font-size: 12px;
  transition: opacity 90ms ease;
}
.edit-preview .ep-history-foot svg { color: var(--ep-ink-3); }

.edit-preview .ep-desk {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  padding: 14px 14px 110px;
  overflow: auto;
  background: var(--ep-canvas);
}

.edit-preview .ep-paper-wrap {
  position: relative;
  width: var(--ep-sheet-w);
  min-width: var(--ep-sheet-w);
  margin: 0 auto;
  transition: width 160ms ease, padding-right 160ms ease;
}
.edit-preview[data-mode="merging"] .ep-paper-wrap {
  width: calc(var(--ep-sheet-w) + 300px);
  padding-right: 300px;
}

.edit-preview .ep-sheet {
  position: relative;
  width: var(--ep-sheet-w);
  min-width: var(--ep-sheet-w);
  min-height: var(--ep-sheet-h);
  padding: 40px 40px 56px;
  overflow: visible;
  color: var(--ep-ink);
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  border-radius: 0;
  box-shadow: var(--ep-shadow);
}

.edit-preview .ep-document-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 14px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--ep-line);
}
.edit-preview .ep-document-head h1 {
  margin: 0;
  color: var(--ep-ink);
  font: 600 26px/1.25 var(--ep-ui);
  letter-spacing: -.015em;
  text-transform: none;
}
.edit-preview .ep-version-pair {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-left: auto;
  color: var(--ep-ink-3);
  font-size: 12px;
}

.edit-preview .ep-document-body {
  color: var(--ep-ink);
  font: 16.5px/1.7 var(--ep-ui);
  letter-spacing: -.003em;
  caret-color: var(--ep-ink);
}
.edit-preview .ep-document-body:focus { outline: none; }
.edit-preview .ep-document-body:focus-visible {
  outline: 1px solid var(--ep-line);
  outline-offset: 8px;
}
.edit-preview .ep-document-body section { display: grid; gap: 14px; margin-bottom: 22px; }
.edit-preview .ep-document-body h2 {
  margin: 0;
  color: var(--ep-ink);
  font: 600 18px/1.35 var(--ep-ui);
  letter-spacing: -.006em;
  text-transform: none;
}
.edit-preview .ep-document-body p,
.edit-preview .ep-document-body blockquote { margin: 0; }
.edit-preview .ep-document-body ul,
.edit-preview .ep-document-body ol { margin: 0; padding-left: 24px; }
.edit-preview .ep-document-body table {
  width: 100%;
  margin: 10px 0;
  border-collapse: collapse;
  font-size: 14px;
}
.edit-preview .ep-document-body th,
.edit-preview .ep-document-body td { padding: 4px 8px; border-bottom: 1px solid var(--ep-line); text-align: left; }
.edit-preview .ep-document-body > blockquote,
.edit-preview .ep-document-body section > blockquote {
  padding-left: 14px;
  color: var(--ep-ink-2);
  border-left: 2px solid var(--ep-line);
}
.edit-preview .ep-document-body a { color: var(--ep-ink); text-decoration: underline; text-underline-offset: 2px; }

.edit-preview .ep-change { position: relative; }
.edit-preview[data-mode="editing"] .ep-review-decoration { display: none !important; }
.edit-preview .ep-review-bar {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: -24px;
  width: 3px;
  border-radius: 2px;
}
.edit-preview .ep-review-bar[data-review-status="fit"] { background: var(--ep-ok); }
.edit-preview .ep-review-bar[data-review-status="kept"] { background: var(--ep-ink-3); }
.edit-preview .ep-review-bar[data-review-status="disagreement"] { background: var(--ep-danger); }

.edit-preview .ep-margin-slot {
  position: absolute;
  top: 1px;
  left: calc(100% + 52px);
  width: 276px;
  font: 11.5px/1.4 var(--ep-ui);
}
.edit-preview .ep-result-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 276px;
  padding: 4px 8px;
  color: var(--ep-ok);
  background: var(--ep-ok-wash);
  border: 1px solid #c6dfd1;
  border-radius: 999px;
  white-space: nowrap;
}
.edit-preview .ep-result-pill.is-undone {
  color: var(--ep-ink-2);
  background: var(--ep-sunken);
  border-color: var(--ep-line);
}
.edit-preview .ep-result-pill button {
  padding: 0;
  color: inherit;
  background: transparent;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.edit-preview .ep-result-with-avatar { padding-left: 4px; }
.edit-preview[data-decision-state="open"] [data-change="fees"] .ep-margin-slot,
.edit-preview[data-decision-state="open"] [data-change="confidentiality"] .ep-margin-slot {
  visibility: hidden;
}

.edit-preview [data-notice-highlight] {
  padding: 0;
  color: inherit;
  background: transparent;
}
.edit-preview[data-mode="merging"][data-decision-state="open"] [data-notice-highlight] {
  padding: 1px 3px;
  color: #8f2f24;
  background: var(--ep-danger-wash);
  border-bottom: 2px solid var(--ep-danger);
}

.edit-preview .ep-decision-card {
  position: absolute;
  top: -10px;
  left: calc(100% + 52px);
  z-index: 5;
  width: 276px;
  padding: 13px;
  color: var(--ep-ink);
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  border-radius: 5px;
  box-shadow: var(--ep-shadow);
  font: 13px/1.5 var(--ep-ui);
}
.edit-preview .ep-decision-title { display: flex; align-items: center; gap: 7px; margin-bottom: 8px; }
.edit-preview .ep-status-dot {
  width: 7px;
  height: 7px;
  background: var(--ep-danger);
  border-radius: 50%;
}
.edit-preview .ep-decision-card > p { margin-bottom: 10px; color: var(--ep-ink-2); }
.edit-preview .ep-decision-options { display: grid; gap: 6px; }
.edit-preview .ep-decision-options button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 36px;
  padding: 7px 9px;
  color: var(--ep-ink);
  background: var(--ep-canvas);
  border: 1px solid var(--ep-line);
  border-radius: 3px;
  cursor: pointer;
  text-align: left;
}
.edit-preview .ep-decision-options button:hover { background: var(--ep-sunken); border-color: var(--ep-ink-3); }
.edit-preview .ep-decision-options button > span:last-child { color: var(--ep-ink-3); }
.edit-preview .ep-decision-options .ep-ask {
  min-height: 28px;
  justify-content: flex-start;
  color: var(--ep-ink-2);
  background: transparent;
  border-color: transparent;
}
.edit-preview .ep-decision-card blockquote {
  padding: 9px 0 0;
  margin: 9px 0 0;
  color: var(--ep-ink-3);
  border-top: 1px solid var(--ep-line-2);
  font-size: 11.5px;
}

.edit-preview .ep-pagefoot {
  display: flex;
  align-items: center;
  gap: 8px;
  width: var(--ep-sheet-w);
  padding: 0 4px;
  margin-top: 9px;
  color: var(--ep-ink-3);
  font-size: 11px;
}

.edit-preview .ep-submit-dock {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  max-width: calc(100vw - 32px);
}
.edit-preview .ep-dock-cluster { position: relative; display: flex; justify-content: flex-end; }
.edit-preview .ep-dock-cluster::before {
  content: "";
  position: absolute;
  right: 0;
  bottom: 100%;
  width: min(304px, calc(100vw - 32px));
  height: 10px;
}
.edit-preview .ep-submit-circle,
.edit-preview .ep-submitted-pill,
.edit-preview .ep-submitting-pill {
  min-height: 44px;
  color: var(--ep-ink);
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  box-shadow: var(--ep-shadow);
}
.edit-preview .ep-submit-circle {
  position: relative;
  display: grid;
  width: 44px;
  height: 44px;
  padding: 0;
  place-items: center;
  border-radius: 50%;
  cursor: pointer;
}
.edit-preview .ep-submit-circle:hover { background: var(--ep-sunken); }
.edit-preview .ep-change-count {
  position: absolute;
  top: -5px;
  right: -5px;
  display: grid;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  place-items: center;
  color: #fff;
  background: var(--ep-amber);
  border: 2px solid var(--ep-canvas);
  border-radius: 9px;
  font-size: 10px;
  font-weight: 700;
}

.edit-preview .ep-submitted-pill {
  display: flex;
  height: 44px;
  align-items: center;
  gap: 8px;
  padding: 4px 13px 4px 4px;
  border-color: #c6dfd1;
  border-radius: 22px;
  cursor: pointer;
}
.edit-preview .ep-check-disc,
.edit-preview .ep-spinner-disc {
  display: grid;
  flex: none;
  width: 34px;
  height: 34px;
  place-items: center;
  border-radius: 50%;
}
.edit-preview .ep-check-disc { color: var(--ep-ok); background: var(--ep-ok-wash); }
.edit-preview .ep-submitted-pill strong { font-weight: 650; }
.edit-preview[data-decision-state="open"] .ep-submitted-pill strong { color: var(--ep-danger); }
.edit-preview:not([data-decision-state="open"]) .ep-submitted-pill strong { color: var(--ep-ok); }
.edit-preview .ep-pill-separator { color: var(--ep-line); }

.edit-preview .ep-submitting-pill {
  display: flex;
  height: 44px;
  align-items: center;
  gap: 9px;
  padding: 4px 14px 4px 4px;
  color: var(--ep-ink-2);
  border-radius: 22px;
}
.edit-preview .ep-spinner-disc { background: var(--ep-sunken); }
.edit-preview .ep-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid var(--ep-line);
  border-top-color: var(--ep-ink);
  border-radius: 50%;
  animation: ep-spin .8s linear infinite;
}
@keyframes ep-spin { to { transform: rotate(360deg); } }

.edit-preview .ep-dock-card {
  position: absolute;
  right: 0;
  bottom: calc(100% + 8px);
  width: min(304px, calc(100vw - 32px));
  padding: 13px 14px;
  visibility: hidden;
  color: var(--ep-ink);
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  border-radius: 5px;
  box-shadow: var(--ep-shadow);
  opacity: 0;
  pointer-events: none;
  transform: translateY(4px);
  transition: opacity 90ms ease, transform 90ms ease;
}
.edit-preview .ep-dock-cluster:hover .ep-dock-card,
.edit-preview .ep-dock-cluster:focus-within .ep-dock-card {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transform: none;
}
.edit-preview .ep-dock-card h2 {
  margin: 0 0 8px;
  color: var(--ep-ink-3);
  font: 700 11px/1.4 var(--ep-ui);
  letter-spacing: .06em;
  text-transform: uppercase;
}
.edit-preview [data-submit-panel="submitted"] .ep-dock-card h2 { color: var(--ep-ok); }
.edit-preview .ep-dock-card > p { margin: 0; color: var(--ep-ink); font-size: 13px; }
.edit-preview .ep-dock-card > p .ep-mono { color: var(--ep-ink-3); }
.edit-preview .ep-change-list,
.edit-preview .ep-result-list {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 13px;
}
.edit-preview .ep-result-list {
  padding-top: 10px;
  margin-top: 10px;
  border-top: 1px solid var(--ep-line-2);
}
.edit-preview .ep-change-list li,
.edit-preview .ep-result-list li { display: grid; grid-template-columns: 3px minmax(0, 1fr); gap: 9px; }
.edit-preview .ep-summary-bar { width: 3px; min-height: 18px; border-radius: 2px; background: var(--ep-line); }
.edit-preview [data-change-kind="meaning"] .ep-summary-bar { background: var(--ep-amber); }
.edit-preview [data-change-kind="wording"] { color: var(--ep-ink-2); }
.edit-preview [data-result="disagreement"] .ep-summary-bar { background: var(--ep-danger); }
.edit-preview [data-result="fit"] .ep-summary-bar { background: var(--ep-ok); }
.edit-preview .ep-summary-location { color: var(--ep-ink-3); }
.edit-preview .ep-dock-actions { display: flex; justify-content: flex-end; gap: 5px; margin-top: 12px; }

@media (max-width: 980px) {
  .edit-preview .ep-history-rail { flex-basis: 212px; width: 212px; }
  .edit-preview .ep-history-rail.is-collapsed { flex-basis: 34px; width: 34px; }
  .edit-preview .ep-crumb strong { max-width: 150px; }
}

@media (max-width: 720px) {
  .edit-preview .ep-topbar { gap: 6px; padding: 0 8px; }
  .edit-preview .ep-crumb a,
  .edit-preview .ep-crumb .ep-crumb-sep { display: none; }
  .edit-preview .ep-topbar-sep,
  .edit-preview .ep-reset { display: none; }
  .edit-preview .ep-mode-switch button { padding-inline: 6px; }
  .edit-preview .ep-history-rail { margin-left: 8px; }
  .edit-preview .ep-desk { padding-inline: 8px; }
  .edit-preview .ep-submit-dock { right: 12px; bottom: 12px; }
}

@media (max-width: 520px) {
  .edit-preview .ep-brand > span:last-child { display: none; }
  .edit-preview .ep-crumb { max-width: 118px; }
  .edit-preview .ep-mode-switch { margin-left: auto; }
  .edit-preview .ep-top-actions { display: none; }
  .edit-preview .ep-history-rail { flex-basis: 196px; width: 196px; }
  .edit-preview .ep-history-rail.is-collapsed { flex-basis: 34px; width: 34px; }
}

@media (prefers-reduced-motion: reduce) {
  .edit-preview *,
  .edit-preview *::before,
  .edit-preview *::after {
    scroll-behavior: auto !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}


/* ---------------------------------------------------------- Writ home dashboard */

/* Home uses the application workspace, not the document controls. */
.shell:has(.writ-home) > .topbar .view { display: none; }

.writ-home {
  display: grid;
  min-height: 100%;
  align-content: start;
  gap: clamp(36px, 6vw, 64px);
  padding: clamp(32px, 6vw, 68px) clamp(18px, 5vw, 56px) var(--s8);
  background: var(--canvas);
}

.writ-home > * {
  width: min(1080px, 100%);
  margin-inline: auto;
}

.writ-home-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--s7);
  padding-bottom: clamp(32px, 5vw, 52px);
  border-bottom: 1px solid var(--line);
}

.writ-home-hero-copy {
  display: grid;
  max-width: 680px;
  gap: var(--s3);
}

.writ-home-eyebrow {
  color: var(--ink-3);
  font-size: var(--t-xs);
  font-weight: 650;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.writ-home-hero h1 {
  max-width: 650px;
  font-size: clamp(30px, 4.2vw, 46px);
  font-weight: 620;
  line-height: 1.08;
  letter-spacing: -.035em;
}

.writ-home-hero-copy > p:last-child {
  max-width: 590px;
  color: var(--ink-2);
  font-size: var(--t-lg);
  line-height: 1.6;
}

.writ-home-actions {
  display: flex;
  gap: var(--s2);
}

.writ-home-actions .btn {
  min-height: 36px;
  padding-inline: var(--s4);
}

.writ-home-projects {
  display: grid;
  gap: var(--s5);
}

.writ-home-heading {
  display: grid;
  gap: var(--s1);
}

.writ-home-heading h1 {
  font-size: clamp(25px, 3vw, 32px);
  font-weight: 620;
  line-height: 1.2;
  letter-spacing: -.025em;
}

.writ-home-heading > p:last-child {
  color: var(--ink-3);
  font-size: var(--t-sm);
}

.writ-home-create {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(130px, .32fr) auto;
  align-items: end;
  gap: var(--s4);
  padding: var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  box-shadow: var(--e1);
}

.writ-home-create-heading {
  display: flex;
  grid-column: 1 / -1;
  align-items: baseline;
  gap: var(--s3);
  padding-bottom: var(--s1);
}

.writ-home-create-heading h2 {
  font-size: var(--t-lg);
  font-weight: 620;
  letter-spacing: -.012em;
}

.writ-home-create-heading p {
  color: var(--ink-3);
  font-size: var(--t-sm);
}

.writ-home-field {
  display: grid;
  gap: var(--s2);
}

.writ-home-field label {
  color: var(--ink-2);
  font-size: var(--t-sm);
  font-weight: 600;
}

.writ-home-field input,
.writ-home-field select {
  width: 100%;
  min-height: 38px;
  padding: var(--s2) var(--s3);
  background: var(--canvas);
  border: 1px solid var(--line);
  border-radius: var(--r2);
}

.writ-home-field input:hover,
.writ-home-field select:hover { border-color: var(--ink-3); }

.writ-home-field small {
  color: var(--ink-3);
  font-size: var(--t-xs);
}

.writ-home-create-submit {
  min-height: 38px;
  padding-inline: var(--s4);
}

.writ-home-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: var(--s4);
  padding: 0;
  margin: 0;
  list-style: none;
}

.writ-home-card {
  display: flex;
  min-height: 150px;
  flex-direction: column;
  gap: var(--s4);
  padding: var(--s5);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  box-shadow: var(--e1);
}

.writ-home-card:hover {
  background: var(--surface);
  border-color: var(--ink-3);
  text-decoration: none;
}

.writ-home-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}

.writ-home-card-name {
  overflow: hidden;
  font-size: var(--t-lg);
  font-weight: 650;
  letter-spacing: -.012em;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.writ-home-visibility {
  flex: none;
  padding: 2px var(--s2);
  color: var(--ink-3);
  background: var(--sunken);
  border-radius: 999px;
  font-size: 10px;
  font-weight: 650;
  letter-spacing: .055em;
  text-transform: uppercase;
}

.writ-home-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s3);
  padding-top: var(--s4);
  color: var(--ink-3);
  border-top: 1px solid var(--line-2);
  font-size: var(--t-xs);
}

.writ-home-stats > span {
  display: grid;
  gap: 1px;
}

.writ-home-stats strong {
  color: var(--ink-2);
  font-size: var(--t-md);
  font-weight: 620;
}

.writ-home-stats .is-blocked,
.writ-home-stats .is-blocked strong { color: var(--danger); }

.writ-home-updated {
  margin-top: auto;
  color: var(--ink-3);
  font-size: var(--t-xs);
}

.writ-home-empty {
  display: grid;
  min-height: 150px;
  place-content: center;
  gap: var(--s1);
  padding: var(--s6);
  color: var(--ink-3);
  border: 1px dashed var(--line);
  border-radius: var(--r3);
  text-align: center;
}

.writ-home-empty h2 {
  color: var(--ink-2);
  font-size: var(--t-md);
  font-weight: 620;
}

.writ-home-empty p { font-size: var(--t-sm); }

@media (max-width: 720px) {
  .writ-home {
    gap: var(--s7);
    padding: var(--s6) var(--s4) var(--s7);
  }

  .writ-home-hero { grid-template-columns: 1fr; gap: var(--s5); }
  .writ-home-actions { justify-content: flex-start; }
  .writ-home-create { grid-template-columns: 1fr; padding: var(--s4); }
  .writ-home-create-heading { display: grid; gap: 2px; }
  .writ-home-create-submit { width: 100%; }
}

/* Auth screens have no document: no history rail, no notes column, no View
   menu, and a form that reads top to bottom. Inherited whole, the editing shell
   put a "Document history" panel and a "0 NOTES" margin on the login page, and
   the label/input pairs laid out side by side so "Email [input] Password"
   collided on one line. */
.desk--bare { grid-template-columns: minmax(0, 1fr) !important; }
.desk--bare .rail,
.desk--bare .rail-handle,
.desk--bare .margin,
.desk--bare .gutter { display: none !important; }
.desk--bare ~ * .menu.view, body:has(.desk--bare) .menu.view { display: none; }

.desk--bare .sheet {
  max-width: 380px; margin: 10vh auto 0; padding: 30px 32px 32px;
  border: 1px solid var(--line); background: var(--surface);
}
.desk--bare .card,
.desk--bare .card.narrow { border: 0; padding: 0; background: none; max-width: none; }
.desk--bare h1 { font-size: 21px; letter-spacing: -.01em; margin: 0 0 6px; }
.desk--bare .muted { font-size: 13px; line-height: 1.55; margin: 0 0 18px; }

/* one field per row: label above its input, input full width */
.desk--bare form label {
  display: block; margin: 0 0 14px;
  font-size: 12px; font-weight: 500; color: var(--ink-2);
}
.desk--bare form input:not([type="hidden"]):not([type="radio"]):not([type="checkbox"]) {
  display: block; width: 100%; box-sizing: border-box; margin-top: 5px;
  padding: 9px 11px; font: inherit; font-size: 14px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
}
.desk--bare form input:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }
.desk--bare form .btn,
.desk--bare form button[type="submit"] { width: 100%; margin-top: 4px; padding: 11px 16px; }
.desk--bare .error { margin: 0 0 14px; }
.desk--bare form + p, .desk--bare .card > p:last-child { margin: 16px 0 0; font-size: 13px; }
/* hints sit under the field they explain, so they must read as secondary to it;
   inherited body size they were larger than the labels above them */
.desk--bare form .muted,
.desk--bare form span.muted { font-size: 12px; line-height: 1.5; margin: 6px 0 16px; display: block; }

/* Theme switch in the topbar. The one in the View menu stays; this is the one
   a reader finds without opening a menu named for something else, and the only
   one on sign-in and sign-up, which have no View menu.

   Three segments, not a two-state toggle: "follow the system" is the default
   and has to stay reachable, or choosing once means choosing forever. A single
   thumb slides between them — at the app's own 90ms and easing, because a
   control that bounces would be the only thing on the page that does. */
.theme-inline {
  position: relative; display: inline-grid; grid-auto-flow: column;
  grid-auto-columns: 1fr; align-items: stretch;
  border: 1px solid var(--line); border-radius: var(--r1);
  margin-right: 10px; overflow: hidden; isolation: isolate;
}
.theme-thumb {
  position: absolute; z-index: 0; inset: 0 auto 0 0; width: calc(100% / 3);
  background: var(--ink); border-radius: calc(var(--r1) - 1px);
  transform: translateX(0);
  transition: transform 140ms var(--ease);
}
.theme-inline[data-mode="light"] .theme-thumb { transform: translateX(100%); }
.theme-inline[data-mode="dark"]  .theme-thumb { transform: translateX(200%); }

.theme-btn {
  position: relative; z-index: 1;
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: 4px 10px; font: 500 11px/1.6 var(--prose); color: var(--ink-3);
  letter-spacing: .01em; white-space: nowrap;
  transition: color var(--fast) var(--ease);
}
.theme-btn:hover { color: var(--ink); }
.theme-btn[aria-pressed="true"] { color: var(--surface); }
.theme-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* Someone who asked for less motion gets the same control, without the slide. */
@media (prefers-reduced-motion: reduce) {
  .theme-thumb { transition: none; }
}
