/* ===========================================================================
   nescio.css — the Nescio theme layer for MkDocs Material.

   Implements docs/design/design-system.md §2 (colour), §3 (typography) and
   §6 (diagram tokens). Every colour below is a canonical §2 token or an alpha
   derivation of one; no hex is invented here. If a value is missing, add it to
   brand/palette.py first — that module is the source of truth and every
   generated SVG reads from it.

   The four self-hosted woff2 files are NOT copied into docs_dir. They are
   shipped straight out of brand/fonts/ by docs_site/hooks/assets.py, which
   drops them at assets/fonts/ in the build output — the path the @font-face
   rules below reference. One source of truth, no duplicated binaries.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. Self-hosted faces (spec §3)

   The one hard typography rule: no CDN, no third-party request at runtime.
   theme.font is false in mkdocs.yml so Material emits no Google Fonts <link>.

   The family names are `Nescio Sans` / `Nescio Mono` and must not be
   "corrected" back to Carlito / Liberation Mono — SIL OFL clause 3 forbids the
   upstream Reserved Font Names on a subset. See brand/fonts/README.md.
   brand/palette.py names the same two families first in FONT_SANS / FONT_MONO,
   so the inlined SVGs and the page resolve to the same faces; brand/test_fonts.py
   holds the two together.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: "Nescio Sans";
  src: url("../fonts/nescio-sans-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nescio Sans";
  src: url("../fonts/nescio-sans-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nescio Mono";
  src: url("../fonts/nescio-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nescio Mono";
  src: url("../fonts/nescio-mono-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ===========================================================================
   2. The two custom schemes

   >>> COUPLING WARNING <<<
   The blocks below are coupled to Material's own CSS-variable names
   (--md-primary-fg-color, --md-accent-fg-color, --md-default-bg-color,
   --md-default-fg-color, --md-typeset-a-color, --md-code-bg-color, and the
   --light / --lighter / --lightest ramps beside them). Those names have changed
   across Material releases. **A Material upgrade is a deliberate task with a
   visual re-check, never a routine dependency bump** — see the pin and its
   comment in .github/workflows/docs.yml. `mkdocs build --strict` catches broken
   links, not a periwinkle that has quietly reverted to the stock accent.

   Verified against mkdocs-material 9.7.7 by reading the shipped
   material/templates/assets/stylesheets/{main,palette}.*.min.css: in that
   version main.css defines the full light token set on
   `:root,[data-md-color-scheme=default]` and palette.css defines the dark set
   on `[data-md-color-scheme=slate]` only. A *custom* scheme name therefore
   inherits the `:root` light defaults and nothing else — which is why the dark
   block below restates the whole surface, code and footer ramp rather than a
   handful of accents.

   mkdocs.yml sets `primary: custom` / `accent: custom` so Material's
   [data-md-color-primary=…] / [data-md-color-accent=…] rules never fight these.

   THE ACCENT RULE IS NON-NEGOTIABLE (spec §2):
     dark  → periwinkle  #8fb0d9  accent + links   (8.3:1 on #0e1319, AAA)
     light → brand blue  #2f4d7a  accent + links   (8.5:1 on #ffffff, AAA)
   Periwinkle on a light ground measures 2.2:1. It is permitted as a fill or a
   border only, never as text or a link — so it does not appear anywhere in the
   nescio-light block below. Do not add it there.
   =========================================================================== */

/* --- Nescio dark — the default scheme (spec §2 "dark by default") ---------- */
[data-md-color-scheme="nescio-dark"] {
  color-scheme: dark;

  /* Surface */
  --md-default-bg-color:              #0e1319;              /* ink-deep */
  --md-default-bg-color--light:       rgba(14, 19, 25, .54);
  --md-default-bg-color--lighter:     rgba(14, 19, 25, .26);
  --md-default-bg-color--lightest:    rgba(14, 19, 25, .07);

  /* Foreground ramp. --light still carries text (ToC titles, h1, h5/h6), so it
     is muted-dark #8d97a4 — 6.3:1 on ink-deep, passes AA. --lighter and
     --lightest are decorative only (scrollbars, rules, nav borders). */
  --md-default-fg-color:              #e9edf2;              /* text-dark */
  --md-default-fg-color--light:       #8d97a4;              /* muted-dark */
  --md-default-fg-color--lighter:     #6b727c;              /* muted */
  --md-default-fg-color--lightest:    #26313f;              /* border-dark */

  /* Primary = the raised chrome (header, nav rail), per §2 ink-raised. */
  --md-primary-fg-color:              #141b24;              /* ink-raised */
  --md-primary-fg-color--light:       #182333;              /* tint-dark */
  --md-primary-fg-color--dark:        #0e1319;              /* ink-deep */
  --md-primary-bg-color:              #e9edf2;
  --md-primary-bg-color--light:       #8d97a4;

  /* Accent = periwinkle. */
  --md-accent-fg-color:               #8fb0d9;              /* periwinkle */
  --md-accent-fg-color--transparent:  rgba(143, 176, 217, .1);
  --md-accent-bg-color:               #0e1319;
  --md-accent-bg-color--light:        #141b24;

  /* Typeset. --md-typeset-a-color must be set explicitly: Material's default
     is var(--md-primary-fg-color), which here is the ink-raised chrome. */
  --md-typeset-color:                 #a8b0ba;              /* body text, dark (§6) */
  --md-typeset-a-color:               #8fb0d9;              /* periwinkle */

  --md-typeset-kbd-color:             #182333;
  --md-typeset-kbd-accent-color:      #26313f;
  --md-typeset-kbd-border-color:      #0e1319;
  --md-typeset-mark-color:            rgba(143, 176, 217, .28);
  --md-typeset-table-color:           rgba(233, 237, 242, .12);
  --md-typeset-table-color--light:    rgba(233, 237, 242, .035);
  --md-typeset-del-color:             rgba(245, 80, 61, .15);
  --md-typeset-ins-color:             rgba(11, 213, 112, .15);

  /* Code */
  --md-code-fg-color:                 #e9edf2;
  --md-code-bg-color:                 #141b24;              /* ink-raised */
  --md-code-bg-color--light:          rgba(20, 27, 36, .9);
  --md-code-bg-color--lighter:        rgba(20, 27, 36, .54);
  /* Syntax hues are Material's own dark set — they are code semantics, not
     brand tokens, and re-inventing them from a 15-token palette would make
     highlighting unreadable. The remaining --md-code-hl-* values are var()
     references to the ramps above and therefore already follow this scheme. */
  --md-code-hl-color:                 #2977ff;
  --md-code-hl-color--light:          rgba(41, 119, 255, .1);
  --md-code-hl-number-color:          #e6695b;
  --md-code-hl-special-color:         #f06090;
  --md-code-hl-function-color:        #c973d9;
  --md-code-hl-constant-color:        #9383e2;
  --md-code-hl-keyword-color:         #6791e0;
  --md-code-hl-string-color:          #2fb170;

  /* Admonitions — tint-dark is §2's "highlighted cards, admonition fills". */
  --md-admonition-fg-color:           var(--md-default-fg-color);
  --md-admonition-bg-color:           #182333;              /* tint-dark */

  /* Footer */
  --md-footer-fg-color:               #e9edf2;
  --md-footer-fg-color--light:        #8d97a4;
  --md-footer-fg-color--lighter:      #6b727c;
  --md-footer-bg-color:               #141b24;              /* ink-raised */
  --md-footer-bg-color--dark:         #0e1319;              /* ink-deep */

  --md-warning-fg-color:              #16191f;
  --md-warning-bg-color:              #8fb0d9;

  --md-shadow-z1: 0 .2rem .5rem rgba(0, 0, 0, .2), 0 0 .05rem rgba(0, 0, 0, .1);
  --md-shadow-z2: 0 .2rem .5rem rgba(0, 0, 0, .25), 0 0 .05rem rgba(0, 0, 0, .25);
  --md-shadow-z3: 0 .2rem .5rem rgba(0, 0, 0, .4),  0 0 .05rem rgba(0, 0, 0, .35);

  /* Diagram tokens — spec §6, dark column. The tokenised SVGs are inlined
     (never <img>), so these reach the artwork and the scheme toggle repaints it
     live. The SVG sources carry the same defaults scoped to `svg:root`, which
     matches only when the file is opened standalone. */
  --diagram-accent-fill:              #182333;              /* emphasised node fill */
  --diagram-accent:                   #8fb0d9;              /* emphasised stroke + label */
  --diagram-node-fill:                #141b24;
  --diagram-node-stroke:              #26313f;
  --diagram-node-label:               #e9edf2;
  --diagram-body:                     #a8b0ba;
  --diagram-muted:                    #8d97a4;
  --diagram-connector:                #26313f;
  --diagram-accent-connector:         #8fb0d9;
  --diagram-deferred:                 #a8b0ba;              /* §7 planned-not-shipped */
}

/* --- Nescio light --------------------------------------------------------- */
/* Inherits Material's :root light defaults; only the tokens below are changed.
   NOTE: #8fb0d9 must never appear in this block. */
[data-md-color-scheme="nescio-light"] {
  color-scheme: light;

  /* Surface */
  --md-default-bg-color:              #ffffff;
  --md-default-bg-color--light:       rgba(255, 255, 255, .7);
  --md-default-bg-color--lighter:     rgba(255, 255, 255, .3);
  --md-default-bg-color--lightest:    rgba(255, 255, 255, .12);

  --md-default-fg-color:              #16191f;              /* text */
  --md-default-fg-color--light:       #5a616b;              /* body      6.3:1 */
  --md-default-fg-color--lighter:     #6b727c;              /* muted     4.9:1 */
  --md-default-fg-color--lightest:    #c9cfd8;              /* border, decorative */

  /* Primary */
  --md-primary-fg-color:              #2f4d7a;              /* brand-blue */
  --md-primary-fg-color--light:       #3c608f;
  --md-primary-fg-color--dark:        #223a5e;
  --md-primary-bg-color:              #ffffff;
  --md-primary-bg-color--light:       rgba(255, 255, 255, .7);

  /* Accent = brand blue. Periwinkle is 2.2:1 here — never used. */
  --md-accent-fg-color:               #2f4d7a;              /* brand-blue */
  --md-accent-fg-color--transparent:  rgba(47, 77, 122, .1);
  --md-accent-bg-color:               #ffffff;
  --md-accent-bg-color--light:        rgba(255, 255, 255, .7);

  /* Typeset */
  --md-typeset-color:                 #5a616b;              /* running text (§2) */
  --md-typeset-a-color:               #2f4d7a;              /* brand-blue */

  --md-typeset-kbd-color:             #eef2f8;
  --md-typeset-kbd-accent-color:      #ffffff;
  --md-typeset-kbd-border-color:      #c9cfd8;
  --md-typeset-mark-color:            rgba(47, 77, 122, .18);
  --md-typeset-table-color:           rgba(22, 25, 31, .12);
  --md-typeset-table-color--light:    rgba(22, 25, 31, .035);

  /* Code */
  --md-code-fg-color:                 #16191f;
  --md-code-bg-color:                 #eef2f8;              /* tint */
  --md-code-bg-color--light:          rgba(238, 242, 248, .7);
  --md-code-bg-color--lighter:        rgba(238, 242, 248, .3);

  --md-admonition-fg-color:           var(--md-default-fg-color);
  --md-admonition-bg-color:           #eef2f8;              /* tint */

  /* Footer */
  --md-footer-fg-color:               #ffffff;
  --md-footer-fg-color--light:        rgba(255, 255, 255, .7);
  --md-footer-fg-color--lighter:      rgba(255, 255, 255, .45);
  --md-footer-bg-color:               #16191f;              /* text */
  --md-footer-bg-color--dark:         #0e1319;              /* ink-deep */

  /* Diagram tokens — spec §6, light column. */
  --diagram-accent-fill:              #eef2f8;
  --diagram-accent:                   #2f4d7a;
  --diagram-node-fill:                #ffffff;
  --diagram-node-stroke:              #c9cfd8;
  --diagram-node-label:               #16191f;
  --diagram-body:                     #5a616b;
  --diagram-muted:                    #6b727c;
  --diagram-connector:                #c9cfd8;
  --diagram-accent-connector:         #2f4d7a;
  --diagram-deferred:                 #a8b0ba;
}

/* ===========================================================================
   3. Type roles (spec §3)
   =========================================================================== */

:root {
  /* Material composes these into --md-text-font-family / --md-code-font-family
     with its own generic fallbacks. theme.font: false means nothing else sets
     them. */
  --md-text-font: "Nescio Sans";
  --md-code-font: "Nescio Mono";
}

/* Headings are 700 in text / text-dark; Material's stock rules set h1 to the
   --light ramp and h1/h2 to weight 300, which is not this system. */
.md-typeset h1,
.md-typeset h2,
.md-typeset h3,
.md-typeset h4 {
  color: var(--md-default-fg-color);
  font-weight: 700;
  letter-spacing: -.01em;
}

.md-typeset h1,
.md-typeset h2 {
  text-wrap: balance;
}

/* Eyebrows and small labels: mono-adjacent uppercase, per §3. */
.md-typeset h5,
.md-typeset h6 {
  color: var(--md-default-fg-color--light);
  font-weight: 700;
  letter-spacing: .12em;
}

/* Anything that is a literal identifier in the system — an agent name, a
   command, a filename, a token — is mono (§3). */
code,
kbd,
pre,
samp,
.md-typeset code,
.md-typeset kbd,
.md-typeset pre,
.md-typeset pre > code {
  font-family: var(--md-code-font-family);
  font-feature-settings: "kern";
}

.md-typeset code {
  color: var(--md-code-fg-color);
}

/* Running text measure, per §5.

   `68ch` is resolved once, on the article, and handed down as a computed
   length. Registering the property is what makes that happen: an *unregistered*
   custom property is substituted as a raw token stream and re-resolved against
   each user's own font, and `68ch` on an h1 is nearly twice `68ch` on a
   paragraph — the headings would quietly outrun the measure. `none` is the
   initial value so that a browser without @property support degrades to an
   uncapped block rather than a zero-width one. */
@property --nescio-measure {
  syntax: "<length> | none";
  inherits: true;
  initial-value: none;
}

/* .md-typeset is also on .md-footer-meta and .md-dialog__inner; they keep the
   cap on the box itself. */
.md-typeset {
  max-width: 68ch;
}

/* The ARTICLE is the exception: the cap moves off the box and onto its blocks.
   A capped article would pin the diagram wrapper to 68ch as well — `width:
   auto` on a child can never exceed its containing block. Uncapping the article
   and capping its blocks leaves prose at exactly the same measure while giving
   the diagram a containing block as wide as the content column.

   This is now the ONLY thing holding running text to §5's measure anywhere on
   the site — the article box is uncapped on every page, not just the homepage.
   Remove it and the prose runs the full content column. It is also the whole
   mechanism by which the diagrams get their width (see the diagram section
   below): put the cap back on .md-content__inner and they are 551px again. */
.md-content__inner.md-typeset {
  --nescio-measure: 68ch;
  max-width: none;
}

.md-content__inner.md-typeset > * {
  max-width: var(--nescio-measure);
}

/* Material's JS wraps every table in this and hangs it .8rem into the gutter on
   each side. Add that bleed back on top of the measure so the wrapper keeps the
   width it had when the article itself was the capped box. */
.md-content__inner.md-typeset > .md-typeset__scrollwrap {
  max-width: calc(var(--nescio-measure) + 1.6rem);
}

.md-typeset table:not([class]),
.md-typeset pre {
  max-width: none;
}

/* ---------------------------------------------------------------------------
   Wide diagrams (§5 "wide content", §6)

   The diagrams are authored much wider than any column this site has, and their
   smallest labels are ~11.5px at the authored size. There were only ever two
   ways out and both cost something:

     (a) render at natural size and scroll the wrapper — legible, but two thirds
         of the picture is off-box and the reader has to discover a scrollbar to
         learn that;
     (b) scale to fit — whole picture, unreadable labels.

   This file shipped (a) for a while, with the "never scale the artwork" rule
   written in capitals right here. THAT RULE IS RETIRED, deliberately. It was
   the right call only while there was nowhere else to see the diagram; now
   there is. The artwork fits its column, AND clicking it opens the same SVG at
   natural size in the modal below, which pans. Legibility moved one click away
   instead of being traded off — see docs/assets/js/diagram-lightbox.js.

   What follows from that: the horizontal-scroll affordance that used to live on
   the wrapper (layered gradients, `background-attachment: local, local, scroll,
   scroll`, the scrollbar gutter, `overscroll-behavior-x`) is dead weight on the
   inline wrapper — a box that can never overflow can never show it. It is NOT
   deleted; it moves verbatim onto `.nescio-lightbox__stage`, which is the box
   that now does the scrolling. Same technique, same tokens, the one surface
   where it still means something.

   >>> THE RULE THAT KEEPS THIS WORKING <<<
   The width comes from the measure cap sitting on the article's BLOCKS rather
   than on the article itself (see §5 above). The homepage — the ONLY page with
   diagrams on it; the two `diagram:` markers in the whole site are both there —
   keeps its navigation rail and hides only the table of contents.

   >>> AND DO NOT BUY WIDTH BY HIDING THE NAV RAIL <<<
   That was tried. `hide: navigation` in the homepage front matter gave the
   diagram the full 61rem grid, and at >=76.25em it simultaneously removed the
   rail, the header hamburger button and the footer prev/next block, leaving the
   GitHub icon as the only persistent link on the page. Mobile kept its drawer,
   so nothing looked broken until someone opened a laptop. Reverted. Navigation
   outranks diagram width — and the artwork was re-authored to fit instead,
   which is what made the argument moot.

   THE ARTWORK NOW FITS. brand/make_diagrams.py authors both diagrams on a
   1000px canvas, down from 1400px, and the SVG below is capped at 100% of its
   column. A diagram that would overflow now SHRINKS rather than sliding under a
   rail or forcing a scrollbar, and the lightbox restores full size on demand.
   That combination is what retired the old "never scale the artwork" rule; see
   the modal section below.

   WHAT WAS HERE BEFORE, so nobody reinvents it. Two rules ago the wrapper broke
   OUT of the article column: negative margins, one sidebar width per side, plus
   a viewport-derived bleed to spend the page margin past the grid cap. That
   deliberately slid the artwork underneath both sticky sidebars, which paint
   above the article, and the only mitigation shipped with it was an opaque
   ground on the sidebar's scroll wrapper. That mitigation was undersized by
   roughly 7x, because the wrapper is content-height and the sidebar COLUMN is
   not. Measured on the built site at 1440x900: nav rail column x=103–345,
   article x=369–1057, ToC x=1081–1323; the diagram wrapper spanned x=37–1389
   (1352px) and was 849px tall, against an opaque ground 99px tall on the left
   and 126px on the right. It covered 12–15% of the collision, and where it did
   cover it punched an opaque rectangle straight through the artwork. 63 SVG
   elements — node rects and text labels — landed inside a sidebar column. At
   1920x1080: 32 under the rail, 26 under the ToC. At 1024x900 the rail is an
   off-canvas drawer, but 37 labels still sat under the ToC. /agents/ and
   /skills/ scanned clean at every width, and prose never collided anywhere: the
   fault was diagram-specific and homepage-only. Narrower artwork plus a fit cap
   removes the whole class of problem rather than trading one compromise for
   another.

   NO VIEWPORT UNIT APPEARS ANYWHERE IN THIS FILE, and none should — the bleed
   above was the last one. A viewport width counts the classic scrollbar (15px
   in Chrome on Windows) and the client box does not, so anything sized off it
   overflows the page by exactly that much and the body scrolls sideways, which
   §5 forbids. The modal below is the obvious place to reach for one and does
   not: `position: fixed` with `inset: 0` takes its box from the viewport
   *without* the scrollbar error, and every length inside it is a percentage of
   that box. The one place the scrollbar width is genuinely needed — padding the
   body when the modal locks it — is measured in JavaScript, where the real
   number is observable.
   --------------------------------------------------------------------------- */

/* Inlined diagrams get this class from hooks/inline_svg.py. `max-width: none`
   is load-bearing: it beats the `--nescio-measure` cap on the article's blocks
   (same specificity, later in the file) so the wrapper gets the whole content
   column rather than 68ch. */
.md-typeset .nescio-diagram {
  max-width: none;
}

/* The activator. hooks/inline_svg.py emits a real <button> around the SVG, so
   this has a browser default look to strip: it must read as artwork, not as a
   control. */
.md-typeset .nescio-diagram__trigger {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  /* A button is a flex item / inline-block by default and will not stretch. */
  width: 100%;
}

/* Pointer and hover affordances are gated on the attribute that
   diagram-lightbox.js sets on <html> when it initialises. Without the script
   the button is inert, and a zoom cursor over an inert control is a lie. Focus
   styling is NOT gated — the button is focusable either way. */
[data-nescio-lightbox="ready"] .md-typeset .nescio-diagram__trigger {
  cursor: zoom-in;
}

[data-nescio-lightbox="ready"] .md-typeset .nescio-diagram__trigger:hover > svg {
  outline: 1px solid var(--md-default-fg-color--lightest);
  outline-offset: .3rem;
}

.md-typeset .nescio-diagram__trigger:focus-visible {
  outline: 2px solid var(--md-accent-fg-color);
  outline-offset: .2rem;
}

/* The artwork fits its column. `height: auto` with the viewBox intact is what
   keeps the aspect ratio while the width shrinks; no width is pinned here,
   because the authored canvas size is brand/make_diagrams.py's number and has
   already changed once. */
.md-typeset .nescio-diagram__trigger > svg {
  display: block;
  height: auto;
  max-width: 100%;
  /* Centred if the canvas is ever narrower than the column. */
  margin-inline: auto;
}

/* ---------------------------------------------------------------------------
   The full-size modal (docs/assets/js/diagram-lightbox.js builds the markup)

   Colours are §2 tokens throughout — the modal shows the same tokenised SVG the
   page does, so a literal hex here would leave the chrome stranded in one
   scheme while the artwork repainted around it.
   --------------------------------------------------------------------------- */

/* The overlay IS the backdrop; the script closes on a click that lands on it
   directly. `inset: 0` on a fixed box is the viewport minus nothing — the whole
   reason no viewport unit is needed here. */
.nescio-lightbox {
  position: fixed;
  inset: 0;
  z-index: 10;                      /* Material's header is z-index 5. */
  display: flex;
  align-items: center;
  justify-content: center;
  /* The gutter that leaves visible backdrop to click on all four sides. */
  padding: 1.2rem;
  background-color: var(--md-default-bg-color--light);
  backdrop-filter: blur(2px);
}

.nescio-lightbox[hidden] {
  display: none;
}

.nescio-lightbox__pane {
  display: flex;
  flex-direction: column;
  max-width: 100%;
  max-height: 100%;
  border: 1px solid var(--md-default-fg-color--lightest);
  border-radius: .3rem;
  background-color: var(--md-default-bg-color);
  box-shadow: var(--md-shadow-z3);
}

.nescio-lightbox__bar {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .5rem .5rem .5rem .9rem;
  border-bottom: 1px solid var(--md-default-fg-color--lightest);
}

/* Labels the dialog via aria-labelledby, so it is a real caption and not
   decoration; §3 puts small labels in the mono-adjacent uppercase role. */
.nescio-lightbox__title {
  margin: 0;
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--md-default-fg-color--light);
}

.nescio-lightbox__close {
  flex: none;
  padding: .3rem .7rem;
  border: 1px solid var(--md-default-fg-color--lightest);
  border-radius: .2rem;
  background: none;
  color: var(--md-default-fg-color);
  font-size: .7rem;
  font-weight: 700;
  cursor: pointer;
}

.nescio-lightbox__close:hover,
.nescio-lightbox__close:focus-visible {
  border-color: var(--md-accent-fg-color);
  color: var(--md-accent-fg-color);
}

.nescio-lightbox__close:focus-visible {
  outline: 2px solid var(--md-accent-fg-color);
  outline-offset: .1rem;
}

/* The panning surface. This is where the wrapper's old scroll affordance went:
   a soft edge shadow masked by a cover in the pane's own ground whenever the
   artwork is panned all the way to that edge. The covers are attached `local`
   so they travel with the content; the shadows are attached `scroll` so they
   stay pinned to the box. Fully panned ⇒ cover over shadow ⇒ nothing shown,
   which is also what an artwork narrower than the box gets. Both colours are §2
   tokens, so the affordance follows the scheme toggle with the artwork.

   `min-height: 0` is required: a flex item's default `min-height: auto` refuses
   to shrink below its content, which would push the pane past the overlay
   instead of scrolling inside it. */
.nescio-lightbox__stage {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  background-color: var(--md-default-bg-color);
  background-image:
    linear-gradient(to right, var(--md-default-bg-color) 45%, rgba(0, 0, 0, 0)),
    linear-gradient(to left,  var(--md-default-bg-color) 45%, rgba(0, 0, 0, 0)),
    radial-gradient(farthest-side at 0    50%, var(--md-default-fg-color--lightest), rgba(0, 0, 0, 0)),
    radial-gradient(farthest-side at 100% 50%, var(--md-default-fg-color--lightest), rgba(0, 0, 0, 0));
  background-position: left center, right center, left center, right center;
  background-size: 2rem 100%, 2rem 100%, .7rem 100%, .7rem 100%;
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll;
}

.nescio-lightbox__stage:focus-visible {
  outline: 2px solid var(--md-accent-fg-color);
  outline-offset: -2px;
}

/* Natural size here — that is the entire point of the modal. `max-width: none`
   is defensive: the clone is outside .md-typeset, but a future global image
   rule would otherwise shrink it back to the size the reader just clicked away
   from. */
.nescio-lightbox__stage > svg {
  display: block;
  max-width: none;
  height: auto;
  /* Centred when the artwork is smaller than the stage. */
  margin: auto;
}

/* ===========================================================================
   4. The homepage hero (spec §5)

   Order is fixed by the spec and enforced by the markup in docs/index.md:
   lockup, name-gloss, descriptor, the 86x3px periwinkle rule, the claim, the
   install commands, the buttons.

   The hero is dark in BOTH schemes ("Homepage hero. Dark.", §5), so it carries
   literal §2 hexes rather than Material tokens — a token would flip it white
   under nescio-light. Everything Material renders *inside* it (the code block,
   its copy button, the buttons) still reads tokens, so the block below
   re-declares the dark values of exactly those tokens on .nescio-hero. Scoping
   the overrides to custom properties rather than to Material's own selectors is
   what keeps this from breaking on a Material bump: the variable names are
   already the coupling recorded above, and no new ones are added.
   =========================================================================== */

.md-typeset .nescio-hero {
  /* Dark-scheme values for the tokens Material uses inside the hero. */
  --md-code-fg-color:                 #e9edf2;
  --md-code-bg-color:                 #141b24;              /* ink-raised */
  --md-code-bg-color--light:          rgba(20, 27, 36, .9);
  --md-code-bg-color--lighter:        rgba(20, 27, 36, .54);
  /* Material 9.7.7 paints the code block's copy button
     --md-default-fg-color--lightest at rest and --md-default-fg-color--light on
     hover (`:hover>*>.md-code__button`) — a deliberate reveal-on-hover
     affordance. Carrying the dark ramp here keeps that affordance working
     inside the always-dark hero when the reader is in the light scheme. */
  --md-default-fg-color--light:       #8d97a4;              /* muted-dark */
  --md-default-fg-color--lighter:     #6b727c;              /* muted */
  --md-default-fg-color--lightest:    #26313f;              /* border-dark */
  --md-code-hl-color:                 #2977ff;
  --md-code-hl-color--light:          rgba(41, 119, 255, .1);
  --md-code-hl-number-color:          #e6695b;
  --md-code-hl-special-color:         #f06090;
  --md-code-hl-function-color:        #c973d9;
  --md-code-hl-constant-color:        #9383e2;
  --md-code-hl-keyword-color:         #6791e0;
  --md-code-hl-string-color:          #2fb170;
  /* .md-button--primary paints itself --md-primary-fg-color on
     --md-primary-bg-color; periwinkle on ink-deep is 8.3:1 (§2). */
  --md-primary-fg-color:              #8fb0d9;              /* periwinkle */
  --md-primary-bg-color:              #0e1319;              /* ink-deep */
  --md-accent-fg-color:               #8fb0d9;
  --md-accent-bg-color:               #0e1319;
  --md-typeset-a-color:               #8fb0d9;

  display: flex;
  flex-direction: column;
  /* Sibling groups are laid out with gap, never per-element margins (§5). */
  gap: 1.1rem;
  margin: 0 0 2rem;
  padding: 2.2rem 1.8rem 2.4rem;
  border: 1px solid #26313f;                                /* border-dark */
  border-radius: .3rem;
  background: #0e1319;                                      /* ink-deep */
  color: #e9edf2;                                           /* text-dark */
}

.md-typeset .nescio-hero > * {
  margin: 0;
}

.nescio-hero__lockup {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Clear space is one eye-diameter (§4); the eye ring is 49/512 of the mark. */
  gap: .75rem;
}

.nescio-hero__owl {
  flex: none;
  width: 3.4rem;
  height: 3.4rem;
}

/* Live text, not the outlined paths — see the note at the top of index.md. */
.nescio-hero__wordmark {
  font-family: var(--md-code-font-family);
  font-size: 2.4rem;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1;
  color: #e9edf2;
}

.md-typeset .nescio-hero__gloss {
  font-size: .7rem;
  color: #8d97a4;                                           /* muted-dark */
}

.md-typeset .nescio-hero__gloss code {
  background: transparent;
  padding: 0;
  color: #8fb0d9;                                           /* periwinkle */
}

.md-typeset .nescio-hero__descriptor {
  font-size: 1rem;
  font-weight: 700;
  color: #e9edf2;
}

/* The rule is 86x3px, spec §5. Fixed px, not rem: it is a measured mark. */
.md-typeset hr.nescio-hero__rule {
  width: 86px;
  height: 3px;
  border: 0;
  background: #8fb0d9;                                      /* periwinkle */
}

.md-typeset .nescio-hero__claim {
  max-width: 48ch;
  color: #a8b0ba;
}

/* Install commands sit ABOVE the buttons on purpose (§5). */
.md-typeset .nescio-hero .highlight {
  border: 1px solid #26313f;                                /* border-dark */
  border-radius: .2rem;
}

/* md_in_html wraps the button list in a <p>; make that the flex row so the
   buttons keep gap-based spacing rather than Material's stock margins. */
.md-typeset .nescio-hero__buttons > p {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin: 0;
}

.md-typeset .nescio-hero__buttons .md-button {
  margin: 0;
}

/* ===========================================================================
   5. The 404 page (spec §7)

   Rendered by overrides/404.html, which replaces Material's stock static
   template. The copy is fixed by the spec; only the setting lives here.
   =========================================================================== */

.md-typeset .nescio-404 {
  padding: 2rem 0 3rem;
}

.md-typeset h1.nescio-404__mark {
  font-family: var(--md-code-font-family);
  font-size: 2.2rem;
  letter-spacing: -.02em;
}

.md-typeset .nescio-404__copy {
  max-width: 48ch;
}

/* ===========================================================================
   6. Footer tagline (spec §7)

   Emitted by overrides/partials/copyright.html. Italic, because §7 sets the
   Minerva line in italic wherever it appears.
   =========================================================================== */

.md-footer-meta .nescio-tagline {
  font-style: italic;
}
