/*
 * Material substitutes a fixed blue for link text whenever primary is
 * black/white/grey/blue-grey (readable primary-coloured text isn't possible in
 * both light and dark scheme otherwise) - see palette.css,
 * [data-md-color-primary=black]{--md-typeset-a-color:#5e8bde}. That overrides
 * our own choice of accent, so put it back: links follow the accent colour
 * (Happy Hare's own #FF69B4 warning pink, see test/README.md) with Material's
 * own per-scheme contrast handling intact.
 */
/* Same element the palette rule above targets (body, via its data-md-color-*
 * attributes), and !important because it's specifically overriding a value
 * that theme sets on that exact element - a lower-specificity :root rule is
 * inherited by body, not compared against its own explicit value, and loses. */
body {
  --md-typeset-a-color: var(--md-accent-fg-color) !important;
}

/*
 * Slate (dark) scheme text contrast: nudge default foreground values a little
 * brighter than Material's stock slate palette for better contrast and easier reading.
 */
[data-md-color-scheme="slate"] {
  --md-default-fg-color: hsla(var(--md-hue), 15%, 95%, 0.96);
  --md-default-fg-color--light: hsla(var(--md-hue), 15%, 95%, 0.80);
  --md-default-fg-color--lighter: hsla(var(--md-hue), 15%, 95%, 0.48);
  --md-default-fg-color--lightest: hsla(var(--md-hue), 15%, 95%, 0.20);
  --md-default-bg-color: hsla(var(--md-hue), 15%, 12%, 1);
  --md-default-bg-color--light: hsla(var(--md-hue), 15%, 12%, 0.54);
  --md-default-bg-color--lighter: hsla(var(--md-hue), 15%, 12%, 0.26);
  --md-default-bg-color--lightest: hsla(var(--md-hue), 15%, 12%, 0.08);
  --md-code-fg-color: hsla(var(--md-hue), 20%, 90%, 0.95);
  --md-code-bg-color: hsla(var(--md-hue), 15%, 20%, 1)
}

[data-md-color-scheme="slate"] .md-typeset h1,
[data-md-color-scheme="slate"] .md-typeset h2,
[data-md-color-scheme="slate"] .md-typeset h3,
[data-md-color-scheme="slate"] .md-typeset h4,
[data-md-color-scheme="slate"] .md-typeset h5,
[data-md-color-scheme="slate"] .md-typeset h6 {
  color: hsla(var(--md-hue), 20%, 98%, 0.98);
}

/*
 * Colourised code blocks. SuperFences is the sole backtick-fence processor so
 * nested fences work; mkdocs.yml pins its wrapper class to `codehilite`.
 * Python-Markdown's codehilite extension uses the same wrapper for traditional
 * indented/:::lang blocks. Material's shipped CSS only maps Pygments token
 * colours beneath its normal `.highlight` wrapper, so these rules re-point the
 * equivalent `.codehilite` token classes at Material's own
 * --md-code-hl-*-color variables. Generated by diffing codehilite's token
 * classes against `.highlight .X{color:var(--md-code-hl-*)}` in Material's
 * compiled main.*.min.css - regenerate the same way if Pygments' class names
 * or Material's variable names ever change.
 */
.codehilite .c,.codehilite .c1,.codehilite .ch,.codehilite .cm,.codehilite .cs,.codehilite .sd{color:var(--md-code-hl-comment-color)}
.codehilite .bp,.codehilite .nb,.codehilite .no{color:var(--md-code-hl-constant-color)}
.codehilite .nc,.codehilite .ne,.codehilite .nf,.codehilite .nn{color:var(--md-code-hl-function-color)}
.codehilite .ge,.codehilite .gh,.codehilite .go,.codehilite .gp,.codehilite .gr,.codehilite .gs,.codehilite .gt,.codehilite .gu{color:var(--md-code-hl-generic-color)}
.codehilite .k,.codehilite .kd,.codehilite .kn,.codehilite .kp,.codehilite .kr,.codehilite .kt,.codehilite .nd,.codehilite .ni,.codehilite .nl,.codehilite .nt{color:var(--md-code-hl-keyword-color)}
.codehilite .kc,.codehilite .n{color:var(--md-code-hl-name-color)}
.codehilite .il,.codehilite .m,.codehilite .mb,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo{color:var(--md-code-hl-number-color)}
.codehilite .o,.codehilite .ow{color:var(--md-code-hl-operator-color)}
.codehilite .p{color:var(--md-code-hl-punctuation-color)}
.codehilite .cp,.codehilite .se,.codehilite .sh,.codehilite .sr,.codehilite .sx{color:var(--md-code-hl-special-color)}
.codehilite .cpf,.codehilite .l,.codehilite .s,.codehilite .s1,.codehilite .s2,.codehilite .sb,.codehilite .sc,.codehilite .si,.codehilite .ss{color:var(--md-code-hl-string-color)}
.codehilite .na,.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi{color:var(--md-code-hl-variable-color)}

/*
 * Code blocks get one additional font-size reduction for long
 * macro/gcode/command examples, including indented ::: blocks nested in
 * lists. Admonitions already use a smaller base font, so the more-specific
 * rule below cancels this extra reduction there; Material's existing `code`
 * font-size still applies. Cover .highlight as a defensive fallback for stale
 * generated HTML; mkdocs.yml pins current output to .codehilite.
 */
.md-typeset .codehilite > pre,
.md-typeset .highlight > pre {
  font-size: 0.85em;
}
.md-typeset .admonition .codehilite > pre,
.md-typeset .admonition .highlight > pre {
  font-size: inherit;
}

/*
 * Console-output blocks (```{.text .console-output}) attach the extra class
 * straight onto SuperFences' <div class="console-output codehilite"> wrapper,
 * including for fences nested in admonitions, lists, and tabs. The .highlight
 * selector remains as a defensive fallback for stale generated HTML. These
 * blocks get a distinct terminal-green instead of the default
 * text colour, so a block that's literal printer/console output at a
 * glance reads differently from a gcode command example or corpus of a
 * .cfg block, both plain ```text/```ini with no special colour. Two shades,
 * one per scheme, for contrast against each page background - same
 * attribute-selector convention as the mermaid dark-mode rule below
 * ([data-md-color-scheme=...] on <body>, not <html> - checked directly).
 * The indented `:::lang` shebang form has no attribute-attachment syntax;
 * use a nested backtick fence when a nested block needs this class.
 *
 * To present a command and its response as one terminal-style container while
 * retaining their different text colours, give the immediately preceding
 * command fence the `console-command` class. The paired rules remove the gap,
 * square the touching corners, and leave a subtle divider. `console-command`
 * is intentionally styled as half of a pair, so don't use it by itself.
 */
.md-typeset .codehilite.console-output code,
.md-typeset .highlight.console-output code {
  color: #2e7d32;
}
[data-md-color-scheme="slate"] .md-typeset .codehilite.console-output code,
[data-md-color-scheme="slate"] .md-typeset .highlight.console-output code {
  color: #4ade80;
}

.md-typeset .console-command > pre {
  margin-bottom: 0;
}
.md-typeset .console-command > pre > code {
  border-radius: 0.4rem 0.4rem 0 0;
}
.md-typeset .console-command + .console-output > pre {
  margin-top: 0;
}
.md-typeset .console-command + .console-output > pre > code {
  border-top: 0.05rem solid var(--md-default-fg-color--lightest);
  border-radius: 0 0 0.4rem 0.4rem;
}

/*
 * "See also" list, every page that has one - smaller and tighter than a
 * normal bullet list, so it reads as a compact index of links rather than
 * body content (which is what its list of bare page/anchor links otherwise
 * looks like at full size). `h2#see-also + ul` needs no markdown changes:
 * `toc`'s heading-id slugifier gives every page's own "## See also" the same
 * id, and it's always followed directly by the list with nothing in
 * between. margin-bottom overrides Material's own default of 0.5em
 * on `.md-typeset ul li`.
 */
.md-typeset h2#see-also + ul {
  font-size: 0.85em;
}
.md-typeset h2#see-also + ul li {
  margin-bottom: 0.1em;
}

/*
 * Section-separator marker on every H2, site-wide - a template-level
 * replacement for the wiki's per-heading tri-colour square images
 * (![#f03c15]![#c5f015]![#1589F0] before every major heading). Same three
 * colours, recreated as a single small CSS gradient swatch (no image files,
 * so it's free on every page including future ones, and it's crisp at any
 * zoom/scale) plus a thin rule underneath for stronger visual separation
 * between major sections - the ask this was added for.
 */
.md-typeset h2 {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  border-bottom: 1px solid var(--md-default-fg-color--lightest);
  padding-bottom: 0.3rem;
}
.md-typeset h2::before {
  content: "";
  flex: 0 0 auto;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 2px;
  background: linear-gradient(90deg,
    #f03c15 0%, #f03c15 33.33%,
    #c5f015 33.33%, #c5f015 66.66%,
    #1589f0 66.66%, #1589f0 100%);
}

/*
 * "On this page" (the secondary/right-hand sidebar TOC) - nested entries
 * (H3s under an H2, etc.) get a slightly smaller font than top-level ones.
 * Material nests a second `nav.md-nav` inside the `li.md-nav__item` of the
 * heading it belongs under, so "a .md-nav__link inside another .md-nav that
 * is itself inside a .md-nav__item" is exactly the nested set - top-level
 * links have no such inner nav ancestor. Scoped to `.md-sidebar--secondary`
 * specifically: the *primary* left-hand nav (page list) reuses this same
 * nesting pattern for its own sub-pages, and that one's untouched.
 */
.md-nav__item .md-nav .md-nav__link {
  font-size: 0.9em;
}
.md-nav__item .md-nav .md-nav__link .md-typeset {
  font-size: 0.9em;
}


/*
 * Closing footer (every page - see TOC.md's "Structure decisions locked
 * in"). Plain <pre>/<p>, not a fenced ```text block: a fenced block goes
 * through codehilite and picks up ITS box/background styling (see above),
 * which reads as "a code sample" - wrong register for what's decoration, not
 * something to copy. A bare <pre> still gets the browser's/Material's
 * monospace handling (needed so the ASCII art's spacing lines up) without
 * the codehilite wrapper, since nothing here is inside a <code> tag.
 */
/*
 * Tried an inline-SVG version of this art (viewBox-scaled text, controlled
 * by a single `width`) to get a reliably small size - reverted: it rendered
 * with garbled/overlapping lines in practice, worse than the sizing problem
 * it was meant to fix. Back to plain monospace text, just smaller than the
 * 0.7rem this had before.
 */
/*
 * "Happy Hare Ready" ASCII art now lives in the real theme footer bar, not
 * the article body - injected by hh-page-nav.js as the middle of the three
 * flex children in .hh-page-nav, between the Previous and Next links (moved
 * there 2026-08-07 from .md-footer-meta__inner, see TOC.md). align-self
 * centres it on the row's cross axis regardless of how tall the two link
 * blocks are; no horizontal margin of its own since .hh-page-nav's own
 * `gap` already spaces all three children evenly. Colour follows the
 * footer's own --md-footer-fg-color--lighter variable (same one "Made with
 * Zensical" uses) so it reads as part of that dark bar rather than the page
 * background it used to sit on.
 */
.hh-footer-art {
  font-family: var(--md-code-font-family, SFMono-Regular, Consolas, monospace);
  white-space: pre;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  align-self: center;
  line-height: 1.3;
  font-size: 0.5rem;
  color: var(--md-footer-fg-color--lighter);
}

/*
 * Copyright line ("Copyright (C) ... Paul Morgan"), the .md-copyright__highlight
 * div Zensical's own partials/copyright.html renders above "Made with
 * Zensical" when `copyright:` is set in mkdocs.yml (see TOC.md) - a touch
 * smaller than the theme's own default so it reads as a fine-print line
 * introducing "Made with Zensical" rather than a same-weight sibling of it.
 */
.md-copyright__highlight {
  font-size: 0.55rem;
}

/*
 * Previous/Next page nav. Injected by extra_javascript/hh-page-nav.js, not
 * written into each page's markdown - it reads the already-rendered primary
 * sidebar (which already has every real page in nav order, indistinguishable
 * from mkdocs.yml's own nav: list) rather than duplicating that order here.
 * Zensical doesn't render Material's own prev/next footer nav (no
 * .md-footer__link markup at all, checked directly - a real gap, not a config
 * miss), hence building it ourselves.
 */
/*
 * Lives inside the real theme <footer> now (moved 2026-08-07, see TOC.md),
 * as the first child of footer.md-footer, directly above .md-footer-meta -
 * not a separate band with its own background/margin. That placement does
 * three things at once, all for free:
 *   - full page width: <footer> itself isn't inside any max-width wrapper,
 *     so its background spans edge to edge without this rule needing one;
 *   - "slightly lighter than the very bottom footer": .md-footer already
 *     sets background-color:var(--md-footer-bg-color), which this nav just
 *     inherits by sitting inside it with no background of its own -
 *     .md-footer-meta below stacks --md-footer-bg-color--dark on TOP of
 *     that same base, so the meta bar reads darker without this nav needing
 *     an explicit colour at all;
 *   - zero gap before the footer proper: both are plain block children of
 *     the same <footer>, so there's no margin between them to remove.
 * Previous/Next aren't just centred in this full-width row - they're meant
 * to line up with the actual content column's left/right edges, which sit
 * inset from this row by however wide the sidebar(s) beside the article
 * happen to be. That inset varies (a page with no headings has no
 * secondary/TOC sidebar; the primary one disappears below Material's
 * mobile breakpoint) in a way this stylesheet has no reliable way to
 * compute on its own, so the real alignment is inline `padding-left`/
 * `padding-right` set by `syncPageNavToContent()` in hh-page-nav.js, which
 * measures the actual rendered content column and matches it exactly. The
 * padding below is only the pre-JS fallback/base case.
 */
/*
 * Grid, not flex: once that inline padding narrows the row to the content
 * column's own width, Previous still needs to pin to ITS left edge, Next to
 * its right edge, with the "Happy Hare Ready" art truly centred between
 * them - `1fr auto 1fr` gives the art column exactly its own content width
 * and splits everything else evenly either side of it, so the two outer
 * columns stay equal (and the art centred) no matter how long either
 * link's title text is. justify-items' default (stretch) then makes each
 * link fill its own column, so its content sits flush against that
 * column's outer edge rather than floating in the middle of it.
 */
.hh-page-nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1.5rem;
  padding: 0.4rem 0.75rem;
}
.hh-page-nav__link {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: var(--md-footer-fg-color);
}
.hh-page-nav__link:hover .hh-page-nav__title {
  color: var(--md-accent-fg-color);
}
.hh-page-nav__link--next {
  align-items: flex-end;
  text-align: right;
}
.hh-page-nav__label {
  font-size: 0.55rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--md-footer-fg-color--light);
}
.hh-page-nav__title {
  font-size: 0.75rem;
  font-weight: 600;
}

/*
 * Header: a bit taller, logo bigger, tagline under the site name. Header
 * height on this theme isn't a separate variable - .md-header__inner is
 * sized by its tallest child's own box (logo margin+padding+height, or the
 * title block), so growing the logo and adding a second title line both
 * just grow the sticky header naturally, no separate height override needed.
 */
/*
 * Logo sized to the largest height that still fits inside the header's own
 * min-height (5.6rem, below) without growing it: Material's compiled CSS
 * puts `margin:.2rem` + `padding:.4rem` on .md-header__button.md-logo (both
 * top and bottom), so the logo's total box is img-height + 1.2rem - solving
 * img-height + 1.2rem = 5.6rem gives the 4.4rem here. width is intentionally
 * left as `auto` (Material's own default) - doc_logo2.png is a wide
 * landscape image, so this comes out noticeably wider than the old square-
 * ish logo, not just taller.
 */
.md-header__button.md-logo img {
  height: 4.4rem !important;
}
/*
 * .md-header__title/.md-header__ellipsis/.md-header__topic have a hard 48px
 * height baked in to support Material's slide-swap animation between the
 * site name and the current page title on scroll. That swap works by
 * stacking BOTH .md-header__topic elements (site name, then page title) at
 * the exact same box - Material's own CSS gives both position:absolute with
 * no explicit inset, so each one's used position defaults to its "static"
 * (in-flow) position, which is identical for both only because neither one
 * actually occupies flow space. Giving the first one position:relative (an
 * earlier attempt at this) broke exactly that: it started occupying real
 * flow height again, which pushed the second topic's own static position
 * down below it instead of overlapping - the page title then rendered in the
 * tagline's spot instead of sliding into the site-name's spot on scroll.
 * Anchoring the tagline to .md-header__ellipsis itself instead (already
 * position:relative in Material's own CSS, and never touched by the topic
 * swap) avoids all of that: neither .md-header__topic gets modified, so the
 * swap's stacking is back to stock behaviour, and the tagline just floats
 * below the ellipsis box regardless of which topic is currently visible in
 * it. .md-header__inner keeps its extra min-height so there's header
 * background for the tagline to float onto.
 *
 * The tagline itself was rendering past the bottom of that background even
 * though the maths above says it should just fit - traced to `line-height`:
 * the ::after inherits the title block's own line-height (sized to the 48px
 * topic-swap box, for vertically centering "Happy Hare"), so its line box
 * was ~48px tall regardless of its 0.6rem font-size, pushing its painted
 * text well below the header's own (shorter) box, onto the page background
 * behind it. `line-height: 1` here collapses that back down to the glyph's
 * own height.
 */
.md-header__inner {
  min-height: 5.6rem;
}

/*
 * Anchor-scroll offset ("On this page"/TOC links, and any #heading URL) -
 * needs to clear the taller header above, or the target heading lands
 * partially behind the sticky bar and only the paragraph below it is
 * visible on landing. Material bakes its own default straight into
 * `.md-typeset :target` at build time (`--md-scroll-margin: 3.6rem`,
 * sized for its *stock* header height) rather than deriving it from the
 * header's actual rendered height, so growing the header above did not
 * grow this to match - confirmed directly: the real header renders at
 * 5.6rem (matching `.md-header__inner`'s min-height above, checked via
 * getBoundingClientRect at both desktop and mobile widths), while a
 * clicked TOC link was landing at Material's stock 3.6rem, leaving the
 * heading itself ~2rem behind the header. Same selector as Material's own
 * rule, so this simply overrides that one custom property in place
 * (equal specificity, later in the cascade) rather than fighting it with
 * a competing `scroll-margin-top`. 6rem, not exactly 5.6rem, for a touch
 * of breathing room below the header rather than the heading landing
 * flush against its bottom edge.
 */
.md-typeset :target {
  --md-scroll-margin: 6rem;
}

.md-header__ellipsis {
  position: relative;
}
.md-header__ellipsis::after {
  content: "Universal Automated Filament Changer / MMU driver for Klipper";
  position: absolute;
  top: 100%;
  left: 0;
  font-size: 0.6rem;
  font-weight: 400;
  line-height: 1;
  opacity: 0.75;
  margin-top: 0.3rem;
  white-space: nowrap;
}

/*
 * Search box: drop the platform keyboard-shortcut hint ("⌘K"/"Ctrl+K") for a
 * cleaner look - it's a nice-to-know, not something worth permanent header
 * real estate. Right padding shrinks to match, now that nothing's reserved
 * there.
 */
[dir="ltr"] .md-search__button {
  padding-right: 0.8rem;
}
.md-search__button::after {
  display: none;
}

/*
 * Widen the main content column. .md-grid's max-width caps the whole header
 * /main/footer row; the two sidebars are fixed-width, so raising this cap
 * hands all the extra room straight to .md-content (the actual doc column)
 * without needing a separate override there.
 */
.md-grid {
  max-width: 75rem;
}

/*
 * Mermaid diagrams (see doc/assets/javascripts/hh-mermaid.js) are deliberately
 * marked class="hh-mermaid", NOT "mermaid" - the plain "mermaid" class is what
 * mermaid.min.js's own bundled auto-render scans for on page load, which races
 * (and loses) against our own controlled render pass. Material's shipped CSS
 * therefore never sees these nodes either; this re-states its one rule for
 * ".mermaid" under our own class so the diagrams still get the same spacing.
 */
/*
 * White card behind every diagram, always - Mermaid's default theme (loaded
 * plain, no darkMode config) hard-codes dark text/lines calibrated for a
 * light page, and the SVG itself has no background of its own. Re-theming
 * Mermaid per-scheme would need re-rendering on every palette toggle
 * (hh-mermaid.js has no hook for that), so instead this card is unconditional
 * and dark scheme gets its own look via a CSS filter below rather than a
 * different Mermaid theme.
 */
.hh-mermaid {
  line-height: normal;
  margin: 1em 0;
  background: #fff;
  border-radius: 0.2rem;
  padding: 0.75rem;
}
/*
 * Dark scheme: invert the whole card instead of leaving it a white rectangle
 * on an otherwise dark page. invert(1) flips the white background to black
 * and every dark line/glyph to light; hue-rotate(180deg) then un-rotates the
 * hue shift invert() introduces on Mermaid's few non-grey colours (the pale
 * lavender actor-box fill/border), so they read as a dark, still-lavender-
 * tinted colour instead of inversion's default sickly yellow-green. Verified
 * directly against a real diagram's actual SVG (not just this rule's own
 * output): text glyphs land near-white, actor boxes land dark navy - not the
 * literal Mermaid "dark" theme's palette, but a legible dark-mode equivalent
 * of this site's existing light-mode diagrams, with no re-render needed.
 * Material sets this attribute on <body>, not <html> - checked directly
 * before relying on it here.
 */
[data-md-color-scheme="slate"] .hh-mermaid {
  filter: invert(1) hue-rotate(180deg);
}

/*
 * Floating "Back to top" button, injected by hh-page-nav.js once and reused
 * across instant navigations. Hidden by default and shown after the user has
 * moved down the page, so it stays available without adding noise at the top.
 */
.hh-back-to-top {
  position: fixed;
  left: 50%;
  bottom: 1rem;
  z-index: 30;
  border: 0;
  border-radius: 999px;
  padding: 0.5rem 0.85rem;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: var(--md-accent-fg-color);
  color: var(--md-accent-bg-color);
  box-shadow: 0 0.25rem 0.8rem rgba(0, 0, 0, 0.25);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 0.5rem);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
}

.hh-back-to-top:hover,
.hh-back-to-top:focus-visible {
  background: var(--md-accent-fg-color--dark, var(--md-accent-fg-color));
  color: var(--md-accent-bg-color);
  outline: none;
}

.hh-back-to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

@media screen and (max-width: 44.984375em) {
  .hh-back-to-top {
    bottom: 0.75rem;
  }
}
