/* kpress: css/page-reset.css */
/* KPress standalone page-shell reset. render_page owns html/body so `.kpress-page-main`
   is the single scroll context (no nested window scrollbar). Inlined render-blocking in
   <head> by render_page only; the embeddable fragment never carries it, so an embedding
   host's own global styles stay untouched. See kpress-design.md "Standalone scroll model"
   / Conscious Departures #14/#15. Lives here, in a real CSS file -- not as a Python string. */
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
}

/* kpress: css/style-tokens.css */
/* KPress design tokens -- the source of truth for the KPress design system.
 *
 * STRUCTURE (one comment-headed section per group; the public subset is pinned by
 * contract.py and the design-system playground exercises the tunable surface):
 *
 *   Foundations (.kpress, .kpress-page-main, .kpress-tooltip) -- theme-INDEPENDENT:
 *     1. Typography: families / weights / sizes / line-heights / section-label caps
 *     2. Spacing & layout    3. Shape (radii)    4. Motion    5. Chrome (card)
 *     6. Print    7. Color (theme-independent roles)    8. Compatibility aliases
 *   Display/Page switches  -- font mode (custom/system), reading font (serif/sans)
 *   Color -- palette x theme -- the single source of truth for every theme color
 *   Webfonts (@font-face)
 *
 * NAMING: foundation tokens are --kpress-<group>-<role>[-<variant>]
 * (--kpress-font-*, --kpress-font-size-*, --kpress-radius-*, --kpress-transition-*);
 * color roles are --kpress-doc-<role>; host override hooks are --kpress-host-*;
 * bare --color-* / --font-* are textpress-compat ALIASES (section 8), not new tokens.
 *
 * Design RULES are kept here as comments next to each group, close to the code
 * (kpress-design.md is the map, not a duplicate ledger). The public subset is pinned by
 * contract.py::PUBLIC_CSS_VARIABLES. Core doc tokens read var(--kpress-host-*, <default>)
 * so an embedding host app can theme them; static pages and static-site builds override
 * via the same variables. */
:root,
.kpress,
.kpress-page-main,
.kpress-tooltip {
  /* Adapted from Kash base_styles.css.jinja and base_webpage.html.jinja.
     This block holds the theme-independent tokens (type, spacing, radii, motion,
     chrome, print, the theme-independent color roles) and the --color-* / --font-*
     compatibility alias map (section 8). The actual theme COLOR VALUES are set per
     theme x palette in the "Color -- palette x theme" section below; an embedding
     host re-themes by overriding those --kpress-doc-* tokens directly. */

  /* ============================ 1. TYPOGRAPHY ============================ */
  /* SIZING POLICY: every font size in KPress derives from ONE base knob,
     --kpress-font-size-base, via calc(base * ratio) -- ratios are design, units
     are the host's choice. No font-size rule may reference `rem` directly (the
     lint in devtools/public_hygiene.py enforces this): `rem` would couple an
     embedded document's proportions to a root font size the embedder may not
     control, while the base knob keeps standalone pages preference-respecting
     (the 1rem default resolves against the reader's browser setting) and lets a
     host with any unit policy scale the whole document as one proportional
     unit. Embedders set --kpress-host-font-size-base ONCE (e.g. `17px` on
     :root); never override individual sizes. Deliberately root-relative and NOT
     derived from the base: layout lengths (--kpress-measure, container-query
     band conditions, grid tracks, tooltip width caps, page margins) -- see
     kpress-design.md "Sizing policy". Intentionally context-relative em/% sizes
     (the summary chevron, checkbox, sup/sub) also stay as they are. */
  --kpress-font-size-base: var(--kpress-host-font-size-base, 1rem);

  /* Families. body = UI/chrome sans; prose = serif reading face; sans = the sans
     stack; mono = code; footnote/table default to sans; punctuation is the
     shipped quote face. Each reads a --kpress-host-* hook so a host can swap it.
     The text families lead with a face KPress ships, so a document draws the
     same glyphs on every machine and a printed page embeds them; the system
     stacks behind each one are the fallback for a face that failed to load, not
     the design. The two places the platform is asked for a font on purpose are
     font_mode="system" and mono_font="system". */
  --kpress-font-body: var(
    --kpress-host-font-body,
    "Source Sans 3 Variable",
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    sans-serif
  );
  /* "KPress Quotes" leads: it answers six code points and passes everything
     else down to PT Serif. See the punctuation token below for why. */
  --kpress-font-prose: var(
    --kpress-host-font-prose,
    var(--kpress-font-punctuation),
    "PT Serif",
    Georgia,
    "Times New Roman",
    serif
  );
  --kpress-font-sans: var(
    --kpress-host-font-sans,
    "Source Sans 3 Variable",
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    sans-serif
  );
  /* The platform's own mono stack, named once. Three rules need it -- the mono
     token below (as the fallback behind the shipped face), the reader's
     system-font switch, and mono_font="system" -- and three hand-copied stacks
     drift apart. Internal, not a host hook: a host swaps the mono face through
     --kpress-host-font-mono, which leads every one of those three rules. */
  --kpress-mono-stack-platform: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* "Planetaire Mono Text" leads: B612 Mono's letterforms with Hack's
     punctuation and symbols, vendored as latin subsets by
     devtools/subset_mono.py. Which of its styles a document actually declares
     is RenderOptions.mono_weights; whether it declares any is
     RenderOptions.mono_font, whose "system" value swaps this token for the
     platform stack alone and drops the faces from the manifest. */
  --kpress-font-mono: var(
    --kpress-host-font-mono,
    "Planetaire Mono Text",
    var(--kpress-mono-stack-platform)
  );
  --kpress-font-footnote: var(--kpress-host-font-footnote, var(--kpress-font-sans));
  --kpress-font-table: var(--kpress-host-font-table, var(--kpress-font-sans));
  /* The punctuation slot: the family that answers the quote and apostrophe code
     points, and nothing else. It leads --kpress-font-prose because PT Serif
     draws those six glyphs badly -- at 18px its opening pair hangs 2px above
     its closing pair and its curly doubles are 16% wider than Georgia's, which
     is why KPress borrowed Georgia's marks through a local() face for years.
     Borrowing made a document's punctuation depend on the reader's machine and
     could not be embedded in a PDF, so the marks are shipped instead: six
     glyphs of Source Serif 4, generated by devtools/subset_quotes.py. See
     kpress-design.md "Quotation Marks".

     A host swaps the marks for another family, or drops them by naming its own
     reading face, in one line:

       --kpress-host-font-punctuation: "PT Serif"; */
  --kpress-font-punctuation: var(--kpress-host-font-punctuation, "KPress Quotes");
  --kpress-font-features-sans: normal;

  /* Regular is the build-time input to instance_sans + katex_text_metrics.
     Regenerate both tools after changing it so math outlines and metrics agree. */
  --kpress-font-weight-sans-regular: 410;
  --kpress-font-weight-sans-light: var(--kpress-font-weight-sans-regular);
  --kpress-font-weight-sans-medium: 550;
  --kpress-font-weight-sans-bold: 650;
  --_kpress-font-weight-prose: 400;

  /* Sizes. One ramp for prose + UI; the mono sizes are a touch smaller so code
     aligns visually with surrounding text. `normal` is the base itself --
     derived, not a second knob: hosts set the base, not normal. */
  --kpress-font-size-large: calc(var(--kpress-font-size-base) * 1.2);
  --kpress-font-size-normal: var(--kpress-font-size-base);
  --kpress-font-size-small: calc(var(--kpress-font-size-base) * 0.95);
  --kpress-font-size-smaller: calc(var(--kpress-font-size-base) * 0.9);
  --kpress-font-size-tiny: calc(var(--kpress-font-size-base) * 0.85);
  /* The mono rung: 0.82 of the prose size, a judgement about how code should read
     beside prose rather than a number the ink forces. What the ink gives is the
     scale to make that judgement on. Planetaire Mono Text draws an x-height of
     1120/2000 = 0.560 em against PT Serif's 500/1000 = 0.500, so parity of
     x-height -- code and prose with the same visual height of lowercase -- would
     want 0.500/0.560 = 0.893. At 0.82 code's x-height is 0.82 x 0.560 = 0.459 em,
     91.8% of the prose x-height beside it: deliberately under parity, so a code
     span reads as an inset in the line rather than competing with it. 0.87 sat at
     97.4%, near enough to parity that code read too large beside the prose; 0.82 is
     the owner's decision of 2026-09-08.

     Width follows from the same number: Planetaire advances 1204/2000 = 0.602 em
     per column, so 45 / (0.82 x 0.602) = 91.16, and 91 columns fit the
     --kpress-measure reading column -- six more than 0.87 allowed.
     `tests/test_mono_face.py` re-derives both numbers from the shipped faces, so
     the ratio's consequences cannot drift from the ink even though the ratio itself
     is a choice.

     A host retunes the three rungs together through --kpress-host-font-size-mono,
     the one sizing hook beside --kpress-host-font-size-base: small and tiny derive
     from the mono rung rather than from the base, so a host that sets it keeps the
     internal proportions instead of forking three tokens.

     The two ramps pair by index -- mono/normal, mono-small/smaller,
     mono-tiny/tiny -- so the small and tiny multipliers are the prose ramp's own
     steps, 0.9 and 0.85. That is what makes the ratio hold: 0.82 x 0.9 / 0.9 is
     0.82 again, so all three rungs sit at the same 91.8% of the prose x-height
     beside them instead of only the first one. They were 0.915 and 0.855, the
     pre-Planetaire absolutes rescaled (0.75/0.82, 0.70/0.82) and tuned for the
     system monos this face replaced, which left small and tiny above the rung they
     sit under instead of level with it. */
  --kpress-font-size-mono: var(
    --kpress-host-font-size-mono,
    calc(var(--kpress-font-size-base) * 0.82)
  );
  --kpress-font-size-mono-small: calc(var(--kpress-font-size-mono) * 0.9);
  --kpress-font-size-mono-tiny: calc(var(--kpress-font-size-mono) * 0.85);

  /* KaTeX sizing. Context-relative (em) on purpose, like the sup/sub sizes the
     policy above exempts and for the same reason: these are ratios against
     whatever face surrounds the math, not another rung of the base-derived ramp.
     KaTeX ships `.katex { font-size: 1.21em }`, a compensation tuned so its own
     KaTeX_Main sits right beside a Times-like body face. PT Serif -- the prose
     face here -- carries a notably larger x-height, so 1.21 reads about a fifth
     too big against it, and further out of scale inside the small Source Sans
     contexts (figure captions, section labels, tab labels), which start below
     the prose size to begin with. Display math keeps a little more of the
     upstream lift because it stands alone rather than sitting in a line of
     text. See components.css "KaTeX sizing" for where each one applies. */
  --kpress-katex-size-prose: 1.05em;
  --kpress-katex-size-sans: 1em;
  --kpress-katex-size-display: 1.1em;

  /* Prose section-heading sizes (H2-H4; H1 and H5/H6 keep their literals).
     These narrow/medium defaults step up on wide panes only (the >=64rem
     container band in components.css); phones and embedded panes keep the
     tighter scale. Body text does not scale with pane width — only the
     section headings do. */
  --kpress-font-size-h2: calc(var(--kpress-font-size-base) * 1.32);
  --kpress-font-size-h3: calc(var(--kpress-font-size-base) * 1.15);
  --kpress-font-size-h4: calc(var(--kpress-font-size-base) * 1.12);

  /* Line heights. */
  --kpress-line-height-normal: 1.5;
  --kpress-line-height-tight: 1.2;

  /* Section-label caps. Section labels use real uppercase, not small-caps. Our
     subset Source Sans may not ship true `smcp` glyphs, so `font-variant:
     small-caps` synthesized thin, mis-sized caps that the 1.42 multiplier then
     propped up. Uppercase renders the font's real capital glyphs at exactly the
     size we ask for, so no compensation is needed and the multiplier is 1 (kept as
     a tunable dial, not removed, so the `calc(base * multiplier)` consumers don't
     all have to change). */
  --kpress-caps-transform: uppercase;
  --kpress-caps-variant: normal;
  --kpress-caps-spacing: 0.021em;
  --kpress-caps-heading-size-multiplier: 1;
  --kpress-caps-heading-line-height: calc(1.2 / var(--kpress-caps-heading-size-multiplier));
  /* One canonical uppercase section-label size (TOC title, metadata summary, tab
     labels, diagnostics) so they all match instead of drifting. */
  --kpress-caps-label-size: calc(
    var(--kpress-font-size-smaller) *
    var(--kpress-caps-heading-size-multiplier)
  );

  /* ========================= 2. SPACING & LAYOUT ========================= */
  /* Prose reading measure: the width of the TEXT itself, padding excluded.
     Every element that bounds the reading column derives from this token and
     the inset below -- see "the width chain" in components.css. It is the one
     knob a host turns to change how wide documents read.

     Derived from the type base rather than the root, `calc(base * 45)`, so the
     column tracks the text it holds: a host that pins
     --kpress-host-font-size-base gets the same characters per line at any size,
     where a root-relative length would hold the pixels and change the line.
     Written as calc(base * N) rather than `45em` deliberately -- an `em` in a
     custom property resolves at each CONSUMING element, and this token is read
     on .kpress-doc, .kpress-doc-layout and .kpress-long-text, which do not
     share a font-size. calc(base * N) is the same idiom the type ramp uses and
     resolves to one length everywhere.

     45 is a character-count decision, converted. Measured over 507 characters
     of representative English prose at 100px (the average glyph advance, not
     the `ch` unit -- `ch` is the advance of "0" and overstates a proportional
     face by ~23%):

       PT Serif (prose default)  0.4335 em/char -> 45em reads ~104 chars
       Source Sans 3 (sans mode) 0.4039 em/char -> 45em reads ~111 chars

     Eight common proportional text faces (PT Serif, Source Sans, Georgia,
     Times, system-ui, Helvetica, Palatino, Charter) span 0.392-0.434 em/char,
     so ~45em lands in the 104-115 character range for any of them. KPress does
     not carry per-face constants: a host may swap the face through
     --kpress-host-font-prose, which would invalidate them. A host that wants a
     true character count measures its own pinned face and sets this token. */
  --kpress-measure: calc(var(--kpress-font-size-base) * 45);
  /* Inline padding inside the reading column, between the text and the content
     card's edge. Band-dependent (the three bands reset it in components.css),
     and paired with the measure everywhere the column is bounded: a cap that
     forgot to add it is what made the measure mean "text" in one band and
     "text + padding" in another. Declared here on the foundation scope and
     reset per band on .kpress-doc -- the same element as .kpress, so the reset
     wins on cascade order (components.css loads after this file) -- and it
     inherits down to the layout and the column, which read the one value. */
  --kpress-column-inset: 1.5rem;
  /* Inline padding on the article, outside the column: the page gutter. Paired
     with the measure in .kpress-doc's cap for the same reason as the inset. */
  --kpress-doc-gutter: 1rem;
  --kpress-page-margin-inline: 2rem;
  --kpress-page-margin-block-start: 2.5rem;
  /* Settings gear position (standalone page shell only). The gear is `position:
     fixed`, pinned to the top-right of the document frame; these two tokens are
     its block/inline insets. Host-overridable through the `--kpress-host-*`
     hooks so a site can move it without touching kpress -- e.g. align it to the
     content column's right edge instead of flush to the window, by setting on :root
       --kpress-host-settings-inset-inline: max(3rem, calc(50vw - 24rem));
     (24rem = half the 48rem --kpress-measure; use the literal on :root, where
     --kpress-measure is not in scope. See kpress-design.md -> Settings menu.) */
  --kpress-settings-inset-block: var(--kpress-host-settings-inset-block, 0.75rem);
  --kpress-settings-inset-inline: var(--kpress-host-settings-inset-inline, 0.75rem);
  /* Document-actions cluster position (doc-actions widget; off by default).
     Defaults to the slot just inline-left of the settings gear (28px button
     + 12px gap = 2.5rem); host-overridable through the --kpress-host-* hooks,
     same pattern as the settings insets above. */
  --kpress-doc-actions-inset-block: var(
    --kpress-host-doc-actions-inset-block,
    var(--kpress-settings-inset-block)
  );
  --kpress-doc-actions-inset-inline: var(
    --kpress-host-doc-actions-inset-inline,
    calc(var(--kpress-settings-inset-inline) + 2.5rem)
  );
  /* Doc-actions badge: the format letters are the caps-label idiom one notch
     down — slightly smaller than the TOC "Contents" label — so the badge
     reads as an icon, not a heading. The frame corner is square by default
     to match the document's square corners. Both are host-tunable. */
  --kpress-doc-actions-badge-size: calc(var(--kpress-caps-label-size) * 0.85);
  --kpress-doc-actions-badge-radius: var(--kpress-radius-none);
  /* Inline gutter the document reserves for the floating TOC toggle in the
     narrow band. Deliberately a little tighter than the toggle's full
     footprint (0.75rem inset + 32px button): the toggle carries a translucent
     document-background fill, so a slight overlap with the text gutter stays
     legible while phones keep more reading width. A host whose pane inset
     already contributes to the gutter shrinks the reservation by redeclaring
     this on an element scope. See components.css -> .kpress-doc. */
  --kpress-toc-toggle-clearance: 2rem;
  /* Task-list checkbox size: slightly larger than body text so the check glyph
     is clearly legible at a glance and not confused with a bullet. */
  --kpress-checkbox-size: 1.15em;
  /* Square list bullet -- one size for prose lists, claim markers, and concept
     items, so every bulleted list matches. This is the ::before's font size, and
     the square is 0.226em of it: the marker is DRAWN rather than set, so the
     token now controls the mark exactly instead of choosing a size at which some
     fallback face's U+25AA would be drawn (see document.css). NOTE for diverging
     hosts: the marker OFFSETS track the base, not this token, so a bullet size
     far from the default shifts the square against the first line. */
  --kpress-bullet-size: calc(var(--kpress-font-size-base) * 0.9);

  /* ============================== 3. SHAPE ============================== */
  /* Corner radii -- one scale used everywhere. Rounded-vs-square is a deliberate
     per-surface choice (use --kpress-radius-none for square). */
  --kpress-radius-none: 0;
  --kpress-radius-sm: 3px;
  --kpress-radius-md: 6px;
  --kpress-radius-lg: 0.5rem;
  --kpress-radius-pill: 999px;

  /* ============================== 4. MOTION ============================= */
  /* Shared motion. One easing; a fast transition for hovers and size/shape
     changes, a slower fade for overlays. Suppressed under prefers-reduced-motion
     (see the reduce block in components.css). */
  --kpress-ease: ease-in-out;
  --kpress-transition-fast: 0.15s var(--kpress-ease);
  --kpress-transition-med: 0.2s var(--kpress-ease);
  --kpress-transition-slow: 0.4s var(--kpress-ease);
  --kpress-transition-fade: 0.3s var(--kpress-ease);

  /* ========================= 5. CHROME (card) ========================== */
  /* Content card chrome: the reading column rendered as a bordered sheet
     floating over the page (see components.css "Content card" and
     kpress-design.md). Border + shadow are textpress's values verbatim; the
     dark themes deepen the shadow (overridden in the dark palette block). */
  --kpress-card-border: 1px solid var(--kpress-doc-border);
  --kpress-card-shadow:
    0 10px 15px -3px oklch(0.00% 0 0 / 0.1), 0 -2px 6px -1px oklch(0.00% 0 0 / 0.07);

  /* ============================== 6. PRINT ============================= */
  --kpress-print-page-margin: 0.7in;
  --kpress-print-font-size: 11pt;
  --kpress-print-footer: "Formatted by KPress";

  /* =============== 7. COLOR -- theme-independent roles ================= */
  /* Most color roles (--kpress-doc-bg/text/muted/border/link/code-bg + the
     surface hover/selected steps, success, danger) are set per theme x palette in
     the "Color -- palette x theme" section below, on :root (standalone) and
     .kpress (embedded), so they cascade to the whole shell (gear, tooltips, TOC)
     with no host-var / literal-fallback indirection that could silently theme-swap
     on a typo. The two roles below do NOT vary by theme, so they live here:
       accent     -- a fixed teal used for small accents
       surface-bg -- the subtle fill shared by code blocks, table headers, and the
                     metadata block (an alias of the code bg) so they read as one
                     family; the hover/selected interaction steps that DO vary are
                     set per palette below. */
  --kpress-doc-accent: oklch(51.09% 0.0861 186.4);
  --kpress-doc-surface-bg: var(--kpress-doc-code-bg);

  /* ===================== 8. COMPATIBILITY ALIASES ===================== */
  /* textpress/kash vocabulary mapped onto the --kpress-* tokens above, so styles
     ported from kash keep working. These are ALIASES, not new tokens -- add new
     design values as --kpress-* and alias here only if a ported consumer needs it. */
  --font-sans: var(--kpress-font-sans);
  --font-serif: var(--kpress-font-prose);
  --font-mono: var(--kpress-font-mono);
  --font-features-sans: var(--kpress-font-features-sans);
  --font-weight-sans-medium: var(--kpress-font-weight-sans-medium);
  --font-weight-sans-bold: var(--kpress-font-weight-sans-bold);
  --font-size-large: var(--kpress-font-size-large);
  --font-size-normal: var(--kpress-font-size-normal);
  --font-size-small: var(--kpress-font-size-small);
  --font-size-smaller: var(--kpress-font-size-smaller);
  --font-size-tiny: var(--kpress-font-size-tiny);
  --font-size-mono: var(--kpress-font-size-mono);
  --font-size-mono-small: var(--kpress-font-size-mono-small);
  --font-size-mono-tiny: var(--kpress-font-size-mono-tiny);
  --line-height-normal: var(--kpress-line-height-normal);
  --line-height-tight: var(--kpress-line-height-tight);
  --caps-transform: var(--kpress-caps-transform);
  --caps-caps-variant: var(--kpress-caps-variant);
  --caps-spacing: var(--kpress-caps-spacing);
  --caps-heading-size-multiplier: var(--kpress-caps-heading-size-multiplier);
  --caps-heading-line-height: var(--kpress-caps-heading-line-height);
  --color-bg: var(--kpress-doc-bg);
  --color-bg-solid: var(--kpress-doc-bg);
  --color-bg-alt: var(--kpress-doc-surface-bg);
  --color-bg-alt-solid: var(--kpress-doc-surface-bg);
  --color-bg-meta-solid: var(--kpress-doc-surface-bg);
  --color-bg-selected: var(--kpress-doc-surface-selected);
  --color-text: var(--kpress-doc-text);
  --color-primary: var(--kpress-doc-link);
  --color-primary-light: color-mix(in srgb, var(--kpress-doc-link) 72%, white);
  --color-secondary: var(--kpress-doc-muted);
  --color-hint: var(--kpress-doc-muted);
  --color-hint-strong: var(--kpress-doc-muted);
  /* Faint, neutral-reading hairline for inline code, code blocks, tooltips and
     the key-claims/concepts boxes -- modeled on textpress's `hint_gentle`
     (~rgba(155,172,175,0.2)). Derived from the muted gray (not the lighter
     blue-tinted doc-border) at low opacity so it reads gray, not blue. */
  --color-hint-gentle: color-mix(in srgb, var(--kpress-doc-muted) 18%, transparent);
  --color-border-hint: var(--kpress-doc-border);
  --color-border-hairline: var(--kpress-doc-border-hairline);
  --color-hover-bg: var(--kpress-doc-surface-hover);
  --color-hover: var(--kpress-doc-border);
  --color-selection: color-mix(in srgb, var(--kpress-doc-link) 20%, transparent);
  --color-scrollbar: color-mix(in srgb, var(--kpress-doc-muted) 45%, transparent);
  --color-scrollbar-hover: color-mix(in srgb, var(--kpress-doc-muted) 70%, transparent);
  --color-success: var(--kpress-doc-success);
  --color-danger: var(--kpress-doc-danger);
}

/* ===================== DISPLAY/PAGE SWITCHES ========================= */

/* Global font switch. Default ("custom", set by render.py on the .kpress
   wrapper) uses the vendored PT Serif / Source Sans reader faces below.
   "system" opts out of the vendored faces and uses the platform system
   font stack, so no reader fonts are downloaded. Switchable via
   RenderOptions.font_mode at render time, and per reader via the settings
   widget's font-set chooser, which stamps data-kpress-font-set on <html>
   (persisted as `kpress.fontSet`; the pre-paint bootstrap re-applies it). */
.kpress[data-kpress-fonts="system"],
[data-kpress-font-set="system"] .kpress,
[data-kpress-font-set="system"] .kpress-page-main,
[data-kpress-font-set="system"] .kpress-tooltip {
  --kpress-font-body: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --kpress-font-sans: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --kpress-font-prose: Georgia, "Times New Roman", Times, serif;
  --kpress-font-punctuation: Georgia, "Times New Roman", Times, serif;
  /* The host hook still leads. This block opts a reader out of the fonts KPress
     downloads; it is not a statement about the face a host installed itself, and
     dropping the var() here would silently retire a documented public seam on a
     control the reader owns. Same shape as the mono_font block below. */
  --kpress-font-mono: var(--kpress-host-font-mono, var(--kpress-mono-stack-platform));
}

/* Mono face switch. Default ("planetaire", the absent attribute) sets code in
   the vendored Planetaire Mono Text subsets. "system" hands code back to the
   platform stack, and the render drops every Planetaire face from the asset
   manifest, so a page under this setting neither fetches nor inlines one. Set by
   RenderOptions.mono_font / `format.mono_font`, stamped as data-kpress-mono-font
   on <html> by the standalone page shell; an embedding host stamps it on its own
   root and passes the same value to the render.

   This is deliberately separate from the font mode above. font_mode="system" is a
   display switch a reader can flip and leaves the asset set alone; mono_font is a
   publishing decision that changes what ships. Either one alone puts code in the
   platform mono. */
.kpress[data-kpress-mono-font="system"],
[data-kpress-mono-font="system"] .kpress,
[data-kpress-mono-font="system"] .kpress-page-main,
[data-kpress-mono-font="system"] .kpress-tooltip {
  --kpress-font-mono: var(--kpress-host-font-mono, var(--kpress-mono-stack-platform));
}

/* Reading-font chooser (settings widget): "sans" flips the prose body from the
   serif reading face to the sans stack, leaving headings/UI/code untouched.
   Stamped as data-kpress-prose-font on <html> (persisted as `kpress.proseFont`;
   pre-paint applies it before first paint). Hosts may point the sans prose
   stack anywhere via --kpress-host-font-prose-sans. */
[data-kpress-prose-font="sans"] .kpress,
[data-kpress-prose-font="sans"] .kpress-page-main,
[data-kpress-prose-font="sans"] .kpress-tooltip,
.kpress[data-kpress-prose-font="sans"] {
  --_kpress-font-weight-prose: var(--kpress-font-weight-sans-regular);
  --kpress-font-prose: var(--kpress-host-font-prose-sans, var(--kpress-font-sans));
}

/* ============================================================================
   COLOR -- PALETTE x THEME -- the single source of truth for every theme color.

   Each theme x palette block sets the document color tokens (--kpress-doc-*)
   DIRECTLY to literal values -- no host-var / fallback indirection, so a typo
   renders obviously broken instead of silently swapping themes.

   SELECTOR GRAMMAR (the one theme vocabulary): CSS reads exactly ONE input,
   `data-kpress-resolved-theme="light|dark"` -- the resolver's OUTPUT. The mode
   attribute (data-kpress-theme: system|light|dark) is resolver state and never
   keys CSS (enforced by devtools/public_hygiene.py). Every keyed state uses the
   same three selector forms, in one rule:

     :root:where([state])            -- the standalone/host shell: tokens on the
                                        root so body-level chrome (video popover,
                                        future overlays) inherits them; :where()
                                        keeps it at :root's own specificity.
     :where([state]) :is(scopes)     -- ANY ancestor theme scope (:root or a host
                                        wrapper), flattened to (0,1,0) so it wins
                                        over the unkeyed defaults by source order
                                        alone.
     :is(scopes)[state]              -- the ELEMENT scope, (0,2,0): an element's
                                        own attribute beats any ancestor state,
                                        so a stamped element is a coherent theme
                                        island in either direction.

   Light and dark are keyed SYMMETRICALLY (light re-asserts its values; the
   unkeyed defaults remain as the no-attribute fallback, identical to light).
   `color-scheme` is declared in an adjacent rule keyed identically (component
   scopes only -- KPress never restyles a host root's UA behavior), so palette
   and color-scheme cannot resolve to different themes. Palette forms are
   flattened with :where() except the explicit palette x theme element forms,
   so the theme axis outranks a lone palette attribute across scopes. Combined
   states (palette + theme) bind fully when both attributes sit on the SAME
   scope element; splitting axes across scopes intentionally resolves theme
   first. Order in this file is load-bearing: defaults -> light -> warm ->
   dark -> warm-dark. `neutral` is the default (bare, no [data-kpress-palette]);
   each other palette overrides ONLY the tokens that differ. An embedding host
   overrides any --kpress-doc-* directly, or stamps the attributes and lets
   these blocks resolve. Add a palette = add its two rules.

   ROLE ORDER within each block (a color family taxonomy -- the same grouping the
   playground palette editor uses): foreground (text, muted) -> hairline (border)
   -> interactive (link) -> surfaces (bg, code-bg, surface-hover, surface-selected)
   -> status (success, danger).

   Design: `warm` is modeled on textpress/kash and is GENTLE. The warmth is ONLY
   the cream code/surface background (hue ~83). Text, muted, border and link stay
   COOL (teal, hue ~208) at neutral's exact lightness -- so text is plain near-black
   (never tan) and borders are a quiet hairline (never warm). Warm reads as
   "neutral with a teal accent over warm paper," not a tan wash.
   ============================================================================ */

/* neutral -- light (default; also the pre-JS / no-attribute state). :root drives
   the standalone shell; .kpress drives the embedded fragment. */
:root,
.kpress {
  --kpress-doc-text: oklch(21.01% 0.0318 264.7);
  --kpress-doc-muted: oklch(50.06% 0.025 259.2);
  --kpress-doc-border: oklch(89.90% 0.0151 260.7);
  /* A crisp near-ink rule for deliberate frames (boxed-text): kash's
     border-hairline (hsl 188 2% 34%) was a *dark* thin line, not a faint one. */
  --kpress-doc-border-hairline: oklch(42.00% 0.006 260.7);
  --kpress-doc-link: oklch(45.76% 0.1445 254.7);
  --kpress-doc-bg: oklch(100.00% 0 0);
  --kpress-doc-code-bg: oklch(97.85% 0.0045 258.3);
  --kpress-doc-surface-hover: color-mix(in srgb, var(--kpress-doc-link) 8%, transparent);
  --kpress-doc-surface-selected: color-mix(in srgb, var(--kpress-doc-link) 10%, transparent);
  --kpress-doc-success: oklch(52.73% 0.1371 150.1);
  --kpress-doc-danger: oklch(50.03% 0.1821 29.5);
}

/* Default color-scheme, component scopes only (never a host's root). */
.kpress,
.kpress-page-main,
.kpress-tooltip {
  color-scheme: light;
}

/* neutral -- light, keyed: the symmetric twin of the dark block below. Values
   MUST mirror the unkeyed defaults above (plus the light card shadow from the
   Chrome section), so a stamped light element inside a dark scope renders as a
   coherent light island instead of a specificity accident. */
:root:where([data-kpress-resolved-theme="light"]),
:where([data-kpress-resolved-theme="light"]) :is(.kpress, .kpress-page-main, .kpress-tooltip),
:is(.kpress, .kpress-page-main, .kpress-tooltip)[data-kpress-resolved-theme="light"] {
  --kpress-doc-text: oklch(21.01% 0.0318 264.7);
  --kpress-doc-muted: oklch(50.06% 0.025 259.2);
  --kpress-doc-border: oklch(89.90% 0.0151 260.7);
  --kpress-doc-border-hairline: oklch(42.00% 0.006 260.7);
  --kpress-doc-link: oklch(45.76% 0.1445 254.7);
  --kpress-doc-bg: oklch(100.00% 0 0);
  --kpress-doc-code-bg: oklch(97.85% 0.0045 258.3);
  --kpress-doc-surface-hover: color-mix(in srgb, var(--kpress-doc-link) 8%, transparent);
  --kpress-doc-surface-selected: color-mix(in srgb, var(--kpress-doc-link) 10%, transparent);
  --kpress-doc-success: oklch(52.73% 0.1371 150.1);
  --kpress-doc-danger: oklch(50.03% 0.1821 29.5);
  --kpress-card-shadow:
    0 10px 15px -3px oklch(0.00% 0 0 / 0.1), 0 -2px 6px -1px oklch(0.00% 0 0 / 0.07);
}

:where([data-kpress-resolved-theme="light"]) :is(.kpress, .kpress-page-main, .kpress-tooltip),
:is(.kpress, .kpress-page-main, .kpress-tooltip)[data-kpress-resolved-theme="light"] {
  color-scheme: light;
}

/* warm -- light values: teal accent + cream surface (textpress/kash). Overrides
   only the cool-identity tokens -- to teal (hue ~208) at neutral's exact
   lightness, so text stays near-black and the border stays a quiet hairline --
   plus the code surface to cream (hue ~83). bg, surfaces, success, danger
   inherit neutral. The lone-element form is :where()-flattened so the dark
   block below outranks a palette attribute split across scopes; the explicit
   warm+light element form binds the combined state at full strength. */
:root:where([data-kpress-palette="warm"]),
:where([data-kpress-palette="warm"]) :is(.kpress, .kpress-page-main, .kpress-tooltip),
:is(.kpress, .kpress-page-main, .kpress-tooltip):where([data-kpress-palette="warm"]),
:is(
  .kpress,
  .kpress-page-main,
  .kpress-tooltip
)[data-kpress-palette="warm"][data-kpress-resolved-theme="light"] {
  --kpress-doc-text: oklch(21.01% 0.022 208);
  --kpress-doc-muted: oklch(50.06% 0.022 208);
  --kpress-doc-border: oklch(89.90% 0.012 208);
  --kpress-doc-border-hairline: oklch(42.00% 0.006 208);
  --kpress-doc-link: oklch(56.60% 0.061 208);
  /* A mild cream -- kept very light (matches textpress's near-white code surface)
     so monospace spans and tooltips read as a gentle tan, not a heavy one. */
  --kpress-doc-code-bg: oklch(98.40% 0.005 83);
}

/* neutral -- dark. */
:root:where([data-kpress-resolved-theme="dark"]),
:where([data-kpress-resolved-theme="dark"]) :is(.kpress, .kpress-page-main, .kpress-tooltip),
:is(.kpress, .kpress-page-main, .kpress-tooltip)[data-kpress-resolved-theme="dark"] {
  --kpress-doc-text: oklch(94.40% 0.0097 252.8);
  --kpress-doc-muted: oklch(76.58% 0.0205 252.9);
  --kpress-doc-border: oklch(33.73% 0.0207 254.1);
  --kpress-doc-border-hairline: oklch(80.00% 0.008 254.1);
  --kpress-doc-link: oklch(76.68% 0.1208 255.4);
  --kpress-doc-bg: oklch(18.98% 0.0094 255.6);
  --kpress-doc-code-bg: oklch(24.16% 0.0153 256.8);
  --kpress-doc-surface-hover: color-mix(in srgb, var(--kpress-doc-muted) 85%, var(--kpress-doc-bg));
  --kpress-doc-surface-selected: color-mix(in srgb, var(--kpress-doc-link) 10%, transparent);
  --kpress-doc-success: oklch(72.27% 0.192 149.6);
  --kpress-doc-danger: oklch(59.30% 0.128 29);
  /* Deeper card shadow so the sheet still reads against the dark page. */
  --kpress-card-shadow:
    0 10px 15px -3px oklch(0.00% 0 0 / 0.3), 0 -2px 6px -1px oklch(0.00% 0 0 / 0.2);
}

:where([data-kpress-resolved-theme="dark"]) :is(.kpress, .kpress-page-main, .kpress-tooltip),
:is(.kpress, .kpress-page-main, .kpress-tooltip)[data-kpress-resolved-theme="dark"] {
  color-scheme: dark;
}

/* warm -- dark: same teal accent (lighter) over neutral's dark surfaces. Must
   override the SAME token set as the warm light block so warm light values
   never leak into dark. */
:root:where([data-kpress-resolved-theme="dark"][data-kpress-palette="warm"]),
:where([data-kpress-resolved-theme="dark"][data-kpress-palette="warm"])
  :is(.kpress, .kpress-page-main, .kpress-tooltip),
:is(
  .kpress,
  .kpress-page-main,
  .kpress-tooltip
)[data-kpress-resolved-theme="dark"][data-kpress-palette="warm"] {
  --kpress-doc-text: oklch(94.40% 0.01 208);
  --kpress-doc-muted: oklch(76.58% 0.02 208);
  --kpress-doc-border: oklch(33.73% 0.016 208);
  --kpress-doc-border-hairline: oklch(80.00% 0.007 208);
  --kpress-doc-link: oklch(74.90% 0.07 208);
  --kpress-doc-code-bg: oklch(24.16% 0.012 83);
}

/* ============================== WEBFONTS ============================== */

@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-display: block;
  font-weight: 400;
  src: url("../fonts/pt-serif-latin-400-normal.4271064a37f3ffc0.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

@font-face {
  font-family: "PT Serif";
  font-style: normal;
  font-display: block;
  font-weight: 700;
  src: url("../fonts/pt-serif-latin-700-normal.bf23a7a4eebedbb8.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

@font-face {
  font-family: "PT Serif";
  font-style: italic;
  font-display: block;
  font-weight: 400;
  src: url("../fonts/pt-serif-latin-400-italic.cb373bde18855c82.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

@font-face {
  font-family: "PT Serif";
  font-style: italic;
  font-display: block;
  font-weight: 700;
  src: url("../fonts/pt-serif-latin-700-italic.3cb3cfab3c562cbb.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

/* KPress Quotes: six glyphs of Source Serif 4 -- the straight quote and
   apostrophe, the curly singles, the curly doubles -- and nothing else. About
   700 bytes, generated by devtools/subset_quotes.py, and shipped rather than
   borrowed from the reader's Georgia so a document sets the same marks on every
   machine and a printed page embeds them. Provenance: static/fonts/README.md. */
@font-face {
  font-family: "KPress Quotes";
  src: url("../fonts/kpress-quotes.c1b4e25238045596.woff2") format("woff2");
  unicode-range: U+0022, U+0027, U+2018, U+2019, U+201C, U+201D;
  font-display: block;
}

@font-face {
  font-family: "Source Sans 3 Variable";
  font-style: normal;
  font-display: block;
  font-weight: 200 900;
  src: url("../fonts/source-sans-3-latin-wght-normal.7a19a7027e125257.woff2") format("woff2-variations");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

@font-face {
  font-family: "Source Sans 3 Variable";
  font-style: italic;
  font-display: block;
  font-weight: 200 900;
  src: url("../fonts/source-sans-3-latin-wght-italic.9a15dafc2c2b2414.woff2") format("woff2-variations");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

/* kpress: css/syntax.css */
/* Syntax palettes key on the resolved theme with the same grammar as the
   document palette in style-tokens.css: unkeyed default = light, then a
   symmetric keyed pair -- ancestor scope flattened with :where(), element
   scope at full strength -- so a stamped element is a coherent island and the
   mode attribute never keys CSS. */
.kpress,
:where([data-kpress-resolved-theme="light"]) .kpress,
.kpress[data-kpress-resolved-theme="light"] {
  --kpress-syntax-text: var(--kpress-doc-text);
  --kpress-syntax-comment: oklch(51.75% 0.0369 198.5);
  --kpress-syntax-error: oklch(50.03% 0.1821 29.5);
  --kpress-syntax-keyword: oklch(46.20% 0.1513 142.6);
  --kpress-syntax-operator: oklch(49.79% 0.0094 253.9);
  --kpress-syntax-punctuation: var(--kpress-doc-text);
  --kpress-syntax-deleted: oklch(45.46% 0.1713 13.7);
  --kpress-syntax-inserted: oklch(52.73% 0.1371 150.1);
  --kpress-syntax-heading: oklch(48.82% 0.2172 264.4);
  --kpress-syntax-output: oklch(55.10% 0.0234 264.4);
  --kpress-syntax-prompt: oklch(48.82% 0.2172 264.4);
  --kpress-syntax-type: oklch(45.87% 0.1697 3.8);
  --kpress-syntax-number: oklch(49.79% 0.0094 253.9);
  --kpress-syntax-string: oklch(49.53% 0.1543 24.8);
  --kpress-syntax-attribute: oklch(55.44% 0.0407 257.4);
  --kpress-syntax-builtin: oklch(50.81% 0.1049 165.6);
  --kpress-syntax-class: oklch(48.82% 0.2172 264.4);
  --kpress-syntax-constant: oklch(44.37% 0.1613 26.9);
  --kpress-syntax-decorator: oklch(54.13% 0.2466 293);
  --kpress-syntax-entity: oklch(55.10% 0.0234 264.4);
  --kpress-syntax-function: oklch(48.82% 0.2172 264.4);
  --kpress-syntax-label: oklch(53.77% 0.1172 109.8);
  --kpress-syntax-tag: oklch(46.20% 0.1513 142.6);
  --kpress-syntax-variable: oklch(39.84% 0.1773 277.4);
  --kpress-syntax-whitespace: oklch(74.52% 0.0164 257.2);
  --kpress-syntax-mark-bg: oklch(96.19% 0.058 95.6);
  --kpress-syntax-deleted-bg: oklch(93.56% 0.0309 17.7);
  --kpress-syntax-inserted-bg: oklch(96.24% 0.0434 156.7);
}

:where([data-kpress-resolved-theme="dark"]) .kpress,
.kpress[data-kpress-resolved-theme="dark"] {
  --kpress-syntax-text: oklch(94.25% 0.0111 243.7);
  --kpress-syntax-comment: oklch(73.29% 0.026 242.5);
  --kpress-syntax-error: oklch(73.45% 0.1626 25.8);
  --kpress-syntax-keyword: oklch(73.45% 0.1626 25.8);
  --kpress-syntax-operator: oklch(78.74% 0.1308 44.1);
  --kpress-syntax-punctuation: oklch(85.69% 0.0141 248);
  --kpress-syntax-deleted: oklch(80.14% 0.1134 25.8);
  --kpress-syntax-inserted: oklch(77.17% 0.188 145.5);
  --kpress-syntax-heading: oklch(78.57% 0.1153 246.7);
  --kpress-syntax-output: oklch(73.29% 0.026 242.5);
  --kpress-syntax-prompt: oklch(78.57% 0.1153 246.7);
  --kpress-syntax-type: oklch(73.45% 0.1626 25.8);
  --kpress-syntax-number: oklch(85.60% 0.0768 244.1);
  --kpress-syntax-string: oklch(85.60% 0.0768 244.1);
  --kpress-syntax-attribute: oklch(87.58% 0.0274 249.9);
  --kpress-syntax-builtin: oklch(84.16% 0.1641 145.8);
  --kpress-syntax-class: oklch(72.73% 0.1534 52.8);
  --kpress-syntax-constant: oklch(78.57% 0.1153 246.7);
  --kpress-syntax-decorator: oklch(80.05% 0.1275 305.9);
  --kpress-syntax-entity: oklch(79.91% 0.1412 60.1);
  --kpress-syntax-function: oklch(80.05% 0.1275 305.9);
  --kpress-syntax-label: oklch(78.57% 0.1153 246.7);
  --kpress-syntax-tag: oklch(84.16% 0.1641 145.8);
  --kpress-syntax-variable: oklch(78.57% 0.1153 246.7);
  --kpress-syntax-whitespace: oklch(66.25% 0.0181 250.9);
  --kpress-syntax-mark-bg: oklch(37.17% 0.0392 257.3);
  --kpress-syntax-deleted-bg: oklch(25.70% 0.102 28.5);
  --kpress-syntax-inserted-bg: oklch(38.94% 0.1019 148.4);
}

.kpress-code {
  color: var(--kpress-syntax-text);
}

.kpress-code .hll {
  background-color: var(--kpress-syntax-mark-bg);
}

.kpress-code .kpress-token-c,
.kpress-code .kpress-token-ch,
.kpress-code .kpress-token-cm,
.kpress-code .kpress-token-cpf,
.kpress-code .kpress-token-c1 {
  color: var(--kpress-syntax-comment);
  font-style: italic;
}

.kpress-code .kpress-token-cp,
.kpress-code .kpress-token-cs {
  color: var(--kpress-syntax-comment);
  font-style: italic;
  font-weight: 700;
}

.kpress-code .kpress-token-err,
.kpress-code .kpress-token-gr,
.kpress-code .kpress-token-gt {
  color: var(--kpress-syntax-error);
}

.kpress-code .kpress-token-k,
.kpress-code .kpress-token-kc,
.kpress-code .kpress-token-kd,
.kpress-code .kpress-token-kn,
.kpress-code .kpress-token-kr {
  color: var(--kpress-syntax-keyword);
  font-weight: 700;
}

.kpress-code .kpress-token-kp {
  color: var(--kpress-syntax-keyword);
}

.kpress-code .kpress-token-o,
.kpress-code .kpress-token-ow {
  color: var(--kpress-syntax-operator);
  font-weight: 700;
}

.kpress-code .kpress-token-gd {
  background-color: var(--kpress-syntax-deleted-bg);
  color: var(--kpress-syntax-deleted);
}

.kpress-code .kpress-token-gi {
  background-color: var(--kpress-syntax-inserted-bg);
  color: var(--kpress-syntax-inserted);
}

.kpress-code .kpress-token-ge {
  font-style: italic;
}

.kpress-code .kpress-token-ges {
  font-style: italic;
  font-weight: 700;
}

.kpress-code .kpress-token-gh,
.kpress-code .kpress-token-gp,
.kpress-code .kpress-token-gu {
  color: var(--kpress-syntax-heading);
  font-weight: 700;
}

.kpress-code .kpress-token-go {
  color: var(--kpress-syntax-output);
}

.kpress-code .kpress-token-gs {
  font-weight: 700;
}

.kpress-code .kpress-token-kt {
  color: var(--kpress-syntax-type);
}

.kpress-code .kpress-token-m,
.kpress-code .kpress-token-mb,
.kpress-code .kpress-token-mf,
.kpress-code .kpress-token-mh,
.kpress-code .kpress-token-mi,
.kpress-code .kpress-token-mo,
.kpress-code .kpress-token-il {
  color: var(--kpress-syntax-number);
}

.kpress-code .kpress-token-s,
.kpress-code .kpress-token-sa,
.kpress-code .kpress-token-sb,
.kpress-code .kpress-token-sc,
.kpress-code .kpress-token-dl,
.kpress-code .kpress-token-s2,
.kpress-code .kpress-token-sh,
.kpress-code .kpress-token-si,
.kpress-code .kpress-token-sr,
.kpress-code .kpress-token-s1,
.kpress-code .kpress-token-ss {
  color: var(--kpress-syntax-string);
}

.kpress-code .kpress-token-sd {
  color: var(--kpress-syntax-string);
  font-style: italic;
}

.kpress-code .kpress-token-se {
  color: var(--kpress-syntax-string);
  font-weight: 700;
}

.kpress-code .kpress-token-sx {
  color: var(--kpress-syntax-builtin);
}

.kpress-code .kpress-token-na {
  color: var(--kpress-syntax-attribute);
}

.kpress-code .kpress-token-nb,
.kpress-code .kpress-token-bp {
  color: var(--kpress-syntax-builtin);
}

.kpress-code .kpress-token-nc {
  color: var(--kpress-syntax-class);
  font-weight: 700;
}

.kpress-code .kpress-token-no {
  color: var(--kpress-syntax-constant);
}

.kpress-code .kpress-token-nd {
  color: var(--kpress-syntax-decorator);
  font-weight: 700;
}

.kpress-code .kpress-token-ni {
  color: var(--kpress-syntax-entity);
  font-weight: 700;
}

.kpress-code .kpress-token-ne {
  color: var(--kpress-syntax-class);
  font-weight: 700;
}

.kpress-code .kpress-token-nf,
.kpress-code .kpress-token-fm {
  color: var(--kpress-syntax-function);
  font-weight: 700;
}

.kpress-code .kpress-token-nl {
  color: var(--kpress-syntax-label);
}

.kpress-code .kpress-token-nn {
  color: var(--kpress-syntax-keyword);
  font-weight: 700;
}

.kpress-code .kpress-token-nt {
  color: var(--kpress-syntax-tag);
  font-weight: 700;
}

.kpress-code .kpress-token-nv,
.kpress-code .kpress-token-vc,
.kpress-code .kpress-token-vg,
.kpress-code .kpress-token-vi,
.kpress-code .kpress-token-vm {
  color: var(--kpress-syntax-variable);
}

.kpress-code .kpress-token-p,
.kpress-code .kpress-token-pm {
  color: var(--kpress-syntax-punctuation);
}

.kpress-code .kpress-token-n,
.kpress-code .kpress-token-nx {
  color: var(--kpress-syntax-text);
}

.kpress-code .kpress-token-w {
  color: var(--kpress-syntax-whitespace);
}

/* kpress: css/document.css */
.kpress {
  background: var(--kpress-doc-bg);
  box-sizing: border-box;
  color: var(--kpress-doc-text);
  font-family: var(--kpress-font-body, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-regular);
  /* Allow height transitions to/from `auto` (e.g. <details> expansion). */
  interpolate-size: allow-keywords;
  line-height: var(--kpress-line-height-normal, 1.5);
  overflow-wrap: break-word;
}

.kpress *,
.kpress *::before,
.kpress *::after {
  box-sizing: inherit;
}

/* Cap #1 of the width chain (see components.css "the width chain"): the article
   holds the reading column plus its inset plus the page gutter. Every term is
   named, because the failure this fixes was a cap that added some of them and
   not others -- the measure then meant "text" at one width and "text minus
   padding" at another. box-sizing is border-box (.kpress above), so the gutter
   belongs inside the cap. */
.kpress-doc {
  margin: 0 auto;
  max-width: min(
    100%,
    calc(var(--kpress-measure) + 2 * var(--kpress-column-inset) + 2 * var(--kpress-doc-gutter))
  );
  padding: 2rem var(--kpress-doc-gutter) 3rem;
}

.kpress-print-surface {
  background: var(--kpress-doc-bg);
}

.kpress-doc-header,
.kpress-source-header {
  border-bottom: 1px solid var(--kpress-doc-border);
  margin-bottom: 1.25rem;
  padding-bottom: 0.75rem;
}

.kpress-metadata {
  color: var(--kpress-doc-text);
}

.kpress-doc-header h1,
.kpress-source-header h1 {
  font-family: var(--kpress-font-prose, Georgia, serif);
  font-size: clamp(
    calc(var(--kpress-font-size-base) * 1.75),
    4.5vw,
    calc(var(--kpress-font-size-base) * 2.3)
  );
  font-weight: var(--_kpress-font-weight-prose);
  line-height: 1.12;
  margin: 0;
}

.kpress-source-meta {
  color: var(--kpress-doc-muted);
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-regular);
  margin: 0.5rem 0 0;
}

.kpress-frontmatter-error,
.kpress-source-truncation-warning {
  background: color-mix(in srgb, var(--kpress-doc-danger) 8%, var(--kpress-doc-bg));
  border: 1px solid color-mix(in srgb, var(--kpress-doc-danger) 42%, var(--kpress-doc-border));
  border-radius: var(--kpress-radius-sm);
  color: color-mix(in srgb, var(--kpress-doc-danger) 84%, var(--kpress-doc-text));
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-size: var(--kpress-font-size-small);
  line-height: 1.35;
  margin: 1rem 0;
  padding: 0.65rem 0.8rem;
}

.kpress-frontmatter-error strong,
.kpress-source-truncation-warning strong {
  display: block;
  font-weight: var(--kpress-font-weight-sans-bold, 650);
  margin-bottom: 0.15rem;
}

/* Cap #2 of the width chain: the column box, text plus its inset. Capping this
   at the bare measure is what clamped the column below the measure in the
   single-column bands -- the inset was then subtracted from a box already
   sized to the text. */
.kpress-doc-layout {
  margin: 0 auto;
  max-width: calc(var(--kpress-measure) + 2 * var(--kpress-column-inset));
}

.kpress-prose {
  font-family: var(--kpress-font-prose, Georgia, serif);
  font-size: var(--kpress-font-size-normal);
  font-weight: var(--_kpress-font-weight-prose);
  line-height: var(--kpress-line-height-normal, 1.5);
}

.kpress-prose h1,
.kpress-prose h2,
.kpress-prose h3,
.kpress-prose h4,
.kpress-prose h5,
.kpress-prose h6 {
  font-feature-settings: var(--kpress-font-features-sans, normal);
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-bold, 650);
  line-height: var(--kpress-line-height-tight, 1.2);
}

.kpress-prose h1 {
  font-family: var(--kpress-font-prose, Georgia, serif);
  font-size: calc(var(--kpress-font-size-base) * 1.7);
  font-weight: var(--_kpress-font-weight-prose);
  margin: 2rem 0 1rem;
}

.kpress-prose h2 {
  font-family: var(--kpress-font-prose, Georgia, serif);
  font-size: var(--kpress-font-size-h2);
  font-style: italic;
  font-weight: var(--_kpress-font-weight-prose);
  margin: 2rem 0 1rem;
}

.kpress-prose h3 {
  font-size: calc(var(--kpress-font-size-h3) * var(--kpress-caps-heading-size-multiplier, 1));
  font-variant-caps: var(--kpress-caps-variant, normal);
  font-weight: var(--kpress-font-weight-sans-medium, 550);
  letter-spacing: var(--kpress-caps-spacing, 0);
  line-height: var(--kpress-caps-heading-line-height, 1.2);
  margin: 1.4rem 0 0.7rem;
  text-transform: var(--kpress-caps-transform, none);
}

.kpress-prose h4 {
  font-size: var(--kpress-font-size-h4);
  font-style: italic;
  font-weight: 540;
  letter-spacing: 0.015em;
  margin: 1rem 0 0.7rem;
}

.kpress-prose h5 {
  font-family: var(--kpress-font-prose, Georgia, serif);
  font-size: var(--kpress-font-size-base);
  font-weight: 700;
  margin: 0.7rem 0 0.5rem;
}

.kpress-prose h6 {
  font-family: var(--kpress-font-prose, Georgia, serif);
  font-size: var(--kpress-font-size-base);
  font-style: italic;
  font-weight: var(--_kpress-font-weight-prose);
  margin: 0.7rem 0 0.5rem;
}

.kpress-prose h2 + h3 {
  margin-top: 1.1rem;
}

.kpress-prose p,
.kpress-prose pre {
  margin-block: 0.75rem;
}

/* Tighten the gap after a low-level heading (declared after `p` so the
   higher-specificity adjacent selector is not a descending-specificity
   override). */
.kpress-prose h4 + p,
.kpress-prose h5 + p,
.kpress-prose h6 + p {
  margin-top: 0;
}

.kpress-prose ul,
.kpress .concepts ul {
  list-style-type: none;
  margin-bottom: 0.7rem;
  margin-left: 1.8rem;
  padding-left: 0;
}

.kpress-prose li,
.kpress .concepts li {
  margin-block-start: 0.7rem;
  position: relative;
}

.kpress-prose li > p {
  margin-block-end: 0;
}

/* The square is DRAWN, not set. It used to be the glyph U+25AA, which no face
   KPress ships carries: it fell down the prose stack to whatever the reader had
   (Georgia on a Mac, something else elsewhere) and put 16 KB of embedded Georgia
   into a printed PDF for 48 bullets. A currentColor box depends on no font and
   is the same mark on every machine.

   The numbers reproduce what Georgia drew, measured in Chromium at a 16px base:
   a 3.255 x 3.255 px square whose ink began 0.921px right of the pen and whose
   centre sat 11.051px left of and 13.143px below the item's top-left corner.
   0.226em of the marker size is that square; the two added offsets move the box
   from the old text origin onto the ink the glyph actually put there, and both
   are in em of the marker size, so the same pair serves the print rule at its
   smaller size. See print.css and the .claim rule in components.css.

   One consequence beyond the face: .claim's marker used to come out 60% larger
   than this one, because its stack is the sans and a different fallback answered
   it. All three bulleted lists now draw the one square --kpress-bullet-size
   describes.

   The inline offset is logical, not physical. The glyph used `left`, and so did
   this box at first, which put the marker on the far side of a right-to-left
   item and pointed the 0.064em ink nudge away from the text instead of toward
   it. `inset-inline-start` resolves to `right` under `dir="rtl"` and the nudge
   follows it, so one pair of numbers serves both directions.

   Forced colors need their own rule; components.css carries it, because that
   file loads later and .claim's marker rule lives there. */
.kpress-prose ul > li::before,
.kpress .concepts ul > li::before {
  background: currentColor;
  content: "";
  font-size: var(--kpress-bullet-size);
  height: 0.226em;
  inset-inline-start: calc(var(--kpress-font-size-base) * -0.85 + 0.064em);
  position: absolute;
  top: calc(var(--kpress-font-size-base) * 0.1 + 0.689em);
  width: 0.226em;
}

.kpress-prose ol {
  list-style-type: decimal;
  margin-bottom: 0.7rem;
  margin-left: 1.8rem;
  padding-left: 0;
}

.kpress-prose ol > li {
  padding-left: 0.25rem;
}

.kpress a {
  color: var(--kpress-doc-link);
  text-decoration: none;
  transition: color var(--kpress-transition-fast);
}

.kpress a:hover {
  color: var(--color-primary-light);
  text-decoration: underline;
}

/* GFM strikethrough: keep the text legible (body colour) but draw the line in the
   muted tone so it reads as struck-through without the heavy dark rule. */
.kpress del,
.kpress s {
  text-decoration-color: var(--kpress-doc-muted);
}

.kpress img,
.kpress-image {
  height: auto;
  margin-block: 1rem;
  max-width: 100%;
}

.kpress-figure {
  margin: 1.5rem auto;
  text-align: center;
}

.kpress-figure .kpress-image {
  display: block;
  margin: 0 auto;
}

.kpress-figcaption {
  color: var(--kpress-doc-muted);
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-size: var(--kpress-font-size-small);
  line-height: 1.35;
  margin-block-start: 0.55rem;
}

.kpress .thumbnail {
  border-radius: var(--kpress-radius-lg);
  display: block;
  margin: 1rem auto 2rem;
  max-height: 24rem;
  object-fit: contain;
}

.kpress blockquote {
  margin: 1.5rem 4rem 1.5rem 1rem;
  padding-inline-start: 1rem;
}

.kpress code,
.kpress-code {
  background: var(--kpress-doc-surface-bg);
  font-family: var(--kpress-font-mono, ui-monospace, monospace);
  font-size: var(--kpress-font-size-mono);
  letter-spacing: -0.025em;
}

.kpress code {
  transition: color var(--kpress-transition-slow);
}

.kpress code:not(pre code) {
  border: 1px solid var(--color-hint-gentle);
  border-radius: var(--kpress-radius-sm);
  padding: 0.175em 0.2em;
}

.kpress-code {
  border: 1px solid var(--color-hint-gentle);
  border-radius: var(--kpress-radius-none);
  line-height: 1.5;
  margin: 0;
  overflow-x: auto;
  overflow-wrap: normal;
  padding: 0.45rem 0.5rem;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
  transition:
    background-color var(--kpress-transition-slow),
    border-color var(--kpress-transition-slow);
  white-space: pre;
}

.kpress-source {
  margin: 0;
  padding: 1.25rem;
}

.kpress-frontmatter {
  border: 1px solid var(--kpress-doc-border);
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-regular);
  margin: 1rem 0;
}

.kpress-frontmatter dl {
  display: grid;
  gap: 0.35rem 0.75rem;
  grid-template-columns: max-content minmax(0, 1fr);
}

/* Distinguish keys from values: keys are medium-weight in the document text
   color, values are muted. Without this both render in the same tertiary grey
   (see the .kpress .kpress-frontmatter rule below), which reads as unstyled. */
.kpress-frontmatter dt {
  color: var(--kpress-doc-text);
  font-weight: var(--kpress-font-weight-sans-medium, 550);
}

.kpress-frontmatter dd {
  color: var(--kpress-doc-muted);
  margin: 0;
}

.kpress details {
  border: 1px solid var(--kpress-doc-border);
  color: var(--kpress-doc-text);
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans, normal);
  margin: 0.75rem 0;
}

/* Disclosure summary: a single background (no two-tone header) plus the canonical
   chevron toggle below. The label text uses the shared small-caps section-label
   treatment — same muted color, caps variant, weight, spacing, and size formula as
   `.kpress-toc-title` (all driven by the `--kpress-caps-*` tokens) so every toggle
   label and the TOC read as one design language. */
.kpress summary {
  align-items: center;
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: flex;
  font-family: var(--kpress-font-sans, system-ui, sans-serif);
  font-size: var(--kpress-caps-label-size);
  font-variant-caps: var(--kpress-caps-variant);
  font-weight: var(--kpress-font-weight-sans-medium);
  gap: 0.4rem;
  letter-spacing: var(--kpress-caps-spacing);
  line-height: var(--kpress-caps-heading-line-height);
  list-style: none;
  padding: 0.5rem 0.75rem;
  text-transform: var(--kpress-caps-transform);
  transition: color var(--kpress-transition-fast);
  user-select: none;
}

.kpress summary::-webkit-details-marker {
  display: none;
}

/* Canonical disclosure toggle: the shared Lucide `chevron-right` glyph (drawn via
   a mask so it inherits the summary color), rotated 90deg when open. One treatment
   for every <details> in KPress; host apps can mirror it for their own chrome. */
.kpress summary::before {
  background-color: currentColor;
  content: "";
  flex: none;
  /* Slightly smaller than the cap height, nudged down via `top` (not transform,
     which the open-state rotate owns) so the glyph sits on the label baseline. */
  height: 0.85em;
  mask:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E")
    center / contain no-repeat;
  position: relative;
  top: 0.06em;
  transition: transform var(--kpress-transition-fast);
  width: 0.85em;
}

.kpress details[open] summary::before {
  transform: rotate(90deg);
}

.kpress summary:hover {
  color: var(--color-primary-light);
}

.kpress details > :not(summary) {
  padding: 0.25rem 0.75rem 0.5rem;
}

.kpress details[open] summary {
  border-bottom: 1px solid var(--kpress-doc-border);
}

.kpress summary:focus-visible {
  outline: 3px solid var(--kpress-doc-link);
  outline-offset: 2px;
}

/* Gently animate expansion where the browser supports it; older browsers open
   instantly. `interpolate-size` lets the height transition to/from `auto`. */
.kpress details::details-content {
  block-size: 0;
  overflow: hidden;
  transition:
    block-size var(--kpress-transition-fast),
    content-visibility var(--kpress-transition-fast);
  transition-behavior: allow-discrete;
}

.kpress details[open]::details-content {
  block-size: auto;
}

.kpress details.metadata,
.kpress .kpress-frontmatter {
  color: var(--color-secondary);
}

.kpress details.metadata summary,
.kpress .kpress-frontmatter summary {
  color: var(--color-secondary);
  font-size: var(--kpress-caps-label-size);
  font-weight: var(--kpress-font-weight-sans-medium, 550);
  font-variant-caps: var(--kpress-caps-variant);
  letter-spacing: var(--kpress-caps-spacing);
  line-height: var(--kpress-caps-heading-line-height);
  text-transform: var(--kpress-caps-transform);
}

.kpress hr {
  border: 0;
  height: 1.5rem;
  margin: 0.5rem auto;
  overflow: visible;
  position: relative;
  text-align: center;
}

.kpress hr::before {
  border-top: 1px solid var(--kpress-doc-muted);
  content: "";
  display: block;
  left: calc(50% - 2rem);
  position: absolute;
  top: 50%;
  width: 4rem;
}

.kpress b,
.kpress strong {
  font-weight: var(--kpress-font-weight-sans-bold, 650);
}

.kpress ::selection {
  background-color: var(--color-selection);
  color: inherit;
}

/* Superscripts (footnote markers most of all) must never change the line box:
   the UA default `vertical-align: super` raises the sup's inline box and
   stretches any line that carries a marker, which reads as ragged leading on
   phones and in print. `line-height: 0` takes the sup out of line-box sizing
   entirely and the relative `top` nudge recreates the raised position
   visually, so lines with and without markers are exactly the same height at
   every pane width and on paper. Same treatment for sub, mirrored downward. */
.kpress sup,
.kpress sub {
  font-size: 85%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

.kpress sup {
  top: -0.45em;
}

.kpress sub {
  top: 0.3em;
}

/* kpress: css/components.css */
/* The document viewport: the one element a KPress document scrolls inside.
   It is the `@container kpress-doc` query container. The standalone page shell
   sets it on `kpress-page-main`; an embedding host sets the same class/attribute
   on its own scroll pane. This is what lets the TOC react to the document's real
   width.

   CRITICAL: the viewport must NOT be a containing block for `position: fixed`
   descendants — no transform/filter/perspective/backdrop-filter here. A
   transformed ancestor makes fixed descendants behave like `position: absolute`,
   and absolutely-positioned children of a scroller move with the scrolled
   content: every piece of floating UI (TOC toggle/drawer/backdrop, settings
   menu) would sit at the top of the *document* and scroll away exactly when it
   should appear. The fixed containing block lives one level up, on the
   non-scrolling `.kpress-frame`. */
.kpress-viewport {
  block-size: 100dvh;
  overflow-y: auto;
  container: kpress-doc / inline-size;
  /* The containing block for the popover layer: footnote and link previews are
     appended here and placed with `position: absolute` in PAGE coordinates, so
     they scroll with the paragraph they belong to (tooltips.js,
     `tooltipParent`). `position: relative` is not one of the properties the
     warning above names: it does NOT make this a containing block for
     `position: fixed` descendants, so that rule stands unchanged. */
  position: relative;
  /* In-document hash navigation (TOC and section links) is native browser
     navigation; the glide lives here so it also honors the reduced-motion
     override below. */
  scroll-behavior: smooth;
}

/* The document frame: a non-scrolling wrapper of the viewport that pins the
   floating UI. `transform` is the universally-honored trigger that makes it the
   containing block for `position: fixed` descendants; because the frame does not
   scroll, the floating UI stays pinned to its box while the viewport's content
   scrolls beneath it. Standalone, the page shell marks `<body>` (whose box is
   exactly the window). An embedding host puts this class/attribute on a
   non-scrolling wrapper of its scroll pane so the floating UI pins to the pane
   instead of escaping over the host's own chrome; a host that omits it gets
   window-pinned floating UI. */
.kpress-frame {
  transform: translateZ(0);
}

/* The standalone page scroller carries the document background/text so the
   whole window is themed (the embedded host owns its own pane background, so
   this targets the standalone-only `.kpress-page-main`, not `.kpress-viewport`). */
.kpress-page-main {
  background: var(--kpress-doc-bg);
  color: var(--kpress-doc-text);
  padding-inline: var(--kpress-page-margin-inline);
  padding-block-start: var(--kpress-page-margin-block-start);
}

/* Standalone pages on phones: the 2rem gutter is a third of the padding stack;
   narrow it so the reading column keeps its width. */
@media (max-width: 767px) {
  .kpress-page-main {
    --kpress-page-margin-inline: 0.5rem;
  }
}

/* ─────────────────────────────────────────────────────────────────
   Document layout + Table of Contents: the responsive system.

   READ THIS BEFORE TOUCHING THE TOC OR TABLE WIDTHS — the pieces are
   coupled and the failure modes are non-obvious.

   DOM (emitted by format/render.py):
     article.kpress-doc                       reading-measure cap #1
       div.kpress-doc-layout.kpress-content-with-toc   cap #2 + TOC grid
         nav.kpress-toc                        the TOC (sidebar OR drawer)
         div.kpress-prose.kpress-long-text     the article body

   THE RAIL IS A LAYOUT SLOT, NOT A CONSEQUENCE OF THE TOC. Three conditions
   select the wide band's two-column grid, and every wide-band rule that keys
   off the TOC must list all three or the layouts split apart:
     .has-toc                              host-forced (embedding escape hatch)
     :has(.kpress-toc)                     a TOC was rendered
     [data-kpress-toc-rail="reserved"]     rail held open, no TOC rendered
   The third is stamped by render.py when RenderOptions.toc_rail = "reserved"
   and the document fell under toc_min_headings. Without it such a document
   drops out of the grid and back to the centred single column — which moves
   the prose sideways AND narrows it (the unguarded 2.5rem
   `.kpress-content-with-toc .kpress-long-text` inset is sized for the 53rem
   grid track, so against the 48rem cap it eats 5rem of measure). Under
   toc_rail "auto" (the default) nothing is stamped and that fallback stands.

   THE WIDTH CHAIN. `--kpress-measure` is the width of the TEXT, padding
   excluded. Three nested elements bound the column, and each adds back exactly
   the padding that sits between it and the text:

     .kpress-doc         measure + 2x inset + 2x gutter   (document.css)
     .kpress-doc-layout  measure + 2x inset               (document.css)
     .kpress-long-text   measure + 2x inset, padded by the inset

   so the text lands at the measure in every band. `--kpress-column-inset` is
   the band's inline padding inside the column (1.5rem phone / 4rem single
   column / 2.5rem in the wide TOC grid) and `--kpress-doc-gutter` is the page
   margin outside it; the bands reset those two tokens on `.kpress-doc` and the
   caps follow. Nothing in the chain restates a length the tokens already carry.

   Write any new cap the same way. The bug this shape replaced was three caps
   that each added a different subset of the padding: `.kpress-long-text` used a
   literal `48rem` that duplicated the measure and omitted the inset, so the
   same token meant "the text" in the wide band and "the text plus 8rem" in the
   single-column one — a 10rem step in reading width across the 75rem boundary —
   and a host that set the token moved almost nothing, because two of the three
   caps and the grid track never read it.

   All three cap elements centre via margin:auto. Plain single-column reading
   wants all of them. Any layout that needs the full pane must uncap the outer
   two — uncapping only the article leaves `.kpress-doc-layout` centred at the
   measure, which reads as a large left margin with the grid tracks spilling off
   its right edge.

   Everything responsive keys off `@container kpress-doc` — the width of the
   scroll pane (`.kpress-viewport`), NOT the window — so it is correct both
   standalone and embedded in a host pane narrower than the window. Three
   mutually exclusive bands, each defined once below:

     >= 75rem   WIDE     TOC is a sticky left sidebar. Uncaps the outer two
                         caps; grid is `15rem <column box>`, left-aligned
                         (justify-content: start) so the reading text is a
                         constant measure and all extra width is one right-hand
                         margin. Column inset 2.5rem.
     48-75rem   TABLET   Single column; TOC is a fixed drawer + toggle.
                         Column inset 4rem.
     < 48rem    MOBILE   Single column; drawer; tighter type. Column inset
                         1.5rem.

   The band conditions are literal lengths because a container query cannot read
   var(). They are therefore the one part of this system that does NOT follow
   the measure: a host that changes the measure changes the reading width, not
   where the bands switch. The wide band's track carries a `min()` guard so an
   over-large measure degrades instead of overflowing the pane at the floor.

   TABLE INVARIANT: only tables marked `data-kpress-table-scale="wide"`
   (stamped by the renderer / tables.js past the TABLE_WIDE_MIN_* cutoff)
   ever leave the reading column; unmarked tables cap at the column in
   every band. The base wide rule (further down) is a column-centred
   full-bleed — left:50% + translateX(-50%), width keyed off 100cqw so it
   caps at the kpress-doc pane, never the window. That centred bleed is
   only right for a centred single column (with or without a pane
   narrower than the window). Bands that left-align the column (the
   wide-band TOC grid) or want internal scrolling instead MUST neutralise
   it (left:0; transform:none; a left-anchored width) AT OR ABOVE THE
   ATTRIBUTE SELECTOR'S SPECIFICITY (list the
   [data-kpress-table-scale="wide"] variant in the reset) or wide tables
   centre on the column and overhang the layout. All three bands carry
   that reset today; if you add a band, add the reset too.
   ───────────────────────────────────────────────────────────────── */
.kpress-content-with-toc {
  position: relative;
}

.kpress-content-with-toc.has-toc,
.kpress-content-with-toc:has(.kpress-toc) {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

/* Cap #3 of the width chain: the column itself. max-width is a border box, so
   it is the measure PLUS the inset; the padding then gives the measure back as
   text. The two must be written from the same token or they drift -- this rule
   previously capped at a literal `48rem` that duplicated the measure and did
   not add the inset, so the text came out one inset-pair narrower than the
   measure asked for, and the literal ignored a host that set the token. */
.kpress-long-text {
  background: var(--kpress-doc-bg);
  margin-inline: auto;
  max-width: min(100%, calc(var(--kpress-measure) + 2 * var(--kpress-column-inset)));
  padding: 1rem var(--kpress-column-inset);
}

/* Container, not viewport: the phone band that undoes this
   (`@container kpress-doc (max-width: 47.99rem)`, far below) keys off the
   scroll pane, so a viewport query here disagrees with it whenever the two
   differ — which is every embedded host. 48rem is the same boundary from the
   other side. */
@container kpress-doc (min-width: 48rem) {
  /* Set on .kpress-doc, not on the column: all three caps in the chain read the
     inset, so a band that changed only the column's padding would move the text
     inside a box the outer two still sized for 1.5rem. */
  .kpress-doc {
    --kpress-column-inset: 4rem;
  }
}

/* Content card: the reading column rendered as a bordered sheet floating
   over the page — textpress's long-text card (hairline + soft shadow at md+;
   narrow screens stay full-bleed, same as textpress). The geometry is
   ALREADY the card's (.kpress-long-text's 48rem cap and md 4rem inline
   padding above are kash's max-w-3xl + md:px-16), so this adds chrome only —
   no layout-affecting properties, which keeps the coupled TOC/table width
   system untouched. Render-time switch: RenderOptions.content_card /
   `format.content_card` stamps data-kpress-card on the article ("on" is the
   default); embedding hosts stamp the same attribute on the fragment wrapper
   themselves. Chrome tokens: --kpress-card-border / --kpress-card-shadow
   (style-tokens.css).

   Container-queried at the same 48rem boundary as the inline padding above,
   and for the same reason: the card's whole premise is that the geometry is
   already the card's. Keyed to the viewport instead, a host pane narrower
   than 48rem inside a window wider than 768px drew the border around the
   phone band's 0.75rem gutter — a hairline pressed against the text and
   through the disclosure chevrons. Narrow stays full-bleed, as textpress
   does; "narrow" just has to mean the pane, not the window. */
@container kpress-doc (min-width: 48rem) {
  .kpress[data-kpress-card="on"] .kpress-long-text {
    border: var(--kpress-card-border);
    box-shadow: var(--kpress-card-shadow);
    /* Breathing room around the text: vertical only here (inline padding is
       the band system's business — 4rem single-column, 2.5rem in the wide
       TOC track — so the card never touches the coupled width math). */
    padding-block: 2.5rem 3rem;
  }

  /* Breathing room below the card before the page (or footer) ends. On the
     article so it composes with .kpress-doc's own padding; vertical only. */
  .kpress-doc[data-kpress-card="on"] {
    padding-block-end: 6rem;
  }
}

.kpress-header-row {
  align-items: center;
  display: flex;
}

.kpress-header-grow {
  flex: 1 1 auto;
  margin-inline-start: 0.5rem;
}

.kpress-header-actions {
  align-items: center;
  display: flex;
  gap: 0.5rem;
  margin-inline-start: auto;
}

.kpress-page-spacer {
  margin-top: 1.5rem;
}

/* Settings menu: a gear button that opens a popover of segmented icon
   choosers (theme today; more can be added). Ported from the original host
   app's settings control, re-tokenized onto KPress document tokens so it
   themes with the document. The wrapper's `aria-expanded` drives menu visibility. */
.kpress-settings {
  inset-block-start: var(--kpress-settings-inset-block);
  inset-inline-end: var(--kpress-settings-inset-inline);
  position: fixed;
  z-index: 250;
}

.kpress-settings-btn {
  align-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: inline-flex;
  height: 28px;
  justify-content: center;
  padding: 4px;
  transition:
    color var(--kpress-transition-fast),
    background var(--kpress-transition-fast);
  width: 28px;
}

.kpress-settings-btn:hover,
.kpress-settings[aria-expanded="true"] .kpress-settings-btn {
  background: var(--color-hover-bg);
  color: var(--kpress-doc-text);
}

.kpress-settings-btn:focus-visible {
  outline: 3px solid var(--color-primary-light);
  outline-offset: 2px;
}

.kpress-settings-btn svg {
  height: 16px;
  width: 16px;
}

.kpress-settings-menu {
  display: none;
  inset-block-start: calc(100% + 4px);
  inset-inline-end: 0;
  position: absolute;
}

/* Document actions (doc-actions widget; off by default, opted in via
   `format.widgets: {doc-actions: on}`): "PDF" exports via the browser print
   dialog, "MD" opens the Markdown twin. Two-layer anatomy, mirroring the
   other chrome controls:

   - The BUTTON is the interaction layer only — the settings gear's quiet
     28px hit target and color states (muted -> text on the shared hover
     surface), no typography of its own.
   - The BADGE is the "icon" — the format letters, hand-drawn from type: the
     caps-label idiom one notch below the TOC "Contents" label, inside a
     tight 1px currentColor frame so the frame always matches the letter
     color through every state. Size and corner are the
     --kpress-doc-actions-badge-* tokens (square by default).

   Placement: on card documents the widget relocates its mount into
   .kpress-long-text and these rules pin it to the card's upper-right corner
   (the placement embedding hosts use for their own document actions);
   without a card it stays fixed at the inset tokens (host-overridable, see
   style-tokens.css). */
.kpress-doc-actions {
  display: flex;
  gap: 0.25rem;
  inset-block-start: var(--kpress-doc-actions-inset-block);
  inset-inline-end: var(--kpress-doc-actions-inset-inline);
  position: fixed;
  z-index: 250;
}

.kpress-long-text:has(.kpress-doc-actions) {
  position: relative;
}

/* A body without a leading in-card H1 has no heading margin to clear the
   absolute action row. Reserve that row before paragraph-first content. */
.kpress-long-text:has(.kpress-doc-actions):not(:has(> h1:first-child)) {
  padding-block-start: 3rem;
}

.kpress-long-text .kpress-doc-actions {
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  position: absolute;
}

.kpress-doc-actions-btn {
  align-items: center;
  background: transparent;
  border: none;
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: inline-flex;
  height: 28px;
  justify-content: center;
  min-width: 28px;
  padding: 0 4px;
  text-decoration: none;
  transition:
    color var(--kpress-transition-fast),
    background var(--kpress-transition-fast);
}

.kpress-doc-actions-btn:hover {
  background: var(--color-hover-bg);
  color: var(--kpress-doc-text);
}

.kpress-doc-actions-badge {
  border: 1px solid currentColor;
  border-radius: var(--kpress-doc-actions-badge-radius);
  font-family: var(--kpress-font-sans);
  font-size: var(--kpress-doc-actions-badge-size);
  font-weight: var(--kpress-font-weight-sans-medium);
  letter-spacing: var(--kpress-caps-spacing);
  line-height: 1;
  padding: 2px 3px;
}

/* The MD badge is an <a>: when the cluster relocates into the card it falls
   under `.kpress a` link styling, which out-specifies the bare class — keep
   the badge quiet (muted, never underlined) at higher specificity. */
.kpress a.kpress-doc-actions-btn {
  color: var(--kpress-doc-muted);
  text-decoration: none;
}

.kpress a.kpress-doc-actions-btn:hover {
  color: var(--kpress-doc-text);
  text-decoration: none;
}

.kpress-doc-actions-btn:focus-visible {
  outline: 3px solid var(--color-primary-light);
  outline-offset: 2px;
}

.kpress-settings[aria-expanded="true"] .kpress-settings-menu {
  display: flex;
}

.kpress-menu {
  background: var(--kpress-doc-bg);
  border: 1px solid var(--kpress-doc-border);
  border-radius: var(--kpress-radius-sm);
  box-shadow:
    0 4px 6px -1px oklch(0.00% 0 0 / 0.1),
    0 2px 4px -1px oklch(0.00% 0 0 / 0.06);
  flex-direction: column;
  min-width: 124px;
  padding: 4px;
  z-index: 300;
}

.kpress-menu-chooser {
  display: flex;
  gap: 2px;
}

.kpress-menu-select {
  background: var(--kpress-doc-bg);
  border: 1px solid var(--kpress-doc-border);
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-text);
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-size: var(--kpress-font-size-tiny);
  padding: 4px 6px;
  width: 100%;
}

/* Stacked chooser groups read as one menu; give successive groups a little
   separation. The native select (font-set chooser) inherits document tokens.
   These adjacency selectors come after the base `.kpress-menu-select` above so
   specificity ascends in source order (biome noDescendingSpecificity). */
.kpress-menu-chooser + .kpress-menu-chooser,
.kpress-menu-chooser + .kpress-menu-select,
.kpress-menu-select + .kpress-menu-chooser {
  margin-top: 4px;
}

.kpress-menu-seg {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: flex;
  flex: 1;
  height: 30px;
  justify-content: center;
  min-width: 34px;
  padding: 0;
  transition:
    color var(--kpress-transition-fast),
    background var(--kpress-transition-fast);
}

.kpress-menu-seg:hover,
.kpress-menu-seg:focus-visible {
  background: var(--color-hover-bg);
  color: var(--kpress-doc-text);
  outline: none;
}

.kpress-menu-seg[aria-checked="true"] {
  background: var(--color-bg-selected);
  color: var(--kpress-doc-link);
}

.kpress-menu-seg svg {
  height: 16px;
  width: 16px;
}

.kpress-toc {
  border: 1px solid var(--kpress-doc-border);
  color: var(--kpress-doc-muted);
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-smaller);
  font-variant-numeric: tabular-nums;
  margin: 0 0 1.5rem;
  padding: 0.5rem 0.7rem 1rem 1.2rem;
}

.kpress-toc ol {
  line-height: 1.2;
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
}

.kpress-toc a {
  border-left: 2px solid transparent;
  color: inherit;
  display: block;
  padding-block: 0.2rem;
  text-decoration: none;
  /* Only the hover/active properties. Never `all`: a late-applied stylesheet
     (slow or failed sheet, host style injection) must snap into place, not
     visibly animate every property. */
  transition:
    color var(--kpress-transition-fast),
    background-color var(--kpress-transition-fast),
    border-color var(--kpress-transition-fast);
}

/* Hover and active both shift the text to the secondary tone; the colored
   left indicator (plus the selected background) is what marks the active
   section. The 2px transparent border above keeps text from shifting when
   active. */
.kpress-toc a:hover {
  background-color: var(--color-hover-bg);
  color: var(--color-secondary);
  text-decoration: none;
}

.kpress-toc a[data-active="true"],
.kpress-toc a.active {
  background-color: var(--color-bg-selected);
  border-left-color: var(--kpress-doc-link);
  color: var(--color-secondary);
  text-decoration: none;
}

/* Each entry indents by heading level. The padding sits on the link (the
   element carrying the active border-left bar) so the bar pins to the
   container edge while the text indents away from it; the -1em hanging
   indent keeps wrapped lines aligned under the first. Deeper levels indent
   further and lighten in weight, echoing the document's heading hierarchy. */
.kpress-toc-level-1 > a,
.kpress-toc-level-2 > a,
.kpress-toc-level-3 > a,
.kpress-toc-level-4 > a,
.kpress-toc-level-5 > a,
.kpress-toc-level-6 > a {
  text-indent: -1em;
}

/* Two-level weight hierarchy, matching textpress (toc-h2=550 medium, toc-h3=370
   light). kpress normalizes TOC levels to structural tree depths, so the top
   content level is level-1 (typically the document's h2): it gets medium;
   everything deeper gets light. (textpress keys the same two weights off raw
   h2/h3.) */
.kpress-toc-level-1 > a {
  font-weight: var(--kpress-font-weight-sans-medium);
  letter-spacing: 0.007em;
}

.kpress-toc-level-2 > a,
.kpress-toc-level-3 > a,
.kpress-toc-level-4 > a,
.kpress-toc-level-5 > a,
.kpress-toc-level-6 > a {
  font-weight: var(--kpress-font-weight-sans-light);
}

.kpress-toc-level-1 > a {
  padding-left: 1.3rem;
}

.kpress-toc-level-2 > a {
  padding-left: 2rem;
}

.kpress-toc-level-3 > a {
  padding-left: 2.7rem;
}

.kpress-toc-level-4 > a {
  padding-left: 3.4rem;
}

.kpress-toc-level-5 > a {
  padding-left: 4.1rem;
}

.kpress-toc-level-6 > a {
  padding-left: 4.8rem;
}

/* Floating TOC control (32px button, 20px icon — a step larger than the
   settings gear so it reads as the document's primary navigation affordance).
   It floats over the narrow band's tightened text gutter, so instead of the
   gear's transparent-until-hover chrome it always carries a translucent
   document-background fill: where it slightly overlaps a long line, both the
   icon and the text beneath stay readable, in light and dark themes alike
   (the fill follows --kpress-doc-bg). */
.kpress-toc-toggle {
  align-items: center;
  background: color-mix(in srgb, var(--kpress-doc-bg) 80%, transparent);
  border: 1px solid transparent;
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  height: 32px;
  justify-content: center;
  padding: 4px;
  transition:
    color var(--kpress-transition-fast),
    background-color var(--kpress-transition-fast);
  width: 32px;
}

.kpress-toc-toggle:hover {
  background-color: var(--color-hover-bg);
  color: var(--kpress-doc-text);
}

.kpress-toc-toggle-icon {
  display: block;
  height: 20px;
  width: 20px;
}

.kpress-toc-title {
  border-bottom: 1px solid var(--kpress-doc-border);
  border-left: none;
  font-size: var(--kpress-caps-label-size);
  font-variant-caps: var(--kpress-caps-variant);
  font-weight: var(--kpress-font-weight-sans-medium);
  letter-spacing: var(--kpress-caps-spacing);
  line-height: var(--kpress-caps-heading-line-height);
  padding-left: 0.3rem;
  text-transform: var(--kpress-caps-transform);
}

/* Collapsible-TOC chrome (server-rendered only when toc_collapse_depth is
   set and the document has deeper entries; toc.js owns the state). The
   header row moves the title's underline onto the full row so the TOC reads
   the same with and without the expand-all button flush right of
   "Contents". */
.kpress-toc-header {
  align-items: center;
  border-bottom: 1px solid var(--kpress-doc-border);
  display: flex;
  gap: 0.3rem;
  justify-content: space-between;
}

.kpress-toc-header .kpress-toc-title {
  border-bottom: none;
  flex: 1 1 auto;
}

/* Deliberately quiet control: no border or fill, colored like the Contents
   label it sits beside (the muted TOC tone), darkening slightly on hover.
   The chevrons glyph pair keeps it minimal — a small disclosure hint, not a
   chrome button. */
.kpress-toc-expand-all {
  align-items: center;
  background: transparent;
  border: none;
  border-radius: var(--kpress-radius-sm);
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  flex: none;
  height: 18px;
  justify-content: center;
  opacity: 0.55;
  padding: 2px;
  transition:
    color var(--kpress-transition-fast),
    opacity var(--kpress-transition-fast);
  width: 18px;
}

.kpress-toc-expand-all:hover {
  background-color: var(--color-hover-bg);
  color: var(--kpress-doc-text);
  opacity: 1;
}

.kpress-toc-expand-all-icon {
  display: block;
  height: 12px;
  width: 12px;
}

/* Both state icons are server-rendered; aria-expanded picks the visible one
   (unfold while collapsed, fold while expanded) so the behavior only flips
   the attribute, never the markup. */
.kpress-toc-expand-all[aria-expanded="true"] .kpress-toc-expand-all-icon:first-of-type,
.kpress-toc-expand-all[aria-expanded="false"] .kpress-toc-expand-all-icon:last-of-type {
  display: none;
}

/* Collapsed entries animate closed rather than display:none (which cannot
   transition): each row eases its block-size and opacity to zero with the
   standard motion tokens, and visibility drops the hidden links from the
   tab order when the transition ends. Scoped to collapse-enabled navs so
   documents without the feature keep today's exact row styling. The
   :where() keeps the ancestor scope at zero specificity so the global
   prefers-reduced-motion block (`.kpress [class]`, later in this file) still
   outranks the transition and suppresses the motion. */
:where(.kpress-toc[data-kpress-toc-collapse-depth]) .toc-list > li {
  max-block-size: 5rem;
  overflow: hidden;
  transition:
    max-block-size var(--kpress-transition-med),
    opacity var(--kpress-transition-med),
    visibility var(--kpress-transition-med);
}

:where(.kpress-toc[data-kpress-toc-collapse-depth]) .toc-list > li.kpress-toc-collapsed {
  max-block-size: 0;
  opacity: 0;
  visibility: hidden;
}

/* `position: fixed` resolves against the non-scrolling `.kpress-frame` (the
   containing block for fixed descendants), so `inset: 0` covers the document
   frame — the whole window standalone, just the pane when embedded. */
.kpress-toc-backdrop {
  background-color: oklch(0.00% 0 0 / 0.5);
  display: none;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  transition:
    opacity var(--kpress-transition-fade),
    visibility var(--kpress-transition-fade);
  visibility: hidden;
  z-index: 199;
}

.kpress-toc-backdrop.kpress-visible {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

/* Footnotes take the small rung of the ramp, not a bare ratio. `0.9em` scaled
   whatever font-size it happened to inherit and compounded with it -- the prose
   size in a standalone page, but a card, a table cell or an embedding host can
   hand the block something else. It also named no rung: against an 18px base it
   resolved to 16.2px, while `--kpress-font-size-small`, the size every other
   small surface uses, is 17.1px. */
.kpress-footnotes {
  border-top: 1px solid var(--kpress-doc-border);
  color: var(--kpress-doc-muted);
  font-family: var(--kpress-font-footnote);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-size: var(--kpress-font-size-small);
  margin-top: 2rem;
  padding-top: 1rem;
}

/* Footnote controls. The reference in the text, the ↑ after its footnote and the ↓
   in its hover preview are one family: each takes the reader to or from a footnote,
   so they look alike at rest and answer a hover the same way the document's other
   small controls do -- the link colour on the glyph, a link-coloured border, and the
   link wash behind it. The border is present at rest, transparent, so nothing moves
   on hover; the wash is the same `--kpress-doc-surface-hover` every hover here uses.

   `.kpress a.…backref` and `.kpress-tooltip …nav-link` carry a class beyond the bare
   one: the generic `.kpress a:hover` underline is (0,2,1) and a bare
   `.kpress-footnote-backref:hover` (0,2,0) would lose to it and underline. */
.kpress-footnote-ref a,
.kpress a.kpress-footnote-backref,
.kpress-tooltip .kpress-footnote-nav-link {
  background-color: var(--color-bg-meta-solid);
  border: 1px solid transparent;
  border-radius: var(--kpress-radius-md);
  color: var(--color-hint-strong);
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-style: normal;
  font-weight: 600;
  padding: 0 0.15rem;
  text-decoration: none;
  /* Hover properties only, never `all` (late-applied styles must not animate). */
  transition:
    color var(--kpress-transition-fast),
    background-color var(--kpress-transition-fast),
    border-color var(--kpress-transition-fast);
}

.kpress-footnote-ref a {
  margin-right: 0.15rem;
}

.kpress-footnote-ref a:hover,
.kpress a.kpress-footnote-backref:hover,
.kpress-tooltip .kpress-footnote-nav-link:hover {
  background-color: var(--color-hover-bg);
  border-color: var(--kpress-doc-link);
  color: var(--kpress-doc-link);
  text-decoration: none;
}

.kpress-tooltip {
  background: var(--color-bg-meta-solid);
  border: 1px solid var(--color-hint-gentle);
  border-left: 2px solid var(--kpress-doc-link);
  border-radius: var(--kpress-radius-none);
  box-shadow:
    0 4px 6px -1px oklch(0.00% 0 0 / 0.1),
    0 2px 4px -1px oklch(0.00% 0 0 / 0.06);
  color: var(--kpress-doc-text);
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-small);
  font-weight: var(--kpress-font-weight-sans-regular);
  hyphens: auto;
  line-height: 1.4;
  max-width: min(20rem, calc(100vw - 2rem));
  min-width: 8rem;
  opacity: 0;
  overflow-wrap: break-word;
  padding: 0.5rem 0.75rem;
  pointer-events: none;
  /* A callout belongs to a place in the text, not to a place on the screen.
     tooltips.js appends it inside the `.kpress-viewport` scroller and writes
     page coordinates (screen position + the scroller's scroll offset), so it
     stays beside the word that opened it while the reader scrolls. The one
     exception is the mobile sheet below, which goes back to `fixed`. */
  position: absolute;
  transition:
    opacity 0.6s ease-in-out,
    visibility 0.6s ease-in-out;
  visibility: hidden;
  white-space: normal;
  width: max-content;
  word-break: normal;
  z-index: 1000;
}

.kpress-tooltip.kpress-tooltip-visible {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

/* Section-preview tooltips navigate to their target on tap/click
   (tooltips.js); the pointer signals it. Footnote popovers stay plain — only
   the links inside them navigate. */
.kpress-tooltip:not(.kpress-tooltip-footnote) {
  cursor: pointer;
}

/* No edge arrow on tooltips (textpress has none; the bare popover reads cleaner).
   The per-position `::after` border-color rules below are inert while this is hidden. */
.kpress-tooltip::after {
  display: none;
}

.kpress-tooltip a {
  color: var(--kpress-doc-link);
  cursor: pointer;
  overflow-wrap: break-word;
  pointer-events: auto;
  text-decoration: underline;
  transition: color var(--kpress-transition-med);
}

.kpress-tooltip a:hover {
  color: var(--color-primary-light);
  text-decoration: underline;
}

.kpress-tooltip code,
.kpress-tooltip pre {
  font-size: 0.9em;
  overflow-wrap: break-word;
  white-space: normal;
  word-break: break-all;
}

.kpress-tooltip p,
.kpress-tooltip span,
.kpress-tooltip div {
  overflow-wrap: break-word;
  word-break: normal;
}

.kpress-tooltip-footnote {
  font-family: var(--kpress-font-footnote);
  line-height: 1.5;
  max-width: min(25rem, calc(100vw - 2rem));
  min-width: 12rem;
  text-align: left;
}

.kpress-tooltip-bottom::after {
  border-bottom-color: var(--kpress-doc-border);
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
}

.kpress-tooltip-bottom-right::after {
  border-bottom-color: var(--kpress-doc-border);
  bottom: 100%;
  left: 1rem;
}

.kpress-tooltip-bottom-left::after {
  border-bottom-color: var(--kpress-doc-border);
  bottom: 100%;
  right: 1rem;
}

.kpress-tooltip-top::after {
  border-top-color: var(--kpress-doc-border);
  left: 50%;
  top: 100%;
  transform: translateX(-50%);
}

.kpress-tooltip-top-right::after {
  border-top-color: var(--kpress-doc-border);
  left: 1rem;
  top: 100%;
}

.kpress-tooltip-top-left::after {
  border-top-color: var(--kpress-doc-border);
  right: 1rem;
  top: 100%;
}

.kpress-tooltip-left::after {
  border-left-color: var(--kpress-doc-border);
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
}

.kpress-tooltip-right::after {
  border-right-color: var(--kpress-doc-border);
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
}

.kpress-tooltip-wide-right {
  box-shadow:
    0 8px 25px -3px oklch(0.00% 0 0 / 0.15),
    0 4px 6px -2px oklch(0.00% 0 0 / 0.05);
  max-width: 24rem;
}

.kpress-tooltip-wide-right::after {
  border-right-color: var(--kpress-doc-border);
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
}

/* The narrow-screen presentation is a sheet across the bottom of the pane, not
   a callout beside an anchor: it has no anchor to stay beside, and a bar that
   slid up the screen as the reader scrolled would read as broken. So it keeps
   the window-fixed frame (and is appended to the body, outside the scroller, by
   tooltips.js) while every other placement scrolls with the text. This rule
   ties with `.kpress-tooltip` on specificity and wins on source order. */
.kpress-tooltip-mobile-bottom {
  box-shadow: 0 -4px 16px oklch(0.00% 0 0 / 0.18);
  box-sizing: border-box;
  max-width: none;
  padding: 0.75rem 1rem;
  position: fixed;
  text-align: left;
}

.kpress-tooltip-mobile-bottom::after {
  display: none;
}

.kpress-footnote-nav {
  float: right;
  margin-inline-start: 0.5rem;
}

/* Tables: only tables marked wide (data-kpress-table-scale="wide", stamped by
   the renderer/tables.js when a table is large on both axes — see
   TABLE_WIDE_MIN_* in format/markdown.py) get the wide presentation. Smaller
   tables keep the reading-column width in every band; content wider than the
   wrap still scrolls internally. The base wide rule below is the
   column-centred, pane-capped full bleed for a centred single column;
   container bands neutralise it where the column is not centred or the
   bleed is unwanted (see the layout comment above). */
.kpress-table-wrap {
  background-color: var(--kpress-doc-bg);
  box-sizing: border-box;
  margin: 2rem 0;
  overflow-x: auto;
  overflow-y: hidden;
  position: relative;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
  width: 100%;
}

.kpress-table-wrap[data-kpress-table-scale="wide"] {
  left: 50%;
  transform: translateX(-50%);
  /* 100cqw, not 100vw: the bleed is capped at the kpress-doc pane, so a
     no-TOC document embedded in a pane narrower than the window stays inside
     the pane (without a query container, cqw falls back to vw — the
     standalone window-width case, where they are equal). */
  width: min(calc(100cqw - 2rem), max(100%, var(--kpress-measure)));
}

.kpress-table {
  border: 1px solid var(--color-border-hint);
  border-collapse: collapse;
  font-family: var(--kpress-font-table);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-small);
  line-height: 1.3;
  min-width: 100%;
  width: auto;
  word-break: break-word;
}

/* Code inside a table steps down a mono tier, at EVERY width.
   The two ramps in style-tokens.css pair by index — mono/normal, mono-small/smaller,
   mono-tiny/tiny, all three at 0.82 — so code keeps one optical weight against
   whatever text surrounds it. A table is the one surface whose text is reduced
   (`small`) while its code was not, leaving code at 0.863 of its own cell: heavier
   inside a table than the identical span in prose, which mono's larger x-height
   exaggerates further.

   `small` has no mono partner (the text ramp has four tiers, mono has three), so
   neither choice is exact, and neither is even closer: mono-small sits at 0.777 of
   the cell and mono at 0.863, equidistant either side of the ramp's 0.82 — 0.043
   out on each side, and equidistant whatever the ramp ratio, since the cell's 0.95
   is the midpoint of the 1.0 and 0.9 steps the two rungs carry. The tie breaks on
   direction — code that reads as subordinate to the cell rather than competing
   with it.

   This rule was previously scoped inside the narrow band, where it looked
   correct only because that band ALSO drops table text to `smaller` — the
   pairing came from the text side, not from this declaration. Unscoped, the
   narrow band keeps the same result and no longer needs its own copy.
   Load order matters: `.kpress code` (document.css) ties on specificity, and
   components.css is loaded after it. */
.kpress-table code,
.kpress-table pre {
  font-size: var(--kpress-font-size-mono-small);
}

.kpress-table th,
.kpress-table td {
  max-width: 40rem;
  min-width: 6rem;
  padding: 0.3rem 0.6rem;
  text-align: start;
  vertical-align: top;
}

.kpress-table th {
  background-color: var(--color-bg-alt-solid);
  border-bottom: 1px solid var(--color-border-hint);
  font-size: var(--kpress-caps-label-size);
  font-variant-caps: var(--kpress-caps-variant);
  font-weight: var(--kpress-font-weight-sans-bold);
  /* Header labels hyphenate rather than inherit the table's break-word
     letter-level wrapping: a narrow column shows "Capitali-zation" over more
     lines, never an unhyphenated mid-word cut. overflow-wrap stays as the
     last resort for strings hyphenation cannot split (needs a `lang` on the
     document/host root to pick a hyphenation dictionary). */
  hyphens: auto;
  letter-spacing: var(--kpress-caps-spacing);
  line-height: var(--kpress-caps-heading-line-height);
  overflow-wrap: break-word;
  text-align: center;
  text-transform: var(--kpress-caps-transform);
  word-break: normal;
}

.kpress-table tbody tr:nth-child(even) {
  background-color: var(--color-bg-alt-solid);
}

.kpress-table [data-kpress-numeric="true"] {
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* Task-list checkboxes: one clean box (no extra prose bullet), drawn with the
   shared Lucide `square` / `square-check` glyphs via a mask. Unchecked is a dimmer
   gray empty box; checked is a darker box with a check. The disabled <input> stays
   for its accessible checked/unchecked state. */
.kpress-prose ul > li.kpress-task::before {
  content: none;
}

.kpress-task {
  list-style: none;
}

.kpress-task input[type="checkbox"] {
  appearance: none;
  background-color: var(--kpress-doc-muted);
  block-size: var(--kpress-checkbox-size);
  cursor: default;
  inline-size: var(--kpress-checkbox-size);
  margin-inline-end: 0.4rem;
  mask:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='18' height='18' x='3' y='3' rx='2'/%3E%3C/svg%3E")
    center / contain no-repeat;
  vertical-align: -0.15em;
}

.kpress-task input[type="checkbox"]:checked {
  background-color: var(--kpress-doc-text);
  mask:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='18' height='18' x='3' y='3' rx='2' stroke-width='2'/%3E%3Cpath d='m7 12.5 3.5 3.5 6.5-7' stroke-width='2.5'/%3E%3C/svg%3E")
    center / contain no-repeat;
}

.kpress-code-scroll {
  overflow-x: auto;
}

/* Minimal scrollbar on the remaining doc scroll surfaces (the main viewport
   keeps the OS scrollbar; tables/code/tabs set theirs inline). */
.kpress-code-scroll,
.kpress-math-display,
.kpress-diagram-source,
.kpress-diagram-rendered {
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
}

.kpress-code:has(.kpress-code-copy) {
  position: relative;
}

/* Icon-only copy control, revealed on hover/focus of the code block
   (host-app parity). The label lives in aria-label/title, never as text. */
.kpress-code-copy {
  align-items: center;
  background: var(--kpress-doc-bg);
  border: 1px solid var(--kpress-doc-border);
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: flex;
  inset-block-start: 0.4rem;
  inset-inline-end: 0.4rem;
  justify-content: center;
  opacity: 0;
  padding: 0.3rem;
  position: absolute;
  transition:
    background-color var(--kpress-transition-fast),
    border-color var(--kpress-transition-fast),
    color var(--kpress-transition-fast),
    opacity var(--kpress-transition-fast);
  z-index: 1;
}

.kpress-code-copy svg {
  block-size: 15px;
  display: block;
  inline-size: 15px;
}

.kpress-code:hover .kpress-code-copy,
.kpress-code:focus-within .kpress-code-copy,
.kpress-code-copy:focus-visible {
  opacity: 1;
}

.kpress-code-copy:hover {
  background: var(--color-hover-bg);
  color: var(--kpress-doc-text);
}

.kpress-code-copy:focus-visible {
  outline: 3px solid var(--kpress-doc-link);
  outline-offset: 2px;
}

.kpress-code-copy[data-kpress-copy-state="copied"] {
  background: color-mix(in srgb, var(--color-success) 12%, transparent);
  border-color: var(--color-success);
  color: var(--color-success);
  opacity: 1;
}

.kpress-code-copy[data-kpress-copy-state="error"] {
  background: color-mix(in srgb, var(--kpress-doc-danger) 12%, transparent);
  border-color: var(--kpress-doc-danger);
  color: var(--kpress-doc-danger);
  opacity: 1;
}

.kpress-math {
  font-family: var(--kpress-font-prose);
  line-height: 1.4;
}

.kpress-math math {
  color: inherit;
  font-size: 1.02em;
}

.kpress-math-inline {
  display: inline-block;
  max-width: 100%;
  /* Let KaTeX's / the MathML fallback's own baseline seat on the text baseline;
     the previous -0.1em nudge dropped inline math ~half an x-height too low. */
  vertical-align: baseline;
}

.kpress-math-display {
  display: block;
  margin-block: 1rem;
  overflow-x: auto;
  padding-block: 0.25rem;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
  text-align: center;
}

.kpress-math-display math {
  display: inline-block;
  max-width: 100%;
}

/* KaTeX sizing. The vendored `katex/katex.min.css` is a pinned upstream bundle
   and stays byte-identical through the next KaTeX bump, so the correction lives
   here instead. Upstream sets `.katex { font-size: 1.21em }` to seat KaTeX_Main
   beside a Times-like body face; KPress reads in PT Serif, whose larger x-height
   makes that lift roughly a fifth too much, and worse inside the small Source
   Sans contexts, where the surrounding text is already below prose size. The
   ratios are the `--kpress-katex-size-*` tokens (style-tokens.css); this is the
   KaTeX counterpart of the `1.02em` nudge `.kpress-math math` gives the MathML
   fallback.

   SPECIFICITY: katex.min.css is linked AFTER these stylesheets (KaTeX is a lazy
   per-document asset merged onto the end of the manifest), so a bare `.katex`
   rule here would lose the tie to upstream's own `.katex`. Every selector below
   therefore carries at least one extra class or type. For the same reason the
   sans-context rule comes after the prose and display rules: it ties them at
   (0,3,0) and wins on order, which is what puts math in a caption at caption
   size.

   THE PREVIEW OVERLAY takes the same tokens. A footnote or section preview is a
   clone of already-rendered math that tooltips.js mounts on the viewport pane or
   the body, outside every `.kpress`, so without a scope of its own it would fall
   back to upstream's 1.21em while the document beside it reads at the token. The
   token's value comes from wherever the overlay sits in the cascade: 1em when
   katex-text-face.css matches the overlay's stamped math mode, style-tokens.css's
   1.05em otherwise, which is exactly the document's own rule. */
.kpress .katex,
.kpress-tooltip .katex {
  font-size: var(--kpress-katex-size-prose);
}

.kpress .katex-display .katex,
.kpress-tooltip .katex-display .katex {
  font-size: var(--kpress-katex-size-display);
}

.kpress .kpress-figcaption .katex,
.kpress .sans-text .katex,
.kpress .kpress-toc-title .katex,
.kpress .kpress-table th .katex,
.kpress .tab-button .katex,
.kpress .kpress-tab-button .katex,
.kpress summary .katex {
  font-size: var(--kpress-katex-size-sans);
}

/* KaTeX progressive enhancement: the semantic MathML renders by default and
   as the no-JS fallback; the TeX source node stays hidden until KaTeX
   replaces it. Once rendered, KaTeX is shown and the MathML is kept in the
   accessibility tree only. */
.kpress-math-render {
  display: none;
}

.kpress-math[data-kpress-math-rendered="true"] .kpress-math-render {
  display: inline;
}

.kpress-math-display[data-kpress-math-rendered="true"] .kpress-math-render {
  display: block;
}

.kpress-math[data-kpress-math-rendered="true"] .kpress-math-semantic {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

[data-kpress-math-pending="true"]
  .kpress-math:not([data-kpress-math-rendered="true"])
  .kpress-math-semantic {
  visibility: hidden;
}

.kpress-math[data-kpress-math-error="true"] {
  background: color-mix(in srgb, var(--kpress-doc-danger) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--kpress-doc-danger) 45%, transparent);
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-danger);
  font-family: var(--kpress-font-mono);
  font-size: var(--kpress-font-size-mono);
  padding: 0.05rem 0.25rem;
}

.kpress-diagram {
  margin: 1.5rem 0;
}

.kpress-diagram svg {
  display: block;
  height: auto;
  margin-inline: auto;
  max-width: 100%;
}

.kpress-svg-diagram,
.kpress-mermaid {
  background: var(--kpress-doc-bg);
  border: 1px solid var(--kpress-doc-border);
  padding: 1rem;
}

.kpress-diagram-source {
  margin: 0;
  overflow-x: auto;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
  white-space: pre;
}

.kpress-diagram-rendered {
  overflow-x: auto;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
}

.kpress-diagram[data-kpress-diagram-status="rendered"] .kpress-diagram-source[hidden] {
  display: none;
}

.kpress-diagram[data-kpress-diagram-status="error"] {
  border-color: color-mix(in srgb, var(--kpress-doc-danger) 45%, var(--kpress-doc-border));
}

.kpress-video-popover[hidden] {
  display: none;
}

/* Tabs: all panels share one grid cell so the container sizes to the TALLEST
   panel and does not jump when switching tabs; capped at ~half a page with
   internal scroll. Inactive panels stay in layout (for sizing) but are hidden
   from view and from the a11y / focus order. */
.kpress-tabs {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin: 1.5rem 0;
}

.kpress-tab-list,
.kpress-tabs [role="tablist"] {
  align-items: flex-end;
  border-bottom: 1px solid var(--kpress-doc-border);
  display: flex;
  gap: 0.25rem;
  grid-column: 1;
  grid-row: 1;
  overflow-x: auto;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
}

.kpress-tab-panel {
  grid-column: 1;
  grid-row: 2;
  max-block-size: 50vh;
  overflow-y: auto;
  padding-block: 1rem;
  scrollbar-color: var(--color-scrollbar) transparent;
  scrollbar-width: thin;
}

.kpress-tab-panel[hidden] {
  display: block;
  visibility: hidden;
}

.kpress .highlight {
  border-radius: var(--kpress-radius-lg);
  padding: 0 0.5rem;
}

.kpress .citation {
  border-radius: var(--kpress-radius-sm);
  color: var(--color-secondary);
  display: inline-block;
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  height: 1.3rem;
  padding: 0 0.4rem;
  transition:
    background-color var(--kpress-transition-med),
    color var(--kpress-transition-med);
}

.kpress .citation::before {
  content: "[";
}

.kpress .citation::after {
  content: "]";
}

.kpress .citation:hover {
  background-color: var(--color-hover-bg);
  color: var(--color-primary);
}

.kpress .debug {
  color: var(--color-hint);
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-tiny);
  font-weight: var(--kpress-font-weight-sans-regular);
}

.kpress .hero h1 {
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: calc(var(--kpress-font-size-base) * 1.6);
  font-weight: var(--kpress-font-weight-sans-medium);
  letter-spacing: 0.03em;
  line-height: 1.05;
  margin-block: 1rem 0;
  text-transform: uppercase;
}

.kpress .hero h1 i {
  font-family: var(--kpress-font-prose);
  font-size: calc(var(--kpress-font-size-base) * 1.5);
  font-style: italic;
  letter-spacing: 0;
  text-transform: none;
}

.kpress .subtitle {
  font-family: var(--kpress-font-prose);
  font-size: var(--kpress-font-size-normal);
  font-style: italic;
}

.kpress .boxed-text {
  /* The deliberate ink rule (kash border-hairline), not the faint doc border:
     a box the author drew should read as drawn. */
  border: 1px solid var(--kpress-doc-border-hairline);
  margin: 1rem 0;
  padding: 0 1rem;
}

.kpress .shaded-text {
  background-color: var(--kpress-doc-code-bg);
}

/* A sans-serif region: front matter, hero blocks, and other UI-voiced passages
   switch from the serif reading face to the sans stack. */
.kpress .sans-text {
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
}

.kpress .sans-text p {
  margin-block: 0.8rem;
}

.kpress .sans-text h1 {
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: calc(var(--kpress-font-size-base) * 1.75);
  font-weight: 380;
  margin-block: 1rem 1.2rem;
}

.kpress .sans-text h2 {
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: calc(var(--kpress-font-size-base) * 1.25);
  font-weight: 440;
  margin-block: 1rem 0.8rem;
}

.kpress .centered-headers h1,
.kpress .centered-headers h2,
.kpress .centered-headers h3 {
  text-align: center;
}

.kpress .justify p {
  text-align: justify;
  text-align-last: left;
}

.kpress .description {
  color: var(--color-secondary);
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-small);
  margin: 2rem 0;
}

.kpress .key-claims,
.kpress .summary,
.kpress .concepts {
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-small);
  margin: 1rem 0;
  padding: 1rem;
}

.kpress .key-claims,
.kpress .concepts {
  border: 1px solid var(--color-hint-gentle);
}

.kpress .key-claims::before,
.kpress .summary::before,
.kpress .concepts::before {
  display: block;
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: calc(var(--kpress-font-size-base) * 1.2 * var(--kpress-caps-heading-size-multiplier));
  font-variant-caps: var(--kpress-caps-variant);
  font-weight: var(--kpress-font-weight-sans-medium);
  letter-spacing: var(--kpress-caps-spacing);
  line-height: var(--kpress-caps-heading-line-height);
  margin-bottom: 0.5rem;
  text-align: center;
  text-transform: var(--kpress-caps-transform);
}

.kpress .key-claims::before {
  content: "Key Claims";
}

.kpress .summary::before {
  content: "Summary";
}

.kpress .concepts {
  column-count: 3;
  column-gap: 2rem;
  font-weight: var(--kpress-font-weight-sans-bold);
  padding-top: 3.5rem;
  position: relative;
}

.kpress .concepts::before {
  content: "Concepts";
  left: 50%;
  position: absolute;
  top: 1rem;
  transform: translateX(-50%);
  width: 100%;
}

.kpress .claim {
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-weight: var(--kpress-font-weight-sans-bold);
  margin: 0.7rem 0 0.7rem 1.8rem;
  position: relative;
}

/* The same drawn square as the prose and concepts markers; document.css carries
   the measurement it reproduces. This rule is where the glyph cost most: .claim
   is sans, so U+25AA fell through a different stack than the prose one and came
   out 5.20px against the prose marker's 3.26px at a 16px base -- 60% larger,
   for a token whose whole purpose is that every bulleted list matches. */
.kpress .claim::before {
  background: currentColor;
  content: "";
  font-size: var(--kpress-bullet-size);
  height: 0.226em;
  inset-inline-start: calc(var(--kpress-font-size-base) * -0.85 + 0.064em);
  position: absolute;
  top: calc(var(--kpress-font-size-base) * 0.1 + 0.689em);
  width: 0.226em;
}

/* Every mark KPress paints rather than sets, in forced colors (Windows High
   Contrast and the browser settings that emulate it).

   The UA forces `background-color` to `Canvas` there, so a box filled with
   `currentColor` paints the page background onto the page background and
   disappears: measured in Chromium under `forced_colors: active`, the marker
   painted 0 ink pixels where the U+25AA glyph it replaces painted 196. That is
   the one behaviour a drawn mark does not inherit from a glyph, and it is lost
   for the readers a high-contrast mode exists for. `CanvasText` is the forced
   foreground, and `forced-color-adjust: none` is what lets it through.

   One rule for all four, and it lives here rather than beside each base rule
   because this file loads after document.css and ties it on specificity. The
   nested print marker needs its own copy in print.css, whose selector is more
   specific still. The disclosure chevron is in the same list for the same
   reason: it is a mask filled with `background-color`. */
@media (forced-colors: active) {
  .kpress-prose ul > li::before,
  .kpress .concepts ul > li::before,
  .kpress .claim::before,
  .kpress summary::before {
    background: CanvasText;
    forced-color-adjust: none;
  }
}

/* Every heading inside a sans block takes the sans face, so its mathematics and its
   words are set in the same one.

   WHY THIS RULE IS NEEDED AT ALL: document.css redeclares the prose face directly on
   the heading -- `.kpress-prose h1`, `h2`, `h5` and `h6` each carry their own
   `font-family: var(--kpress-font-prose, ...)` -- and a declaration on the element
   itself beats the sans family a container sets on an ancestor, which reaches the
   heading only by inheritance. So `## Heading` inside one of these blocks drew PT
   Serif while the block around it drew Source Sans.

   WHY THAT MATTERS MORE THAN A FONT SLIP: the sans math composite is scoped by
   CONTAINER, not by the heading's own face. katex-init.js resolves its `SANS_CONTEXT`
   with `closest()` from each node it renders, which climbs straight past the heading
   to `.description`, so mathematics in a heading here is laid out from the sans tables
   and stamped for the sans rules in katex-text-face.css to draw from `KPress Math Text
   Sans`. The heading therefore read as serif words wrapped around a sans formula,
   which is the disagreement that whole mechanism exists to remove. Headings are
   deliberately not sans-math roles of their own (katex-text-face.css records why: a
   heading's sans weight is not the weight the pinned tables were built at), so the
   agreement has to be reached from the text side, here.

   `.sans-text` is in the list too. It already redeclares the sans family on its own
   `h1` and `h2` above -- that pair is where this pattern comes from -- but it stops
   there, so its `h5` and `h6` were on the prose face exactly like the other five
   containers. `h3` and `h4` reach the sans face through the `.kpress-prose h1..h6`
   group rule rather than a rule of their own; they are listed anyway so this states
   the whole invariant and does not quietly depend on that group rule keeping them.

   SPECIFICITY: (0,2,1), which clears `.kpress-prose h1` at (0,1,1). It ties the
   `.sans-text` heading rules above at (0,2,1) and wins on source order, declaring the
   same family they do -- their size, weight and margins are untouched. */
.kpress :is(.sans-text, .description, .key-claims, .summary, .concepts, .claim)
  :is(h1, h2, h3, h4, h5, h6) {
  font-family: var(--kpress-font-sans);
  --_kpress-font-weight-prose: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
}

.kpress .full-text {
  margin: 1rem 0;
}

.kpress .annotated-para {
  align-items: flex-start;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

.kpress .annotated-para .para-caption {
  align-self: center;
  border-right: 2px solid var(--color-hint);
  color: var(--color-secondary);
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-font-size-smaller);
  margin: 0.5rem 1rem 0.5rem 0;
  max-height: 15rem;
  order: 0;
  overflow-y: auto;
  padding-right: 0.5rem;
  scrollbar-color: var(--color-hint-gentle) transparent;
  scrollbar-width: thin;
  width: 20%;
}

.kpress .annotated-para .para {
  order: 1;
  width: 80%;
}

.kpress .frame-capture {
  border: 1px solid var(--color-hint);
  border-radius: var(--kpress-radius-lg);
  display: block;
  margin: 1rem auto;
  max-width: 225px;
}

.kpress .video-gallery {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

.kpress .video-item {
  background-color: var(--kpress-doc-bg);
  border: 1px solid var(--kpress-doc-border);
  border-radius: var(--kpress-radius-lg);
  padding: 1rem;
}

/* Inline YouTube player. The server emits a safe placeholder (no iframe, so
   sanitized + hashed output stay network-free); video-popover.js swaps in this
   responsive 16:9 frame on load. */
.kpress-video {
  margin: 1.5rem 0;
}

.kpress-video-embed {
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--kpress-radius-sm);
  display: block;
  width: 100%;
}

.kpress .kpress-video-placeholder {
  align-items: center;
  aspect-ratio: 16 / 9;
  background:
    linear-gradient(135deg, oklch(0.00% 0 0 / 0.72), oklch(0.00% 0 0 / 0.42)), var(--color-primary);
  border: 1px solid var(--kpress-doc-border);
  border-radius: var(--kpress-radius-none);
  color: oklch(100.00% 0 0);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-feature-settings: var(--kpress-font-features-sans);
  gap: 0.75rem;
  justify-content: center;
  min-height: 12rem;
  padding: 1rem;
  text-align: center;
  width: 100%;
}

.kpress .kpress-video-placeholder:focus-visible {
  outline: 3px solid var(--kpress-doc-link);
  outline-offset: 3px;
}

/* One sans bold across the design system: these two carried a literal 700, the
   only sans chrome above the bold token, until 2026-09-07. */
.kpress .kpress-video-placeholder-action {
  align-items: center;
  background: oklch(100.00% 0 0 / 0.92);
  border-radius: var(--kpress-radius-pill);
  color: oklch(17.76% 0 0);
  display: inline-flex;
  font-size: var(--kpress-font-size-small);
  font-weight: var(--kpress-font-weight-sans-bold);
  height: 3rem;
  justify-content: center;
  line-height: 1;
  width: 3rem;
}

.kpress .kpress-video-placeholder-title {
  font-size: var(--kpress-font-size-small);
  font-weight: var(--kpress-font-weight-sans-bold);
}

.kpress-video-popover iframe {
  aspect-ratio: 16 / 9;
  border: 0;
  width: 100%;
}

.kpress .video-item iframe {
  border: 0;
  border-radius: var(--kpress-radius-lg);
  height: 200px;
  width: 100%;
}

/* The caps-label idiom, at the label weight the TOC title, the disclosure
   summaries and the print tab label all use. It asked for a literal 600 until
   2026-09-07, so the same label came out heavier on screen than on paper. */
.kpress .tab-button,
.kpress-tab-button {
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--color-secondary);
  cursor: pointer;
  flex: 1 1 auto;
  font-family: var(--kpress-font-sans);
  font-feature-settings: var(--kpress-font-features-sans);
  font-size: var(--kpress-caps-label-size);
  font-variant-caps: var(--kpress-caps-variant);
  font-weight: var(--kpress-font-weight-sans-medium);
  letter-spacing: var(--kpress-caps-spacing);
  line-height: 1.2;
  max-width: 10rem;
  min-width: 5rem;
  outline: none;
  padding: 0 0.5rem;
  text-align: center;
  text-transform: var(--kpress-caps-transform);
}

.kpress-tab-button:focus-visible {
  outline: 3px solid var(--kpress-doc-link);
  outline-offset: 2px;
}

.kpress .tab-button-active,
.kpress-tab-button[aria-selected="true"] {
  border-color: var(--color-primary-light);
  color: var(--color-primary);
}

.kpress .tab-button-inactive,
.kpress-tab-button[aria-selected="false"] {
  border-color: transparent;
  color: var(--color-secondary);
}

.kpress .tab-button-inactive:hover,
.kpress-tab-button[aria-selected="false"]:hover {
  border-color: var(--color-hover);
  color: var(--color-primary);
}

.hidden {
  display: none;
}

.kpress .hidden {
  display: none;
}

.kpress-video-backdrop {
  background-color: oklch(0.00% 0 0 / 0.5);
  inset: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  transition:
    opacity var(--kpress-transition-fade),
    visibility var(--kpress-transition-fade);
  visibility: hidden;
  z-index: 199;
}

.kpress-video-backdrop.kpress-visible {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

.kpress-video-popover {
  background: var(--kpress-doc-bg);
  border: 1px solid var(--kpress-doc-border);
  color: var(--kpress-doc-text);
  display: flex;
  flex-direction: column;
  left: 1rem;
  max-height: calc(100dvh - 2rem);
  overflow: hidden;
  position: fixed;
  top: 1rem;
  width: min(44rem, calc(100dvw - 2rem));
  z-index: 200;
}

.kpress-video-popover.kpress-maximized {
  bottom: 1rem;
  right: 1rem;
  width: auto;
}

.kpress-video-header {
  align-items: center;
  display: flex;
  gap: 0.5rem;
  justify-content: space-between;
  padding: 0.5rem 0.6rem 0.5rem 0.85rem;
}

/* Icon-only close (Lucide `x`), not a text button. */
.kpress-video-header [data-kpress-video-close] {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--kpress-radius-sm);
  color: var(--kpress-doc-muted);
  cursor: pointer;
  display: flex;
  justify-content: center;
  padding: 0.25rem;
  transition:
    background var(--kpress-transition-fast),
    color var(--kpress-transition-fast);
}

.kpress-video-header [data-kpress-video-close]:hover,
.kpress-video-header [data-kpress-video-close]:focus-visible {
  background: var(--color-hover-bg);
  color: var(--kpress-doc-text);
}

.kpress-video-header [data-kpress-video-close] svg {
  block-size: 18px;
  display: block;
  inline-size: 18px;
}

/* Heading step-up band: from 64rem the H2-H4 section headings take their
   larger sizes; narrow and medium panes keep the tighter defaults from
   style-tokens.css. Deliberately one notch below the 75rem TOC band so the
   heading step is not swallowed by that layout reflow, and deliberately
   headings ONLY: body text stays 1rem at every width (kash parity — kash
   bumps prose above 1rem only for print). */
@container kpress-doc (min-width: 64rem) {
  .kpress {
    --kpress-font-size-h2: calc(var(--kpress-font-size-base) * 1.4);
    --kpress-font-size-h3: calc(var(--kpress-font-size-base) * 1.2);
    --kpress-font-size-h4: calc(var(--kpress-font-size-base) * 1.2);
  }
}

/* Wide document: TOC is a sticky sidebar. Widths track the document viewport
   via `cqw` (not the window), so this is correct standalone and embedded. */
@container kpress-doc (min-width: 75rem) {
  body.kpress-video-open .kpress-toc,
  .kpress-viewport.kpress-video-open .kpress-toc {
    display: none;
  }

  /* The TOC sidebar needs the full document width. Both the article
     (`.kpress-doc`, max-width 76ch+2rem) AND the inner grid wrapper
     (`.kpress-doc-layout`, max-width 76ch) are otherwise capped at the
     single-column reading measure and centred via margin:auto — so left
     in place the two-column grid is crushed into ~600px and shoved toward
     the centre/right (a left margin on the TOC, the fixed tracks spilling
     past the cap to the right). Uncap both so the sidebar + content span
     the pane, left-aligned. */
  .kpress-doc:has(.kpress-toc),
  .kpress-doc:has(> [data-kpress-toc-rail="reserved"]),
  .kpress-doc-layout:has(.kpress-toc),
  .kpress-doc-layout[data-kpress-toc-rail="reserved"] {
    margin-inline: 0;
    max-width: none;
  }

  /* The whole wide band carries a tighter inset than the single-column bands:
     the large outer margin (and, with a TOC, the 3rem rail gap) already holds
     the column off its surroundings, so 2.5rem is enough for the content card's
     chrome to breathe.

     UNCONDITIONAL, and it must stay that way. This is the band's inset, not the
     grid's: 0.3.3 applied it through `.kpress-content-with-toc .kpress-long-text`,
     a selector that matches whether or not a TOC was rendered, because
     `.kpress-content-with-toc` is on the layout wrapper always. 0.3.4 briefly
     gated this on the rail conditions, which quietly moved every TOC-less wide
     document from 2.5rem to 4rem -- the text still landed at the measure, since
     the caps follow the inset, but the card's inner padding changed and a host
     sizing its own surfaces to `measure + 2 x 2.5rem` (the documented wide track)
     no longer matched the document inside them.

     Do not add the rail conditions back here. The neighbouring rules carry them
     because they build the two-column grid, which genuinely needs a rail; the
     inset is the band's and applies to a single column too. */
  .kpress-doc {
    --kpress-column-inset: 2.5rem;
  }

  /* BOTH columns are width-bounded and the pair is left-aligned with
     justify-content, so the content column is a constant at every pane width.
     The earlier `clamp(.., 15cqw, ..)` TOC column grew with the pane and, under
     a fixed 68rem group cap, ate into the 1fr content column -- so the text got
     narrower the wider you dragged. Left-aligning pins the TOC + content to the
     start so all extra pane width becomes a single trailing margin -- no empty
     band to the left of the TOC.

     The content track is the column box, measure + 2x inset, so the TEXT lands
     at exactly the measure. The `min()` against the pane is a guard, not a
     resize: this band's floor is the literal 75rem below (a container query
     cannot read var()), so a host that raises the measure past what the floor
     assumed would otherwise overflow the pane rather than fit in it. At the
     default the guard never binds -- at the 75rem floor it evaluates to 55rem
     against a 50rem track -- so the column stays the promised constant and only
     an over-large measure degrades, gracefully, instead of spilling.
     100cqw - 20rem is the pane less the 15rem rail, the 3rem gap and the two
     1rem page gutters. */
  .kpress-content-with-toc.has-toc,
  .kpress-content-with-toc:has(.kpress-toc),
  .kpress-content-with-toc[data-kpress-toc-rail="reserved"] {
    align-items: start;
    column-gap: 3rem;
    /* The two TOC-bearing conditions are already grid from the base rule
       above; the reserved rail is not, and only becomes a grid inside this
       band — narrow bands have no rail to reserve and stay single-column. */
    display: grid;
    grid-template-columns:
      15rem
      min(calc(var(--kpress-measure) + 2 * var(--kpress-column-inset)), calc(100cqw - 20rem));
    justify-content: start;
    min-height: 100dvh;
  }

  /* The site header/footer rows follow the same geometry as the doc grid: with a
     TOC the grid left-anchors inside .kpress-doc (1rem doc pad + 15rem track +
     3rem gap) and .kpress-long-text pads 2.5rem, so the reading text's inline
     start sits at 21.5rem. A centered header would drift off that column as the
     pane widens; align its content instead. Only the inline centering is
     cancelled: the footer keeps its block gap above. */
  .kpress-page-main:has(.kpress-toc) .kpress-site-header,
  .kpress-page-main:has(.kpress-toc) .kpress-site-footer,
  .kpress-page-main:has([data-kpress-toc-rail="reserved"]) .kpress-site-header,
  .kpress-page-main:has([data-kpress-toc-rail="reserved"]) .kpress-site-footer {
    margin-inline: 0;
    max-width: none;
    padding-inline-start: 21.5rem;
  }

  /* The track above is already the column box, so the column fills it and the
     inset comes from the token the article set for this band -- the padding is
     not restated here, or it would be a second place to keep in step with the
     track. */
  .kpress-content-with-toc .kpress-long-text {
    grid-column: 2;
    margin: 0;
    max-width: none;
  }

  /* The optional thumbnail is the layout's third child and the only one with
     no explicit track, so grid auto-placement drops it into whichever cell is
     free — the rail itself once the rail is reserved and empty. Pin it to the
     reading column with the prose it belongs to. Child combinator: an author's
     own .thumbnail inside the prose is not a grid item and must not be
     re-placed. */
  .kpress-content-with-toc > .thumbnail {
    grid-column: 2;
  }

  .kpress-toc {
    align-self: start;
    grid-column: 1;
    margin: 0;
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    position: sticky;
    scrollbar-color: var(--kpress-doc-border) transparent;
    scrollbar-width: thin;
    top: 2rem;
    width: auto;
  }

  .kpress-toc::-webkit-scrollbar {
    width: 2px;
  }

  .kpress-toc::-webkit-scrollbar-track {
    background: transparent;
  }

  .kpress-toc::-webkit-scrollbar-thumb {
    background: var(--kpress-doc-border);
    border-radius: var(--kpress-radius-sm);
  }

  .kpress-toc-toggle,
  .kpress-toc-backdrop {
    display: none;
  }

  /* Tables marked wide bleed rightward into the trailing margin instead of
     scrolling inside the reading column. The grid is 15rem TOC + 3rem gap +
     53rem column (left-aligned), and `.kpress-long-text` insets 2.5rem, so the
     table's left edge sits 20.5rem from the pane's left; cap the width at the
     pane's right edge (a matching 2.5rem inset → `100cqw - 23rem`). Unmarked
     tables cap at the reading column (`max-width: 100%`) and scroll internally;
     wide-marked ones grow to their content and scroll internally only once
     they pass the pane edge. `100cqw` is the pane, so the table never centres
     on / spills past the window. */
  .kpress-content-with-toc.has-toc .kpress-table-wrap,
  .kpress-content-with-toc:has(.kpress-toc) .kpress-table-wrap,
  .kpress-content-with-toc[data-kpress-toc-rail="reserved"] .kpress-table-wrap {
    left: 0;
    margin-inline: 0;
    max-width: 100%;
    min-width: 100%;
    transform: none;
    width: max-content;
  }

  .kpress-content-with-toc.has-toc .kpress-table-wrap[data-kpress-table-scale="wide"],
  .kpress-content-with-toc:has(.kpress-toc) .kpress-table-wrap[data-kpress-table-scale="wide"],
  .kpress-content-with-toc[data-kpress-toc-rail="reserved"]
    .kpress-table-wrap[data-kpress-table-scale="wide"] {
    max-width: calc(100cqw - 23rem);
  }

  .kpress-content-with-toc.has-toc .kpress-table,
  .kpress-content-with-toc:has(.kpress-toc) .kpress-table,
  .kpress-content-with-toc[data-kpress-toc-rail="reserved"] .kpress-table {
    max-width: none;
    width: 100%;
  }
}

/* Narrow document: TOC collapses to a floating list-icon button that fades in
   once toc.js detects the reader has scrolled past the first section; clicking
   it slides a fixed drawer (with backdrop) over the prose. Fixed positioning
   resolves against the non-scrolling `.kpress-frame`, so the drawer covers the
   document pane, not the browser window. */
@container kpress-doc (max-width: 74.99rem) {
  .kpress-toc-toggle {
    left: 0.75rem;
    opacity: 0;
    position: fixed;
    top: 1rem;
    transition:
      opacity var(--kpress-transition-fade),
      visibility var(--kpress-transition-fade),
      color var(--kpress-transition-fast),
      background-color var(--kpress-transition-fast);
    visibility: hidden;
    z-index: 201;
  }

  .kpress-toc-toggle.show-toggle {
    opacity: 1;
    visibility: visible;
  }

  /* The floating toggle lives in the pane's top-left gutter (fixed against
     the frame), so the document must keep an inline gutter at least as wide
     as the toggle's footprint (0.75rem inset + 32px button + breathing
     room) or prose slides under the control — both at mid widths, where the
     centered column's left edge moves inside the footprint, and on phones,
     where the 1rem base padding leaves the first characters covered. Hosts
     that add their own pane inset shrink the reservation through the
     clearance variable (their inset already contributes to the gutter). */
  /* Set the gutter token, not padding-inline: .kpress-doc's cap adds the gutter
     (box-sizing is border-box), so widening the padding alone would take the
     extra room out of the reading column instead of out of the page margin. */
  .kpress-doc {
    --kpress-doc-gutter: max(1rem, var(--kpress-toc-toggle-clearance));
  }

  .kpress-toc-backdrop {
    display: block;
  }

  .kpress-toc {
    -webkit-overflow-scrolling: touch;
    background: var(--kpress-doc-code-bg);
    color: var(--color-secondary);
    left: 1rem;
    /* dvh, not vh: on phones 100vh includes the area behind the collapsing
       browser chrome, which pushed the drawer's bottom below the visible
       viewport and trapped the last entries. The extra block-end padding
       gives the list scroll room past its final entry so it is always
       comfortably reachable. */
    max-height: calc(100dvh - 6rem);
    opacity: 0;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-block-end: 2.5rem;
    pointer-events: none;
    position: fixed;
    top: 4rem;
    touch-action: pan-y;
    transform: translateY(-0.5rem);
    transition:
      opacity var(--kpress-transition-fade),
      transform var(--kpress-transition-fade),
      visibility var(--kpress-transition-fade);
    visibility: hidden;
    width: calc(100cqw - 2rem);
    z-index: 200;
  }

  .kpress-toc a {
    color: var(--color-secondary);
  }

  .kpress-toc a:hover {
    color: var(--kpress-doc-text);
  }

  .kpress-toc a[data-active="true"],
  .kpress-toc a.active {
    color: var(--kpress-doc-text);
  }

  .kpress-toc-title {
    font-size: var(--kpress-caps-label-size);
  }

  /* In the drawer the expand-all control returns to a comfortable touch
     size; the extra-small sizing is for the wide sidebar, where it sits
     beside the small-caps Contents label. */
  .kpress-toc-expand-all {
    height: 20px;
    width: 20px;
  }

  .kpress-toc-expand-all-icon {
    height: 14px;
    width: 14px;
  }

  .kpress-toc ol {
    font-size: var(--kpress-font-size-small);
  }

  .kpress-toc.kpress-mobile-visible {
    opacity: 1;
    pointer-events: auto;
    transform: translateY(0);
    visibility: visible;
  }

  .kpress .annotated-para {
    display: block;
  }

  .kpress .annotated-para .para,
  .kpress .annotated-para .para-caption {
    width: auto;
  }

  /* Tablet band (48–75rem): the wide (>=75rem) and mobile (<48rem) blocks
     both neutralize the base wide-table full-bleed (left:50% +
     translateX(-50%) centred on the viewport), but this band did not — so
     the centred bleed leaked through. Embedded in a host pane narrower than
     the window, that centres the table on the window and spills it past the
     pane on both sides. Anchor it to the prose column and scroll internally,
     matching the other two breakpoints. (The attribute variant is listed so
     this reset also out-specifies the base wide-table bleed rule.) */
  .kpress-table-wrap,
  .kpress-table-wrap[data-kpress-table-scale="wide"] {
    left: 0;
    margin-inline: 0;
    max-width: 100%;
    transform: none;
    width: 100%;
  }

  .kpress-table {
    max-width: none;
    width: 100%;
  }
}

@container kpress-doc (max-width: 47.99rem) {
  /* Narrow screens get a tighter frame: the wide-screen gutters stack up to several
     rem per side, which is dead space on a phone (kash used 1.5rem total). The doc
     and long-text pads shrink here; the standalone page gutter shrinks in the
     media rule by .kpress-page-main below. The floating TOC toggle is present in
     this whole band, so the shrink must still respect its clearance reservation
     (this rule ties with the clearance rule above and wins by order). */
  .kpress-doc {
    /* The two phone gutters are held in variables so the wide-table
       edge-bleed below can extend across exactly this inset. The stray-wide-
       content clip guard lives HERE (not on .kpress-long-text): the doc's
       padding box ends at the pane edge, so wide tables may bleed across the
       gutters and are clipped only at the pane, never at the text margin.
       Unlike the mid-width band above, the phone band does NOT reserve the
       floating-toggle clearance: on a phone every rem of gutter is reading
       width, and the toggle's translucent document-background fill keeps the
       control and any text it slightly overlaps legible (design review
       decision — see the toggle's own comment). */
    --kpress-doc-gutter: 0.5rem;
    overflow-x: hidden;
    padding-inline: var(--kpress-doc-gutter);
  }

  .kpress-long-text {
    --kpress-prose-gutter: 0.75rem;
    max-width: 100%;
    padding-inline: var(--kpress-prose-gutter);
  }

  /* The wide 4rem pull-in reads as wasted width on a phone. */
  .kpress blockquote {
    margin-inline: 0.75rem 1rem;
  }

  /* Shallower list indents on narrow screens (kash parity). */
  .kpress-prose ul,
  .kpress-prose ol,
  .kpress .concepts ul {
    margin-left: 1rem;
  }

  /* Three columns are unreadable at phone width. */
  .kpress .concepts {
    column-count: 1;
  }

  /* Any table too wide for the phone column keeps its left edge on the text
     column but bleeds rightward across the phone gutters to the pane edge, so
     the scroll region is cut off at the screen, not at the text's right
     margin (the screen-edge cut also signals scrollability). This
     deliberately ignores the wide mark: on a phone the mark decides nothing —
     `max-content` under the pane-edge cap means small tables sit at the
     column width and only overflowing ones grow. The gutter variables are the
     two inline pads above; the doc-level clip guard keeps any rounding
     overflow at the pane edge. */
  .kpress-table-wrap,
  .kpress-table-wrap[data-kpress-table-scale="wide"] {
    left: 0;
    margin-inline: 0;
    max-width: calc(100% + var(--kpress-doc-gutter, 0.5rem) + var(--kpress-prose-gutter, 0.75rem));
    min-width: 100%;
    transform: none;
    width: max-content;
  }

  .kpress-table {
    font-size: var(--kpress-font-size-smaller);
    max-width: none;
    min-width: 100%;
    width: auto;
  }
}

@media (max-width: 1199px) {
  body.kpress-video-open {
    overflow: hidden;
    position: fixed;
    touch-action: none;
    width: 100%;
  }

  .kpress-viewport.kpress-video-open {
    overflow: hidden;
    touch-action: none;
  }
}

@media (min-width: 1200px) {
  .kpress-tooltip-wide-right {
    max-width: 22rem;
    min-width: 16rem;
  }

  .kpress-tooltip-footnote.kpress-tooltip-wide-right {
    max-width: 24rem;
    min-width: 18rem;
  }
}

@media (min-width: 1600px) {
  .kpress-tooltip-wide-right {
    max-width: 28rem;
    min-width: 20rem;
  }

  .kpress-tooltip-footnote.kpress-tooltip-wide-right {
    max-width: 30rem;
    min-width: 22rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kpress [class],
  .kpress [class]::before,
  .kpress [class]::after,
  .kpress summary,
  .kpress summary::before,
  .kpress details::details-content,
  .kpress-toc-backdrop,
  .kpress-video-backdrop,
  /* The viewport pane sits above .kpress, so the smooth hash-navigation
     glide needs its own reduced-motion opt-out. */
  .kpress-viewport {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    scroll-behavior: auto;
    transition-duration: 0.01ms;
  }
}

/* Site chrome slots. The header/footer content is site-owned raw HTML (page
   chrome slots); these wrappers only align it with the document column and
   hand it the document's sans typography and theme tokens. KPress never
   styles the slot's inner markup. */
.kpress-site-header,
.kpress-site-footer {
  box-sizing: border-box;
  color: var(--kpress-doc-text);
  font-family: var(--kpress-font-sans);
  font-weight: var(--kpress-font-weight-sans-regular);
  font-size: var(--kpress-font-size-small);
  margin: 0 auto;
  max-width: var(--kpress-measure);
  padding: 0.75rem 1.5rem;
  width: 100%;
}

.kpress-site-header {
  border-bottom: 1px solid var(--kpress-doc-border);
}

.kpress-site-footer {
  border-top: 1px solid var(--kpress-doc-border);
  color: var(--kpress-doc-muted);
  margin-top: 2rem;
}

/* Slot <-> reading-column alignment in the md single-column state: from 48rem
   container width .kpress-long-text pads 4rem inline (kash's md:px-16), so the
   slot content must pad the same or header/footer chrome sits 2.5rem off the
   text's inline edge. Below 48rem both sides land at 1.5rem (long-text 1rem +
   .kpress-doc 0.5rem); at >=75rem the docked-TOC rule re-anchors
   padding-inline-start at the reading column, overriding this by specificity. */
@container kpress-doc (min-width: 48rem) {
  .kpress-site-header,
  .kpress-site-footer {
    padding-inline: 4rem;
  }
}

.kpress-site-header a,
.kpress-site-footer a {
  color: var(--kpress-doc-link);
  text-decoration: none;
}

.kpress-site-header a:hover,
.kpress-site-footer a:hover {
  text-decoration: underline;
}

/* kpress: css/print.css */
@page {
  margin: var(--kpress-print-page-margin);

  @bottom-left {
    color: oklch(44.95% 0 0);
    content: var(--kpress-print-footer);
    font-family: var(--kpress-font-sans);
    font-weight: var(--kpress-font-weight-sans-regular);
    font-size: var(--kpress-font-size-small);
  }

  @bottom-right {
    color: oklch(17.76% 0 0);
    content: counter(page);
    font-family: var(--kpress-font-prose);
    font-weight: var(--_kpress-font-weight-prose);
    font-size: var(--kpress-font-size-small);
  }
}

@media print {
  :root,
  .kpress {
    color-scheme: light;
    /* Re-root the whole type ramp at the print size: every size derives from
       the base (see style-tokens.css SIZING POLICY), so headings, code, and
       bullets keep their designed ratios to the 11pt body on paper. Without
       this re-rooting, rem-derived sizes would track the 16px screen root
       while the body stays pinned, printing h1/body at ~1.85 instead of the
       designed 1.7. */
    --kpress-font-size-base: var(--kpress-print-font-size);
    --kpress-doc-bg: oklch(100.00% 0 0);
    --kpress-doc-text: oklch(17.76% 0 0);
    --kpress-doc-muted: oklch(38.67% 0 0);
    --kpress-doc-border: oklch(78.26% 0 0);
    --kpress-doc-link: oklch(0.00% 0 0);
  }

  /* Print sans: the static instances lead, the variable face stays as the fallback.
     Chromium's PDF writer cannot embed a variable font away from its default position,
     so it draws every glyph as a Type3 outline path, and a viewer that smooths text
     drawn through the font machinery leaves those paths thin -- the sans then reads a
     step lighter than the serif and the mathematics beside it. A static instance embeds
     as a font and is smoothed as one. The faces are declared in print-fonts.css and
     generated by devtools/instance_sans.py, under a family of their own because the
     OFL reserves the name of the one they are derived from. The selector list mirrors
     the one style-tokens.css declares --kpress-font-sans on, so no scope falls back to
     the screen stack, and it includes :root because the @page margin boxes above
     resolve custom properties from the root element.

     Two host hooks, in precedence order. --kpress-host-font-sans-print names the print
     stack outright: a host that ships instances of its own points at them here. A host
     that sets only the screen hook, --kpress-host-font-sans, has named a family for
     both media and gets it in both -- the static set is a default, not an override, and
     a host's own sans is not overruled on paper. That host prints whatever its family
     prints, Type3 outlines included if it is a variable webfont, so the operations
     guide asks it to set the print hook as well. */
  :root,
  .kpress,
  .kpress-page-main,
  .kpress-tooltip {
    --kpress-font-sans: var(
      --kpress-host-font-sans-print,
      var(
        --kpress-host-font-sans,
        "KPress Print Sans",
        "Source Sans 3 Variable",
        system-ui,
        -apple-system,
        BlinkMacSystemFont,
        "Segoe UI",
        sans-serif
      )
    );
  }

  body {
    background: oklch(100.00% 0 0);
  }

  /* The document viewport is a 100dvh scroll container on screen; for print it
     must release its height and overflow (and drop containment) so the whole
     document flows across pages instead of clipping to one screen. The frame's
     transform layer must go too: a transformed ancestor breaks print
     fragmentation. (The viewport keeps `transform: none` defensively.) */
  .kpress-viewport {
    block-size: auto;
    container-type: normal;
    overflow: visible;
    transform: none;
  }

  .kpress-frame {
    transform: none;
  }

  .kpress {
    background: oklch(100.00% 0 0);
    color: oklch(17.76% 0 0);
    font-size: var(--kpress-print-font-size);
    line-height: 1.4;
  }

  .kpress-no-print,
  .kpress button,
  .kpress-site-footer,
  .kpress-site-header,
  .kpress-tooltip,
  .kpress-toc,
  .kpress-toc-backdrop,
  .kpress-video-backdrop,
  .kpress-video-popover {
    display: none;
  }

  .kpress-print-only {
    display: block;
  }

  .kpress h1,
  .kpress h2,
  .kpress h3,
  .kpress h4,
  .kpress h5,
  .kpress h6 {
    break-after: avoid;
    page-break-after: avoid;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .kpress p,
  .kpress li,
  .kpress blockquote {
    hyphens: auto;
    orphans: 3;
    text-align: justify;
    widows: 3;
  }

  .kpress p {
    margin-block: 0.65rem;
  }

  .kpress li {
    margin-block-start: 0.6rem;
  }

  .kpress li > p:first-child {
    margin-block-start: 0;
  }

  .kpress li > p:last-child {
    margin-block-end: 0;
  }

  .kpress-long-text {
    /* Card chrome (data-kpress-card) never prints: paper is the sheet. */
    border: none;
    box-shadow: none;
    padding: 0;
  }

  /* The @page margin is the reading margin on paper: the margin boxes
     (@bottom-left footer, @bottom-right page number) sit exactly at it, so
     the shells drop their screen-side gutters to align the text column with
     them. Without this the text sits 1rem (embedded) to 3rem (standalone)
     inside the margin boxes.

     The block axis is dropped for the same reason and was previously missed.
     `.kpress-page-main` carries `padding-block-start` as a screen gutter, and
     it survived into print stacked on top of the @page margin, pushing the
     first page's content down by that much for no reason a reader can see.
     Measured on a 13-page document at Letter with a 1in margin: 40px of it,
     which cost a whole page. Removing it took the document to 12 pages and
     premature whitespace from 25.51in to 17.58in, because a heading and its
     paragraph that no longer fit above a full-page figure now do. No page
     grows and nothing is stranded; on A4, whose taller box never produced the
     shape, the change is a no-op. */
  .kpress-doc,
  .kpress-page-main {
    padding-inline: 0;
    padding-block-start: 0;
  }

  .kpress blockquote,
  .kpress pre,
  .kpress table,
  .kpress figure {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .kpress tr {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .kpress thead {
    display: table-header-group;
  }

  .kpress sup {
    font-size: 110%;
  }

  .kpress details.metadata {
    display: none;
  }

  .kpress-tabs [role="tablist"] {
    display: none;
  }

  .kpress-tab-panel,
  .kpress-tab-panel[hidden] {
    break-inside: avoid;
    display: block;
    max-block-size: none;
    overflow: visible;
    page-break-inside: avoid;
    padding-block: 0.75rem;
    visibility: visible;
  }

  .kpress-tab-panel::before {
    color: oklch(17.76% 0 0);
    content: attr(data-kpress-tab-title);
    display: block;
    font-family: var(--kpress-font-sans);
    font-feature-settings: var(--kpress-font-features-sans);
    font-size: calc(var(--kpress-font-size-base) * var(--kpress-caps-heading-size-multiplier));
    font-variant-caps: var(--kpress-caps-variant);
    font-weight: var(--kpress-font-weight-sans-medium);
    letter-spacing: var(--kpress-caps-spacing);
    line-height: var(--kpress-caps-heading-line-height);
    margin-block: 0 0.5rem;
    text-transform: var(--kpress-caps-transform);
  }

  .kpress-table-wrap {
    margin-inline: 0;
    overflow: visible;
  }

  .kpress-code {
    background-color: transparent;
    overflow-wrap: break-word;
    white-space: pre-wrap;
    word-break: break-word;
  }

  .kpress-math,
  .kpress-math math {
    color: oklch(17.76% 0 0);
  }

  .kpress-math-display {
    break-inside: avoid;
    overflow: visible;
    page-break-inside: avoid;
  }

  .kpress-math[data-kpress-math-error="true"] {
    background: transparent;
    border-color: oklch(17.76% 0 0);
    color: oklch(17.76% 0 0);
  }

  .kpress-diagram,
  .kpress-diagram svg {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .kpress-diagram {
    border-color: oklch(17.76% 0 0);
  }

  .kpress-diagram-source {
    overflow: visible;
    white-space: pre-wrap;
  }

  .kpress-diagram-rendered {
    overflow: visible;
  }

  .kpress ol {
    counter-reset: kpress-print-list-item;
    list-style: none;
    margin-left: 1rem;
    padding-left: 0;
  }

  .kpress ol > li {
    align-items: baseline;
    break-inside: avoid;
    counter-increment: kpress-print-list-item;
    display: grid;
    gap: 0 0.5rem;
    grid-template-columns: 2.5rem minmax(0, 1fr);
    margin-block: 0 0.5rem;
    padding-left: 0;
    page-break-inside: avoid;
    position: static;
  }

  .kpress ol > li::before {
    content: counter(kpress-print-list-item) ".";
    font-family: var(--kpress-font-prose);
    grid-column: 1;
    text-align: right;
  }

  .kpress ol > li > * {
    grid-column: 2;
    hyphens: auto;
    overflow-wrap: break-word;
    word-break: break-word;
  }

  .kpress ol > li p,
  .kpress ol > li blockquote {
    text-align: left;
    text-justify: none;
  }

  .kpress ol ol,
  .kpress ol ul {
    margin-left: 1.5rem;
    margin-top: 0.5rem;
  }

  .kpress ol ol {
    counter-reset: none;
    list-style: decimal outside;
  }

  .kpress ol ul {
    list-style: none;
  }

  .kpress ol ol > li,
  .kpress ol ul > li {
    display: list-item;
    margin-top: 0.4rem;
    padding-left: 0.25rem;
    text-align: left;
    text-justify: none;
  }

  .kpress ol ol > li::before {
    content: none;
  }

  /* The drawn square, at this rule's own smaller marker size; document.css
     carries the measurement, and the em offsets there are why one pair of
     numbers serves both sizes. */
  .kpress ol ul > li::before {
    background: currentColor;
    content: "";
    font-size: calc(var(--kpress-font-size-base) * 0.7);
    height: 0.226em;
    inset-inline-start: calc(var(--kpress-font-size-base) * -0.85 + 0.064em);
    position: absolute;
    top: calc(var(--kpress-font-size-base) * 0.1 + 0.689em);
    width: 0.226em;
  }

  /* This selector outranks the shared forced-colors rule in components.css, so
     the nested print marker needs its own copy or it prints as nothing in a
     high-contrast profile. The reasoning is with that rule. */
  @media (forced-colors: active) {
    .kpress ol ul > li::before {
      background: CanvasText;
      forced-color-adjust: none;
    }
  }

  .kpress .kpress-long-list,
  .kpress .kpress-long-list > li {
    break-inside: auto;
    page-break-inside: auto;
  }

  .kpress-footnote-ref a,
  .kpress-footnote-backref,
  .footnote-ref a,
  .footnote {
    background-color: transparent;
    color: oklch(17.76% 0 0);
    font-family: var(--kpress-font-prose);
    font-feature-settings: normal;
    font-variant-caps: all-small-caps;
    font-weight: var(--_kpress-font-weight-prose);
    padding: 0 0.05rem;
  }

  .kpress-footnote-backref,
  a[href^="#fnref"],
  .reversefootnote {
    display: none;
  }

  .kpress .concepts {
    column-count: 2;
  }

  .kpress .video-gallery {
    display: block;
  }

  .kpress .kpress-video-placeholder {
    aspect-ratio: auto;
    background: transparent;
    border: 1px solid oklch(68.30% 0 0);
    color: oklch(17.76% 0 0);
    min-height: 0;
    padding: 0.75rem;
  }

  .kpress .kpress-video-placeholder-action {
    display: none;
  }

  .kpress .video-item,
  .kpress .boxed-text,
  .kpress .frame-capture,
  .kpress .key-claims,
  .kpress .summary,
  .kpress .concepts {
    break-inside: avoid;
    page-break-inside: avoid;
  }
}

/* kpress: css/mono-planetaire-400-normal.css */
/* Generated by `python -m devtools.subset_mono`; do not edit.

   Planetaire Mono Text regular, the face KPress sets code in. One
   stylesheet per style because `RenderOptions.mono_weights` decides which
   styles a document declares, and an inlined page carries every face it
   declares. Provenance: static/fonts/README.md. */

@font-face {
  font-family: "Planetaire Mono Text";
  font-style: normal;
  font-display: block;
  font-weight: 400;
  src: url("../fonts/planetaire-mono-text-latin-400-normal.0ecd530d8bdc55cc.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

/* kpress: css/mono-planetaire-700-normal.css */
/* Generated by `python -m devtools.subset_mono`; do not edit.

   Planetaire Mono Text bold, the face KPress sets code in. One
   stylesheet per style because `RenderOptions.mono_weights` decides which
   styles a document declares, and an inlined page carries every face it
   declares. Provenance: static/fonts/README.md. */

@font-face {
  font-family: "Planetaire Mono Text";
  font-style: normal;
  font-display: block;
  font-weight: 700;
  src: url("../fonts/planetaire-mono-text-latin-700-normal.5b01f48d9a28b265.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

/* kpress: css/mono-planetaire-400-italic.css */
/* Generated by `python -m devtools.subset_mono`; do not edit.

   Planetaire Mono Text italic, the face KPress sets code in. One
   stylesheet per style because `RenderOptions.mono_weights` decides which
   styles a document declares, and an inlined page carries every face it
   declares. Provenance: static/fonts/README.md. */

@font-face {
  font-family: "Planetaire Mono Text";
  font-style: italic;
  font-display: block;
  font-weight: 400;
  src: url("../fonts/planetaire-mono-text-latin-400-italic.079d54b75f6d5822.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}

/* kpress: css/mono-planetaire-700-italic.css */
/* Generated by `python -m devtools.subset_mono`; do not edit.

   Planetaire Mono Text bold-italic, the face KPress sets code in. One
   stylesheet per style because `RenderOptions.mono_weights` decides which
   styles a document declares, and an inlined page carries every face it
   declares. Provenance: static/fonts/README.md. */

@font-face {
  font-family: "Planetaire Mono Text";
  font-style: italic;
  font-display: block;
  font-weight: 700;
  src: url("../fonts/planetaire-mono-text-latin-700-italic.4665ca22f8e64518.woff2") format("woff2");
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
    U+FFFD;
}
