/* ============================================================
   semnas — AI reading workspace
   A professional desktop-app interface: dark, dense chrome
   (toolbar + inspector) wrapped around a light sheet of paper.

   Structure of this sheet:
     1. Design tokens (two appearances, one set of names)
     2. Global primitives (reset, focus, scrollbars, motion)
     3. Control kit (buttons, fields, segments, switches…)
     4. Toolbar + status display (LCD)
     5. Document list page
     6. Editor: desk, paper, blocks
     7. Inspector sidebar
     8. Threads, proposals, variants
     9. Overlays: selection bar, popovers, drawers, toasts
    10. Utilities + responsive collapse

   THE TOKEN RULE, and its documented limit. Section 1 is the only
   place a value is authored. testStudioDesignSystem enforces this
   mechanically, per declaration, with var() references stripped:

     - Colour and elevation are TOTALLY tokenized. No colour literal
       in any notation (hex, rgb(), hsl/hwb/lab/lch/oklab/oklch/
       color(), or any of the 147 CSS named colours) may appear
       outside section 1, and every shadow comes from --elev-*,
       --paper-shadow or --knob-shadow.
     - The systematic scales — UI type (--fs-*), document type
       (--fs-doc*), spacing (--sp-*), radii (--r-*), control heights,
       chrome metrics, page geometry (--paper-*) and motion (--dur-*,
       --ease) — are declared once here and referenced by var()
       everywhere else. No component re-states a scale value.
     - CARVE-OUT: a short, closed set of one-off structural
       dimensions has no scale equivalent, and minting a single-use
       token for each would add names without meaning. These stay
       literal on purpose, and they are exhaustively:
         * hairlines and separators: 1px borders, 1px/2px gaps and
           paddings, the 18px toolbar separator
         * marker geometry: the 2px selected-block rule, the 7px
           annotation dot, 50% circles, and the 1px/2px sheet and
           marker radii
         * control geometry: the 30x18px switch with its 14px knob,
           the 27px panel header, the 84px textarea well, the 12px
           scrollbar
         * page-edge runways off the 4px grid: 80px, 120px, 64px,
           48px, and the -28px page-margin marker offsets
         * the .92em relative code size and the .7s spinner period
       Section 2 of the test enumerates exactly this allowlist for
       the families it scans (type size, radius, duration) and fails
       on any other literal, so the carve-out cannot quietly widen.

   Ink values are tuned so every text/surface pair in the UI
   clears WCAG AA (4.5:1) in BOTH appearances — testStudioDesignSystem
   recomputes the ratios from these tokens on every run.
   ============================================================ */

/* ------------------------------------------------------------
   1. Design tokens
   Dark is the pro default and lives on bare `:root`, so the app
   is already dark before any script runs. Light is opted into
   with [data-appearance="light"]; the attribute selector
   outranks bare `:root`, so the override needs no !important.
   ------------------------------------------------------------ */
:root,
:root[data-appearance="dark"] {
  /* Tells the engine to render native widgets and scrollbars dark. */
  color-scheme: dark;

  /* Chrome & surfaces, darkest (workspace) to lightest (raised control). */
  --desk: #101013;
  --desk-glow: rgba(255, 255, 255, .05);
  --window: #17171A;
  --chrome: rgba(42, 42, 47, .72);
  --chrome-solid: #2a2a2f;
  --surface: #1e1e22;
  --surface-2: #26262b;
  --surface-3: #303038;

  /* Hairlines are translucent so they read correctly over any surface. */
  --hairline: rgba(255, 255, 255, .09);
  --hairline-strong: rgba(255, 255, 255, .17);

  /* Ink, in Apple's label / secondary / tertiary hierarchy. */
  --label: #f2f2f7;
  --label-2: #a8a8b0;
  --label-3: #949499;
  --on-accent: #ffffff;

  /* Controls: a raised body, a top highlight, an inset pressed state. */
  --control: #34343b;
  --control-hover: #3d3d45;
  --control-hi: rgba(255, 255, 255, .11);
  --control-press: rgba(0, 0, 0, .40);
  --field: rgba(0, 0, 0, .26);
  --lcd: #0b0b0e;
  --thumb: rgba(255, 255, 255, .22);
  /* Switch: an off track and a knob. The knob is white in BOTH
     appearances, as on Apple platforms. */
  --track: rgba(255, 255, 255, .16);
  --knob: #ffffff;

  /* The brand accent, dark variants. The guide ships ONE colour — plum
     #7A2748 — and it is 1.87:1 on this chrome, so it cannot be the accent
     here: dark is the default appearance, and an accent that vanishes into
     its own background is not an accent. So the roles split.

     --accent is plum with the lights up (5.27:1 on --window). It draws
     bars, borders, tints, links and focus — everything that is COLOUR ON
     A DARK GROUND, which is the case the brand guide has nothing to say
     about. --accent-fill is the TRUE brand plum, kept because a fill is
     the one place white sits on top: white on #7A2748 is 9.50:1, on the
     lightened plum only 3.36:1. So dark uses the real brand colour exactly
     where the brand itself uses it — as a ground under paper-coloured
     content — and a tint only where it must. */
  --accent: #C9718F;
  --accent-fill: #7A2748;
  --accent-ink: #E0A0B4;
  --accent-soft: rgba(201, 113, 143, .18);
  --focus: rgba(201, 113, 143, .65);
  --red: #ff453a;
  --red-soft: rgba(255, 69, 58, .16);
  --green: #30d158;
  --green-soft: rgba(48, 209, 88, .16);
  --orange: #ff9f0a;
  --orange-soft: rgba(255, 159, 10, .16);

  /* Elevation: tight and dark, the way pro apps stack panels. */
  --elev-1: 0 1px 1px rgba(0, 0, 0, .30);
  --elev-2: 0 2px 8px rgba(0, 0, 0, .38);
  --elev-3: 0 18px 48px rgba(0, 0, 0, .58);

  /* Code surface, dark appearance: the editor-card look of the code viewer
     and the on-sheet code textarea. Declared per appearance (not in the
     shared block below) so the code block follows the light/dark toggle. */
  --code-bg: #1d1d22;
  --code-ink: #e7e7ef;

  /* The PAGE, dark appearance. The sheet used to be white in both scopes —
     the document was treated as an artefact outside the theme — but a
     full-brightness page inside dark chrome is exactly what makes night
     reading painful, so the document now follows the appearance too. The
     sheet sits a step ABOVE --window so it still reads as a page lying on
     the workspace rather than as a hole in it — and far enough above that
     surfaces made OF the paper (the chat dock) stay distinguishable from the
     page behind them. It also stays above --code-bg, so a code well recedes
     into the sheet here exactly as it does on the white page. Each ink clears
     4.5:1 on that sheet (testStudioDesignSystem recomputes them per scope).
     Only the colours are per-appearance; the page GEOMETRY and shadow stay
     in the shared block. */
  --paper: #202027;
  --paper-2: #282830;
  --paper-ink: #ededf2;
  --paper-ink-2: #b0b0b8;
  --paper-ink-3: #8e8e98;
  --paper-line: #3a3a43;
}

:root[data-appearance="light"] {
  color-scheme: light;

  /* A warm desk, as Pages and Preview use a cool one, so the white sheet
     still reads as a physical page rather than merging into the app. The
     greys carry the brand's paper rather than Apple's neutral: beside a
     plum accent a blue-grey chrome reads as a different product.

     The SHEET is not warmed — see --paper below. Paper is a chrome colour
     here; the page stays white. */
  --desk: #C9C5BA;
  --desk-glow: rgba(255, 255, 255, .55);
  --window: #EDEAE3;
  --chrome: rgba(247, 245, 240, .78);
  --chrome-solid: #F7F5F0;
  --surface: #F3F2EE;
  --surface-2: #ffffff;
  --surface-3: #ffffff;

  --hairline: rgba(0, 0, 0, .11);
  --hairline-strong: rgba(0, 0, 0, .20);

  --label: #1d1d1f;
  --label-2: #5c5c61;
  --label-3: #6c6c72;
  --on-accent: #ffffff;

  --control: #ffffff;
  --control-hover: #f4f4f6;
  --control-hi: rgba(255, 255, 255, .90);
  --control-press: rgba(0, 0, 0, .12);
  --field: #ffffff;
  --lcd: #fbfbfd;
  --thumb: rgba(0, 0, 0, .26);
  --track: #dcdce2;
  --knob: #ffffff;

  /* Light needs no split: the true brand plum is 9.50:1 on the sheet and
     carries white at the same ratio, so one colour does every job. Fill and
     ink step deeper by the same convention the blues used. */
  --accent: #7A2748;
  --accent-fill: #6B2240;
  --accent-ink: #5E1E38;
  --accent-soft: rgba(122, 39, 72, .12);
  --focus: rgba(122, 39, 72, .55);
  --red: #d70015;
  --red-soft: rgba(215, 0, 21, .10);
  --green: #1d8c3e;
  --green-soft: rgba(29, 140, 62, .12);
  --orange: #b25000;
  --orange-soft: rgba(178, 80, 0, .12);

  --elev-1: 0 1px 1px rgba(0, 0, 0, .07);
  --elev-2: 0 2px 8px rgba(0, 0, 0, .10);
  --elev-3: 0 18px 48px rgba(0, 0, 0, .22);

  /* Code surface, light appearance: a pale card with dark ink, like the
     code blocks GitHub renders on a white page. Same token names as the
     dark scope so every code-surface consumer flips with the appearance. */
  --code-bg: #f6f8fa;
  --code-ink: #1f2328;

  /* The PAGE, light appearance: the white sheet and warm-grey inks the app
     has always drawn, unchanged — going dark at night must not alter how
     the document reads by day. */
  --paper: #ffffff;
  --paper-2: #f7f7f5;
  --paper-ink: #1c1c1e;
  --paper-ink-2: #55555b;
  --paper-ink-3: #74746c;
  --paper-line: #e4e2dd;
}

/* Appearance-independent tokens: geometry, type, and motion. The page's
   COLOURS now live in the two appearance scopes above (the document follows
   the light/dark toggle); what remains here is the page's geometry and its
   shadow, which are the same measurements whichever way the sheet is inked. */
:root {
  /* Type: a modern grotesque for UI, a technical-paper serif for prose, a
     modern mono for code — each prepended to the platform stack so the
     system faces remain guaranteed fallbacks (and the design-system test's
     stack assertions keep passing). */
  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-doc: "Source Serif 4", "New York", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* The DOCUMENT's headings wear a clean grotesque, so the masthead reads
     crisp and light rather than as a heavy display serif. It shares the UI
     sans stack; the document body uses the same family for a calm, modern feel. */
  --font-display: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* A compact scale. It used to say "pro apps sit near 11–13px" and name the
     authored numbers, which was only ever true of the numbers — at --ui-zoom 1.25
     those 11–13px steps were drawn at 14–16px, and 14–16px is what the app has
     actually looked like for its whole life.

     THE SCALE WENT UP ABOUT 15% ON 2026-09-21, the same day --ui-zoom went from
     1.25 to 1, and for the same reason. The old arrangement — author small, draw a
     quarter larger — got the text right and every width wrong, because a zoom
     multiplies the measure and the margins along with the letters. Killing the zoom
     fixed the widths and shrank the text by a fifth; this is the text coming back
     without the widths coming with it.

     It is a PARTIAL recovery on purpose. Every step below still renders smaller
     than it did at 1.25 (--fs-4 is 15px where it was drawn at 16.25), which is what
     was asked for: the reading size of about a 1.1–1.15 interface, not the old one.

     testStudioDesignSystem still caps all of this, and the cap is now written as a
     RENDERED ceiling — authored times --ui-zoom — rather than an authored one. That
     is not a loosening. The authored form was a proxy that only meant what it said
     while the zoom was 1.25, and when the dial moved it silently became a much
     tighter rule that nobody chose; the rendered form is the envelope the test has
     always permitted on screen, and it holds whatever the dial says. */
  --fs-1: 12px;
  --fs-2: 13px;
  --fs-3: 14px;
  --fs-4: 15px;
  --fs-5: 17px;
  --fs-6: 22px;
  --fs-7: 29px;

  /* The DOCUMENT type scale, kept separate from the UI scale above:
     the sheet is set at a comfortable reading size, so its steps are
     larger and looser than any chrome value. Calm, modern hierarchy —
     a readable body and a clean sans H1. Appearance-independent,
     because the page is light in both. */
  --fs-doc: 20px;
  --fs-doc-1: 39px;
  --fs-doc-2: 30px;
  --fs-doc-3: 23px;

  /* 4px spacing scale. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;

  /* Radii stay small: Apple chrome is precise, not pill-shaped. */
  --r-1: 4px;
  --r-2: 6px;
  --r-3: 9px;
  --r-pill: 999px;

  /* Control heights. They went up with the type on 2026-09-21 — 15px of label in a
     26px button is cramped — and by the same ~15%, so the proportion between a
     control and the text inside it is the one it has always had. */
  --h-control: 30px;
  --h-control-sm: 25px;
  --h-control-xs: 22px;

  /* Chrome metrics. The toolbar followed the controls it holds: 50px, which is still
     shorter than the 55px it was drawn at while --ui-zoom was 1.25. */
  --toolbar-h: 50px;
  --inspector-w: 328px;
  --inspector-w-min: 260px;

  /* The interface scale: the multiple of the sizes declared here that the app is
     actually drawn at, applied as a zoom on the root element (see section 2) rather
     than by inflating the token layer itself.

     IT IS 1, AND IT WENT TO 1 ON 2026-09-21. It was 1.25 for most of this app's life,
     on the theory that the token layer is a compact desktop scale and a real window can
     afford more. What that actually did was make a 1440px window behave like a 1150px
     one: a fifth of the usable width gone on every page, the Brief cards shouting a
     three-line headline, the library rows wider than they need to be. The theory was
     wrong — the token layer is not too small, it is the size it was designed at.

     The dial is kept rather than deleted, because one number is the cheapest possible
     way to try a different answer. But moving it is NOT free, and this is the part to
     read before doing it: a CSS media query cannot read a custom property, so every
     width breakpoint in the app is authored as its design width times this scale, in
     eight places across six files. Change this and all of them move.
     testInterfaceScaleBreakpoints is what stops them drifting. */
  --ui-zoom: 1;

  /* One viewport unit, corrected for that zoom. A zoomed element's lengths are
     multiplied by the zoom, but viewport units are NOT — 1vh stays a hundredth
     of the real window — so a raw `100vh` inside the zoomed root renders more than one
     viewport tall. Anything measured against the window therefore multiplies
     these instead: `calc(100 * var(--vh))` is a real screenful. */
  --vw: calc(1vw / var(--ui-zoom));
  --vh: calc(1vh / var(--ui-zoom));

  /* Motion: short and functional. */
  --dur-1: 90ms;
  --dur-2: 160ms;
  --dur-3: 220ms;
  --ease: cubic-bezier(.4, 0, .2, 1);

  /* Neutral veils. These are appearance-independent because each one
     sits on top of a colour that is itself identical in both
     appearances: the accent fill, the white switch knob, or a modal
     backdrop. Keeping them here means no component rule owns a
     literal, which is what makes "one token layer" verifiable. */
  --sheen: rgba(255, 255, 255, .22);
  --sheen-strong: rgba(255, 255, 255, .45);
  --press-shade: rgba(0, 0, 0, .32);
  --knob-shadow: 0 1px 2px rgba(0, 0, 0, .35);
  --scrim-veil: rgba(0, 0, 0, .45);

  /* The page's geometry. Measure and padding are one proportional set, so
     they are declared together here rather than restated in .paper and its
     responsive override. The top runway is a touch more generous for a
     modern, airy masthead. (The page's colours are per-appearance, above.) */
  --paper-measure: 800px;
  --paper-min-h: 1040px;
  --paper-pad-x: 76px;
  --paper-pad-top: 88px;
  --paper-pad-bottom: 104px;
  --paper-shadow:
    0 0 0 .5px rgba(0, 0, 0, .10),
    0 1px 2px rgba(0, 0, 0, .14),
    0 24px 60px rgba(0, 0, 0, .30);
}

/* ------------------------------------------------------------
   2. Global primitives
   ------------------------------------------------------------ */
* {
  box-sizing: border-box;
  /* Thin overlay scrollbars, matched to the appearance. */
  scrollbar-width: thin;
  scrollbar-color: var(--thumb) transparent;
}
/* min-height, not height: a position:sticky element can only stick inside its containing
   block, so a body box fixed at exactly one viewport unsticks the toolbar as soon as the
   reader scrolls past 100vh (and strands the fixed progress rail beneath it). min-height
   keeps the full-viewport floor for short pages while letting the box grow to the
   document's scroll height. The editor shell sizes itself from 100vh, so it is unaffected.
   Any fixed height here (100%, 100vh, a px value) reintroduces the bug, so this rule must
   carry no `height` at all. */
html, body { margin: 0; padding: 0; min-height: 100%; }

/* The interface scale, applied once, to everything. `zoom` (unlike a transform)
   is a LAYOUT scale: the page reflows at the new size rather than being stretched
   over the old one, so text stays crisp and nothing is clipped. Two consequences
   are handled deliberately elsewhere in these sheets: viewport units do not shrink
   under zoom (hence --vw/--vh above), and a width media query still sees the real
   window, so each breakpoint is written at its design width times this scale. */
html {
  zoom: var(--ui-zoom);
  /* iOS SAFARI INFLATES TEXT ON ITS OWN unless told not to: `text-size-adjust: auto` is
     the default there, and it boosts font sizes inside blocks it judges too narrow —
     without widening the boxes those blocks sit in. Text clipped mid-word at the right
     edge of a card that is itself the right width is the signature, and that is what came
     back from a phone. No desktop engine does this, which is why nothing in the suite
     could have caught it. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* THE PHONE DOES NOT GET THE INTERFACE SCALE.

   --ui-zoom exists so a desktop window can be rendered at some multiple of the token
   layer; a phone already applies a scale of its own, and a second one on top of it is
   what made /brief come back from an iPhone about a quarter too wide. It is also the
   declaration iOS handles least like everything else — a root `zoom` interacts with the visual viewport there in ways no
   headless engine reproduces, so the honest fix at this width is not to reach for it.

   THIS RULE STILL EARNS ITS KEEP NOW THAT THE SCALE IS 1 and it declares 1 on top of 1.
   It is the rule that says a phone never gets a scale, which is the thing that must stay
   true if the dial above is ever turned back up — and it is where
   testInterfaceScaleBreakpoints reads the line from.

   NOT a design width, and deliberately not written as one: this query decides WHICH scale
   is in force, so it cannot be a width times that scale. It is a device class. Everything
   below it is therefore authored 1:1 — the two breakpoints that fire under it (brief.css,
   link-preview.css) are written at their design widths with no multiplier, and
   testInterfaceScaleBreakpoints reads this rule to know where the line is. */
@media (max-width: 720px) {
  :root { --ui-zoom: 1; }
}

body {
  font-family: var(--font-ui);
  font-size: var(--fs-4);
  line-height: 1.45;
  color: var(--label);
  background: var(--window);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
button, input, select, textarea { font-family: inherit; }
code { font-family: var(--font-mono); font-size: .92em; }

/* One focus treatment for the whole app: a keyboard-only accent ring.
   Pointer interaction never draws it, so the UI stays quiet in use. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
  border-radius: var(--r-1);
}

::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--thumb);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-corner { background: transparent; }

::selection { background: var(--accent-soft); }

/* Respect the system motion preference: keep the UI usable, drop the animation. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------------
   3. Control kit
   Apple push button: a raised body with a top highlight, a
   hairline border, and a genuinely inset pressed state.
   ------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  height: var(--h-control); padding: 0 10px;
  font-size: var(--fs-3); font-weight: 510; letter-spacing: -.01em;
  color: var(--label); white-space: nowrap;
  /* OUTLINE, not filled. A control is a hairline and a label; what makes it read as
     pressable is its border, not a gradient body and a lift off the surface. The
     destructive button was always drawn this way — red ink, no fill — so this is the
     rest of the kit matching it rather than the other way round. */
  background: transparent;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-2);
  cursor: pointer;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
    color var(--dur-1) var(--ease);
}
.btn:hover { background: var(--control); }
/* An anchor wearing this class is a BUTTON, whatever element it happens to be, and a
   button does not underline. Without this the global `a:hover` rule treats it as body
   text — which is the same thing `.brand-link`, `.crumb` and the two document-list links
   each opt out of privately further down. This is that opt-out, written once, where the
   component is. */
a.btn:hover { text-decoration: none; }
/* Pressed is a deeper wash rather than an inset shadow: there is no raised body left
   to press INTO, and an inset shadow over a transparent background draws a dark rim
   around nothing. */
.btn:active { background: var(--control-press); }

/* Primary: the accent as a border and ink over a soft wash — unmistakably the
   emphasised control, without being a block of colour beside two that are not.
   It no longer needs --on-accent, which is what forced the grouped-primary special
   case further down this sheet. */
.btn.primary {
  color: var(--accent-ink);
  background: var(--accent-soft);
  border-color: var(--accent);
}
.btn.primary:hover { border-color: var(--accent-ink); }
.btn.primary:active { background: var(--accent-soft); border-color: var(--accent-ink); }

/* Ghost: chromeless until hovered — used for low-salience actions. */
.btn.ghost {
  background: transparent; border-color: transparent; box-shadow: none;
  color: var(--label-2);
}
.btn.ghost:hover { background: var(--surface-2); color: var(--label); }

/* Destructive: red ink, red-tinted body on hover. Unchanged by the outline pass — it
   was already the shape everything else just moved to. */
.btn.danger { color: var(--red); }
.btn.danger:hover { background: var(--red-soft); border-color: var(--red); }

.btn.sm { height: var(--h-control-sm); padding: 0 9px; font-size: var(--fs-3); }
.btn.xs { height: var(--h-control-xs); padding: 0 7px; font-size: var(--fs-2); border-radius: var(--r-1); }
.btn.block-btn { width: 100%; }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: .42; cursor: default; box-shadow: none; filter: none;
}

/* Square icon button for close boxes and toolbar glyphs. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--h-control); height: var(--h-control);
  color: var(--label-2);
  background: transparent; border: 1px solid transparent; border-radius: var(--r-2);
  cursor: pointer;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.icon-btn:hover { background: var(--surface-3); color: var(--label); }
.icon-btn:active { background: var(--control); box-shadow: inset 0 1px 3px var(--control-press); }

/* The appearance toggle carries both icons; show the one that
   describes the CURRENT appearance's counterpart action. No font
   glyph is involved, so it renders identically everywhere. */
#appearanceBtn .gl { display: block; }
#appearanceBtn .gl-moon { display: none; }
:root[data-appearance="light"] #appearanceBtn .gl-sun { display: none; }
:root[data-appearance="light"] #appearanceBtn .gl-moon { display: block; }

/* Status pill: a quiet count/label badge. */
.pill {
  display: inline-flex; align-items: center;
  height: var(--h-control-xs); padding: 0 var(--sp-2);
  font-size: var(--fs-2); font-variant-numeric: tabular-nums;
  color: var(--label-2); background: var(--field);
  border: 1px solid var(--hairline); border-radius: var(--r-pill);
  white-space: nowrap;
}

/* Segmented control: an inset track; the selected segment is a
   RAISED chip, which is how AppKit draws it — not an accent block. */
.seg {
  display: inline-flex; align-items: center; gap: 1px;
  padding: 2px;
  background: var(--track);
  border: 1px solid var(--hairline);
  border-radius: var(--r-2);
  box-shadow: inset 0 1px 2px var(--control-press);
}
.seg button {
  height: var(--h-control-sm); padding: 0 11px;
  font-size: var(--fs-3); font-weight: 510; color: var(--label-2);
  background: transparent; border: none; border-radius: var(--r-1);
  cursor: pointer;
  transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.seg button:hover { color: var(--label); }
.seg button.on {
  color: var(--label);
  background: linear-gradient(var(--control-hi), transparent 60%), var(--control);
  box-shadow: var(--elev-1);
}
.seg.sm button { height: var(--h-control-xs); padding: 0 9px; font-size: var(--fs-2); }

/* Checkbox rendered as an Apple toggle switch: grey track when off,
   accent track when on, white knob throughout. */
.switch {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-3); color: var(--label-2); cursor: pointer;
}
.switch input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  position: relative; flex: none;
  width: 30px; height: 18px; margin: 0;
  background: var(--track);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.switch input[type="checkbox"]::after {
  content: ""; position: absolute; top: 1px; left: 1px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--knob); box-shadow: var(--knob-shadow);
  transition: transform var(--dur-2) var(--ease);
}
.switch input[type="checkbox"]:checked {
  background: var(--accent); border-color: transparent;
}
.switch input[type="checkbox"]:checked::after { transform: translateX(12px); }

/* Text fields sit INSET, so entry areas read as recessed wells.
   Deliberately scoped to the surfaces that own forms — a bare
   `input, textarea` rule would also style the document's block
   editors on the canvas, which must stay chromeless. The inspector's
   diagram source editor (.panel .diagram-source textarea) is a form
   control too, so it joins these groups rather than minting new tokens. */
.field { margin-bottom: var(--sp-3); }
.field.spaced { margin-top: var(--sp-2); }
.field > label {
  display: block; margin-bottom: var(--sp-1);
  font-size: var(--fs-2); font-weight: 560; color: var(--label-2);
}
.field input, .field select, .field textarea,
.panel .row input, .panel .row select,
.panel .diagram-source textarea,
.drawer-body input, .drawer-body select, .drawer-body textarea,
.thread .composer input {
  width: 100%; height: var(--h-control); padding: 0 var(--sp-2);
  font-family: inherit; font-size: var(--fs-3); color: var(--label);
  background: var(--field);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-2);
  box-shadow: inset 0 1px 2px var(--control-press);
  transition: border-color var(--dur-1) var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus,
.panel .row input:focus, .panel .row select:focus,
.panel .diagram-source textarea:focus,
.drawer-body input:focus, .drawer-body select:focus, .drawer-body textarea:focus,
.thread .composer input:focus { border-color: var(--accent); }
.field input::placeholder, .field textarea::placeholder,
.drawer-body input::placeholder, .drawer-body textarea::placeholder { color: var(--label-3); }
.field textarea, .drawer-body textarea, .panel .diagram-source textarea {
  height: auto; min-height: 84px; padding: var(--sp-2);
  font-family: var(--font-mono); font-size: var(--fs-3); line-height: 1.5;
  resize: vertical;
}
.field select, .panel .row select, .drawer-body select { padding-right: var(--sp-1); cursor: pointer; }

/* Small uppercase group label, used above lists and tool grids. */
.mini-label {
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--label-3);
}

.hintline { margin-top: var(--sp-1); font-size: var(--fs-2); color: var(--label-3); }
.hintline.top { margin-top: 0; }

/* Busy indicator for in-flight AI calls. */
.spin {
  display: inline-block; width: 12px; height: 12px;
  border: 2px solid var(--hairline-strong); border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
/* The spinner now sits on a light wash rather than a saturated fill, so it takes the
   accent pair like every other spinner in the app instead of white-on-accent. */
.btn.primary .spin { border-color: var(--accent-soft); border-top-color: var(--accent); }
@keyframes spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------
   4. Toolbar + status display
   A fixed translucent bar: brand cluster, centred LCD, grouped
   control clusters on the right.
   ------------------------------------------------------------ */
.toolbar {
  position: sticky; top: 0; z-index: 40;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--sp-3);
  height: var(--toolbar-h); padding: 0 var(--sp-3);
  background: var(--chrome);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--hairline);
}
/* Flat two-column variant for pages with no live status readout (the
   library): the middle LCD column is dropped, so the left cluster
   (brand + breadcrumb) and the right controls sit at the two edges.
   The base .toolbar keeps its 1fr auto 1fr grid for the editor, whose
   centred agent status still needs to be optically centred. */
.toolbar-flat { grid-template-columns: 1fr auto; }
.tb-left { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.tb-right { display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-2); }

.brand {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-4); font-weight: 620; letter-spacing: -.01em; color: var(--label);
}
.brand-link { color: inherit; display: inline-flex; align-items: center; gap: var(--sp-2); }
.brand-link:hover { text-decoration: none; color: var(--accent); }
.brand .dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}

/* The mark and the wordmark. HERE, not in a page sheet, because every page loads this
   one and only two of them ever loaded a copy: /usage shipped in 0.17.0 with a black
   mark and a black wordmark, because the rules lived in bookmarks.css and reader.css
   and that page loads neither. An SVG path with no `fill` rule defaults to black, so
   the failure is silent and looks like a design decision.

   The mark is a single closed path taking the accent through currentColor, which is why
   it needs no second rule for dark appearance — the token already answers both.
   flex: none because .tb-left carries min-width: 0 for the editor's breadcrumb, and a
   shrinkable mark in that cell gets compressed to nothing.

   The wordmark is the brand's own outlined lettering, so it carries no font dependency
   and cannot fall back to the wrong face. It takes --label rather than the accent: the
   name is the app's, not a piece of interface state, and a plum word beside a plum mark
   reads as one blurred lump at 13px.

   What stays per-page is WHEN the wordmark hides: the library drops it at its single
   breakpoint, the reader at its second and narrower one, and /usage and /notes never do
   because neither has a centre cell competing for the room. */
.brand-mark { display: block; width: 24px; height: 24px; color: var(--accent); flex: none; }
.brand-mark .mark-s { fill: currentColor; }
.brand-word { display: block; height: 13px; width: auto; color: var(--label); flex: none; }
.brand-word .word-path { fill: currentColor; }

/* Breadcrumb trail: the office-style location line, shown in place of the
   centred LCD (the library has no live document state; the editor shows the
   document title here instead). Tokens only — the separator is a quiet
   tertiary glyph. */
.crumbs { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; font-size: var(--fs-3); }
.crumb {
  color: var(--label-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
a.crumb:hover { color: var(--accent); text-decoration: none; }
.crumb.current { color: var(--label); font-weight: 560; }
.crumb-sep { color: var(--label-3); user-select: none; }

/* Buttons collected into one cluster, separated by hairlines. */
.tb-group {
  display: inline-flex; align-items: center;
  background: linear-gradient(var(--control-hi), transparent 58%), var(--control);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-2);
  box-shadow: var(--elev-1);
  overflow: hidden;
}
.tb-group > .btn {
  height: var(--h-control-sm);
  background: transparent; border: none; border-radius: 0; box-shadow: none;
}
.tb-group > .btn + .btn { border-left: 1px solid var(--hairline); }
.tb-group > .btn:hover { background: var(--control-hover); }
.tb-group > .btn:active { background: var(--control); box-shadow: inset 0 1px 3px var(--control-press); }
/* A grouped primary still needs its wash restored — the cluster rule above strips
   every child button's background. It no longer needs its INK restored: the outline
   primary is accent-on-wash rather than white-on-accent, so the white-on-white failure
   this override was written for cannot happen any more. Three rules become one. */
.tb-group > .btn.primary,
.tb-group > .btn.primary:hover,
.tb-group > .btn.primary:active {
  color: var(--accent-ink);
  background: var(--accent-soft);
}
.tb-sep { width: 1px; height: 18px; background: var(--hairline-strong); }

/* The LCD: an inset readout, mono meta line, truncating title. Retained in
   the design system (and used as the visual model for the editor's centred
   agent-status chip); the library and editor headers no longer render an
   #lcd element themselves. */
.lcd {
  min-width: 220px; max-width: calc(40 * var(--vw));
  padding: 2px var(--sp-3);
  text-align: center;
  background: var(--lcd);
  border: 1px solid var(--hairline);
  border-radius: var(--r-2);
  box-shadow: inset 0 1px 3px var(--control-press);
}
.lcd .lcd-title {
  font-size: var(--fs-3); font-weight: 560; letter-spacing: -.01em; color: var(--label);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lcd .lcd-meta {
  font-family: var(--font-mono); font-size: var(--fs-1);
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--label-3); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ------------------------------------------------------------
   5. Document list page
   ------------------------------------------------------------ */
.wrap { max-width: 1000px; margin: 0 auto; padding: var(--sp-6) var(--sp-5) 80px; }
.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
  margin-bottom: var(--sp-5);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--hairline);
}
.page-head h1 { margin: 0 0 2px; font-size: var(--fs-7); font-weight: 640; letter-spacing: -.02em; }
.page-head p { margin: 0; font-size: var(--fs-4); color: var(--label-2); }

.doc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--sp-3); }
.doc-card {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-3);
  background: var(--surface-2);
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  box-shadow: var(--elev-1);
  transition: border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.doc-card:hover { border-color: var(--hairline-strong); box-shadow: var(--elev-2); }
.doc-card h3 { margin: 0; font-size: var(--fs-5); font-weight: 590; letter-spacing: -.015em; }
.doc-card h3 a { color: var(--label); }
.doc-card h3 a:hover { color: var(--accent); text-decoration: none; }
.doc-card .meta {
  display: flex; gap: var(--sp-3);
  font-size: var(--fs-2); color: var(--label-3); font-variant-numeric: tabular-nums;
}
/* The card's action row is a flex bar — distinct from the inspector's
   label/control grid, which is scoped to .panel .row. */
.doc-card .row {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: var(--sp-1); padding-top: var(--sp-2);
  border-top: 1px solid var(--hairline);
}
.doc-card .del { color: var(--label-3); }
.doc-card .del:hover { color: var(--red); }

.empty {
  padding: 64px var(--sp-5); text-align: center;
  color: var(--label-3);
  background: var(--surface);
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--r-3);
}
.empty h3 { margin: 0 0 var(--sp-1); font-size: var(--fs-5); color: var(--label-2); }
.empty p { margin: 0; font-size: var(--fs-3); }

/* ---------- 5b. Document library — sortable, filterable list view ----------
   The card grid's replacement: a control bar (origin filters + a live count)
   over a simple table with a sortable header and one row per document. All
   scanned properties (colour, font-size, border-radius, padding/margin/gap and
   transition durations) draw from the token layer; the COLUMN geometry uses
   grid-template-columns / minmax / fixed px, which the design-system scanner
   deliberately does not police (there is no scale for one-off column widths). */

/* Control bar: origin toggles on the left, a live count pushed to the right. */
.doc-list-controls {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--hairline);
}
.doc-filter-label {
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--label-3);
}
.doc-filter-group { display: flex; align-items: center; gap: var(--sp-4); }
.doc-count {
  margin-left: auto;
  font-size: var(--fs-2); color: var(--label-3);
  font-variant-numeric: tabular-nums;
}

/* The list: a header row and one row per document, sharing a column template
   so their cells line up. Name flexes; the two time columns and the actions
   column are sized to content. */
.doc-table {
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  box-shadow: var(--elev-1);
}
.doc-thead, .doc-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 128px 128px 148px;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
}
.doc-thead {
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}
.doc-row + .doc-row { border-top: 1px solid var(--hairline); }
.doc-row:hover { background: var(--surface-3); }

/* Sort headers are quiet uppercase buttons; the active one shows a direction
   arrow in the accent colour. */
.sort-btn {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 0;
  font-family: inherit; font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--label-3); text-align: left;
  background: transparent; border: none; border-radius: var(--r-1);
  cursor: pointer;
  transition: color var(--dur-1) var(--ease);
}
.sort-btn:hover { color: var(--label); }
.sort-btn .arrow { font-size: var(--fs-1); color: var(--accent); }

/* Name cell: the document link over a muted metadata sub-line. */
.doc-row .name { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.doc-row .name > a {
  font-size: var(--fs-5); font-weight: 590; letter-spacing: -.015em;
  color: var(--label);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.doc-row .name > a:hover { color: var(--accent); text-decoration: none; }
.doc-row .sub {
  display: flex; align-items: center; gap: var(--sp-2); min-width: 0;
  font-size: var(--fs-2); color: var(--label-3);
  font-variant-numeric: tabular-nums;
}
.doc-row .sub .url {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The imported badge reuses the accent-soft pill treatment. */
.badge-import {
  flex: none;
  padding: 1px var(--sp-2);
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .05em;
  color: var(--accent-ink); background: var(--accent-soft);
  border: 1px solid var(--accent-soft); border-radius: var(--r-pill);
}

/* The relative-time columns read as quiet tabular numerals. */
.doc-time {
  font-size: var(--fs-3); color: var(--label-2);
  font-variant-numeric: tabular-nums;
}

/* Actions cell keeps its buttons together at the row's end. */
.doc-row .acts { display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-1); }
.doc-row .acts .del { color: var(--label-3); }
.doc-row .acts .del:hover { color: var(--red); }
/* The header's actions column is an empty spacer that just holds the track. */
.doc-th-actions { display: block; }

/* ------------------------------------------------------------
   6. Editor: the desk and the sheet of paper
   ------------------------------------------------------------ */
/* A fixed-height app shell: the toolbar owns the top 44px and this row
   fills the rest of the viewport WITHOUT scrolling the body. The two
   panes below each scroll internally, so the document's scrollbar lives
   at the right edge of the canvas column and the inspector's stays at
   the sidebar's right edge — never stacked together at the window edge. */
.editor { display: grid; grid-template-columns: 1fr; height: calc(100 * var(--vh) - var(--toolbar-h)); overflow: hidden; }
/* min-height: 0 is essential: as a grid item this row (and its children
   below) default to min-height: auto, which forbids shrinking below the
   content's intrinsic height. Without it the panes grow to full content
   height, their overflow-y never engages, and .editor's overflow:hidden
   simply CLIPS the overflow — i.e. nothing scrolls. Allowing them to
   shrink to the shell height is what makes the internal scrollers work.
   The middle `auto` track holds the drag handle (.inspector-resizer); the
   inspector's own width comes from --inspector-w, which the resize module
   rewrites inline on this element as the user drags. */
.editor-main {
  display: grid; grid-template-columns: minmax(0, 1fr) auto var(--inspector-w);
  align-items: stretch;
  height: 100%; min-height: 0;
}

/* The workspace: a dark desk with a soft overhead glow, so the
   page below reads as a physical object lying on it. It is its own
   scroll container (overflow-y: auto), so the document scrolls here
   and its scrollbar sits at the canvas column's right edge. min-height:0
   lets it shrink to the shell height so that scroller actually engages. */
.canvas-col {
  height: 100%; min-height: 0; overflow-y: auto;
  padding: var(--sp-6) clamp(var(--sp-3), calc(5 * var(--vw)), 64px) 120px;
  background:
    radial-gradient(120% 70% at 50% -20%, var(--desk-glow), transparent 65%),
    var(--desk);
}

.paper {
  display: block;
  max-width: var(--paper-measure); min-height: var(--paper-min-h); margin: 0 auto;
  padding: var(--paper-pad-top) var(--paper-pad-x) var(--paper-pad-bottom);
  background: var(--paper);
  border-radius: 2px;
  box-shadow: var(--paper-shadow);
  font-family: var(--font-ui);
  color: var(--paper-ink);
  /* Clean sans reading setting: kerning and ligatures on, and pretty
     wrapping so lines break gracefully. None of these are values the
     design-system scale test scans. */
  font-feature-settings: "kern" 1, "liga" 1;
  text-wrap: pretty;
}

/* Nothing on the page inherits chrome control styling: the sheet owns
   its own quiet, paper-tinted affordances in either appearance. */
.paper textarea, .paper input {
  min-height: 0; box-shadow: none;
}
.paper .btn {
  color: var(--paper-ink); background: var(--paper-2);
  border-color: var(--paper-line); box-shadow: none;
}
.paper .btn:hover { background: var(--paper-line); }
.paper .btn.danger { color: var(--red); }
.paper .switch { color: var(--paper-ink-2); }
.paper .switch input[type="checkbox"] { background: var(--paper-line); border-color: var(--paper-line); }
.paper .switch input[type="checkbox"]:checked { background: var(--accent); border-color: transparent; }
.paper .mini-label { color: var(--paper-ink-3); }

/* ---------- Blocks on the page ---------- */
.block { position: relative; margin: 0 0 var(--sp-4); }
.block:last-child { margin-bottom: 0; }
.block-inner { position: relative; }

/* Page-margin markers instead of hover fills: the sheet stays clean. */
.block.selected::before {
  content: ""; position: absolute; left: -28px; top: 2px; bottom: 2px;
  width: 2px; border-radius: 1px; background: var(--accent);
}
.block.has-annotation::after {
  content: ""; position: absolute; right: -28px; top: var(--sp-2);
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px var(--orange-soft);
}

.block textarea, .block .txt {
  width: 100%; height: auto; padding: 0;
  font-family: var(--font-ui); font-size: var(--fs-doc); line-height: 1.7;
  color: var(--paper-ink);
  background: transparent; border: none; border-radius: 0;
  outline: none; resize: none; overflow: hidden;
}
.block textarea::placeholder { color: var(--paper-ink-3); }

/* Headings wear the clean display sans with tight tracking, so the masthead
   reads crisp and light. The step above each heading gives the document a
   clear, modern rhythm. */
.block[data-type="heading"] { margin-top: var(--sp-6); }
.block[data-type="heading"]:first-child { margin-top: 0; }
.block[data-type="heading"] textarea {
  font-family: var(--font-display);
  font-weight: 600; letter-spacing: -.02em; line-height: 1.18;
  /* Balance heading line lengths for a polished, paper-like masthead. */
  text-wrap: balance;
}
.block[data-type="heading"][data-level="1"] textarea { font-size: var(--fs-doc-1); }
.block[data-type="heading"][data-level="2"] textarea { font-size: var(--fs-doc-2); }
.block[data-type="heading"][data-level="3"] textarea { font-size: var(--fs-doc-3); }

/* Pull-quote: an accent margin rule and a slightly larger italic sans lift the
   quotation off the running text, the way a modern feature article sets its
   callouts. */
.block[data-type="quote"] .block-inner {
  padding-left: var(--sp-5); border-left: 2px solid var(--accent);
}
.block[data-type="quote"] textarea {
  font-family: var(--font-ui);
  font-size: var(--fs-doc-3); font-style: italic; line-height: 1.5;
  color: var(--paper-ink-2);
}

/* Only code blocks carry an on-sheet mono editor now: the diagram's source
   moved into the inspector, so the sheet shows only its rendered preview. */
.block[data-type="code"] textarea {
  padding: var(--sp-3);
  font-family: var(--font-mono); font-size: var(--fs-4); line-height: 1.6;
  color: var(--code-ink); background: var(--code-bg);
  border-radius: var(--r-2);
}

.block .list-item { display: flex; align-items: flex-start; gap: var(--sp-2); }
/* A little air between successive items reads more modern than a tight stack. */
.block .list-item + .list-item { margin-top: var(--sp-2); }
.block .list-item .bullet {
  padding-top: 2px; user-select: none;
  font-family: var(--font-ui); font-size: var(--fs-doc); font-weight: 600; color: var(--accent);
}
.block .row-tools {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  margin-top: var(--sp-2);
}

table.edit-table {
  width: 100%; border-collapse: collapse;
  font-family: var(--font-ui); font-size: var(--fs-4);
}
table.edit-table td, table.edit-table th { padding: 0; border: 1px solid var(--paper-line); }
table.edit-table input {
  height: auto; padding: var(--sp-2);
  font: inherit; color: var(--paper-ink);
  background: transparent; border: none; border-radius: 0;
}
table.edit-table th input { font-weight: 660; background: var(--paper-2); }

/* An image block on the sheet shows only the image and (optionally) its caption,
   or a placeholder — the URL/alt/caption are edited in the inspector. */
.img-block img {
  display: block; max-width: 100%;
  border: 1px solid var(--paper-line); border-radius: var(--r-1);
}
.img-figure { margin: 0; }
.img-caption {
  margin-top: var(--sp-2);
  font-family: var(--font-ui); font-size: var(--fs-3); color: var(--paper-ink-3);
  text-align: center;
}
/* Sheet placeholders for an image block: an empty state (no URL set) and an
   error state (a set URL that failed to load). Paper-toned, tokens only. */
.img-placeholder {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-1); padding: var(--sp-6) var(--sp-4);
  text-align: center; color: var(--paper-ink-3);
  background: var(--paper-2);
  border: 1px dashed var(--paper-line); border-radius: var(--r-2);
}
.img-placeholder.error { color: var(--red); border-color: var(--red); }
.img-ph-icon { font-size: var(--fs-7); line-height: 1; }
.img-ph-title { font-size: var(--fs-4); font-weight: 600; color: var(--paper-ink-2); }
.img-placeholder.error .img-ph-title { color: var(--red); }
.img-ph-hint { font-size: var(--fs-2); }

.diagram-preview {
  margin-top: var(--sp-2); padding: var(--sp-3);
  overflow: auto; text-align: center;
  background: var(--paper-2); border: 1px solid var(--paper-line); border-radius: var(--r-2);
}
.diagram-preview.error {
  text-align: left; color: var(--red);
  font-family: var(--font-mono); font-size: var(--fs-3);
}

.block[data-type="divider"] hr {
  margin: var(--sp-6) 0; border: none; border-top: 1px solid var(--paper-line);
}

/* Caret measurement mirror: a textarea exposes no caret geometry, so the client
   measures line tops and caret points in this off-screen twin. It must lay out
   (visibility, not display) and wrap exactly like the textarea it copies. */
.caret-mirror {
  position: absolute; top: 0; left: -9999px;
  visibility: hidden; pointer-events: none;
  box-sizing: content-box; margin: 0; height: auto;
  white-space: pre-wrap; overflow-wrap: break-word; word-break: normal;
}

/* ------------------------------------------------------------
   7. Inspector sidebar (Xcode-style)
   Flush sections divided by hairlines, sticky uppercase headers,
   and label/control rows — no floating cards.
   ------------------------------------------------------------ */
/* Fills the app-shell row height and scrolls internally, so its scrollbar
   sits at the sidebar's own right edge. It is no longer position:sticky —
   the body no longer scrolls, so there is nothing to stick to; the fixed
   row height (from .editor-main) keeps it pinned beside the canvas.
   min-height:0 lets this grid item shrink to the shell height instead of
   growing to its content, which is what lets overflow-y:auto scroll. */
.inspector {
  display: flex; flex-direction: column;
  height: 100%; min-height: 0; overflow-y: auto;
  padding-bottom: 80px;
  background: var(--surface);
  border-left: 1px solid var(--hairline);
}

/* Drag handle between the canvas and the inspector: a thin grab strip that
   widens/narrows the sidebar. It occupies the `auto` middle column of
   .editor-main; the visible seam is the inspector's own left hairline, so the
   strip stays transparent until hovered or dragged. The 6px width is structural
   control geometry (like the 30x18px switch and the 12px scrollbar), and every
   colour/duration below comes from the token layer. It is keyboard-focusable as
   a role=separator; the global :focus-visible ring covers its focus state, so no
   component focus rule is added here. */
.inspector-resizer {
  width: 6px; align-self: stretch;
  background: transparent;
  cursor: col-resize;
  transition: background var(--dur-1) var(--ease);
}
.inspector-resizer:hover,
.inspector-resizer.dragging { background: var(--accent-soft); }
/* While dragging, lock the whole page to the resize cursor and suppress text
   selection so nothing is highlighted as the pointer sweeps the canvas. */
body.inspector-resizing { cursor: col-resize; user-select: none; }

.panel { padding: 0 var(--sp-3) var(--sp-3); border-bottom: 1px solid var(--hairline); }
.panel-head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  height: 27px; margin: 0 calc(-1 * var(--sp-3)) var(--sp-2);
  padding: 0 var(--sp-3);
  background: var(--chrome-solid);
  border-bottom: 1px solid var(--hairline);
}
.panel-title {
  margin: 0;
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .07em;
  color: var(--label-3);
}
.panel-empty {
  padding: var(--sp-3);
  font-size: var(--fs-3); color: var(--label-3);
  background: var(--field);
  border: 1px solid var(--hairline); border-radius: var(--r-2);
}
.panel-empty p { margin: 0; }

/* Stacked field: a left-aligned label above a full-width control, flush with the
   panel content (matching the drawer .field pattern) — no indented label gutter,
   so single-field rows line up with the panel's other content (the stat tiles). */
.panel .row { margin-bottom: var(--sp-2); }
.panel .row > label, .panel .row .row-label {
  display: block; margin-bottom: var(--sp-1);
  font-size: var(--fs-2); color: var(--label-2); text-align: left;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.panel .row input, .panel .row select { height: var(--h-control-sm); }

/* The diagram source group is a stacked control block (label over editor over
   hint), so it carries its own spacing rather than the label/control grid. */
#diagramRow { margin-bottom: var(--sp-3); }
#diagramRow .mini-label { margin-bottom: var(--sp-1); }

/* Document stats as three tight readouts. */
.stat-row { display: flex; gap: var(--sp-1); }
.stat {
  flex: 1 1 0; min-width: 0; padding: var(--sp-1) var(--sp-2);
  background: var(--field);
  border: 1px solid var(--hairline); border-radius: var(--r-2);
}
.stat .n {
  font-size: var(--fs-5); font-weight: 620; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.stat .l {
  font-size: var(--fs-1); font-weight: 620;
  text-transform: uppercase; letter-spacing: .05em; color: var(--label-3);
}

/* Block panel */
.kind-line { margin-bottom: var(--sp-2); }
.kind-badge {
  display: inline-flex; align-items: center; height: var(--h-control-xs); padding: 0 var(--sp-2);
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--accent-ink); background: var(--accent-soft);
  border: 1px solid var(--accent-soft); border-radius: var(--r-pill);
}
.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-bottom: var(--sp-3); }
.tool-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.scope-hint {
  padding: 1px var(--sp-2);
  font-size: var(--fs-2); color: var(--accent-ink);
  background: var(--accent-soft); border-radius: var(--r-pill);
  white-space: nowrap;
}

/* The AI verbs live here, as a dense command grid. */
.tool-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-1); }
.chip {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-1);
  height: var(--h-control-sm); padding: 0 var(--sp-2);
  font-size: var(--fs-2); font-weight: 510; color: var(--label-2); text-align: center;
  background: linear-gradient(var(--control-hi), transparent 58%), var(--control);
  border: 1px solid var(--hairline-strong); border-radius: var(--r-1);
  box-shadow: var(--elev-1);
  cursor: pointer;
  transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.chip:hover { color: var(--label); background: var(--control-hover); }
.chip:active { box-shadow: inset 0 1px 3px var(--control-press); }
.chip:disabled { opacity: .42; cursor: default; box-shadow: none; }
.chip.wide { grid-column: 1 / -1; }

#toolResult { margin-top: var(--sp-3); }
#toolResult:empty { margin-top: 0; }
.result-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  margin-bottom: var(--sp-1);
}
.variant-stack { display: flex; flex-direction: column; gap: var(--sp-1); }
.variant-card {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-2);
  background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--r-2);
}
.variant-card .v-body {
  font-family: var(--font-doc); font-size: var(--fs-4); line-height: 1.5;
  white-space: pre-wrap; color: var(--label);
}

/* ------------------------------------------------------------
   8. Comment threads, proposals
   ------------------------------------------------------------ */
#commentScope { margin-bottom: var(--sp-2); }
.thread {
  margin-bottom: var(--sp-2); padding: var(--sp-2);
  background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--r-2);
}
.thread.resolved { opacity: .6; }
.thread.active { border-color: var(--orange); box-shadow: 0 0 0 2px var(--orange-soft); }
.thread .thead {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: var(--sp-2);
}
.thread .quote {
  margin-bottom: var(--sp-2); padding-left: var(--sp-2);
  font-family: var(--font-doc); font-size: var(--fs-4); font-style: italic;
  color: var(--label-2); border-left: 2px solid var(--orange);
}
.thread .badge, .badge {
  padding: 1px var(--sp-2);
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .05em;
  border-radius: var(--r-pill);
}
.badge.open { color: var(--orange); background: var(--orange-soft); }
.badge.resolved { color: var(--green); background: var(--green-soft); }

.msgs { display: flex; flex-direction: column; gap: var(--sp-1); }
.msg { margin: var(--sp-1) 0; font-size: var(--fs-3); }
.msg .who {
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .05em; color: var(--label-3);
}
.msg .bubble {
  padding: var(--sp-2);
  white-space: pre-wrap;
  border: 1px solid var(--hairline); border-radius: var(--r-2);
}
.msg.ai .bubble { background: var(--accent-soft); border-color: var(--accent-soft); }
.msg.user .bubble { background: var(--field); }

.proposal-card {
  margin-top: var(--sp-1); overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--accent); border-radius: var(--r-2);
}
.proposal-card .pc-body {
  padding: var(--sp-2);
  font-family: var(--font-doc); font-size: var(--fs-4); line-height: 1.5;
  white-space: pre-wrap;
  background: var(--accent-soft);
}
.proposal-card .pc-actions {
  display: flex; gap: var(--sp-1);
  padding: var(--sp-2);
  border-top: 1px solid var(--hairline);
}
.thread .composer { display: flex; gap: var(--sp-1); margin-top: var(--sp-2); }
.thread .composer input { flex: 1; min-width: 0; height: var(--h-control-sm); }
.thread .tactions { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-2); }

/* ------------------------------------------------------------
   9. Overlays: selection bar, popovers, drawers, toasts
   ------------------------------------------------------------ */
.sel-toolbar {
  position: absolute; z-index: 60;
  display: none; align-items: center; gap: 1px;
  padding: 3px;
  color: var(--label);
  background: var(--chrome-solid);
  border: 1px solid var(--hairline-strong); border-radius: var(--r-2);
  box-shadow: var(--elev-3);
}
.sel-toolbar.show { display: flex; }
.sel-toolbar button {
  height: var(--h-control-sm); padding: 0 var(--sp-2);
  font-size: var(--fs-3); font-weight: 560; color: inherit;
  background: transparent; border: none; border-radius: var(--r-1);
  cursor: pointer;
}
.sel-toolbar button:hover { background: var(--surface-3); }
.sel-toolbar .divider { width: 1px; height: 16px; margin: 0 2px; background: var(--hairline-strong); }

.popover {
  position: absolute; z-index: 70;
  padding: var(--sp-2);
  background: var(--chrome-solid);
  border: 1px solid var(--hairline-strong); border-radius: var(--r-3);
  box-shadow: var(--elev-3);
}
.block-menu { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-1); min-width: 250px; }
.block-menu button {
  display: flex; align-items: center; gap: var(--sp-2);
  height: var(--h-control); padding: 0 var(--sp-2);
  font-size: var(--fs-3); color: var(--label); text-align: left;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-1);
  cursor: pointer;
}
.block-menu button:hover { background: var(--accent-fill); color: var(--on-accent); }
.block-menu .em { font-size: var(--fs-4); }

.scrim { position: fixed; inset: 0; z-index: 80; display: none; background: var(--scrim-veil); }
.scrim.show { display: block; }
.drawer {
  position: fixed; top: 0; right: 0; z-index: 90;
  display: flex; flex-direction: column;
  width: min(420px, calc(92 * var(--vw))); height: calc(100 * var(--vh));
  background: var(--surface);
  border-left: 1px solid var(--hairline);
  box-shadow: var(--elev-3);
  transform: translateX(100%);
  transition: transform var(--dur-3) var(--ease);
}
.drawer.show { transform: translateX(0); }
.drawer-head {
  display: flex; align-items: center; justify-content: space-between;
  height: var(--toolbar-h); padding: 0 var(--sp-3);
  background: var(--chrome-solid);
  border-bottom: 1px solid var(--hairline);
}
.drawer-head h3 { margin: 0; font-size: var(--fs-4); font-weight: 620; }
.drawer-body { flex: 1; overflow-y: auto; padding: var(--sp-4); }

.manual-skill { margin-bottom: var(--sp-3); }
.manual-skill summary { cursor: pointer; font-size: var(--fs-3); color: var(--label-2); }

.skill-item {
  margin-bottom: var(--sp-2); padding: var(--sp-2) var(--sp-3);
  background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--r-2);
}
.skill-item .si-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
}
.skill-item h4 { margin: 0; font-size: var(--fs-4); font-weight: 590; }
.skill-item .desc { margin: var(--sp-1) 0 var(--sp-2); font-size: var(--fs-3); color: var(--label-2); }
.skill-item .si-foot { display: flex; align-items: center; gap: var(--sp-2); }
/* The enabled toggle is a switch, not a full-width form control. */
.skill-item .switch input[type="checkbox"] { width: 30px; }

.ver-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--hairline);
}
.ver-item .vi-main { font-size: var(--fs-3); }
.ver-item .vi-meta { font-size: var(--fs-2); color: var(--label-3); font-variant-numeric: tabular-nums; }
.trigger-tag {
  padding: 1px var(--sp-1);
  font-size: var(--fs-1); font-weight: 680;
  text-transform: uppercase; letter-spacing: .05em;
  color: var(--label-2); background: var(--field);
  border: 1px solid var(--hairline); border-radius: var(--r-1);
}
.trigger-tag.revision { color: var(--accent-ink); background: var(--accent-soft); border-color: var(--accent-soft); }
.trigger-tag.restore { color: var(--orange); background: var(--orange-soft); border-color: var(--orange-soft); }

.export-preview {
  margin-top: var(--sp-3); padding: var(--sp-3);
  max-height: calc(46 * var(--vh)); overflow: auto;
  font-family: var(--font-mono); font-size: var(--fs-3); line-height: 1.5;
  white-space: pre-wrap;
  background: var(--field);
  border: 1px solid var(--hairline); border-radius: var(--r-2);
  box-shadow: inset 0 1px 2px var(--control-press);
}

.toast-host {
  position: fixed; bottom: var(--sp-5); left: 50%; transform: translateX(-50%);
  z-index: 200;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
}
.toast {
  padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-3); color: var(--label);
  background: var(--chrome-solid);
  border: 1px solid var(--hairline-strong); border-radius: var(--r-3);
  box-shadow: var(--elev-3);
  opacity: 0; transform: translateY(6px);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast.err { color: var(--red); border-color: var(--red); }

/* ------------------------------------------------------------
   10. Utilities + responsive collapse
   ------------------------------------------------------------ */
.rule { margin: var(--sp-4) 0; border: none; border-top: 1px solid var(--hairline); }
.hidden { display: none !important; }
/* Visually hidden, still read by assistive tech. It lives HERE, with the other
   utilities, because it is an accessibility primitive rather than a component: it was
   written into bookmarks.css and then copied verbatim into reader.css, and the third
   page to need it would have made three copies of a rule nobody would think to keep in
   step. Both copies were deleted on 2026-09-20; this is now the app's only definition,
   and six pages share it.
   clip-path rather than the older clip + negative margin the page copies used: it needs
   no margin, and a -1px margin is not on the spacing scale. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.flex { display: flex; }
.between { justify-content: space-between; }
.center { align-items: center; }
.gap { gap: var(--sp-2); }
.wrapline { flex-wrap: wrap; }
.spacer { flex: 1; }
.spaced { margin-top: var(--sp-3); }
.spaced-bottom { margin-bottom: var(--sp-2); }

/* Under 980px the inspector stacks beneath the page so the tools stay reachable.
   Here the app-shell is dissolved: the editor and its panes revert to natural
   page flow (height:auto, no internal overflow) so the WHOLE PAGE scrolls as one
   again — otherwise the stacked inspector would be trapped below a fixed-height
   canvas. The sheet drops to grid-scale padding here instead of its own page
   geometry, and the margin markers pull in by one spacing step — written as
   calc() on the token so a negative offset cannot restate a scale value as a literal. */
/* 980px of DESIGN width, times the interface scale (--ui-zoom), which is 1 — so this
   fires at the design width itself. It read a fifth higher while the scale did: the
   window is not zoomed, only the page inside it, so a media query sees more width than
   the layout actually has whenever that scale is above 1. */
@media (max-width: 980px) {
  .editor { height: auto; overflow: visible; }
  .editor-main { grid-template-columns: 1fr; height: auto; }
  /* The drag handle only makes sense beside a side-by-side inspector; once the
     layout stacks it has nothing to resize, so it is removed from flow. */
  .inspector-resizer { display: none; }
  .inspector {
    height: auto; overflow-y: visible;
    border-left: none; border-top: 1px solid var(--hairline);
  }
  .canvas-col { height: auto; overflow-y: visible; min-height: 0; padding: var(--sp-4) var(--sp-3) var(--sp-5); }
  .paper { min-height: 0; padding: var(--sp-6) var(--sp-5) 48px; }
  .block.selected::before { left: calc(-1 * var(--sp-3)); }
  .block.has-annotation::after { right: calc(-1 * var(--sp-3)); }
  .lcd { min-width: 0; }
}
