/* ============================================================
   jellyroll-chrome.css — navigation chrome: sidebars, ToC,
   mobile header, search.
   ============================================================

   WHAT THIS FILE IS
   -----------------
   An *adapter*. furo reads every colour it paints from a documented set of
   ~60 CSS custom properties. So the way to re-skin it is not to fight its
   element rules with `!important` — it is to feed those properties JellyRoll
   values and let furo's own cascade do the work. Almost nothing here is an
   element rule, and nothing here is `!important`.

   This replaces the previous approach in `slim-docs.css`, which styled
   `.sphinxsidebar` — an *Alabaster* class. conf.py selects furo whenever furo
   imports, and furo emits `.sidebar-drawer` / `.sidebar-tree` / `.toc-drawer`.
   The published build therefore matched none of those selectors and fell
   through to furo's stock dark chrome. Both themes are handled below, because
   conf.py genuinely uses both: furo when installed, Alabaster as the laptop
   fallback.

   WHY NOT html_theme_options?
   ---------------------------
   furo does expose `light_css_variables` / `dark_css_variables`, which would be
   the idiomatic home for a furo-only palette. Three reasons it is the wrong
   vehicle here:

     1. Those options reach furo and nothing else. conf.py also falls back to
        Alabaster when furo is not importable, which is the normal state on a
        laptop. A palette in theme options leaves the fallback's rail unstyled,
        so the local preview would stop representing the published site — the
        exact class of drift that produced the bug this replaces.
     2. The rails are navy in BOTH modes. Expressing "always" through a
        light_ dict and a dark_ dict means defining it twice, and two copies of
        one intention drift.
     3. As `_static/*.css` the palette is a diffable asset that the SLIM
        application's own JellyRoll layer can share, rather than configuration.

   THE RAILS DO NOT FOLLOW THE MODE — THAT IS DELIBERATE
   -----------------------------------------------------
   JellyRoll defines `--color-surface-navbar: blue-1000` and documents it as
   "global nav — deepest navy, always". We honour "always": the rails are navy
   in light and dark, matching the SLIM application shell the docs are served
   inside. Only the content column follows the theme toggle.

   This is also why the rail tokens below are pinned to ramp *literals* rather
   than to furo's `--color-background-secondary`. furo's dark block re-declares
   that primitive; anything referencing it would silently flip to #1a1c1e
   neutral black in dark mode. Pinning makes the rails immune. Verified: furo's
   dark block hard-codes 33 primitives and re-declares no `--color-sidebar-*`
   or `--color-toc-*` token, so a single declaration set here holds in both
   modes.
*/

/* ─── Canvas propagation ─────────────────────────────────────────
   furo paints the canvas with `.skip-to-content,body,html { background:
   var(--color-background-primary) }`. That token is declared on `body`, so
   `html` resolves it against whatever `:root` holds — and upstream JellyRoll
   uses the same NAME for primary *button fill* (blue-600). The canvas went
   navy below the content, most visibly on index.

   Making html transparent restores CSS background propagation: with no
   background of its own, the root takes body's, so the canvas follows the mode
   in both directions instead of being pinned to one literal here. This is the
   same fix the previous stylesheet used, kept because the trap is real — but
   without `!important`, since this file loads after the theme stylesheet and
   wins on source order at equal specificity. */
html {
  background: transparent;
}

body {
  /* ── furo primitives → JellyRoll content semantics ──
     Setting these four is what colours the document column; furo's own rules
     (`.skip-to-content,body,html`, `.back-to-top`, admonitions, tables …) all
     read from them, so no per-element overrides are needed. */
  --color-background-primary:   var(--jr-canvas);
  --color-background-secondary: var(--jr-canvas-sunken);
  --color-foreground-primary:   var(--jr-ink);
  --color-foreground-secondary: var(--jr-ink-subtle);
  --color-foreground-muted:     var(--jr-ink-subtle);
  --color-foreground-border:    var(--jr-border-default);
  --color-background-border:    var(--jr-border-subtle);
  --color-background-hover:     var(--jr-canvas-sunken);
  --color-background-item:      var(--jr-border-default);

  /* Content links. Pinned rather than routed through --color-brand-content
     because furo's dark block re-declares the brand tokens. */
  --color-link:                       var(--jr-link);
  --color-link--hover:                var(--jr-link-hover);
  --color-link--visited:              var(--jr-link-visited);
  --color-link--visited--hover:       var(--jr-link-hover);
  --color-link-underline:             var(--jr-border-default);
  --color-link-underline--hover:      var(--jr-link-hover);
  --color-link-underline--visited:    var(--jr-border-default);
  --color-link-underline--visited--hover: var(--jr-link-hover);
  --color-brand-primary: var(--jr-link);
  --color-brand-content: var(--jr-link);
  --color-brand-visited: var(--jr-link-visited);

  /* ── Left rail (navigation) — navy, always ──
     Contrast against blue-1000 #020D24 / hover indigo-800 #1D2A4F:
       grey-400  #C4CAD6  11.76:1  /  8.53:1
       blue-300  #9CB3E1   9.16:1  /  6.65:1
       teal-300  #9DCDCC  11.09:1  /  8.05:1
       grey-600  #9CA7BA   7.97:1  /  5.78:1
       white               19.34:1 / 14.03:1
     All pass AA; all but two pass AAA. Compare the scheme this replaces:
     blue-600 #0841B4 on #1a1c1e was 1.97:1, and visited blue-800 was 1.23:1. */
  --color-sidebar-background:        var(--sl-blue-1000);
  --color-sidebar-background-border: var(--sl-indigo-800);
  --color-sidebar-brand-text:        var(--sl-white);
  /* Group headings were grey-600 (7.97:1), dimmer than the blue-300 page links
     they head. Grey-300 #D7DCE3 is 14.55:1 on blue-1000 and reads as the
     brightest thing in the rail after the brand, which is what a heading should
     be. Part of the hierarchy fix - see "Chrome type scale" below. */
  --color-sidebar-caption-text:      var(--sl-grey-300);
  --color-sidebar-link-text:         var(--sl-grey-400);
  --color-sidebar-link-text--top-level: var(--sl-blue-300);

  --color-sidebar-item-background:          var(--sl-blue-1000);
  --color-sidebar-item-background--current: var(--sl-indigo-800);
  /* furo ships a left-to-right gradient here so the tint bleeds off the edge.
     A flat fill reads better against a saturated navy and matches JellyRoll,
     which is flat by default and reserves gradients for brand moments. */
  --color-sidebar-item-background--hover:   var(--sl-indigo-800);
  --color-sidebar-item-expander-background:        transparent;
  --color-sidebar-item-expander-background--hover: var(--sl-indigo-700);

  /* THE SEARCH FIELD, FIXED 3 Sep 2026 - entered text was invisible.
     Reported as "illegible given the lack of contrast"; it was not low contrast,
     it was 1.00:1 - the ink was the same colour as the field, exactly.

     furo has a define/consume mismatch of its own here. It DEFINES
     `--color-sidebar-search-text` (which is why that name was overridden below,
     and why the override looked right) but its `.sidebar-search` rule reads
     `color: var(--color-sidebar-search-foreground)`, a name nothing defines -
     not furo, not this file. An undefined `var()` with no fallback makes the
     declaration invalid at computed-value time, so `color` falls back to the
     INHERITED value, which is `body { color: var(--color-text-body) }` from
     jellyroll-tokens.css: indigo-900, #131C35. That is the same indigo-900 set
     as the field background on the line below. Ink and field, one colour.

     Only typed text was affected. furo never styles the placeholder, so it kept
     the user agent's own grey and stayed visible - which is why the field looked
     fine until someone used it.

     Both names are set. The one furo reads today is `--foreground`; `--text` is
     kept so this keeps working if furo ever fixes its rule to match its own
     variable. There is also an element rule further down, because a variable
     override cannot protect against the name changing again. */
  --color-sidebar-search-foreground:      var(--sl-white);   /* the one furo reads */
  --color-sidebar-search-text:            var(--sl-white);   /* the one furo defines */
  --color-sidebar-search-background:      var(--sl-indigo-900);
  --color-sidebar-search-background--focus: var(--sl-indigo-800);
  --color-sidebar-search-border:          var(--sl-indigo-700);
  --color-sidebar-search-icon:            var(--sl-grey-600);

  /* Three more variables furo consumes and never defines, found by the same
     audit. Same failure mode: the declaration is dropped and the property falls
     back to its inherited or initial value, silently.

       --color-background-muted    `.toc-drawer { border-left }` - live on all 57
                                   pages; the right rail's separator is simply
                                   absent. Cosmetic, but it is absent by accident.
       --color-code-*              `pre.literal-block`, `.code-block-caption` and
                                   `.highlighttable td.linenos`. Zero pages emit
                                   any of the three today, so this is latent - it
                                   arrives with the first `.. code-block::` that
                                   carries a `:caption:` or line numbers, and it
                                   would arrive as unreadable code. Set now.

     Inline literals and highlighted blocks are handled in jellyroll-content.css
     and do not use these. */
  --color-background-muted: var(--sl-indigo-800);
  --color-code-background:  var(--jr-code-bg);
  --color-code-foreground:  var(--color-text-body);

  /* ── Right rail ("On this page") — same navy ── */
  --color-toc-background:      var(--sl-blue-1000);
  --color-toc-title-text:      var(--sl-grey-300);   /* matches the left rail's headings */
  --color-toc-item-text:       var(--sl-grey-400);
  --color-toc-item-text--hover: var(--sl-white);
  --color-toc-item-text--active: var(--sl-teal-300);

  /* ── Mobile header — chrome, so navy too ── */
  --color-header-background: var(--sl-blue-1000);
  --color-header-border:     var(--sl-indigo-800);
  --color-header-text:       var(--sl-white);

  /* ── Chrome type scale ──
     THE SIZE FIX. furo expresses every chrome size as a PERCENTAGE, tuned
     against its own `html { font-size: 110% }` (= 17.6px). The previous
     stylesheet set `body { font-size: 14px !important }`, so those percentages
     compounded from 14px instead and every label shrank ~20.5%:

       sidebar items   87.5%   15.4px intended -> 12.25px  ( 9.2pt)
       captions        81.25%  14.3px          -> 11.38px  ( 8.5pt)
       ToC items       75%     13.2px          -> 10.50px  ( 7.9pt)
       ToC title       62.5%   11px            ->  8.75px  ( 6.6pt)

     The last two fell below the smallest step JellyRoll even defines
     (small = 12px, micro = 10px). Worse, furo's *spacing* is in `rem`
     (root-relative) so it did NOT shrink: rows kept a 35.2px box around 12.25px
     glyphs, which is the "tiny text floating in lots of space" effect.

     Fixed two ways. First, absolute px below, so no percentage can ever
     compound again. Second — and this is why `body { font-size }` is NOT set
     anywhere in this asset set — leaving body at furo's 17.6px also restores
     the rail *widths*: furo sizes them `15em`, which had collapsed from 264px
     to 210px under the 14px body. Content type is set on the content column
     instead, in jellyroll-content.css.

     Values are JellyRoll steps: body 14px/22px for navigation rows, label
     14px/22px/800 for the uppercase group headings.

     THE HIERARCHY FIX (2 Sep 2026). The first pass set the two uppercase titles
     to JellyRoll's `small` step, 12px, against 14px navigation rows. That
     inverts the hierarchy: a section heading was SMALLER than the pages filed
     under it, and dimmer as well (caption grey-600 against top-level link
     blue-300), so the rail read as a flat list with faint dividers rather than
     as groups. It was wrong in furo too, not just in the fallback — furo's stock
     default is `--font-size--small--2`, two steps below its item size, and
     inheriting the shape of that default was the mistake.

     Captions now take JellyRoll's `label` recipe - 14px/22px at weight 800,
     uppercase, tracked 0.06em - which is the step the design system defines for
     exactly this job. Size parity with the rows is deliberate: the heading is
     distinguished by weight, case, tracking and a brighter ink (see the caption
     element rule below), not by outgrowing its own contents. JellyRoll's next
     step up, `cardtitle` at 18px, was measured and rejected: "Understanding
     SLIM" and "Help and Reference" wrap in a 15em rail at 18px extrabold
     uppercase, and a wrapped heading destroys the grouping this is meant to
     create. */
  --sidebar-item-font-size:         14px;   /* 10.5pt - JellyRoll body  */
  --sidebar-caption-font-size:      14px;   /* 10.5pt - JellyRoll label */
  --sidebar-search-input-font-size: 14px;   /* 10.5pt */
  --toc-font-size:                  14px;   /* 10.5pt */
  --toc-font-size--mobile:          14px;
  --toc-title-font-size:            14px;   /* 10.5pt - matches the left rail */

  /* Row height follows the text: JellyRoll's 22px body line. furo derives
     --sidebar-item-height from this, so rows become 22 + 2x8 = 38px and the
     larger label sits in a proportionate box rather than a roomy one. */
  --sidebar-item-line-height: 22px;
  --sidebar-item-spacing-vertical: 8px;

  /* ── Grouping ──
     Spacing carries the grouping, and it only reads as grouping if the two gaps
     differ. furo's defaults are 1rem above a caption (17.6px) against 8px of
     item padding below it - 25.6px versus 16px, close enough to read as evenly
     spaced. Both values move onto the JellyRoll 4-pt grid and pull apart:

       last item of a section  ->  next caption   8 + 20 + 8  = 36px
       caption                 ->  its first item      4 + 8   = 12px

     Three to one. Pages sit against their heading; sections separate without a
     rule or a divider, which JellyRoll does not use here. The 4px bottom is set
     on the caption element below, because furo pays for caption padding with a
     single symmetrical `--sidebar-item-spacing-vertical`. */
  --sidebar-caption-space-above: var(--space-5);   /* 20px, was 1rem  */
  --sidebar-tree-space-above:    var(--space-4);   /* 16px, was 1.5rem */
  --sidebar-item-spacing-horizontal: var(--space-4);  /* 16px, was 1rem */

  --admonition-font-size:       14px;
  --admonition-title-font-size: 14px;
  --code-font-size:             12px;       /* JellyRoll --type-small-size */
}

/* ─── Chrome controls that furo colours from content tokens ─────
   These four read --color-foreground-primary / --color-foreground-secondary,
   which we have just mapped to *content* ink. They sit on navy, so in light
   mode they would be near-black on near-black. They are the only element rules
   in this file, and each exists because furo has no dedicated token for it. */
.theme-toggle svg {
  color: var(--sl-grey-400);
}
.theme-toggle:hover svg {
  color: var(--sl-white);
}
.nav-overlay-icon .icon,
.toc-overlay-icon .icon {
  color: var(--sl-grey-400);
}
.sidebar-brand-text {
  font-weight: var(--type-heading-weight);
}

/* Uppercase section captions. JellyRoll tracks uppercase labels at 0.06em and
   sets the `label` recipe at weight 800; furo applies the transform and its own
   700, so the transform is left alone and the weight is corrected.

   The asymmetric padding is the grouping: furo pays for caption padding with one
   symmetrical `--sidebar-item-spacing-vertical`, so a caption that hugs its list
   cannot be expressed in variables alone. 8px above / 4px below, against the
   20px `--sidebar-caption-space-above` margin, is what produces the 36px:12px
   ratio described in "Grouping" above. */
.sidebar-tree .caption,
.sidebar-tree :not(.caption) > .caption-text,
.toc-title {
  font-weight: var(--type-label-weight);
  letter-spacing: 0.06em;
}

.sidebar-tree .caption,
.sidebar-tree :not(.caption) > .caption-text {
  padding: var(--space-2) var(--space-4) var(--space-1);
}

/* The rail is its own scroll container in furo (`.sidebar-scroll` is
   `overflow: auto` inside a `position: sticky` 100vh column). What furo does not
   set is scroll chaining: when the rail reaches its end, the wheel carries on
   into the document, so a flick over the navigation scrolls the page. Containing
   the overscroll makes the rail scroll only while the pointer is over it and only
   while it has somewhere left to go. Alabaster gets the same treatment further
   down, where the sticky column has to be built rather than configured. */
.sidebar-scroll,
.toc-scroll {
  overscroll-behavior: contain;
}

/* ─── The rail search field (furo) ───────────────────────────────
   The colour is declared as an element rule as well as through the variable
   above, and deliberately so. The variable override alone is hostage to furo
   keeping the name it reads today, and the name it reads today is one furo does
   not define - so the override that was in place looked correct, matched a real
   furo variable, and did nothing. `.sidebar-search` is (0,1,0), the same as
   furo's own rule, and this file loads after furo's, so source order decides.

   White on indigo-900 is 16.86:1 at rest and 14.03:1 on focus, where furo swaps
   the container to indigo-800.

   The placeholder is set explicitly because furo styles only its font-size and
   otherwise leaves the user agent's grey on a dark field. grey-600 gives 6.95:1
   at rest and 5.78:1 focused - both clear AA, and it stays distinct from the
   entered text so the field still reads as empty when it is. */
.sidebar-search {
  color: var(--sl-white);
}

.sidebar-search::placeholder {
  color: var(--sl-grey-600);
  opacity: 1;          /* Firefox applies a default opacity to placeholders */
}

/* furo defines `--color-toc-item-text--hover` and never reads it, so the right
   rail had no hover response at all. Same class of defect as the search field,
   with no contrast consequence - just a dead interaction. */
.toc-tree a.reference:hover {
  color: var(--color-toc-item-text--hover);
}

/* Keyboard focus. JellyRoll's focus ring is teal-600, which is invisible
   against navy, so the rails get the light teal step instead. Without this the
   rails have no visible focus indicator at all (WCAG 2.4.7). */
.sidebar-drawer a:focus-visible,
.toc-drawer a:focus-visible,
.sidebar-search:focus-visible,
.theme-toggle:focus-visible {
  outline: 2px solid var(--sl-teal-300);
  outline-offset: 2px;
}

/* ============================================================
   Alabaster fallback
   ============================================================
   conf.py falls back to Alabaster when furo is not importable, which is the
   normal state on a laptop — both current builds in _build/ are Alabaster.
   The previous stylesheet's `.sphinxsidebar` rules were dead under furo but
   live here, so deleting them outright would have regressed the local preview
   into unstyled chrome. They are ported to the navy palette instead, so both
   themes render the same design and the preview stays representative.

   Alabaster has one rail only; there is no right-hand ToC and no mode toggle,
   so there is nothing mode-dependent to do.

   WHAT ALABASTER DOES NOT GIVE YOU, AND FURO DOES
   -----------------------------------------------
   furo builds its rail as `position: sticky; height: 100vh` around an
   `overflow: auto` scroller, and paints the drawer itself, so the panel is
   full-height and independently scrollable for free. Alabaster does none of
   that: `div.sphinxsidebar` is an ordinary in-flow block, 300px wide, as tall as
   its own content. Three consequences, all reported against the local preview on
   2 Sep 2026 and all fixed below rather than in furo, because furo never had
   them:

     - the rail scrolls away with the document (fixed: sticky column);
     - the navy stops just under the last link, leaving white beneath it
       (fixed: full-height column plus a page-length stripe);
     - the group headings are smaller than the pages beneath them
       (fixed here and in the furo variables above - this one was in both). */
div.sphinxsidebar {
  background: var(--sl-blue-1000);
  border-right: 1px solid var(--sl-indigo-800);
  color: var(--sl-grey-400);
  font-family: var(--font-sans);
  font-size: 14px;          /* 10.5pt — was Alabaster's 0.9em of a 14px body */
  line-height: 22px;
}

div.sphinxsidebarwrapper {
  padding: var(--space-5) 0;   /* the rows carry their own horizontal padding */
}

/* The rail's own title ("Navigation"), which Alabaster emits and furo has no
   equivalent of. Kept one step BELOW the group headings, at JellyRoll `small`
   with the label weight: it names the rail, so it must not compete with the
   headings inside it. Alabaster's stock rule here is Georgia 24px, heavier than
   anything else in the panel. */
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
  color: var(--sl-grey-600);
  font: var(--type-label-weight) 12px/18px var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0;
  padding: var(--space-2) var(--space-4);
}

/* Group headings. Alabaster gives `p.caption` `font-size: inherit` and no weight,
   then sizes the links at `font-size: 120%` - so a heading rendered at 14px above
   its own pages at 16.8px. Both halves of that are corrected: the heading takes
   JellyRoll's `label` recipe, the rows take `body`, and the margins produce the
   same 36px:12px grouping as the furo rail. */
div.sphinxsidebar p.caption {
  color: var(--sl-grey-300);
  font: var(--type-label-weight) var(--type-label-size)/var(--type-label-line) var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: var(--space-5) 0 0 0;
  padding: var(--space-2) var(--space-4) var(--space-1);
}

div.sphinxsidebar p.caption .caption-text {
  color: inherit;
}

div.sphinxsidebar ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

div.sphinxsidebar ul ul {
  margin-left: var(--space-4);
}

/* Full-width rows, so the hover tint is a band rather than a smear behind the
   glyphs, and every row is the same 38px as furo's (22px line + 2x8px).

   The `ul` in the middle of these selectors is load-bearing and must not be
   tidied away. Alabaster's own rules are `div.sphinxsidebar ul li.toctree-l1 > a`
   (0,2,4) and `… li.toctree-l2 > a`, and the shorter form written here first -
   `div.sphinxsidebar li.toctree-l1 > a` - is (0,2,3), so it LOST the cascade and
   the rows kept rendering at Alabaster's 120% and 110%. That left the group
   headings at 14px under 16.8px page entries: the exact inversion this file is
   meant to fix, still on the page, with a rule in it that looked like the fix.
   Matching Alabaster's selector shape makes the two equal on specificity, and
   this file loads after `alabaster.css`, so later wins. */
div.sphinxsidebar ul li.toctree-l1 > a,
div.sphinxsidebar ul li.toctree-l2 > a,
div.sphinxsidebar ul li.toctree-l3 > a {
  display: block;
  font-size: var(--type-body-size);   /* kills Alabaster's 120% / 110% */
  line-height: var(--sidebar-item-line-height);
  padding: var(--space-2) var(--space-4);
}

/* Top-level entries take the same accent furo gives them. */
div.sphinxsidebar ul li.toctree-l1 > a {
  color: var(--sl-blue-300);
}

div.sphinxsidebar li.current > a {
  background: var(--sl-indigo-800);
}

div.sphinxsidebar a,
div.sphinxsidebar p,
div.sphinxsidebar ul,
div.sphinxsidebar li {
  color: var(--sl-grey-400);
}

div.sphinxsidebar a {
  text-decoration: none;
  /* Alabaster underlines every sidebar link with `border-bottom: 1px dotted
     #999`, which draws a dotted rule under all 20 rows. */
  border-bottom: none;
}

div.sphinxsidebar a:hover {
  background: var(--sl-indigo-800);
  color: var(--sl-white);
  text-decoration: underline;
  border-bottom: none;
}

div.sphinxsidebar a:focus-visible {
  outline: 2px solid var(--sl-teal-300);
  outline-offset: 2px;
}

/* ─── Search (Alabaster only) ─────────────────────────────────────
   furo's rail search is a single bare input with a masked icon and no button, so
   none of this exists there - it is not a published-site concern. Alabaster
   emits `<input type="text">` next to `<input type="submit" value="Go">` and
   styles both with one rule: `border: 1px solid #CCC; font-family: Georgia`.
   The submit therefore renders as the user agent's default push button - the
   plain grey box reported on 2 Sep 2026 - which is the one control in the panel
   that looked like it belonged to another site. */
div.sphinxsidebar #searchbox {
  margin: 0 var(--space-4) var(--space-4);
}

div.sphinxsidebar form.search {
  display: flex;
  gap: var(--space-2);
}

div.sphinxsidebar input[type="text"] {
  background: var(--sl-indigo-900);
  border: 1px solid var(--sl-indigo-700);
  border-radius: var(--radius-sm);
  color: var(--sl-white);
  font: 400 14px/22px var(--font-sans);
  padding: 6px 8px;
  flex: 1 1 auto;
  min-width: 0;             /* lets the field shrink inside the flex row */
}

div.sphinxsidebar input[type="text"]::placeholder {
  color: var(--sl-grey-600);
}

div.sphinxsidebar input[type="text"]:focus {
  background: var(--sl-indigo-800);
  border-color: var(--sl-teal-300);
  outline: none;
}

/* JellyRoll primary button: blue-600 fill, white label at the `label` recipe,
   radius-sm. White on blue-600 is 8.64:1.

   Hover is blue-700 (11.08:1) and NOT `--color-background-primary-hover`, which
   JellyRoll defines as the teal-to-blue gradient. That token was tried and
   rejected on contrast: the gradient's first stop is #0B9D9B, where white text
   falls to 2.9:1 and fails AA for 14px. A gradient whose left half cannot carry
   its own label is not usable for a button this small, so the hover darkens
   within the blue ramp instead. Pressed uses teal-400 with dark ink, which is
   the one place the teal accent clears AA (8.13:1 against indigo-900). */
div.sphinxsidebar input[type="submit"] {
  background: var(--sl-blue-600);
  border: 1px solid var(--sl-blue-600);
  border-radius: var(--radius-sm);
  color: var(--sl-white);
  cursor: pointer;
  font: var(--type-label-weight) var(--type-label-size)/var(--type-label-line) var(--font-sans);
  padding: 6px var(--space-3);
  flex: 0 0 auto;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard);
}

div.sphinxsidebar input[type="submit"]:hover {
  background: var(--sl-blue-700);
  border-color: var(--sl-blue-700);
}

div.sphinxsidebar input[type="submit"]:active {
  background: var(--sl-teal-400);
  border-color: var(--sl-teal-400);
  color: var(--sl-indigo-900);
}

div.sphinxsidebar input[type="submit"]:focus-visible {
  outline: 2px solid var(--sl-teal-300);
  outline-offset: 2px;
}

/* ─── The sticky, full-height rail (Alabaster only) ───────────────
   Scoped to Alabaster's own single breakpoint. Above 1200px the rail is the
   left 300px of a centred 1200px `div.document`; at 1200px and below Alabaster
   restacks it as a full-width block after the content
   (`div.sphinxsidebar { display: block; float: none; margin: 50px -30px … }`),
   where a sticky 100vh column and a 300px stripe would both be wrong. */
@media screen and (min-width: 1201px) {
  /* Alabaster opens `div.document` with `margin: 30px auto 0`, which would leave
     a 30px white band above a rail pinned to `top: 0`. The gap moves onto the
     content column, where it was doing the real work anyway, so the panel can
     start at the top of the viewport. */
  div.document {
    margin-top: 0;
    min-height: 100vh;
  }

  div.body {
    padding-top: var(--space-8);
    /* Alabaster hard-codes `background-color: #fff` here, so in dark mode the
       content column stayed white inside a dark canvas. Not one of the five
       reported items, but it is this rule that the stripe below sits against,
       and the stripe made it obvious. */
    background: var(--jr-canvas);
  }

  /* THE COLUMN. `overflow-y: auto` on a fixed height is what makes the rail its
     own scroller: it scrolls only when the tree is taller than the viewport, and
     only while the pointer is over it. `overscroll-behavior: contain` stops the
     wheel chaining into the document at either end. `max-height: none` is
     required - Alabaster sets `max-height: 100%`, which would clamp the 100vh. */
  div.sphinxsidebar {
    position: sticky;
    top: 0;
    height: 100vh;
    max-height: none;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-color: var(--sl-indigo-700) transparent;
    scrollbar-width: thin;
  }

  /* THE STRIPE. The sticky column paints navy for the height of the viewport,
     which covers every scroll position - but `div.footer` sits outside
     `div.document`, below the column's containing block, so the bottom of a
     scrolled-to-the-end page would show white beside the footer. Painting the
     same 300px band on `body` closes that: the background box is sized to
     `div.document`'s own 1200px and centred the same way, so the band lands
     exactly under the rail and runs the full length of the page rather than the
     height of the panel.

     `background-image` rather than the `background` shorthand on purpose -
     jellyroll-tokens.css sets `body { background: var(--color-surface-primary) }`
     and the shorthand would take the canvas colour with it.

     `:has(div.sphinxsidebar)` is what keeps this rule off the published site, and
     it is the reason this is not simply `body`. Every other rule in this section
     names Alabaster-only markup and so is inert under furo, but `body` exists in
     both themes: unscoped, this stripe would paint a 300px navy band down the
     left edge of a centred 1200px box on the furo build, where the real sidebar
     is a `position: fixed` drawer at the left edge of the *viewport*. The band
     would land in the wrong place, over content, on the site readers actually
     see. `div.sphinxsidebar` is emitted by Alabaster and by nothing in furo
     (checked: 2 occurrences in the Alabaster build's index.html, 0 in furo's).

     Where `:has()` is unsupported the stripe is dropped and the sticky column
     still paints navy for the full viewport at every scroll position; the only
     loss is the band beside the footer. That is the right way round for a
     progressive enhancement, and it is a fallback theme regardless. */
  body:has(div.sphinxsidebar) {
    background-image: linear-gradient(
      to right,
      var(--sl-blue-1000) 0 300px,
      transparent 300px
    );
    background-repeat: no-repeat;
    background-position: top center;
    background-size: 1200px 100%;
  }
}
