/* ==========================================================================
   AWS Deep Dives — shared design tokens and page shell.

   Every colour is declared once here as an unprefixed RGB channel triplet so
   that Tailwind can apply opacity modifiers to it (`bg-accent/10`) and so a
   single [data-theme] swap re-themes the whole site. Pages must not hardcode
   hex values; add a token instead.

   The text ramp steps below were chosen against measured contrast, not by eye.
   Ratios in the comments are against --c-app / --c-surface. Every ink step
   clears WCAG AA 4.5:1 on all four surface tokens, because the ramp is used
   for 10-11px mono labels where the large-text exemption does not apply.
   ========================================================================== */

:root {
    color-scheme: dark;

    /* Surfaces. True neutral: same OKLCH lightness steps as the previous navy
       scale with chroma zeroed out, so the elevation ladder (app < surface <
       raise, etc.) is unchanged -- only the blue cast is gone. */
    --c-app: 15 15 15;
    --c-surface: 24 24 24;
    --c-surface-2: 21 21 21;
    --c-raise: 34 34 34;
    --c-code: 10 10 10;
    --c-tint: 30 30 30;
    --c-line: 45 45 45;
    --c-line-2: 58 58 58;

    /* Base for translucent row/cell overlays; flips to black in light theme */
    --c-hover: 255 255 255;

    /* Ink ramp. Unchanged -- re-verified against the new neutral surfaces and
       every step still clears the same AA/AAA bar it did on navy. */
    --c-ink: 230 237 246;
    /* 16.3 / 15.0 */
    --c-ink-2: 201 213 227;
    /* 12.9 / 11.9 */
    --c-ink-3: 168 184 204;
    /*  9.5 /  8.8 */
    --c-ink-4: 148 163 184;
    /*  7.5 /  6.9 */
    --c-ink-5: 130 147 169;
    /*  6.1 /  5.7 */

    /* Accents. Meaning is fixed site-wide: accent = brand and interactive,
       ok = capacity fits, bad = capacity fails, info = neutral reference,
       alt = a fourth data series. Never pick one of these for decoration. */
    --c-accent: 245 158 11;
    /*  9.0 */
    --c-accent-2: 251 191 36;
    /* 11.5 */
    --c-ok: 52 211 153;
    /* 10.0 */
    --c-info: 96 165 250;
    /*  7.6 */
    --c-bad: 248 113 113;
    /*  7.0 */
    --c-alt: 167 139 250;
    /*  7.1 */

    /* Foreground for text/icons sitting on a solid --c-accent fill */
    --c-on-accent: 0 0 0;

    /* "Ink" variants: the colour to use for TEXT, including text sitting on a
       tint of its own hue (a status chip, a numbered step badge, .hl, inline
       code). The base tokens above are for fills, borders and strokes. Using a
       base token as text on its own 20% tint measures 3.5-4.2:1; these clear
       4.5:1 in both themes. Dark shifts brighter, light shifts darker. */
    --c-accent-ink: 251 191 36;
    --c-ok-ink: 110 231 183;
    --c-info-ink: 147 197 253;
    --c-bad-ink: 252 165 165;
    --c-alt-ink: 196 181 253;

    /* Layout */
    --shell: 1180px;
    --measure: 68ch;
    --radius: 12px;
    --radius-sm: 8px;
    --topbar-h: 52px;

    /* Motion. One curve for the whole site so nothing feels borrowed. */
    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --dur: 180ms;
    --dur-slow: 420ms;
}

[data-theme="light"] {
    color-scheme: light;

    --c-app: 248 250 252;
    --c-surface: 255 255 255;
    --c-surface-2: 241 245 249;
    --c-raise: 241 245 249;
    --c-code: 241 245 249;
    --c-tint: 226 232 240;
    --c-line: 226 232 240;
    --c-line-2: 203 213 225;

    --c-hover: 0 0 0;

    --c-ink: 15 23 42;
    /* 17.1 / 17.9 */
    --c-ink-2: 30 41 59;
    /* 14.0 / 14.7 */
    --c-ink-3: 51 65 85;
    /*  9.9 / 10.4 */
    --c-ink-4: 71 85 105;
    /*  7.2 /  7.6 */
    --c-ink-5: 92 107 127;
    /*  5.2 /  5.4 */

    /* Amber has to travel a long way to clear 4.5:1 on white. It lands on a
       burnt amber rather than the dark-theme gold; the alternative is failing
       contrast on every mono label. */
    --c-accent: 162 86 10;
    /*  5.2 */
    --c-accent-2: 180 83 9;
    /*  4.8 */
    --c-ok: 4 120 87;
    /*  5.2 */
    --c-info: 29 78 216;
    /*  6.4 */
    --c-bad: 185 28 28;
    /*  6.2 */
    --c-alt: 109 40 217;
    /*  6.8 */

    --c-on-accent: 255 255 255;

    --c-accent-ink: 146 64 14;
    --c-ok-ink: 6 95 70;
    --c-info-ink: 30 64 175;
    --c-bad-ink: 153 27 27;
    --c-alt-ink: 91 33 182;
}

/* ==========================================================================
   Base
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    /* Anchor targets must clear the sticky top bar and the sticky section nav */
    scroll-padding-top: calc(var(--topbar-h) + 68px);
    -webkit-text-size-adjust: 100%;
}

body {
    background: rgb(var(--c-app));
    color: rgb(var(--c-ink));
    /* Guard against the theme flip repainting text and surfaces at different
       moments, which reads as a flash on slower machines. */
    transition: background-color var(--dur) linear, color var(--dur) linear;
}

::selection {
    background: rgb(var(--c-accent) / 0.28);
}

strong,
b {
    font-weight: 500;
    color: rgb(var(--c-ink));
}

hr {
    border: 0;
    border-top: 1px solid rgb(var(--c-line));
}

/* ==========================================================================
   Focus. One ring for every interactive element.

   Drawn with `outline`, not `box-shadow`: any component that sets its own
   shadow (the light-theme card, for one) has higher specificity than a generic
   `a:focus-visible` and silently replaced the ring, leaving card links with no
   visible focus at all. outline occupies a separate property, so nothing can
   overwrite it by accident.
   ========================================================================== */

:focus-visible {
    outline: 2px solid rgb(var(--c-accent));
    outline-offset: 2px;
    /* Lift above neighbours so the ring is never overlapped by a sibling */
    position: relative;
    z-index: 5;
}

/* Inside a scroll container the ring would be clipped at the container edge,
   so it is drawn just inside the element instead. */
.table-wrap :focus-visible,
.section-nav-inner :focus-visible {
    outline-offset: -2px;
}

.skip-link {
    position: absolute;
    left: 12px;
    top: 8px;
    z-index: 200;
    padding: 8px 14px;
    border-radius: var(--radius-sm);
    background: rgb(var(--c-surface));
    border: 1px solid rgb(var(--c-accent));
    color: rgb(var(--c-accent));
    font-size: 12px;
    text-decoration: none;
    transform: translateY(-200%);
    transition: transform var(--dur) var(--ease);
}

.skip-link:focus-visible {
    transform: translateY(0);
}

/* ==========================================================================
   Layout
   ========================================================================== */

.shell {
    width: 100%;
    max-width: var(--shell);
    margin-inline: auto;
    padding-inline: 20px;
}

@media (min-width: 768px) {
    .shell {
        padding-inline: 32px;
    }
}

/* Running prose caps at a readable measure even inside a wide shell */
.measure {
    max-width: var(--measure);
}

/* ==========================================================================
   Top bar. Present on every page, and the only home for the theme toggle —
   a floating fixed button has no anchor and collides with page content.
   ========================================================================== */

.topbar {
    position: sticky;
    top: 0;
    z-index: 100;
    height: var(--topbar-h);
    display: flex;
    align-items: center;
    border-bottom: 1px solid rgb(var(--c-line));
    /* Translucent + blur so long tables and diagrams stay legible as they pass
       beneath, instead of being cut by an opaque band. */
    background: rgb(var(--c-app) / 0.72);
    backdrop-filter: blur(14px) saturate(1.4);
}

@supports not (backdrop-filter: blur(1px)) {
    .topbar {
        background: rgb(var(--c-app));
    }
}

.topbar-inner {
    display: flex;
    align-items: center;
    gap: 16px;
    justify-content: space-between;
}

.wordmark {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    text-decoration: none;
    color: rgb(var(--c-ink));
    font-size: 13px;
    font-weight: 600;
    letter-spacing: -0.01em;
    padding: 6px 8px 6px 0;
    transition: color var(--dur) var(--ease);
}

.wordmark svg {
    color: rgb(var(--c-accent));
    transition: transform var(--dur-slow) var(--ease);
}

.wordmark:hover svg {
    transform: translateY(-1px) rotate(-4deg);
}

.topbar-actions {
    display: flex;
    align-items: center;
    gap: 4px;
}

.topbar-link {
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    color: rgb(var(--c-ink-4));
    text-decoration: none;
    padding: 7px 10px;
    border-radius: 6px;
    transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.topbar-link:hover {
    color: rgb(var(--c-accent));
    background: rgb(var(--c-hover) / 0.05);
}

.topbar-link[aria-current="page"] {
    color: rgb(var(--c-ink));
}

.theme-toggle {
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    border-radius: 6px;
    border: 1px solid transparent;
    background: transparent;
    color: rgb(var(--c-ink-4));
    cursor: pointer;
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
        background-color var(--dur) var(--ease);
}

.theme-toggle:hover {
    color: rgb(var(--c-accent));
    border-color: rgb(var(--c-line-2));
    background: rgb(var(--c-hover) / 0.05);
}

.theme-toggle svg {
    /* Rotate through the swap so the change of state is legible, not abrupt */
    transition: transform var(--dur-slow) var(--ease);
}

.theme-toggle:hover svg {
    transform: rotate(18deg);
}

.theme-toggle .icon-sun {
    display: none;
}

[data-theme="light"] .theme-toggle .icon-sun {
    display: block;
}

[data-theme="light"] .theme-toggle .icon-moon {
    display: none;
}

/* ==========================================================================
   Hero. One pattern for all four pages: kicker, title, dek, left aligned to
   the same grid as the content below it.
   ========================================================================== */

.hero {
    position: relative;
    overflow: hidden;
    border-bottom: 1px solid rgb(var(--c-line));
    background:
        radial-gradient(ellipse 70% 130% at 88% -10%, rgb(var(--c-accent) / 0.10), transparent 62%),
        radial-gradient(ellipse 60% 120% at 8% 120%, rgb(var(--c-info) / 0.07), transparent 62%);
}

.hero-inner {
    padding-block: 44px 40px;
}

@media (min-width: 768px) {
    .hero-inner {
        padding-block: 64px 56px;
    }
}

.kicker {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: rgb(var(--c-accent));
    margin: 0 0 18px;
}

.kicker svg {
    /* The mark reads as part of the label, so it takes the label's colour
       rather than the slate it used to be hardcoded to. */
    color: rgb(var(--c-accent));
    flex: none;
}

.hero h1 {
    font-size: clamp(30px, 5.2vw, 46px);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.03em;
    margin: 0 0 16px;
    max-width: 20ch;
    text-wrap: balance;
}

.hero .dek {
    font-family: 'IBM Plex Mono', monospace;
    font-size: 12.5px;
    line-height: 1.7;
    color: rgb(var(--c-ink-4));
    margin: 0;
    /* Wide enough that a one-sentence dek stays on one line at desktop; mono
       runs about 40% wider per character than the sans measure. */
    max-width: 84ch;
}

/* ==========================================================================
   Section nav. Sticks under the top bar so orientation survives a 2500-line
   page. Used both for in-page anchors and for tab panels.
   ========================================================================== */

.section-nav {
    position: sticky;
    top: var(--topbar-h);
    z-index: 90;
    border-bottom: 1px solid rgb(var(--c-line));
    background: rgb(var(--c-app) / 0.82);
    backdrop-filter: blur(14px) saturate(1.4);
}

@supports not (backdrop-filter: blur(1px)) {
    .section-nav {
        background: rgb(var(--c-app));
    }
}

.section-nav-inner {
    display: flex;
    gap: 8px;
    padding-block: 12px;
    overflow-x: auto;
    /* Anchor clicks would otherwise leave the active pill half off-screen */
    scroll-behavior: smooth;
    scrollbar-width: none;
    /* Twelve pills do not fit at any desktop width, and a cut-off pill with no
       fade reads as a layout bug rather than as "scroll for more". Removed at
       both ends once there is nothing further to scroll to. */
    mask-image: linear-gradient(to right, transparent 0, #000 24px, #000 calc(100% - 32px), transparent 100%);
}

.section-nav-inner.is-at-start {
    mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent 100%);
}

.section-nav-inner.is-at-end {
    mask-image: linear-gradient(to right, transparent 0, #000 24px);
}

.section-nav-inner.is-at-start.is-at-end {
    mask-image: none;
}

.section-nav-inner::-webkit-scrollbar {
    display: none;
}

.pill {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    line-height: 1;
    /* 32px tall: the smallest target that still reads as tappable in a
       horizontally scrolling row */
    padding: 10px 14px;
    border-radius: 999px;
    border: 1px solid rgb(var(--c-line));
    background: rgb(var(--c-surface));
    color: rgb(var(--c-ink-4));
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
        background-color var(--dur) var(--ease);
}

/* Coarse pointer (touch): the 32px row height stays for the dense desktop
   scroll strip, but touch devices get the full 44px target via padding. */
@media (pointer: coarse) {
    .pill {
        padding: 14px 16px;
    }
}

.pill svg {
    flex: none;
    opacity: 0.85;
}

.pill:hover {
    color: rgb(var(--c-ink));
    border-color: rgb(var(--c-line-2));
}

.pill.active,
.pill[aria-current="true"],
.pill[aria-selected="true"] {
    color: rgb(var(--c-accent));
    border-color: rgb(var(--c-accent) / 0.55);
    background: rgb(var(--c-accent) / 0.10);
}

/* ==========================================================================
   Sections
   ========================================================================== */

.section {
    padding-block: 48px;
    border-bottom: 1px solid rgb(var(--c-line));
}

@media (min-width: 768px) {
    .section {
        padding-block: 72px;
    }
}

.section:last-of-type {
    border-bottom: 0;
}

/* Space above a heading always exceeds the space below it, so the heading
   binds to its own content rather than floating between two blocks. */
.eyebrow {
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgb(var(--c-accent));
    margin: 0 0 12px;
}

.section h2,
.section-h2 {
    font-size: clamp(22px, 2.6vw, 28px);
    font-weight: 700;
    letter-spacing: -0.022em;
    line-height: 1.2;
    margin: 0 0 10px;
    text-wrap: balance;
}

.section h3 {
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0 0 8px;
}

.section-lede {
    font-size: 15px;
    line-height: 1.7;
    color: rgb(var(--c-ink-3));
    max-width: var(--measure);
    margin: 0 0 36px;
}

/* Panel title inside a card: was amber at body size everywhere, which left no
   hierarchy and spent the accent on decoration. */
.panel-title {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: rgb(var(--c-ink));
    margin: 0 0 6px;
}

.panel-title svg {
    color: rgb(var(--c-accent));
    flex: none;
}

.panel-note {
    font-size: 13px;
    line-height: 1.65;
    color: rgb(var(--c-ink-4));
    margin: 0 0 20px;
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
    background: rgb(var(--c-surface));
    border: 1px solid rgb(var(--c-line));
    border-radius: var(--radius);
    transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
        box-shadow var(--dur) var(--ease);
}

[data-theme="light"] .card {
    box-shadow: 0 1px 2px rgb(15 23 42 / 0.05), 0 1px 3px rgb(15 23 42 / 0.04);
}

.card:hover {
    border-color: rgb(var(--c-line-2));
}

.card-inner {
    background: rgb(var(--c-surface-2));
    border: 1px solid rgb(var(--c-line));
    border-radius: var(--radius-sm);
}

/* Only cards that are themselves links lift; a static panel that moves on
   hover promises an interaction it does not have. */
a.card:hover,
.card-link:hover {
    border-color: rgb(var(--c-accent) / 0.6);
    transform: translateY(-3px);
    box-shadow: 0 10px 24px -12px rgb(var(--c-accent) / 0.22),
        0 4px 10px -6px rgb(0 0 0 / 0.3);
}

[data-theme="light"] a.card:hover,
[data-theme="light"] .card-link:hover {
    box-shadow: 0 12px 24px -14px rgb(var(--c-accent) / 0.35),
        0 2px 6px -2px rgb(15 23 42 / 0.08);
}

/* ==========================================================================
   Tags and callouts
   ========================================================================== */

.tag {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    padding: 3px 9px;
    border-radius: 999px;
    border: 1px solid currentColor;
    /* Border and fill both derive from the text colour, so a tag only needs
       one colour class to stay coherent. */
    background: rgb(var(--c-hover) / 0.04);
}

.tag-accent {
    color: rgb(var(--c-accent-ink));
    border-color: rgb(var(--c-accent) / 0.35);
    background: rgb(var(--c-accent) / 0.10);
}

.tag-ok {
    color: rgb(var(--c-ok-ink));
    border-color: rgb(var(--c-ok) / 0.35);
    background: rgb(var(--c-ok) / 0.10);
}

.tag-info {
    color: rgb(var(--c-info-ink));
    border-color: rgb(var(--c-info) / 0.35);
    background: rgb(var(--c-info) / 0.10);
}

.tag-bad {
    color: rgb(var(--c-bad-ink));
    border-color: rgb(var(--c-bad) / 0.35);
    background: rgb(var(--c-bad) / 0.10);
}

.tag-alt {
    color: rgb(var(--c-alt-ink));
    border-color: rgb(var(--c-alt) / 0.35);
    background: rgb(var(--c-alt) / 0.10);
}

.tag-neutral {
    color: rgb(var(--c-ink-4));
    border-color: rgb(var(--c-line-2));
    background: rgb(var(--c-hover) / 0.04);
}

/*
 * Colour-named aliases inherited from the pre-token markup. They now resolve
 * through the same semantic tokens, so they are theme-correct and contrast-safe.
 * New markup should use .tag-accent / .tag-ok / .tag-info / .tag-bad instead:
 * "blue" describes a pixel, "info" describes a meaning.
 */
.tag-yellow,
.tag-orange {
    color: rgb(var(--c-accent-ink));
    border-color: rgb(var(--c-accent) / 0.35);
    background: rgb(var(--c-accent) / 0.10);
}

.tag-green {
    color: rgb(var(--c-ok-ink));
    border-color: rgb(var(--c-ok) / 0.35);
    background: rgb(var(--c-ok) / 0.10);
}

.tag-blue {
    color: rgb(var(--c-info-ink));
    border-color: rgb(var(--c-info) / 0.35);
    background: rgb(var(--c-info) / 0.10);
}

.tag-red {
    color: rgb(var(--c-bad-ink));
    border-color: rgb(var(--c-bad) / 0.35);
    background: rgb(var(--c-bad) / 0.10);
}

.note {
    border-radius: 10px;
    border: 1px solid rgb(var(--c-line));
    background: rgb(var(--c-surface-2));
    padding: 14px 16px;
    font-size: 13px;
    line-height: 1.65;
    color: rgb(var(--c-ink-3));
}

.note-accent {
    border-color: rgb(var(--c-accent) / 0.28);
    background: rgb(var(--c-accent) / 0.07);
}

.note-info {
    border-color: rgb(var(--c-info) / 0.28);
    background: rgb(var(--c-info) / 0.07);
}

.note-ok {
    border-color: rgb(var(--c-ok) / 0.28);
    background: rgb(var(--c-ok) / 0.07);
}

.note-bad {
    border-color: rgb(var(--c-bad) / 0.28);
    background: rgb(var(--c-bad) / 0.07);
}

/* Same legacy-alias note as the tags above. */
[class*="info-box-"] {
    border-radius: 10px;
    border: 1px solid rgb(var(--c-line));
    background: rgb(var(--c-surface-2));
    padding: 14px 16px;
    font-size: 13px;
    line-height: 1.65;
    color: rgb(var(--c-ink-3));
}

.info-box-yellow {
    border-color: rgb(var(--c-accent) / 0.28);
    background: rgb(var(--c-accent) / 0.07);
}

.info-box-blue {
    border-color: rgb(var(--c-info) / 0.28);
    background: rgb(var(--c-info) / 0.07);
}

.info-box-green {
    border-color: rgb(var(--c-ok) / 0.28);
    background: rgb(var(--c-ok) / 0.07);
}

.info-box-red {
    border-color: rgb(var(--c-bad) / 0.28);
    background: rgb(var(--c-bad) / 0.07);
}

/* Icon frame. Neutral by default so a coloured payload (an AWS service icon,
   for instance) is the only saturated thing in the tile. */
.icon-frame {
    width: 42px;
    height: 42px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: 10px;
    border: 1px solid rgb(var(--c-line));
    background: rgb(var(--c-surface-2));
}

.icon-frame svg {
    color: rgb(var(--c-accent));
}

/* ==========================================================================
   Forms
   ========================================================================== */

label,
.field-label {
    display: block;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    color: rgb(var(--c-ink-4));
    margin-bottom: 7px;
}

select,
input[type="text"],
input[type="number"] {
    width: 100%;
    height: 40px;
    padding: 0 11px;
    border-radius: 6px;
    border: 1px solid rgb(var(--c-line));
    background: rgb(var(--c-surface-2));
    color: rgb(var(--c-ink));
    font-family: inherit;
    font-size: 13px;
    font-weight: 400;
    line-height: 1.3;
    transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

select {
    /* Native chevron is unstyleable and clashes with the icon set */
    appearance: none;
    padding-right: 34px;
    /* Long option labels must truncate rather than run under the chevron */
    text-overflow: ellipsis;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 11px center;
}

select:hover,
input[type="text"]:hover,
input[type="number"]:hover {
    border-color: rgb(var(--c-line-2));
}

select:focus,
input[type="text"]:focus,
input[type="number"]:focus {
    border-color: rgb(var(--c-accent));
}

input[type="checkbox"] {
    accent-color: rgb(var(--c-accent));
    cursor: pointer;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 36px;
    padding: 0 14px;
    border-radius: 6px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 12px;
    cursor: pointer;
    border: 1px solid transparent;
    transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
        color var(--dur) var(--ease);
}

.btn-primary {
    background: rgb(var(--c-accent));
    color: rgb(var(--c-on-accent));
    font-weight: 600;
}

.btn-primary:hover {
    background: rgb(var(--c-accent-2));
}

.btn-ghost {
    background: rgb(var(--c-raise));
    border-color: rgb(var(--c-line));
    color: rgb(var(--c-ink-3));
}

.btn-ghost:hover {
    border-color: rgb(var(--c-accent) / 0.6);
    color: rgb(var(--c-accent));
}

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
}

.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
    background: rgb(var(--c-raise));
    border-color: rgb(var(--c-line));
    color: rgb(var(--c-ink-4));
}

/* ==========================================================================
   Tables. Headers stick inside their own scroll container, because these
   tables are read by scanning a column, and a header that scrolls away turns
   every row into an unlabelled tuple.
   ========================================================================== */

table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 13px;
}

th {
    background: rgb(var(--c-raise));
    border-bottom: 1px solid rgb(var(--c-line));
    padding: 11px 14px;
    text-align: left;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgb(var(--c-ink-4));
    vertical-align: bottom;
    white-space: nowrap;
}

td {
    border-bottom: 1px solid rgb(var(--c-line));
    padding: 10px 14px;
    color: rgb(var(--c-ink-3));
    vertical-align: top;
}

tbody tr:last-child td {
    border-bottom: 0;
}

tbody tr {
    transition: background-color 120ms linear;
}

tbody tr:hover td {
    background: rgb(var(--c-hover) / 0.03);
}

.table-wrap {
    position: relative;
    overflow: auto;
    border: 1px solid rgb(var(--c-line));
    border-radius: var(--radius-sm);
}

/* Sticky headers are opt-in via the scroll container, so a short table on a
   long page does not pin its header to the viewport. */
.table-wrap th {
    position: sticky;
    top: 0;
    z-index: 2;
}

/* A capped scroll region must look capped. Without this the last visible row
   is sliced in half and reads as a rendering fault. */
.table-wrap.is-capped {
    max-height: 420px;
    mask-image: linear-gradient(to bottom, #000 calc(100% - 40px), transparent 100%);
}

.table-wrap.is-capped.is-at-end {
    mask-image: none;
}

.num {
    font-family: 'IBM Plex Mono', monospace;
    /* Digits must not reflow as values recalculate */
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Code
   ========================================================================== */

code {
    font-family: 'IBM Plex Mono', monospace;
    font-size: 0.92em;
}

:not(pre) > code {
    padding: 1px 5px;
    border-radius: 4px;
    background: rgb(var(--c-accent) / 0.10);
    color: rgb(var(--c-accent-ink));
}

pre.code-block {
    margin: 16px 0;
    padding: 16px 18px;
    overflow-x: auto;
    border: 1px solid rgb(var(--c-line));
    border-radius: var(--radius-sm);
    background: rgb(var(--c-code));
    color: rgb(var(--c-ink-3));
    font-family: 'IBM Plex Mono', monospace;
    font-size: 12px;
    line-height: 1.7;
}

.code-block .comment {
    color: rgb(var(--c-ink-5));
    font-style: italic;
}

.code-block .key {
    color: rgb(var(--c-info));
}

.code-block .val {
    color: rgb(var(--c-ok));
}

.code-block .str {
    color: rgb(var(--c-accent-2));
}

.code-block .num {
    color: rgb(var(--c-alt));
}

.code-block .cmd {
    color: rgb(var(--c-ink));
    font-weight: 600;
}

details.code-collapse {
    margin: 16px 0;
    overflow: hidden;
    border: 1px solid rgb(var(--c-line));
    border-radius: var(--radius-sm);
    background: rgb(var(--c-code));
}

details.code-collapse summary {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 11px 16px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11.5px;
    color: rgb(var(--c-ink-4));
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: color var(--dur) var(--ease);
}

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

details.code-collapse summary::before {
    content: '';
    flex: none;
    width: 0;
    height: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transition: transform var(--dur) var(--ease);
}

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

details.code-collapse summary:hover {
    color: rgb(var(--c-ink));
}

details.code-collapse[open] summary {
    color: rgb(var(--c-accent));
    border-bottom: 1px solid rgb(var(--c-line));
}

details.code-collapse pre.code-block {
    margin: 0;
    border: 0;
    border-radius: 0;
}

/* ==========================================================================
   Stat rows and key-value readouts
   ========================================================================== */

.stat {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 8px 0;
    border-bottom: 1px solid rgb(var(--c-line) / 0.55);
    font-size: 13px;
    color: rgb(var(--c-ink-4));
}

.stat:last-child {
    border-bottom: 0;
}

.stat > :last-child {
    font-family: 'IBM Plex Mono', monospace;
    font-variant-numeric: tabular-nums;
    color: rgb(var(--c-ink));
}

/* Inline highlight for a computed value inside a sentence */
.hl {
    padding: 1px 6px;
    border-radius: 4px;
    background: rgb(var(--c-accent) / 0.12);
    color: rgb(var(--c-accent-ink));
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

/* Recalculated figures settle in rather than snapping, so it is obvious which
   readouts responded to the input just edited. Deliberately opacity only: a
   background flash on a full-width figure reads as a loading skeleton, and a
   translate makes it twitch on every keystroke. */
@keyframes value-settled {
    from {
        opacity: 0.35;
    }

    to {
        opacity: 1;
    }
}

.is-updated {
    animation: value-settled 260ms var(--ease);
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
    border-top: 1px solid rgb(var(--c-line));
}

.site-footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding-block: 28px 36px;
    font-family: 'IBM Plex Mono', monospace;
    font-size: 11px;
    color: rgb(var(--c-ink-5));
}

.site-footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 18px;
}

.site-footer a {
    color: rgb(var(--c-ink-4));
    text-decoration: none;
    transition: color var(--dur) var(--ease);
}

.site-footer a:hover {
    color: rgb(var(--c-accent));
}

/* ==========================================================================
   Scrollbars
   ========================================================================== */

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: rgb(var(--c-line-2));
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgb(var(--c-ink-5));
}

/* ==========================================================================
   Entrance. One authored moment per page: content settles upward once, on
   arrival. Sections already in the first viewport are excluded so the page
   never appears to load empty.
   ========================================================================== */

@keyframes rise {
    from {
        opacity: 0;
        transform: translateY(14px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* Scoped to .js (set by the inline head snippet) so a scripting failure can
   never leave content permanently at opacity 0. */
.js [data-reveal] {
    opacity: 0;
}

.reveal {
    animation: rise var(--dur-slow) var(--ease) both;
}

/* ==========================================================================
   Reduced motion. Transforms and entrances are removed; colour transitions
   stay, because an instant colour jump is the thing motion sensitivity does
   not object to and legibility of state depends on it.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
    }

    .reveal {
        animation: none;
    }

    a.card:hover,
    .card-link:hover,
    .wordmark:hover svg,
    .theme-toggle:hover svg {
        transform: none;
    }
}

/* ==========================================================================
   Structural subhead inside a section. Sits between h2 and body: tracked mono
   for the technical register the site already uses, but neutral in colour so
   the accent keeps meaning something.
   ========================================================================== */

.subhead {
    font-family: 'IBM Plex Mono', monospace;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgb(var(--c-ink-3));
    /* More space above than below, so the label binds to what follows it */
    margin: 44px 0 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid rgb(var(--c-line));
}
