/* ============================================
   General — Base styles and content
   RamJet mdBook theme
   ============================================ */

/* --- Base --- */

:root {
    color-scheme: var(--color-scheme);
}

html {
    -webkit-tap-highlight-color: transparent;
    font-family: var(--body-font);
    font-size: 16px;
    color: var(--page-foreground);
    background-color: var(--page-background);
    text-size-adjust: none;
    -webkit-text-size-adjust: none;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-optical-sizing: auto;
    scrollbar-gutter: stable;
    /* Smooth-scroll in-page anchor jumps (clicking a content heading scrolls
       it up to just under the sticky header, matching the "On this page"
       links). scroll-padding-top mirrors the JS offset used there
       (menu-bar height + 16px). This only affects same-page fragment
       navigation — cross-document page transitions land at the top instantly,
       so the transition itself never animates a scroll. */
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--menu-bar-height) + 16px);
}

/* 1.6, not the 1.5 this was. The measure is what decides it: the content
   column runs 780px at 16px, which is ~94 characters — long, and a long line
   needs more leading for the eye to find the start of the next one. 1.5 is the
   floor a short measure can hold and the one WCAG asks a page to survive, not
   a value to set a 94-character column at.

   Prose in this book is also dense with inline code, and each chip is a padded
   box on the line box: at 1.5 a chip sat 2.5px under the chip above it and a
   paragraph of them read as a grid of tiles. 1.6 is 4px, which is the
   difference between a run of text and a wall of it. Code blocks keep 1.5 —
   code wants its lines closer, not further. */
body {
    margin: 0;
    font-size: inherit;
    line-height: 1.6;
}

code {
    font-family: var(--mono-font);
    font-size: var(--code-font-size);
    font-variant-ligatures: none;
    direction: ltr;
}

main {
    overflow-wrap: break-word;
}

strong,
b {
    font-weight: 550;
}

/* --- Typography --- */

h1,
h2,
h3,
h4,
h5,
h6 {
    color: var(--page-foreground);
    font-weight: 560;
    line-height: 1.25;
    letter-spacing: -0.01em;
}

h1 {
    font-size: 2.25em;
    margin: 2.25rem 0 1.375rem;
    font-weight: 650;
    font-optical-sizing: none;
}
h2 {
    font-size: 1.75em;
    margin: 2.625rem 0 1rem;
    padding-bottom: 0.25em;
    font-weight: 620;
    font-optical-sizing: none;
    /* Painted rather than bordered so it can carry the two-colour fade.
       --separator-color is the flat fallback. */
    border-bottom: 2px solid transparent;
    background-image: var(--separator-gradient, linear-gradient(
        var(--separator-color), var(--separator-color)));
    background-repeat: no-repeat;
    background-position: left bottom;
    background-size: 100% 2px;
}
h3 {
    font-size: 1.4375em;
    margin: 2.125rem 0 0.6rem;
}
h4 {
    font-size: 1.188em;
    margin: 1.4em 0 0.5rem;
    line-height: 1.3;
}
h5 {
    font-size: 1.125em;
}
h6 {
    font-size: 1em;
}
h5,
h6 {
    margin: 1.3em 0 0.4rem;
    line-height: 1.36;
}

/* Don't change font size in headers */
h1 code,
h2 code,
h3 code,
h4 code,
h5 code,
h6 code {
    font-size: unset;
}

/* Consecutive headers — tighter spacing */
.header + .header h3,
.header + .header h4,
.header + .header h5 {
    margin-block-start: 1em;
}

/* Target indicator */
h1:target::before,
h2:target::before,
h3:target::before,
h4:target::before,
h5:target::before,
h6:target::before,
dt:target::before {
    display: inline-block;
    content: "»";
    margin-inline-start: -35px;
    width: 35px;
    text-align: center;
    transform: translateY(-1px);
    color: var(--gray-500);
}

/* page subtitle  (em after h1) */
h1 + p em {
    display: block;
    margin-top: -1.2rem;
    margin-bottom: 2rem;
    font-style: normal;
    color: var(--gray-500);
}

:target {
    scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
}

sup,
sub {
    font-size: 0.688rem;
    font-weight: 520;
    color: var(--link-color);
}

small {
    line-height: 1.55;
    color: var(--page-foreground-alt);
    font-size: 0.8em;
}
small strong,
small b {
    color: var(--page-foreground);
}

/* --- Content area --- */

.content {
    padding: 0 var(--page-padding) 3rem;
    min-width: 0;
}

.content main {
    max-width: var(--content-max-width);
    margin-inline: auto;
    min-height: calc(100% - 16.5rem);
}

/* A paragraph break has to read as one. At 14px against a 24px line it was
   less than a line of space and consecutive paragraphs ran together; 18px
   against 25.6px is clearly more than a line, which is what says the thought
   ended. */
.content p {
    margin: 18px 0;
}
.content .blockquote-tag p,
.content .markdown-alert p,
.content blockquote p {
    margin: 10px 0;
}
.content p.blockquote-tag-title,
.content p.markdown-alert-title {
    margin: 0;
}
.content .blockquote-tag p.blockquote-tag-title + p,
.content .markdown-alert p.markdown-alert-title + p {
    margin-top: 8px;
}

.content ol,
.content ul {
    line-height: 1.6;
}

.content li {
    margin: 10px 0;
}
.content li > p {
    margin: 0;
}

.content a {
    text-decoration: none;
    color: var(--link-color);
}
.content p a,
.content li a,
.content dd a,
.content td a {
    font-weight: 500;
}
.content a:hover {
    text-decoration: underline;
}

/* A Lini figure is an image the compiler drew, so it wears the picture
   treatment rather than one of its own: rounded in the column, full bleed on a
   phone. The wrapper is the framed box — it scrolls, so it already clips, and
   the radius has something to cut. */
.content img,
.content video,
.content .lini-figure {
    max-width: 100%;
    border-radius: var(--border-radius-lg);
}

@media only screen and (max-width: 768px) {
    .content img,
    .content video,
    .content .lini-figure {
        margin-left: calc(var(--page-padding) * -1);
        margin-right: calc(var(--page-padding) * -1);
        max-width: 100vw;
        border-radius: 0;
    }
    .content ol,
    .content ul {
        padding-inline-start: 2.25rem;
    }
}

.content .header:link,
.content .header:visited {
    color: var(--page-foreground);
}
.content .header:link:hover,
.content .header:visited:hover {
    text-decoration: none;
}

/* --- Page navigation (bottom prev/next) --- */

.page-nav {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    margin-top: 4.2rem;
    /*padding-top: 1.5rem;*/
    /*border-top: 2px solid var(--separator-color);*/
    max-width: var(--content-max-width);
    margin-inline: auto;
    /* Angled well off the horizontal: at 100deg the wash ran straight across
       and read as two flat halves meeting in the middle. Set on the
       container, not on `.page-nav a` — as a rule on the link it would
       out-specify `.page-nav-prev` and the flip below would never apply. The
       cards inherit it instead. */
    --page-nav-angle: 142deg;
}

.page-nav a {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    gap: 3px;
    padding: 12px 16px;
    max-width: calc(50% - 8px);
    border-radius: var(--border-radius-lg);
    text-decoration: none;
    min-width: 0;
    box-sizing: border-box;
    /* Its own stacking context, so the hover wash below can sit between this
       background and this text. */
    position: relative;
    isolation: isolate;

    /* The wash is assembled here rather than in variables.css so the prev card
       can flip --page-nav-angle: a var() inside a custom property resolves
       where that property is declared, not where it is used, so an angle
       overridden on the card would never reach a gradient defined on :root.

       --warm-turn sits between the two stops as a bare percentage, which is
       an interpolation hint rather than a third colour: it moves where the
       blend reaches halfway without putting a stop there. A real third stop
       is what this used to be, and a pale one in the middle read as a
       washed-out band across the card rather than one continuous wash. */
    background-image: linear-gradient(
        var(--page-nav-angle) in oklab,
        var(--nav-wash-1) var(--nav-wash-start),
        var(--warm-turn),
        var(--nav-wash-3) var(--nav-wash-end)
    );
}

/* The hover wash is a second layer that fades in, not a swap of the stops.
   `background-image` is not an animatable property and a gradient built from
   unregistered custom properties has nothing to interpolate either, so the
   swap this used to do arrived whole on the first frame — a flash rather than
   a hover, whatever duration was written beside it. Opacity animates, so the
   deeper wash is painted once, held at zero, and brought up.

   Negative z-index inside the card's own stacking context puts it above the
   card's background and below its text, so the text never fades with it. */
.page-nav a::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background-image: linear-gradient(
        var(--page-nav-angle) in oklab,
        var(--nav-wash-1-hover) var(--nav-wash-start),
        var(--warm-turn),
        var(--nav-wash-3-hover) var(--nav-wash-end)
    );
    opacity: 0;
    transition: opacity 0.4s ease;
}

.page-nav a:hover::before,
.page-nav a:focus-visible::before {
    opacity: 1;
}

.page-nav a:hover {
    text-decoration: none;
}

/* Mirrored, so the warm end sits under the "Previous" label rather than
   opposite it. The pair ends up framing the page: cool where the two cards
   meet, ember at the two outer edges they point towards. */
.page-nav-prev {
    --page-nav-angle: 218deg;
}


.page-nav-next {
    text-align: right;
    margin-left: auto;
    align-items: flex-end;
}

.page-nav-label {
    font-size: 0.8125rem;
    color: var(--page-foreground-muted);
    font-weight: 500;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin: 0 -2px;
}

.page-nav-label svg {
    vertical-align: middle;
}

.page-nav-title {
    color: var(--link-color);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    align-self: stretch;
    max-width: 100%;
}

@media only screen and (max-width: 480px) {
    .page-nav a {
        padding: 10px 12px;
    }
    .page-nav-title {
        font-size: 0.9375rem;
    }
    .page-nav-label {
        font-size: 0.75rem;
    }
}

/* --- Page footer --- */

.page-footer {
    max-width: var(--content-max-width);
    margin-inline: auto;
    margin-top: 3rem;
    font-size: 0.875rem;
    color: var(--page-foreground-muted);
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.page-footer-links {
    display: flex;
    align-items: center;
    gap: 28px;
}

/* The left half: the book's title, then the site's own links as text —
   the doors out, beside the name of where you are; the page tools keep the
   right. Empty, the links take no room — `display: contents` lets them
   join the row's gap. */
.page-footer-site {
    display: flex;
    align-items: center;
    gap: 20px;
}
.page-footer-site .site-links {
    display: contents;
}

/* The same pair as the header icon cluster: quiet grey at rest, ember on
   hover. These are chrome, not content, so they should behave like the icons
   at the top rather than like a body link. */
/* Scoped through .content and spelling out :link/:visited, because the footer
   sits inside .content and chrome.css carries `.content a:link` at (0,2,1) and
   loads later — anything lighter loses the tie and the icons come out teal. */
.content .page-footer-site a,
.content .page-footer-site a:link,
.content .page-footer-site a:visited,
.content .page-footer-links a,
.content .page-footer-links a:link,
.content .page-footer-links a:visited {
    color: var(--icon-color);
    text-decoration: none;
    transition: color 0.2s;
}

.content .page-footer-site a:hover,
.content .page-footer-links a:hover {
    color: var(--icon-hover-color);
}

/* Selecting text is the reader acting on the page, so it takes ember. */
::selection {
    background-color: var(--selection-background);
    color: var(--selection-foreground);
}

hr {
    border: none;
    border-top: 5px dotted var(--hr-color);
    margin: 2.625em auto;
    width: 60%;
}

/* --- Utility classes --- */

.left {
    float: left;
}
.right {
    float: right;
}
.boring {
    opacity: 0.6;
}
.hide-boring .boring {
    display: none;
}
.hidden {
    display: none !important;
}

/* --- Tables --- */

.table-wrapper {
    overflow-x: auto;
    margin: 16px 0;
}

table {
    width: 100%;
    border-spacing: 0;
    margin: 16px 0;
    font-size: var(--table-font-size);
    border: var(--accent-border-width) solid var(--table-border-color);
    border-radius: var(--border-radius-lg);
    line-height: 1.4;
    overflow: hidden;
}

.table-wrapper table {
    margin: 0;
}

table thead {
    background-color: var(--table-header-background);
    background-image: var(--table-header-gradient);
}

td:not([align]),
th:not([align]) {
    text-align: left;
}

table thead th,
table thead td {
    padding: 7px 8px;
    font-weight: 500;
    font-size: var(--table-font-size);
    border-bottom: 1px solid var(--table-cell-border-color);
    border-right: 1px solid var(--table-cell-border-color);
}

table th:first-child,
table td:first-child {
    padding-left: 10px;
}

table th:last-child,
table td:last-child {
    border-right: none;
}

/* No rule between body rows — the stripe already separates them, and running
   both made a grid where a list would do. The columns keep theirs, and the
   thead keeps the one under it, because neither is redundant. */
table tbody td {
    padding: 8px;
    border-right: 1px solid var(--table-cell-border-color);
}

table tbody tr:nth-child(2n) {
    background: var(--table-alternate-background);
}

/* --- Code blocks --- */

pre {
    position: relative;
    margin: 16px 0;
}

/* The `.hljs` duplicate is a specificity fight, not a typo: the dark
   highlight sheet ships `.hljs { background: #1d1f21 }` at (0,1,0), which
   outranks a plain `pre > code` at (0,0,2) and was quietly winning the code
   background in dark mode. `pre > code.hljs` takes it back so the theme's own
   --code-background applies in both themes. */
pre > code,
pre > code.hljs {
    display: block;
    overflow-x: auto;
    padding: 14px 16px;
    font-variant-ligatures: none;
    line-height: 1.5;
    border-radius: var(--border-radius-lg);
    border: var(--accent-border-width) solid var(--code-border-color);
    background-color: var(--code-background);
}

/* A two-colour hairline. The fill is clipped to the padding box and the
   gradient to the border box, so a transparent border reveals it —
   border-image would be the obvious tool but it ignores border-radius.
   Guarded because an unsupported gradient invalidates the whole `background`
   shorthand, which would leave the block with no fill at all; without the
   guard the plain border above stands. */
@supports (background: linear-gradient(in oklab, red, blue) border-box) {
    pre > code,
    pre > code.hljs {
        border-color: transparent;
        background:
            linear-gradient(var(--code-background), var(--code-background))
                padding-box,
            var(--edge-gradient) border-box;
    }
}

/* ACE editors */
.hljs.ace_editor {
    padding: 0;
}

pre > .result {
    margin-block-start: 0.875rem;
    background-color: var(--code-background);
}

/* --- Inline code --- */

:not(pre) > .hljs {
    display: inline;
    padding: 2px 6px;
    border-radius: 6px;
    background-color: var(--inline-code-background);
    background-image: var(--inline-code-gradient);
    /* Each line fragment gets the whole gradient rather than a slice of one
       spanning them all, so a chip that wraps still reads as a chip. */
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}

:not(pre):not(a) > .hljs {
    color: var(--inline-code-color);
    overflow-x: initial;
}

a:hover > .hljs {
    text-decoration: underline;
}

/* --- Details/Summary --- */

details {
    margin: 16px 0;
    padding: 0;
    border: var(--accent-border-width) solid var(--quote-border-color);
    border-radius: var(--border-radius-lg);
    background-color: var(--quote-background);
    overflow: hidden;
}

details summary {
    padding: 10px 16px;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
    user-select: none;
}

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

details summary::before {
    content: "";
    display: inline-block;
    width: 0;
    height: 0;
    border-left: 6px solid var(--page-foreground-muted);
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    transition: transform 0.2s;
    flex-shrink: 0;
}

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

details > :not(summary) {
    padding: 0 16px;
}

details > :last-child {
    margin-bottom: 12px;
}

/* --- Blockquotes --- */
blockquote {
    color: var(--page-foreground);
    background-color: var(--quote-background);
    border: var(--accent-border-width) solid var(--quote-border-color);
}

/* --- Admonitions (shared base for blockquote-tag + markdown-alert) --- */
.blockquote-tag,
.markdown-alert,
blockquote {
    padding: 12px 16px;
    margin: 16px 0;
    border-radius: var(--border-radius-lg);
    font-size: var(--alert-font-size);
}
.blockquote-tag,
.markdown-alert {
    border: var(--accent-border-width) solid;
}

.blockquote-tag p:last-child,
.markdown-alert p:last-child,
blockquote p:last-child {
    margin-bottom: 0;
}
.markdown-alert > :first-child,
blockquote p:first-child {
    margin-top: 0;
}

.blockquote-tag-title,
.markdown-alert-title {
    font-weight: 600;
    font-size: 0.875rem;
    display: flex;
    align-items: center;
    margin: 0 0 8px 0;
}

.blockquote-tag .blockquote-tag-title p,
.markdown-alert .markdown-alert-title p {
    margin: 0;
}

.blockquote-tag-title svg,
.blockquote-tag-title .fa-svg,
.markdown-alert-title svg {
    width: 14px;
    height: 14px;
    fill: currentColor;
    margin-right: 8px;
}

/* --- Admonition variants (note, tip, important, warning, caution) --- */

.blockquote-tag-note,
.markdown-alert-note {
    --alert-fill: var(--admonition-note-gradient);
    --alert-edge: var(--alert-note-gradient);
    border-color: var(--blockquote-note-color);
    background-color: var(--admonition-note-background);
}
.blockquote-tag-note .blockquote-tag-title,
.markdown-alert-note .markdown-alert-title {
    color: var(--blockquote-note-color);
}

.blockquote-tag-tip,
.markdown-alert-tip {
    --alert-fill: var(--admonition-tip-gradient);
    --alert-edge: var(--alert-tip-gradient);
    border-color: var(--blockquote-tip-color);
    background-color: var(--admonition-tip-background);
}
.blockquote-tag-tip .blockquote-tag-title,
.markdown-alert-tip .markdown-alert-title {
    color: var(--blockquote-tip-color);
}

.blockquote-tag-important,
.markdown-alert-important {
    --alert-fill: var(--admonition-important-gradient);
    --alert-edge: var(--alert-important-gradient);
    border-color: var(--blockquote-important-color);
    background-color: var(--admonition-important-background);
}
.blockquote-tag-important .blockquote-tag-title,
.markdown-alert-important .markdown-alert-title {
    color: var(--blockquote-important-color);
}

.blockquote-tag-warning,
.markdown-alert-warning {
    --alert-fill: var(--admonition-warning-gradient);
    --alert-edge: var(--alert-warning-gradient);
    border-color: var(--blockquote-warning-color);
    background-color: var(--admonition-warning-background);
}
.blockquote-tag-warning .blockquote-tag-title,
.markdown-alert-warning .markdown-alert-title {
    color: var(--blockquote-warning-color);
}

.blockquote-tag-caution,
.markdown-alert-caution {
    --alert-fill: var(--admonition-caution-gradient);
    --alert-edge: var(--alert-caution-gradient);
    border-color: var(--blockquote-caution-color);
    background-color: var(--admonition-caution-background);
}
.blockquote-tag-caution .blockquote-tag-title,
.markdown-alert-caution .markdown-alert-title {
    color: var(--blockquote-caution-color);
}

/* The same gradient hairline the code blocks use, on every other rounded
   card. It has to sit after the admonition variants: the shared selectors
   here tie with theirs on specificity, so only source order wins the
   border-color back. See the code block above for why this is two background
   layers rather than a border-image.

   The padding-box layer is what holds the gradient back from the card's
   interior, so it is nearly opaque rather than fully: the last few points of
   alpha let the edge bleed inward instead of stopping dead at the border. A
   table needs a fill of its own because its odd rows are otherwise
   transparent and would show the gradient at full strength. */
@supports (background: linear-gradient(in oklab, red, blue) border-box) {
    table {
        border-color: transparent;
        background:
            linear-gradient(var(--table-fill), var(--table-fill)) padding-box,
            var(--edge-gradient) border-box;
    }

    .blockquote-tag,
    .markdown-alert {
        border-color: transparent;
        background:
            var(--alert-fill) padding-box,
            var(--alert-edge) border-box;
    }

    blockquote {
        border-color: transparent;
        background:
            linear-gradient(var(--quote-background), var(--quote-background))
                padding-box,
            var(--edge-gradient) border-box;
    }
}

/* --- Legacy .warning class --- */

.warning {
    margin: 16px 0;
    padding: 0 16px;
    border-inline-start: var(--accent-border-width) solid
        var(--warning-border-color);
}
.warning:before {
    position: absolute;
    width: 3rem;
    height: 3rem;
    margin-inline-start: calc(-1.5rem - 21px);
    content: "ⓘ";
    text-align: center;
    background-color: var(--page-background);
    color: var(--warning-border-color);
    font-weight: bold;
    font-size: 2rem;
}
blockquote .warning:before {
    background-color: var(--quote-background);
}

/* --- Keyboard element --- */

kbd {
    background-color: var(--quote-background);
    border-radius: 4px;
    border: 1px solid var(--quote-border-color);
    box-shadow: inset 0 -1px 0 var(--theme-hover-background);
    display: inline-block;
    font-size: var(--code-font-size);
    font-family: var(--mono-font);
    line-height: 10px;
    padding: 4px 6px;
    vertical-align: middle;
}

/* --- Superscript --- */

sup {
    line-height: 0;
}

/* --- Footnotes --- */

.footnote-definition {
    font-size: 0.9em;
}
.footnote-definition ul,
.footnote-definition ol {
    padding-left: 20px;
}
.footnote-definition > li {
    position: relative;
}
.footnote-definition > li:target {
    scroll-margin-top: 50vh;
}
.footnote-reference:target {
    scroll-margin-top: 50vh;
}
.footnote-definition > li:target::before {
    border: 2px solid var(--footnote-highlight-color);
    border-radius: 6px;
    position: absolute;
    top: -8px;
    right: -8px;
    bottom: -8px;
    left: -32px;
    pointer-events: none;
    content: "";
}

@media not (prefers-reduced-motion) {
    .footnote-reference:target {
        animation: fn-highlight 0.8s;
        border-radius: 2px;
    }
    @keyframes fn-highlight {
        from {
            background-color: var(--footnote-highlight-color);
        }
    }
}

/* --- Definition lists --- */

dt {
    font-weight: bold;
    margin-top: 0.5em;
    margin-bottom: 0.1em;
}

dl,
dt {
    counter-reset: dd-counter;
}

dd:has(+ dd),
dd + dd {
    counter-increment: dd-counter;
    display: flex;
    align-items: flex-start;
}

dd:has(+ dd)::before,
dd + dd::before {
    content: counter(dd-counter) ". ";
    font-weight: 600;
    display: inline-block;
    margin-right: 0.5em;
}

dd > p {
    margin-top: 0;
}

/* --- Tooltips --- */

.tooltiptext {
    position: absolute;
    visibility: hidden;
    color: var(--tooltip-foreground);
    background-color: var(--tooltip-background);
    transform: translateX(-50%);
    left: -8px;
    top: -35px;
    font-size: 0.8em;
    text-align: center;
    border-radius: 6px;
    padding: 5px 8px;
    margin: 5px;
    z-index: 1000;
}
.tooltipped .tooltiptext {
    visibility: visible;
}

/* --- Sidebar part titles --- */

.chapter li.part-title {
    color: var(--page-foreground);
    margin: 0;
    padding: 8px;
    font-weight: 460;
    font-size: 0.9333em;
    border-top: 1px solid var(--sidebar-spacer-color);
    color: var(--gray-500);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    user-select: none;
}

/* No separator above the first section */
.chapter > li.part-title:first-child,
.chapter > li:first-child + li.part-title {
    border-top: none;
    padding-top: 4px;
}

/* Collapse/expand chevron — rendered as a pseudo-element so it appears
   on the initial paint (no flash from JS injection). */
.chapter li.part-title::after {
    content: "";
    display: inline-block;
    width: 18px;
    height: 18px;
    background-color: var(--page-foreground-muted);
    mask: url("data:image/svg+xml;utf8,<svg 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'><polyline points='6 9 12 15 18 9'/></svg>")
        center / contain no-repeat;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg 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'><polyline points='6 9 12 15 18 9'/></svg>")
        center / contain no-repeat;
    transition: transform 0.2s;
}
.part-title.collapsed::after {
    transform: rotate(-90deg);
}

/* --- Result with no output --- */

.result-no-output {
    font-style: italic;
}

/* --- FA SVG icons --- */

.fa-svg svg {
    width: 1em;
    height: 1em;
    fill: currentColor;
    margin-bottom: -0.1em;
}

iframe {
    display: block;
    border: none;
    border-radius: var(--border-radius-lg);
}

@media only screen and (max-width: 768px) {
    .table-wrapper {
        margin-left: calc(var(--page-padding) * -1);
        margin-right: calc(var(--page-padding) * -1);
    }

    .table-wrapper table {
        border-radius: 0;
        border-bottom-color: var(--table-cell-border-color);
        border-top-color: var(--search-bar-shadow-color);
        border-left: none;
        border-right: none;
    }

    table th:first-child,
    table td:first-child {
        padding-left: var(--page-padding);
    }

    table th:last-child,
    table td:last-child {
        padding-right: calc(var(--page-padding) * 0.625);
    }

    pre {
        margin-left: calc(var(--page-padding) * -1);
        margin-right: calc(var(--page-padding) * -1);
    }

    /* The block keeps the two-layer fill it has in the column and gives up
       only the frame: a hairline drawn edge to edge is a line across the page,
       not the edge of a card. Zeroing the border's *width* rather than the
       border itself is what keeps the gradient — it is clipped to the border
       box, so a border box equal to the padding box puts it under the whole
       block instead of round it, and the fill's own alpha is what lets it
       through (variables.css). One gradient, one direction, whatever the
       width: the wash the border carried in the column now carries the block.

       `.hljs` for the reason given above the base rule: mdbook's script puts
       that class on every code element, so the (0,1,2) selector that dresses
       the block would keep its border and its rounding here unless this reset
       matches it too. */
    pre > code,
    pre > code.hljs {
        padding: 16px calc(var(--page-padding) + 2px);
        border-width: 0;
        border-radius: 0;
        background: var(--code-background-mobile);
    }

    /* Guarded like its twin above, and for the same reason: the gradient rides
       a `var()`, so an unsupported one is invalid at computed-value time — the
       shorthand would resolve to `unset` rather than falling back to the flat
       fill stated above it. */
    @supports (background: linear-gradient(in oklab, red, blue) border-box) {
        pre > code,
        pre > code.hljs {
            background:
                linear-gradient(
                        var(--code-background-mobile),
                        var(--code-background-mobile)
                    )
                    padding-box,
                var(--edge-gradient) border-box;
        }
    }
}

/* ============================================
   Lini figures
   ============================================

   A ```lini block compiles to inline SVG carrying live `var(--lini-*)`
   colours (Lini SPEC 10.1 / 18). Two things wire a figure to the theme:
   the `color-scheme` flip below — which is what Lini's `light-dark()`
   values read — and a few role variables aliased to the theme palette.
   Lini's own defaults sit in `@layer lini.defaults`, so these unlayered
   rules win without `!important`.

   The preprocessor ships a stylesheet of its own, in `@layer mdbook-lini`.
   It carries the parts a book cannot supply — Lini's token palette, and the
   structure that makes the toggle work — and this file, being unlayered,
   carries the look: a figure is dressed as a picture and a listing as a code
   block, by the rules that dress every other one on the page. Nothing here
   restates a box the theme already draws.

   That last line is a rule, not a description. The figure's own box — its
   margin, its scroll and the floor under its scale — belongs to the
   preprocessor, which is the thing that knows a figure's natural width. This
   file had a copy of both rules, identical but for a padding, and being
   unlayered the copy is what won: a change made where it belonged had no
   effect at all until the copy went.
*/

/* The listing beside a figure needs no rule of its own: the panel is a bare
   wrapper and the `<pre>` inside it is a code block, dressed by the `pre` rules
   above — rounded in the column, full bleed on a phone, one frame either way.

   The theme toggle is the whole light/dark integration — no script, no
   re-render, because every Lini colour is a `light-dark()` pair keyed on
   `color-scheme`. */
html.light .lini {
    color-scheme: light;
}

html.coal .lini {
    color-scheme: dark;
}

/* Aliases that hand Lini the book's own palette and font.

   `--lini-bg` is the paper tone, and SPEC 10.1 is clear about whose paper:
   `fill: --bg` on a root is a figure asking for *the host's* backdrop, which
   makes supplying it this file's job. It goes to the page's own ground rather
   than to `transparent` — a hole and a sheet then match the page exactly
   instead of showing whatever happens to be behind them, and in dark mode
   Lini's stock `#1b1b1f` sat visibly above this theme's near-black and read
   as a card under the figure.

   The cost is that a figure redeclaring `--bg` for a paper of its own loses:
   an unlayered alias beats the figure's own `@layer lini.defaults`. That is
   the right way round — `--bg` means the host's paper — and a figure that
   wants a different one says so with a literal `fill:`, which nothing here
   can reach.

   The font must stay close to what Lini measured against at compile time: it
   bakes every label's position and sizes each box to fit, so a wider face
   spills its text past the border. Lini measures a proportional sans, which
   is what `--body-font` is — the theme's self-hosted Google Sans Flex. A
   monospace alias here is what breaks it. */
.lini {
    --lini-bg: var(--page-background);
    --lini-font-family: var(--body-font);
    --lini-fg: var(--page-foreground);
    --lini-accent: var(--accent-600);

    /* Lini's own named ramps, repointed at the book's two colours, so a
       two-series figure comes out in teal and ember instead of Lini's stock
       hues. The other ramps (sky, purple, green, amber) are left alone —
       they're what a figure reaches for when it needs a third series.

       These stay light-dark() pairs because that is the whole light/dark
       integration: the toggle flips color-scheme on .lini and nothing
       re-renders. A flat var() here would freeze figures in light mode. */
    --lini-teal-wash: light-dark(var(--accent-50), var(--accent-950));
    --lini-teal-soft: light-dark(var(--accent-200), var(--accent-800));
    --lini-teal: light-dark(var(--accent-500), var(--accent-400));
    --lini-teal-deep: light-dark(var(--accent-600), var(--accent-400));
    --lini-teal-ink: light-dark(var(--accent-800), var(--accent-200));

    --lini-orange-wash: light-dark(var(--secondary-50), var(--secondary-950));
    --lini-orange-soft: light-dark(var(--secondary-200), var(--secondary-800));
    --lini-orange: light-dark(var(--secondary-500), var(--secondary-400));
    --lini-orange-deep: light-dark(var(--secondary-600), var(--secondary-400));
    --lini-orange-ink: light-dark(var(--secondary-800), var(--secondary-200));
}

/* A block that failed to compile. The message is already on stderr; this
   puts it on the page too, so a broken diagram can't ship unnoticed. */
.lini-error {
    color: var(--blockquote-important-color);
    background: var(--admonition-important-background);
    border: var(--accent-border-width) solid var(--blockquote-important-color);
    padding: 12px 16px;
    border-radius: var(--border-radius-lg);
    white-space: pre-wrap;
    font-size: 0.875rem;
}
