/* ============================================================
   JAX CORE — every rule, no colour values.

   This is the whole design language minus the palette. It reads
   the colour contract documented at the top of tokens.css, which
   exactly one schemes/<name>.css must supply.

   Import a scheme alongside it; import jax.css instead to get the
   core and the default scheme (ember) in one file.
   ============================================================ */

/* ============================================================
   JAX DESIGN TOKENS
   Single source of truth for everything that is NOT a colour.
   Dark-first. Monospace.

   Colours live in schemes/<name>.css — one file per scheme, each
   defining the same contract. This file never names a colour; it
   only defines the formulas (rings, tints, shadows) that read the
   contract, so a scheme can be swapped at import with no other
   change anywhere in the bundle.

   THE COLOUR CONTRACT — every scheme must define all of:
     --bg-primary --bg-secondary --bg-tertiary
      --text-primary --text-secondary --text-muted --text-dim
      (dim is licensed on the page background only; --text-muted is
      the floor on panels -- the contrast gate enforces both)
     --border-color --border-subtle
     --accent --accent-hover
     --color-accent-solid --color-accent-solid-hover --text-on-accent
     --color-ok --color-warn --color-error --color-info
     --text-accent --text-ok --text-warn --text-error --text-info
     --highlight --shadow
     --scheme  (the scheme's own name, as a quoted string)

   `uv run jax-contrast-check` gates every scheme against the same
   accessibility targets. A scheme that misses one fails the build.
   ============================================================ */

/* --- Colour scheme resolution --- */
/* Schemes carry both modes in light-dark(); this is what picks one.
   Dark is the default, light comes from the OS or [data-theme]. One
   declaration per mode instead of a duplicated palette per mode. */

:root {
    color-scheme: dark;
}

@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        color-scheme: light;
    }
}

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

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

/* --- Typography --- */

:root {
    --font-mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Courier New', monospace;

    --font-size-xs: 11px;
    --font-size-sm: 12px;
    --font-size-base: 14px;
    /* Mobile-only floor for text-entry controls: below it iOS Safari
       force-zooms the viewport when an input takes focus. */
    --font-size-input: 16px;
    --font-size-lg: 18px;
    --font-size-xl: 24px;
    /* Long-form only: the ceiling for an article or profile title,
       reached through clamp(). Never a UI control size. */
    --font-size-display: 32px;

    --line-height: 1.5;
    --line-height-tight: 1.25;
    --line-height-loose: 1.75;
    --line-height-display: 1.1;

    --font-weight-normal: 400;
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --font-weight-bold: 700;

    --letter-spacing-tight: -0.02em;
    --letter-spacing-normal: 0;
    --letter-spacing-wide: 0.05em;
    --letter-spacing-wider: 0.08em;
}

/* --- Spacing (4px base) --- */

:root {
    --space-0: 0;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-16: 64px;
}

/* --- Border Radius (STRICT) --- */

:root {
    --radius-none: 0;
    --radius-sm: 2px;
    --radius-pill: 12px;
    --radius-full: 50%;
    /* NO other values. No 4px, 6px, 8px. */
}

/* --- Shadows (MINIMAL) --- */

:root {
    --shadow-focus: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
    --shadow-focus-error: 0 0 0 2px color-mix(in srgb, var(--color-error) 25%, transparent);
    --shadow-focus-ok: 0 0 0 2px color-mix(in srgb, var(--color-ok) 25%, transparent);
    --shadow-dropdown: 0 4px 12px color-mix(in srgb, var(--shadow) 30%, transparent);
    /* NO decorative shadows on cards, panels, sections. */
}

/* --- Transitions --- */

:root {
    --transition-fast: 150ms ease;
    --transition-normal: 250ms ease;
    --transition-slow: 400ms ease;
    --transition-view-duration: 220ms;
    --transition-spinner-duration: 600ms;
    --transition-skeleton-duration: 1500ms;
}

/* --- Breakpoints (as custom properties for reference; use in @media directly) --- */
/* --bp-mobile: 768px   */
/* --bp-laptop: 1280px  */
/* --bp-desktop: 1600px */

/* --- Layout --- */

:root {
    --max-width: 1200px;
    --max-width-sm: 480px;
    --max-width-md: 640px;
    --max-width-lg: 960px;

    /* Reading measure, in characters of the mono stack. Body copy
       sits at --measure; a lede or summary tightens to --measure-tight. */
    --measure: 68ch;
    --measure-tight: 58ch;

    --navbar-height: 48px;
    --sidebar-width: 240px;

    --control-height-sm: 32px;
    --control-height-md: 36px;
    --control-height-lg: 44px;
    --touch-target-min: 44px;
}

/* --- Status aliases --- */

:root {
    --status-ok: var(--color-ok);
    --status-warn: var(--color-warn);
    --status-err: var(--color-error);
}

/* ============================================================
   JAX RESET
   Modern CSS reset. Minimal, opinionated.
   ============================================================ */

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

* { margin: 0; }

body {
    min-height: 100vh;
    min-height: 100dvh;
    -webkit-font-smoothing: antialiased;
}

img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
}

input, button, textarea, select {
    font: inherit;
    color: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
    overflow-wrap: break-word;
}

a { color: inherit; }

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ============================================================
   JAX BASE
   Semantic element styles. No classes needed.
   Drop in tokens.css + reset.css + base.css and plain HTML looks good.
   ============================================================ */

/* --- Root --- */

html {
    font-family: var(--font-mono);
    font-size: var(--font-size-base);
    line-height: var(--line-height);
    color: var(--text-primary);
    background: var(--bg-primary);
    tab-size: 4;
}

body {
    font-family: var(--font-mono);
}

/* --- Headings --- */

h1, h2, h3, h4, h5, h6 {
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--text-primary);
    margin-top: 1.5em;
    margin-bottom: 0.5em;
}

h1 { font-size: var(--font-size-xl); }
h2 { font-size: var(--font-size-lg); }
h3 { font-size: var(--font-size-base); text-transform: uppercase; letter-spacing: var(--letter-spacing-wider); }
h4 { font-size: var(--font-size-base); }
h5 { font-size: var(--font-size-sm); text-transform: uppercase; letter-spacing: var(--letter-spacing-wide); }
h6 { font-size: var(--font-size-sm); color: var(--text-secondary); }
h1, h2 { text-wrap: balance; }

h1:first-child, h2:first-child, h3:first-child,
h4:first-child, h5:first-child, h6:first-child {
    margin-top: 0;
}

/* --- Body Text --- */

p {
    margin-bottom: 1em;
    text-wrap: pretty;
    overflow-wrap: break-word;
}

p:last-child {
    margin-bottom: 0;
}

strong, b { font-weight: var(--font-weight-bold); }
em, i { font-style: italic; }
small { font-size: var(--font-size-sm); }

mark {
    background: color-mix(in srgb, var(--color-warn) 20%, transparent);
    color: inherit;
    padding: 1px 3px;
}

/* --- Links --- */

a {
    color: var(--text-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
    transition: color var(--transition-fast);
}

a:hover {
    color: var(--accent-hover);
}

a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* --- Lists --- */

ul, ol {
    padding-left: var(--space-6);
    margin-bottom: 1em;
}

li {
    margin-bottom: var(--space-1);
    overflow-wrap: break-word;
}
li:last-child { margin-bottom: 0; }

ul ul, ol ol, ul ol, ol ul {
    margin-bottom: 0;
    margin-top: var(--space-1);
}

dl { margin-bottom: 1em; }
dt { font-weight: var(--font-weight-semibold); margin-top: var(--space-2); }
dd {
    margin-left: var(--space-6);
    color: var(--text-secondary);
    overflow-wrap: anywhere;
}

/* --- Code --- */

code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    background: var(--bg-tertiary);
    padding: 2px var(--space-1);
    border-radius: var(--radius-sm);
    overflow-wrap: anywhere;
}

pre {
    position: relative;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    padding: var(--space-4);
    overflow-x: auto;
    margin-bottom: 1em;
    line-height: var(--line-height-loose);
}

pre code {
    background: none;
    padding: 0;
    font-size: var(--font-size-sm);
}

kbd {
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding: 1px var(--space-1);
}

/* --- Blockquote --- */

blockquote {
    border-left: 3px solid var(--accent);
    padding-left: var(--space-4);
    color: var(--text-secondary);
    margin: 0 0 1em 0;
    font-style: italic;
}

/* --- Horizontal Rule --- */

hr {
    border: none;
    border-top: 1px solid var(--border-color);
    margin: var(--space-8) 0;
}

/* --- Table --- */

table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 1em;
    font-size: var(--font-size-base);
}

th, td {
    border: 1px solid var(--border-color);
    padding: var(--space-2) var(--space-3);
    text-align: left;
    vertical-align: top;
    overflow-wrap: anywhere;
}

th {
    background: var(--bg-secondary);
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-sm);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    color: var(--text-secondary);
}

/* --- Images & Figures --- */

img {
    height: auto;
    border-radius: var(--radius-sm);
}

figure {
    margin: 0 0 1em 0;
}

figcaption {
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    margin-top: var(--space-2);
}

/* --- Details / Summary (Accordion) --- */

details {
    border: 1px solid var(--border-color);
    margin-bottom: var(--space-2);
}

details + details {
    margin-top: calc(-1 * var(--space-2));
    border-top: none;
}

summary {
    padding: var(--space-3) var(--space-4);
    cursor: pointer;
    font-weight: var(--font-weight-semibold);
    list-style: none;
    user-select: none;
}

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

summary::before {
    content: '\25B8  ';
    color: var(--text-muted);
}

details[open] > summary::before {
    content: '\25BE  ';
}

details[open] > summary {
    border-bottom: 1px solid var(--border-color);
}

summary:hover {
    color: var(--text-accent);
}

summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* --- Form Elements --- */

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="url"],
input[type="tel"],
input[type="date"],
input[type="time"],
input[type="datetime-local"],
textarea,
select {
    background: var(--bg-primary);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    padding: var(--space-2) var(--space-3);
    min-height: var(--control-height-md);
    font-family: var(--font-mono);
    font-size: var(--font-size-base);
    width: 100%;
    transition: border-color var(--transition-fast);
}

input:focus,
textarea:focus,
select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--shadow-focus);
}

input:disabled,
textarea:disabled,
select:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

input::placeholder,
textarea::placeholder {
    color: var(--text-dim);
}

/* Touch layouts: entry controls meet the 44px touch minimum and step to
   the 16px input floor so iOS Safari does not force-zoom on focus. */
@media (max-width: 768px) {
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="search"],
    input[type="url"],
    input[type="tel"],
    input[type="date"],
    input[type="time"],
    input[type="datetime-local"],
    textarea,
    select {
        min-height: var(--touch-target-min);
        font-size: var(--font-size-input);
    }
}

textarea {
    resize: vertical;
    min-height: 80px;
}

label {
    display: block;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--text-secondary);
    margin-bottom: var(--space-1);
}

fieldset {
    border: 1px solid var(--border-color);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
}

legend {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-sm);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    padding: 0 var(--space-2);
}

/* --- Button (base — components/button.css has full styling) --- */

button {
    cursor: pointer;
    font-family: var(--font-mono);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: transparent;
    color: var(--text-primary);
    padding: var(--space-2) var(--space-3);
    min-height: var(--control-height-md);
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

button:hover {
    border-color: var(--text-muted);
}

button:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* --- Dialog --- */

dialog {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--bg-secondary);
    color: var(--text-primary);
    padding: 0;
    max-width: var(--max-width-sm);
    width: 90%;
}

dialog::backdrop {
    background: color-mix(in srgb, var(--shadow) 70%, transparent);
}

/* --- Meter & Progress --- */

meter, progress {
    width: 100%;
    height: 8px;
    border: none;
    border-radius: var(--radius-sm);
    background: var(--bg-tertiary);
}

progress::-webkit-progress-bar {
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
}

progress::-webkit-progress-value {
    background: var(--accent);
    border-radius: var(--radius-sm);
}

progress::-moz-progress-bar {
    background: var(--accent);
    border-radius: var(--radius-sm);
}

/* --- Selection --- */

::selection {
    background: color-mix(in srgb, var(--accent) 30%, transparent);
    color: var(--text-primary);
}

/* --- Scrollbar (subtle) --- */

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

::-webkit-scrollbar-track {
    background: var(--bg-primary);
}

::-webkit-scrollbar-thumb {
    background: var(--border-color);
    border-radius: var(--radius-sm);
}

::-webkit-scrollbar-thumb:hover {
    background: var(--text-dim);
}

/* ============================================================
   JAX LAYOUT
   Container, Grid, Sidebar, Stack.
   ============================================================ */

/* --- Container --- */

.container {
    max-width: var(--max-width);
    margin: 0 auto;
    padding: 0 var(--space-4);
}

.container-sm { max-width: var(--max-width-sm); }
.container-md { max-width: var(--max-width-md); }
.container-lg { max-width: var(--max-width-lg); }

/* --- Grid --- */

.grid {
    display: grid;
    gap: var(--space-4);
}

.grid-2,
.grid-3,
.grid-4,
.grid-auto,
.grid-auto-sm {
    display: grid;
    gap: var(--space-4);
}

.grid > *,
.grid-2 > *,
.grid-3 > *,
.grid-4 > *,
.grid-auto > *,
.grid-auto-sm > * {
    min-width: 0;
}

.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-auto { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-auto-sm { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.col-span-2 { grid-column: span 2; }
.col-span-3 { grid-column: span 3; }
.col-span-full { grid-column: 1 / -1; }

/* --- Main + rail layout --- */

.layout-main-rail {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
}

.layout-main {
    min-width: 0;
}

.layout-rail {
    min-width: 0;
}

.layout-rail > * + * {
    margin-top: var(--space-4);
}

.layout-marginalia {
    display: none;
    min-width: 0;
}

@media (max-width: 768px) {
    .grid-2, .grid-3, .grid-4 {
        grid-template-columns: 1fr;
    }

    .col-span-2, .col-span-3 {
        grid-column: span 1;
    }
}

@media (min-width: 1024px) {
    .layout-main-rail {
        grid-template-columns: minmax(0, 1fr) 280px;
    }

    .layout-rail {
        position: sticky;
        top: calc(var(--navbar-height) + var(--space-4));
        align-self: start;
    }
}

@media (min-width: 1600px) and (pointer: fine) {
    .layout-main-rail-marginal {
        grid-template-columns: minmax(0, 1fr) 136px 280px;
    }

    .layout-main-rail-marginal .layout-marginalia {
        display: block;
        position: sticky;
        top: calc(var(--navbar-height) + var(--space-8));
        align-self: start;
        min-height: 140px;
        opacity: 0.84;
    }

    .layout-main-rail-marginal .layout-marginalia-rail {
        position: relative;
        min-height: 144px;
    }

    .layout-main-rail-marginal .layout-marginalia-note {
        position: absolute;
        inset: 0;
        margin: 0;
        max-width: 18ch;
        font-size: var(--font-size-xs);
        line-height: var(--line-height-loose);
        color: var(--text-muted);
        text-wrap: pretty;
        opacity: 0;
        transform: translateY(6px);
        transition: opacity var(--transition-normal), transform var(--transition-normal);
        pointer-events: none;
    }

    .layout-main-rail-marginal .layout-marginalia-note.is-visible {
        opacity: 0.72;
        transform: translateY(0);
        pointer-events: auto;
    }

    .layout-main-rail-marginal .layout-marginalia-note a {
        text-decoration: none;
    }

    .layout-main-rail-marginal .layout-marginalia-note .tag {
        display: inline-block;
        margin-top: var(--space-2);
    }

    .layout-main-rail-marginal .layout-marginalia-note .tag:hover {
        background: color-mix(in srgb, var(--accent) 12%, transparent);
        color: var(--text-accent);
    }
}

@media (prefers-reduced-motion: reduce) and (min-width: 1600px) and (pointer: fine) {
    .layout-main-rail-marginal .layout-marginalia-note {
        transition: none;
        transform: none;
    }
}

/* --- Sidebar layout --- */

.sidebar-layout {
    display: grid;
    grid-template-columns: var(--sidebar-width) 1fr;
    min-height: 100vh;
    min-height: 100dvh;
}

.sidebar {
    position: sticky;
    top: var(--navbar-height);
    height: calc(100vh - var(--navbar-height));
    height: calc(100dvh - var(--navbar-height));
    overflow-y: auto;
    border-right: 1px solid var(--border-color);
    background: var(--bg-secondary);
    padding: var(--space-4);
    padding-right: max(var(--space-4), env(safe-area-inset-right));
    padding-left: max(var(--space-4), env(safe-area-inset-left));
    padding-bottom: max(var(--space-4), env(safe-area-inset-bottom));
}

.sidebar-nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.sidebar-link {
    display: flex;
    align-items: center;
    padding: var(--space-2) var(--space-3);
    min-height: var(--control-height-md);
    color: var(--text-secondary);
    text-decoration: none;
    font-size: var(--font-size-base);
    border-radius: var(--radius-sm);
    transition: color var(--transition-fast), background var(--transition-fast);
}

.sidebar-link:hover {
    color: var(--text-primary);
    background: var(--bg-tertiary);
}

.sidebar-link.active,
.sidebar-link[aria-current="page"] {
    color: var(--text-accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.sidebar-section {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    color: var(--text-muted);
    padding: var(--space-4) var(--space-3) var(--space-2);
}

.sidebar-section:first-child {
    padding-top: 0;
}

.sidebar-content {
    padding: var(--space-4);
    padding-right: max(var(--space-4), env(safe-area-inset-right));
    padding-left: max(var(--space-4), env(safe-area-inset-left));
    padding-bottom: max(var(--space-4), env(safe-area-inset-bottom));
    min-width: 0;
}

@media (max-width: 768px) {
    .sidebar-layout {
        grid-template-columns: 1fr;
        min-height: auto;
    }

    .site-navbar + .sidebar-layout {
        padding-top: calc(var(--navbar-height) + env(safe-area-inset-top));
    }

    .sidebar {
        display: none;
    }

    .sidebar-content {
        padding-top: var(--space-4);
        overflow-x: hidden;
    }

    @supports (overflow: clip) {
        .sidebar-content {
            overflow-x: clip;
        }
    }
}

/* --- Stack --- */

.stack {
    display: flex;
    flex-direction: column;
}

.stack > * + * { margin-top: var(--space-4); }
.stack-sm > * + * { margin-top: var(--space-2); }
.stack-lg > * + * { margin-top: var(--space-8); }
.stack-xs > * + * { margin-top: var(--space-1); }

.hstack {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
}

/* --- Page header --- */

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--space-6);
    gap: var(--space-4);
}

.page-title {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    margin: 0;
}

.page-header-actions {
    display: flex;
    gap: var(--space-2);
}

/* --- Section --- */

.section {
    margin-bottom: var(--space-8);
}

.section-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    color: var(--text-muted);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border-subtle);
}

/* ============================================================
   JAX BUTTON
   Corner-marked actions. Solid primary, quiet everything else.

   THE MARKS
   A button is framed by four corner marks rather than by side
   brackets. Brackets sat inside the box and cost real width: at
   14px mono they added ~17px of optical inset per side on top of
   the CSS padding, which is what made the old button read squat
   (measured 3.23:1 horizontal-to-vertical inset against a target
   of 1.5-2.0). Corner marks sit ON the box edge and cost nothing,
   so the same label fits a box with 1.84:1 proportions.

   TECHNIQUE
   Eight background-image layers -- per corner, one horizontal and
   one vertical hairline. currentColor means the marks track the
   label colour for free. background-origin: border-box aligns them
   with the box edge. No pseudo-elements are consumed, so .btn-text
   and .btn-loading stay free and the htmx loading swap still works.
   Because the marks live in background-image, every background
   declaration below must be background-COLOR; the shorthand would
   wipe them out.

   The marks are sized to 0 at rest and grow in on hover and focus.
   The filled primary is the exception: the fill is already the
   affordance, so it carries no marks.
   ============================================================ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2);
    font-family: var(--font-mono);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-normal);
    line-height: var(--line-height-tight);
    text-decoration: none;
    white-space: nowrap;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background-color: transparent;
    color: var(--text-primary);
    cursor: pointer;
    min-height: var(--control-height-md);
    position: relative;
    transition: border-color var(--transition-fast), color var(--transition-fast),
        background-color var(--transition-fast), background-size var(--transition-fast);
}

/* --- Corner marks --- */

.btn {
    --btn-corner: var(--space-2);

    background-image:
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor),
        linear-gradient(currentColor, currentColor);
    background-position:
        left top, left top,
        right top, right top,
        left bottom, left bottom,
        right bottom, right bottom;
    background-repeat: no-repeat;
    background-origin: border-box;
    background-size: 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0;
}

.btn:hover,
.btn:focus-visible {
    background-size:
        var(--btn-corner) 1px, 1px var(--btn-corner),
        var(--btn-corner) 1px, 1px var(--btn-corner),
        var(--btn-corner) 1px, 1px var(--btn-corner),
        var(--btn-corner) 1px, 1px var(--btn-corner);
}

/* A filled primary is already a solid block; marks on top of it read
   as damage rather than as a frame. */
.btn-primary:hover,
.btn-primary:focus-visible {
    background-size: 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0;
}

/* Grouped buttons share edges and sit 1px apart, so neighbouring
   corner sets would merge into a single shape. The group manages its
   own borders; it does not get marks. */
.btn-group .btn:hover,
.btn-group .btn:focus-visible {
    background-size: 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0;
}

.btn:hover {
    border-color: var(--text-muted);
    color: var(--text-primary);
}

.btn:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}

.btn:active {
    background-color: var(--bg-tertiary);
}

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* --- Variants --- */

/* Primary is the one filled control in the kit. It uses the solid
   accent pair rather than --accent, which is tuned for lines. */
.btn-primary {
    padding: var(--space-2) var(--space-4);
    background-color: var(--color-accent-solid);
    border-color: var(--color-accent-solid);
    color: var(--text-on-accent);
}

.btn-primary:hover {
    background-color: var(--color-accent-solid-hover);
    border-color: var(--color-accent-solid-hover);
    color: var(--text-on-accent);
}

.btn-primary:active {
    background-color: var(--color-accent-solid-hover);
}

/* An accent-tinted ring is invisible against an accent fill, so the
   primary's ring moves inside as a knockout and keeps the outer glow. */
.btn-primary:focus-visible {
    outline: 2px solid var(--text-on-accent);
    outline-offset: -4px;
    box-shadow: var(--shadow-focus);
}

/* Secondary carries no frame at rest -- the corner marks are its
   whole affordance, and they arrive on hover and focus. */
.btn-secondary {
    border-color: transparent;
    color: var(--text-secondary);
}

.btn-secondary:hover {
    border-color: transparent;
    color: var(--text-primary);
}

.btn-ghost {
    border-color: transparent;
    color: var(--text-secondary);
}

.btn-ghost:hover {
    border-color: transparent;
    color: var(--text-primary);
    background-color: var(--bg-tertiary);
}

.btn-danger {
    border-color: var(--color-error);
    color: var(--text-error);
}

.btn-danger:hover {
    border-color: var(--color-error);
    background-color: color-mix(in srgb, var(--color-error) 10%, transparent);
    color: var(--text-error);
}

.btn-ok {
    border-color: var(--color-ok);
    color: var(--text-ok);
}

.btn-ok:hover {
    border-color: var(--color-ok);
    background-color: color-mix(in srgb, var(--color-ok) 10%, transparent);
    color: var(--text-ok);
}

/* --- Sizes ---
   These follow the variants so a size always outranks a variant's
   padding at equal specificity. */

.btn-sm {
    font-size: var(--font-size-sm);
    padding: var(--space-1) var(--space-2);
    min-height: var(--control-height-sm);
}

/* lg is a density step, not a volume step: more room, same voice.
   It deliberately does NOT change font-size -- no other control in
   the kit steps its type, and an 18px label was the reason lg read
   as a different component rather than a bigger one. Its metrics
   match what the mobile branch below turns every button into, so
   the two rules agree instead of fighting. */
.btn-lg {
    padding: var(--space-3) var(--space-4);
    min-height: var(--control-height-lg);
}

/* --- Icon button --- */

.btn-icon {
    padding: var(--space-2);
    min-width: var(--control-height-md);
    min-height: var(--control-height-md);
}

.btn-icon.btn-sm {
    padding: var(--space-1);
    min-width: var(--control-height-sm);
    min-height: var(--control-height-sm);
}

/* --- Full width --- */

.btn-block {
    display: flex;
    width: 100%;
}

/* --- Button group --- */

.btn-group {
    display: inline-flex;
}

.btn-group .btn + .btn {
    margin-left: -1px;
}

.btn-group .btn:first-child { border-right: none; }
.btn-group .btn:last-child { border-left: none; }

/* --- HTMX loading state ---
   The marks live in background-image, not in a pseudo-element, so
   swapping the label out leaves the frame intact. */

.btn .btn-loading {
    display: none;
}

.htmx-request .btn-text {
    display: none;
}

.htmx-request .btn-loading {
    display: inline;
}

/* --- Mobile: larger touch targets ---
   Equal specificity to .btn-lg and later in the file, so this wins.
   That used to make lg NARROWER on mobile than on desktop; lg now
   declares these same metrics, so the collision is a no-op. */

@media (max-width: 768px) {
    .btn {
        min-height: var(--touch-target-min);
        padding: var(--space-3) var(--space-4);
    }

    .btn-sm {
        min-height: var(--touch-target-min);
        padding: var(--space-2) var(--space-3);
    }

    /* Coarse pointers never fire :hover, so a secondary would have no
       affordance at all. Its marks are always on. */
    .btn-secondary {
        background-size:
            var(--btn-corner) 1px, 1px var(--btn-corner),
            var(--btn-corner) 1px, 1px var(--btn-corner),
            var(--btn-corner) 1px, 1px var(--btn-corner),
            var(--btn-corner) 1px, 1px var(--btn-corner);
    }
}

/* ============================================================
   JAX CARD
   Sharp corners. Borders, not shadows. Uppercase headers.
   ============================================================ */

.card {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: var(--bg-secondary);
    box-shadow: none;
}

.card-header {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
}

.card-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    color: var(--text-secondary);
    margin: 0;
}

.card-actions {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    margin-left: auto;
}

.card-body {
    padding: var(--space-4);
}

.card-body:empty {
    display: none;
}

.card-footer {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border-color);
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* --- Compact variant --- */

.card-compact .card-header {
    padding: var(--space-2) var(--space-3);
}

.card-compact .card-body {
    padding: var(--space-3);
}

/* --- No header variant --- */

.card-body:first-child {
    border-radius: var(--radius-none);
}

/* --- Interactive card --- */

a.card,
.card-link {
    text-decoration: none;
    color: inherit;
    transition: border-color var(--transition-fast);
}

a.card:hover,
.card-link:hover {
    border-color: var(--accent);
}

/* --- Card with status border --- */

.card-ok { border-left: 3px solid var(--color-ok); }
.card-warn { border-left: 3px solid var(--color-warn); }
.card-error { border-left: 3px solid var(--color-error); }
.card-info { border-left: 3px solid var(--color-info); }

/* ============================================================
   JAX MEDIA
   Cover image + metadata. The card family for anything that
   leads with a picture: galleries, projects, vehicles, albums.

   Ratios are classes, never inline style — the template guard
   fails the build on style=, so the shipped ratios are the
   whole set.
   ============================================================ */

/* --- Grid --- */

.media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    grid-auto-rows: 1fr;
    gap: var(--space-4);
}

.media-grid-sm { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
.media-grid-lg { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.media-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.media-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.media-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.media-grid > * {
    min-width: 0;
    height: 100%;
}

/* --- Card --- */

.media-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: var(--bg-secondary);
    box-shadow: none;
    overflow: hidden;
}

a.media-card,
.media-card-link {
    text-decoration: none;
    color: inherit;
    display: block;
    height: 100%;
    transition: border-color var(--transition-fast);
}

.media-card-link > .media-card {
    transition: border-color var(--transition-fast);
}

a.media-card:hover,
.media-card-link:hover > .media-card {
    border-color: var(--accent);
}

.media-card-link:hover .media-title {
    color: var(--text-accent);
}

a.media-card:focus-visible,
.media-card-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* --- Cover --- */

.media-cover {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    background: var(--bg-tertiary);
    overflow: hidden;
}

.media-cover img,
.media-cover video,
.media-cover svg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    border-radius: var(--radius-none);
    margin: 0;
}

/* Line art and logos are not photographs — fit them, do not crop them. */
.media-cover-contain img,
.media-cover-contain svg {
    object-fit: contain;
    padding: var(--space-4);
}

.media-ratio-1-1 { aspect-ratio: 1 / 1; }
.media-ratio-4-3 { aspect-ratio: 4 / 3; }
.media-ratio-3-2 { aspect-ratio: 3 / 2; }
.media-ratio-16-9 { aspect-ratio: 16 / 9; }
.media-ratio-21-9 { aspect-ratio: 21 / 9; }
.media-ratio-auto { aspect-ratio: auto; }

/* --- Placeholder (no cover available) --- */

.media-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    background: var(--bg-tertiary);
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    text-align: center;
    padding: var(--space-4);
}

/* --- Badge slot on the cover --- */

.media-cover-badge {
    position: absolute;
    top: var(--space-2);
    left: var(--space-2);
    z-index: 1;
}

/* --- Body --- */

.media-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4);
    flex: 1 1 auto;
    min-width: 0;
}

.media-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--text-primary);
    text-transform: none;
    letter-spacing: var(--letter-spacing-normal);
    margin: 0;
    overflow-wrap: anywhere;
    transition: color var(--transition-fast);
}

.media-title a {
    color: inherit;
    text-decoration: none;
}

.media-title a:hover {
    color: var(--text-accent);
}

/* One line carries every fact: kind, date, place, count. Separated
   by a generated middot so the template stays a plain list of spans. */
.media-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    font-variant-numeric: tabular-nums;
}

.media-meta > * + *::before {
    content: '\00B7';
    color: var(--text-muted);
    margin-right: var(--space-2);
}

.media-meta .is-kind {
    color: var(--text-accent);
}

.media-desc {
    font-size: var(--font-size-sm);
    line-height: var(--line-height);
    color: var(--text-secondary);
    margin: var(--space-1) 0 0;
}

.media-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4) var(--space-3);
    margin-top: auto;
}

/* --- Horizontal variant --- */

.media-card-h {
    display: grid;
    grid-template-columns: minmax(120px, 200px) minmax(0, 1fr);
    align-items: stretch;
}

.media-card-h .media-cover {
    height: 100%;
    aspect-ratio: auto;
}

.media-card-h .media-body {
    justify-content: center;
    padding: var(--space-4);
    border-left: 1px solid var(--border-subtle);
}

.media-card-h-lg {
    grid-template-columns: minmax(200px, 320px) minmax(0, 1fr);
}

/* --- Overlay variant --- */
/* The body sits on the image, so it needs a scrim of its own —
   text over an unknown photograph is the one place a gradient
   earns its keep. */

.media-card-overlay {
    position: relative;
}

.media-card-overlay .media-cover {
    height: 100%;
}

.media-card-overlay .media-body {
    position: absolute;
    inset: auto 0 0 0;
    padding: var(--space-6) var(--space-4) var(--space-3);
    background: linear-gradient(
        to top,
        color-mix(in srgb, var(--shadow) 88%, transparent) 0%,
        color-mix(in srgb, var(--shadow) 70%, transparent) 55%,
        transparent 100%
    );
}

.media-card-overlay .media-title {
    color: var(--highlight);
}

.media-card-overlay .media-meta {
    color: color-mix(in srgb, var(--highlight) 75%, transparent);
}

.media-card-overlay .media-meta > * + *::before {
    color: color-mix(in srgb, var(--highlight) 45%, transparent);
}

/* --- Responsive --- */

@media (max-width: 768px) {
    .media-grid {
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
        gap: var(--space-3);
    }

    .media-grid-3,
    .media-grid-4 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .media-card-h,
    .media-card-h-lg {
        grid-template-columns: 1fr;
    }

    .media-card-h .media-cover {
        aspect-ratio: 16 / 9;
    }

    .media-card-h .media-body {
        border-left: none;
        border-top: 1px solid var(--border-subtle);
    }
}

@media (max-width: 480px) {
    .media-grid,
    .media-grid-2,
    .media-grid-3,
    .media-grid-4 {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
   JAX BADGE
   Status indicators: [OK] [WARN] [ERR] [3 items]
   ============================================================ */

.badge {
    display: inline-block;
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    border-radius: var(--radius-sm);
    line-height: var(--line-height-tight);
    vertical-align: middle;
    white-space: nowrap;
}

.badge-wrap {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* Default: muted */
.badge {
    color: var(--text-secondary);
    background: var(--bg-tertiary);
}

.badge-ok {
    color: var(--text-ok);
    background: color-mix(in srgb, var(--color-ok) 15%, transparent);
}

.badge-warn {
    color: var(--text-warn);
    background: color-mix(in srgb, var(--color-warn) 15%, transparent);
}

.badge-error {
    color: var(--text-error);
    background: color-mix(in srgb, var(--color-error) 15%, transparent);
}

.badge-info {
    color: var(--text-info);
    background: color-mix(in srgb, var(--color-info) 15%, transparent);
}

.badge-accent {
    color: var(--text-accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
}

/* --- Chip (pill-shaped tag) --- */

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-sm);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    background: var(--bg-secondary);
    max-width: 100%;
    overflow-wrap: anywhere;
}

.chip-pill {
    border-radius: var(--radius-pill);
}

.chip-active {
    border-color: var(--accent);
    color: var(--text-accent);
}

.chip-sm {
    font-size: var(--font-size-xs);
    padding: var(--space-1);
}

.chip-remove {
    cursor: pointer;
    color: var(--text-muted);
    margin-left: var(--space-1);
}

.chip-remove:hover {
    color: var(--text-error);
}

/* ============================================================
   JAX FORM COMPONENTS
   Labels, rows, validation, custom controls.
   Base input styling is in base.css.
   ============================================================ */

/* --- Form layout --- */

.form-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
}

.form-row:last-child {
    margin-bottom: 0;
}

.form-label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    color: var(--text-secondary);
}

.form-label-required::after {
    content: ' *';
    color: var(--text-error);
}

.form-help {
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    overflow-wrap: break-word;
}

.form-error {
    font-size: var(--font-size-sm);
    color: var(--text-error);
    overflow-wrap: break-word;
}

/* --- Validation states --- */

.is-error,
.form-input.is-error {
    border-color: var(--color-error);
}

.is-error:focus,
.form-input.is-error:focus {
    border-color: var(--color-error);
    box-shadow: var(--shadow-focus-error);
}

.is-ok,
.form-input.is-ok {
    border-color: var(--color-ok);
}

.is-ok:focus,
.form-input.is-ok:focus {
    border-color: var(--color-ok);
    box-shadow: var(--shadow-focus-ok);
}

.form-row :is(
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
    textarea
):invalid:not(:placeholder-shown),
.form-row select:invalid {
    border-color: var(--color-error);
}

.form-row :is(
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
    textarea
):invalid:not(:placeholder-shown):focus,
.form-row select:invalid:focus {
    border-color: var(--color-error);
    box-shadow: var(--shadow-focus-error);
}

.form-row:has(
    :is(
        input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
        textarea
    ):invalid:not(:placeholder-shown),
    select:invalid
) .form-label {
    color: var(--text-error);
}

@supports selector(input:user-invalid) {
    .form-row :is(
        input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
        textarea,
        select
    ):user-invalid {
        border-color: var(--color-error);
    }

    .form-row :is(
        input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
        textarea,
        select
    ):user-invalid:focus {
        border-color: var(--color-error);
        box-shadow: var(--shadow-focus-error);
    }

    .form-row :is(
        input[required]:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
        textarea[required],
        select[required]
    ):user-valid {
        border-color: var(--color-ok);
    }

    .form-row :is(
        input[required]:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
        textarea[required],
        select[required]
    ):user-valid:focus {
        border-color: var(--color-ok);
        box-shadow: var(--shadow-focus-ok);
    }

    .form-row:has(
        :is(
            input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
            textarea,
            select
        ):user-invalid
    ) .form-label {
        color: var(--text-error);
    }

    .form-row:has(
        :is(
            input[required]:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
            textarea[required],
            select[required]
        ):user-valid
    ) .form-label {
        color: var(--text-ok);
    }
}

/* --- Inline form --- */

.form-inline {
    display: flex;
    align-items: flex-end;
    gap: var(--space-3);
}

.form-inline .form-row {
    margin-bottom: 0;
    flex: 1;
}

/* --- Custom checkbox --- */

.checkbox {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--control-height-md);
    cursor: pointer;
    font-size: var(--font-size-base);
    user-select: none;
}

.checkbox input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: var(--space-4);
    height: var(--space-4);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--bg-primary);
    cursor: pointer;
    flex-shrink: 0;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}

.checkbox input[type="checkbox"]:checked {
    background: var(--accent);
    border-color: var(--accent);
}

.checkbox input[type="checkbox"]:checked::after {
    content: '\2713';
    display: block;
    text-align: center;
    color: var(--bg-primary);
    font-size: var(--font-size-sm);
    line-height: var(--space-4);
}

.checkbox input[type="checkbox"]:focus-visible {
    box-shadow: var(--shadow-focus);
}

/* --- Custom radio --- */

.radio {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--control-height-md);
    cursor: pointer;
    font-size: var(--font-size-base);
    user-select: none;
}

.radio input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    width: var(--space-4);
    height: var(--space-4);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    background: var(--bg-primary);
    cursor: pointer;
    flex-shrink: 0;
    transition: border-color var(--transition-fast);
}

.radio input[type="radio"]:checked {
    border-color: var(--accent);
    border-width: var(--space-1);
}

.radio input[type="radio"]:focus-visible {
    box-shadow: var(--shadow-focus);
}

/* --- Toggle / Switch --- */

.toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--control-height-md);
    cursor: pointer;
    user-select: none;
}

.toggle-track {
    position: relative;
    width: var(--space-10);
    height: var(--space-6);
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-pill);
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.toggle-thumb {
    position: absolute;
    top: var(--space-1);
    left: var(--space-1);
    width: var(--space-4);
    height: var(--space-4);
    background: var(--text-muted);
    border-radius: var(--radius-full);
    transition: transform var(--transition-fast), background var(--transition-fast);
}

.toggle input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle input:checked + .toggle-track {
    background: color-mix(in srgb, var(--accent) 20%, transparent);
    border-color: var(--accent);
}

.toggle input:checked + .toggle-track .toggle-thumb {
    transform: translateX(var(--space-4));
    background: var(--accent);
}

.toggle input:focus-visible + .toggle-track {
    box-shadow: var(--shadow-focus);
}

/* --- Select (custom styled) --- */

.select-custom {
    position: relative;
}

.select-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-3);
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    cursor: pointer;
    color: var(--text-primary);
    transition: border-color var(--transition-fast);
}

.select-trigger:hover {
    border-color: var(--text-muted);
}

.select-trigger::after {
    content: '\25BE';
    color: var(--text-muted);
    margin-left: var(--space-2);
}

.select-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-top: none;
    max-height: 200px;
    overflow-y: auto;
    z-index: 100;
    box-shadow: var(--shadow-dropdown);
}

.select-option {
    padding: var(--space-2) var(--space-3);
    cursor: pointer;
    transition: background var(--transition-fast);
}

.select-option:hover,
.select-option[aria-selected="true"] {
    background: var(--bg-tertiary);
    color: var(--text-accent);
}

/* --- Range / Slider --- */

input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 4px;
    background: var(--bg-tertiary);
    border: none;
    border-radius: var(--radius-sm);
    outline: none;
}

input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    background: var(--accent);
    border: none;
    border-radius: var(--radius-full);
    cursor: pointer;
}

input[type="range"]::-moz-range-thumb {
    width: 16px;
    height: 16px;
    background: var(--accent);
    border: none;
    border-radius: var(--radius-full);
    cursor: pointer;
}

input[type="range"]:focus-visible::-webkit-slider-thumb {
    box-shadow: var(--shadow-focus);
}

/* --- File Input --- */

.file-input {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.file-input input[type="file"] {
    display: none;
}

.file-input-name {
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    overflow-wrap: anywhere;
}

.file-drop {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed var(--border-color);
    padding: var(--space-6) var(--space-4);
    min-height: var(--control-height-lg);
    text-align: center;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.file-drop:hover,
.file-drop.drag-over {
    border-color: var(--accent);
    color: var(--text-secondary);
}

.file-drop:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.file-drop input[type="file"] {
    display: none;
}

.file-drop-label {
    overflow-wrap: anywhere;
}

/* --- Radio Group --- */

.radio-group {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-3);
}

.radio-group .radio {
    display: flex;
    align-items: flex-start;
    padding: var(--space-3);
    min-height: var(--control-height-lg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    transition: border-color var(--transition-fast);
}

.radio-group .radio:has(input:checked) {
    border-color: var(--accent);
}

.radio-desc {
    display: block;
    font-size: var(--font-size-xs);
    color: var(--text-muted);
    margin-top: var(--space-1);
    font-weight: var(--font-weight-normal);
}

/* --- Mobile --- */

@media (max-width: 768px) {
    .form-inline {
        flex-direction: column;
        align-items: stretch;
    }

    .checkbox input[type="checkbox"],
    .radio input[type="radio"] {
        width: var(--space-5);
        height: var(--space-5);
    }

    .checkbox,
    .radio,
    .toggle {
        min-height: var(--touch-target-min);
    }

    .toggle-track {
        width: var(--touch-target-min);
        height: var(--space-6);
    }

    .toggle input:checked + .toggle-track .toggle-thumb {
        transform: translateX(var(--space-5));
    }
}

/* ============================================================
   JAX TABLE (enhanced)
   Base table styling is in base.css.
   ============================================================ */

.table-striped tbody tr:nth-child(even) {
    background: var(--bg-secondary);
}

.table-hover tbody tr:hover {
    background: var(--bg-tertiary);
}

.table-compact th,
.table-compact td {
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-sm);
}

/* --- Dense data wrapping helpers --- */

.table-wrap th,
.table-wrap td {
    overflow-wrap: anywhere;
}

.table-data {
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
    white-space: break-spaces;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* --- Responsive table --- */

@media (max-width: 768px) {
    .table-responsive {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .table-stack thead {
        display: none;
    }

    .table-stack tbody tr {
        display: block;
        border: 1px solid var(--border-color);
        margin-bottom: var(--space-2);
        padding: var(--space-2);
    }

    .table-stack tbody td {
        display: flex;
        justify-content: space-between;
        border: none;
        padding: var(--space-1) 0;
    }

    .table-stack tbody td::before {
        content: attr(data-label);
        font-weight: var(--font-weight-semibold);
        font-size: var(--font-size-sm);
        text-transform: uppercase;
        letter-spacing: var(--letter-spacing-wide);
        color: var(--text-muted);
        margin-right: var(--space-3);
    }
}

/* ============================================================
   JAX DATA TABLE
   A real <table> with a toolbar over it. Search, sort, and a
   live count, all optional, all progressive: with JS off the
   table still renders and reads correctly in source order.

   Behaviour comes from <jax-table> (js/components/datatable.js).
   Presentation is here, and is table.css plus a toolbar.
   ============================================================ */

jax-table {
    display: block;
}

/* --- Toolbar --- */

.datatable-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.datatable-search {
    flex: 1 1 200px;
    max-width: var(--max-width-sm);
    min-width: 0;
}

.datatable-filter {
    flex: 0 1 auto;
    width: auto;
    min-width: 0;
}

/* Reading, not acting: the count sits at the quiet end of the bar. */
.datatable-count {
    margin-left: auto;
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.datatable-count b {
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

/* --- Sortable headers --- */
/* The control is a real button inside the th, so keyboard and
   screen readers get the same affordance the mouse gets. */

.datatable th:has(.th-sort),
th.sortable {
    padding: 0;
}

.th-sort {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    width: 100%;
    min-height: var(--control-height-sm);
    padding: var(--space-2) var(--space-3);
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    text-align: left;
    cursor: pointer;
    transition: color var(--transition-fast), background-color var(--transition-fast);
}

.th-sort:hover {
    color: var(--text-primary);
    background-color: var(--bg-tertiary);
    border-color: transparent;
}

.th-sort:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
    outline-offset: -2px;
}

/* Fixed-width marker: the column must not resize when it sorts. */
.th-sort::after {
    content: '\2195';
    flex: 0 0 auto;
    width: 1ch;
    text-align: center;
    color: var(--text-muted);
    transition: color var(--transition-fast);
}

.th-sort:hover::after {
    color: var(--text-primary);
}

[aria-sort="ascending"] .th-sort,
[aria-sort="descending"] .th-sort {
    color: var(--text-accent);
}

[aria-sort="ascending"] .th-sort::after {
    content: '\25B2';
    color: var(--accent);
}

[aria-sort="descending"] .th-sort::after {
    content: '\25BC';
    color: var(--accent);
}

/* --- Numeric columns --- */

.datatable td.num,
.datatable th.num .th-sort,
.col-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Reversed and packed right: the marker hugs the label instead of
   drifting to the far edge of a wide numeric column. */
.datatable th.num .th-sort {
    flex-direction: row-reverse;
    justify-content: flex-start;
}

/* --- Filtered-out and empty rows --- */
/* Specific enough to beat .table-stack's mobile display rule: a row
   the filter removed must stay gone at every width. */

.datatable tbody tr[hidden],
.datatable tbody tr.datatable-empty[hidden] {
    display: none;
}

.datatable-empty td {
    padding: var(--space-8) var(--space-4);
    text-align: center;
    color: var(--text-muted);
}

/* --- Sticky header --- */

.datatable-sticky {
    max-height: 70vh;
    overflow: auto;
}

.datatable-sticky thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg-secondary);
}

/* --- Responsive --- */

@media (max-width: 768px) {
    .datatable-toolbar {
        gap: var(--space-2);
    }

    .datatable-search {
        flex: 1 1 100%;
        max-width: none;
    }

    .datatable-count {
        margin-left: 0;
    }

    .th-sort {
        min-height: var(--touch-target-min);
    }
}

/* ============================================================
   JAX ALERT / CALLOUT
   Left-border accent. No background fills.
   ============================================================ */

.alert {
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--accent);
    padding: var(--space-3) var(--space-4);
    background: var(--bg-secondary);
    margin-bottom: var(--space-4);
}

.alert:last-child {
    margin-bottom: 0;
}

.alert-ok { border-left-color: var(--color-ok); }
.alert-warn { border-left-color: var(--color-warn); }
.alert-error { border-left-color: var(--color-error); }
.alert-info { border-left-color: var(--color-info); }

.alert-title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-sm);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    margin-bottom: var(--space-1);
}

.alert-ok .alert-title { color: var(--text-ok); }
.alert-warn .alert-title { color: var(--text-warn); }
.alert-error .alert-title { color: var(--text-error); }
.alert-info .alert-title { color: var(--text-info); }

.alert-body {
    font-size: var(--font-size-base);
    color: var(--text-secondary);
    overflow-wrap: anywhere;
}

/* ============================================================
   JAX DIALOG / MODAL
   Native <dialog>. No shadows. Focus trap built-in.
   ============================================================ */

.dialog {
    position: fixed;
    inset: 0;
    margin: auto;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--bg-secondary);
    color: var(--text-primary);
    padding: 0;
    max-width: var(--max-width-sm);
    width: 90%;
    max-height: 85vh;
    max-height: 85dvh;
}

.dialog::backdrop {
    background: color-mix(in srgb, var(--shadow) 70%, transparent);
}

.dialog-header {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.dialog-title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-base);
    margin: 0;
}

.dialog-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: var(--space-1);
    min-width: var(--control-height-sm);
    min-height: var(--control-height-sm);
    font-size: var(--font-size-lg);
    line-height: 1;
}

.dialog-close:hover {
    color: var(--text-primary);
}

.dialog-body {
    padding: var(--space-4);
}

.dialog-footer {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border-color);
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* --- Size variants --- */

.dialog-sm { max-width: 360px; }
.dialog-md { max-width: var(--max-width-sm); }
.dialog-lg { max-width: var(--max-width-md); }

@media (max-width: 768px) {
    .dialog-close {
        min-width: var(--touch-target-min);
        min-height: var(--touch-target-min);
    }
}

/* ============================================================
   JAX NAVBAR
   Sticky top. Keyboard-indexed. Collapsible option on mobile.
   ============================================================ */

.navbar {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--bg-secondary);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    padding-right: var(--space-4);
    padding-left: var(--space-4);
    padding-right: max(var(--space-4), env(safe-area-inset-right));
    padding-left: max(var(--space-4), env(safe-area-inset-left));
    height: var(--navbar-height);
    transition: background var(--transition-normal), border-color var(--transition-normal);
}

.navbar-brand {
    font-weight: var(--font-weight-bold);
    color: var(--text-primary);
    text-decoration: none;
    margin-right: var(--space-6);
    white-space: nowrap;
}

.navbar-brand:hover {
    color: var(--text-accent);
}

.navbar-nav-wrap {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 1;
    min-width: 0;
}

.navbar-toggle {
    display: none;
    margin-left: auto;
    white-space: nowrap;
}

.navbar-nav {
    display: flex;
    gap: var(--space-1);
    overflow-x: auto;
    flex: 1;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.navbar-nav::-webkit-scrollbar {
    display: none;
}

.navbar-link {
    display: inline-flex;
    align-items: center;
    padding: var(--space-2) var(--space-3);
    min-height: var(--control-height-md);
    color: var(--text-secondary);
    text-decoration: none;
    white-space: nowrap;
    font-size: var(--font-size-base);
    border-bottom: 2px solid transparent;
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.navbar-link:hover {
    color: var(--text-primary);
}

.navbar-link.active,
.navbar-link[aria-current="page"] {
    color: var(--text-accent);
    border-bottom-color: var(--accent);
}

.navbar-end {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
}

/* --- Separator --- */

.navbar-sep {
    width: 1px;
    height: 24px;
    background: var(--border-color);
    margin: 0 var(--space-2);
}

@media (max-width: 768px) {
    .navbar {
        padding-right: var(--space-2);
        padding-left: var(--space-2);
        padding-right: max(var(--space-2), env(safe-area-inset-right));
        padding-left: max(var(--space-2), env(safe-area-inset-left));
    }

    .navbar:not(.navbar-collapsible) .navbar-brand {
        margin-right: var(--space-3);
    }

    .navbar:not(.navbar-collapsible) .navbar-link {
        padding: var(--space-2);
        font-size: var(--font-size-sm);
    }

    .site-navbar.navbar-collapsible {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        height: calc(var(--navbar-height) + env(safe-area-inset-top));
        min-height: calc(var(--navbar-height) + env(safe-area-inset-top));
        flex-wrap: nowrap;
        align-items: center;
        padding-top: env(safe-area-inset-top);
        padding-bottom: 0;
    }

    .navbar-collapsible .navbar-brand {
        margin-right: 0;
    }

    .navbar-collapsible .navbar-toggle {
        display: inline-flex;
        margin-left: auto;
    }

    .navbar-collapsible .navbar-nav-wrap {
        display: flex;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        border-top: 1px solid var(--border-color);
        border-bottom: 1px solid var(--border-color);
        background: var(--bg-secondary);
        padding-right: max(var(--space-2), env(safe-area-inset-right));
        padding-left: max(var(--space-2), env(safe-area-inset-left));
        padding-top: var(--space-2);
        padding-bottom: max(var(--space-2), env(safe-area-inset-bottom));
        max-height: calc(100dvh - var(--navbar-height));
        overflow-y: auto;
    }

    .site-navbar.navbar-collapsible .navbar-nav-wrap {
        max-height: calc(100dvh - (var(--navbar-height) + env(safe-area-inset-top)));
    }

    .jax-js .navbar-collapsible .navbar-nav-wrap {
        opacity: 0;
        transform: translateY(-8px);
        visibility: hidden;
        pointer-events: none;
        transition: opacity var(--transition-normal), transform var(--transition-normal), visibility 0s linear var(--transition-normal);
    }

    .jax-js .navbar-collapsible.is-open .navbar-nav-wrap {
        opacity: 1;
        transform: translateY(0);
        visibility: visible;
        pointer-events: auto;
        transition-delay: 0s;
    }

    .navbar-collapsible .navbar-nav {
        width: 100%;
        flex: none;
        flex-direction: column;
        gap: 0;
        overflow: visible;
        padding: 0;
    }

    .navbar-collapsible .navbar-link {
        display: flex;
        align-items: center;
        width: 100%;
        padding: var(--space-2) var(--space-2);
        min-height: var(--touch-target-min);
        font-size: var(--font-size-base);
        border-bottom: none;
        border-left: 2px solid transparent;
    }

    .navbar-collapsible .navbar-link.active,
    .navbar-collapsible .navbar-link[aria-current="page"] {
        border-left-color: var(--accent);
        border-bottom-color: transparent;
        background: color-mix(in srgb, var(--accent) 10%, transparent);
    }

    .navbar-collapsible .navbar-end {
        margin-left: 0;
        padding-top: var(--space-1);
        justify-content: flex-start;
    }

    .navbar-collapsible .navbar-end .btn {
        width: auto;
        justify-content: flex-start;
    }

    .navbar-collapsible .navbar-sep {
        display: none;
    }

    .navbar-collapsible {
        background: var(--bg-primary);
        border-bottom-color: transparent;
    }

    .navbar-collapsible.is-scrolled,
    .navbar-collapsible.is-open {
        background: var(--bg-secondary);
        border-bottom-color: var(--border-color);
    }

}

/* ============================================================
   JAX NAVIGATION
   Breadcrumb, Pagination, Tabs (CSS-only base).
   ============================================================ */

/* --- Breadcrumb --- */

.breadcrumb {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    margin-bottom: var(--space-4);
}

.breadcrumb a {
    color: var(--text-secondary);
    text-decoration: none;
}

.breadcrumb a:hover {
    color: var(--text-accent);
}

.breadcrumb-sep {
    color: var(--text-dim);
}

.breadcrumb-sep::before {
    content: '/';
}

.breadcrumb-current,
.breadcrumb [aria-current="page"] {
    color: var(--text-primary);
}

.breadcrumb [aria-current="page"] {
    text-decoration: none;
}

/* --- Pagination --- */

.pagination {
    display: flex;
    gap: var(--space-1);
    align-items: center;
}

.page-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--control-height-md);
    height: var(--control-height-md);
    padding: 0 var(--space-2);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    text-decoration: none;
    font-size: var(--font-size-sm);
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.page-link:hover {
    border-color: var(--text-muted);
    color: var(--text-primary);
}

.page-link.active,
.page-link[aria-current="page"],
.page-link[aria-current="step"] {
    border-color: var(--accent);
    color: var(--text-accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.page-link:disabled,
.page-link[aria-disabled="true"] {
    opacity: 0.4;
    pointer-events: none;
}

/* --- Tabs (CSS base — WebComponent adds behavior) --- */

.tabs {
    display: flex;
    border-bottom: 1px solid var(--border-color);
    gap: 0;
    overflow-x: auto;
}

.tab {
    padding: var(--space-2) var(--space-4);
    min-height: var(--control-height-md);
    display: inline-flex;
    align-items: center;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: var(--font-size-base);
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    background: none;
    border-top: none;
    border-left: none;
    border-right: none;
    transition: color var(--transition-fast), border-color var(--transition-fast);
}

.tab:hover {
    color: var(--text-primary);
}

.tab[aria-selected="true"],
.tab.active {
    color: var(--text-accent);
    border-bottom-color: var(--accent);
}

.tab-panel {
    padding: var(--space-4) 0;
}

.tab-panel[hidden] {
    display: none;
}

/* --- Segmented Control --- */

.segmented {
    display: inline-flex;
    width: fit-content;
    border: 1px solid var(--border-color);
}

.segment {
    padding: var(--space-2) var(--space-3);
    min-height: var(--control-height-md);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-sm);
    color: var(--text-secondary);
    background: none;
    border: none;
    border-right: 1px solid var(--border-color);
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
    transition: color var(--transition-fast), background var(--transition-fast);
}

.segment:last-child {
    border-right: none;
}

.segment:hover {
    color: var(--text-primary);
    background: var(--bg-tertiary);
}

.segment.active,
.segment[aria-pressed="true"],
.segment[aria-current="page"] {
    color: var(--text-accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}


/* --- Pagination bar --- */
/* Count, pages, and page-size in one row. The count is a reading,
   so it sits apart from the controls that change it. */

.pagination-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-6);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-subtle);
}

.pagination-count {
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.pagination-count b {
    color: var(--text-primary);
    font-weight: var(--font-weight-semibold);
}

.pagination-gap {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--control-height-md);
    height: var(--control-height-md);
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    user-select: none;
}

.per-page {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.per-page label {
    margin-bottom: 0;
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    color: var(--text-muted);
    white-space: nowrap;
}

.per-page select {
    width: auto;
    min-width: 0;
    height: var(--control-height-md);
    min-height: var(--control-height-md);
    padding: 0 var(--space-2);
    font-size: var(--font-size-sm);
}

/* --- Previous / next --- */
/* Detail-page navigation. Each side names where it goes, not just
   that it goes somewhere. */

.prevnext {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    margin-top: var(--space-8);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-color);
}

.prevnext-link {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3);
    border: 1px solid var(--border-color);
    color: inherit;
    text-decoration: none;
    min-width: 0;
    transition: border-color var(--transition-fast);
}

.prevnext-link:hover {
    border-color: var(--accent);
}

.prevnext-link:hover .prevnext-title {
    color: var(--text-accent);
}

.prevnext-link:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
    border-color: var(--accent);
}

.prevnext-dir {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    color: var(--text-muted);
}

.prevnext-title {
    font-size: var(--font-size-sm);
    color: var(--text-primary);
    line-height: var(--line-height-tight);
    overflow-wrap: anywhere;
    transition: color var(--transition-fast);
}

.prevnext-next {
    text-align: right;
    grid-column: 2;
}

.prevnext-empty {
    border-style: dashed;
    opacity: 0.45;
    pointer-events: none;
}

@media (max-width: 768px) {
    .pagination-bar {
        justify-content: flex-start;
    }

    .prevnext {
        grid-template-columns: 1fr;
    }

    .prevnext-next {
        grid-column: 1;
    }

    .page-link {
        min-width: var(--touch-target-min);
        height: var(--touch-target-min);
    }

    .tab {
        padding: var(--space-2) var(--space-3);
        font-size: var(--font-size-sm);
        min-height: var(--touch-target-min);
    }

    .segment {
        min-height: var(--touch-target-min);
    }
}

/* ============================================================
   JAX FEEDBACK
   Spinner, Skeleton, Progress (enhanced), Toast.
   ============================================================ */

/* --- Spinner --- */

.spinner {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 2px solid var(--border-color);
    border-top-color: var(--accent);
    border-radius: var(--radius-full);
    animation: jax-spin var(--transition-spinner-duration) linear infinite;
}

.spinner-sm { width: 14px; height: 14px; }
.spinner-lg { width: 32px; height: 32px; }

@keyframes jax-spin {
    to { transform: rotate(360deg); }
}

/* --- Skeleton --- */

.skeleton {
    background: linear-gradient(
        90deg,
        var(--bg-tertiary) 25%,
        var(--bg-secondary) 50%,
        var(--bg-tertiary) 75%
    );
    background-size: 200% 100%;
    animation: jax-skeleton var(--transition-skeleton-duration) ease-in-out infinite;
    border-radius: var(--radius-sm);
}

.skeleton-text {
    height: 14px;
    margin-bottom: var(--space-2);
}

.skeleton-title {
    height: 24px;
    width: 60%;
    margin-bottom: var(--space-3);
}

.skeleton-avatar {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
}

.skeleton-block {
    height: 80px;
}

@keyframes jax-skeleton {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* --- Progress (semantic variants) --- */

.progress-ok::-webkit-progress-value { background: var(--color-ok); }
.progress-ok::-moz-progress-bar { background: var(--color-ok); }

.progress-warn::-webkit-progress-value { background: var(--color-warn); }
.progress-warn::-moz-progress-bar { background: var(--color-warn); }

.progress-error::-webkit-progress-value { background: var(--color-error); }
.progress-error::-moz-progress-bar { background: var(--color-error); }

/* --- Toast --- */

.toast-container {
    position: fixed;
    top: var(--space-4);
    right: var(--space-4);
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 360px;
}

.toast {
    padding: var(--space-3) var(--space-4);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--accent);
    color: var(--text-primary);
    font-size: var(--font-size-sm);
    overflow-wrap: anywhere;
    animation: jax-toast-in var(--transition-normal);
}

.toast-ok { border-left-color: var(--color-ok); }
.toast-warn { border-left-color: var(--color-warn); }
.toast-error { border-left-color: var(--color-error); }
.toast-info { border-left-color: var(--color-info); }

.toast-dismiss {
    opacity: 0;
    transition: opacity var(--transition-normal);
}

@keyframes jax-toast-in {
    from {
        transform: translateX(100%);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

/* --- HTMX loading states --- */

.htmx-indicator {
    display: none;
}

.htmx-request .htmx-indicator {
    display: inline;
}

.htmx-request.htmx-indicator {
    display: inline;
}

.htmx-settling {
    opacity: 0;
}

.htmx-swapping {
    opacity: 0;
    transition: opacity var(--transition-fast);
}

.htmx-added {
    opacity: 0;
    transition: opacity var(--transition-normal);
}

/* --- Tooltip --- */

.tooltip {
    position: relative;
}

.tooltip::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: calc(100% + var(--space-2));
    left: 50%;
    transform: translateX(-50%);
    padding: var(--space-1) var(--space-2);
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    font-size: var(--font-size-xs);
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--transition-fast);
    z-index: 100;
}

.tooltip:hover::after,
.tooltip:focus-visible::after {
    opacity: 1;
}

.tooltip-bottom::after {
    bottom: auto;
    top: calc(100% + var(--space-2));
}

/* --- HTMX button loading --- */

.btn.htmx-request {
    opacity: 0.6;
    pointer-events: none;
}

.btn .btn-loading {
    display: none;
}

.btn.htmx-request .btn-text {
    display: none;
}

.btn.htmx-request .btn-loading {
    display: inline;
}

.btn-loading .spinner {
    width: 14px;
    height: 14px;
    vertical-align: middle;
}

/* --- Copy to clipboard (clipboard.js) ---
   Injected into every pre>code. Invisible at rest, revealed on hover of
   the block and on keyboard focus — never a focusable control the user
   cannot see. clipboard.js owns the click and the reported outcome. */

.copy-btn {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    opacity: 0;
    transition: opacity var(--transition-fast);
}

pre:hover .copy-btn,
pre:focus-within .copy-btn,
.copy-btn:focus-visible {
    opacity: 1;
}

/* --- Reduced motion --- */

@media (prefers-reduced-motion: reduce) {
    .spinner { animation: none; border-top-color: var(--accent); }
    .skeleton { animation: none; }
    .toast { animation: none; }
}

/* ============================================================
   JAX TAG
   Lightweight inline label. Lighter than badge (no border,
   no uppercase, 0.1 opacity bg vs badge's 0.15).
   ============================================================ */

.tag {
    display: inline-block;
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-tight);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    background: color-mix(in srgb, var(--highlight) 6%, transparent);
    vertical-align: middle;
    white-space: nowrap;
}

.tag-wrap {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.tag-ok {
    color: var(--text-ok);
    background: color-mix(in srgb, var(--color-ok) 10%, transparent);
}

.tag-warn {
    color: var(--text-warn);
    background: color-mix(in srgb, var(--color-warn) 10%, transparent);
}

.tag-error {
    color: var(--text-error);
    background: color-mix(in srgb, var(--color-error) 10%, transparent);
}

.tag-info {
    color: var(--text-info);
    background: color-mix(in srgb, var(--color-info) 10%, transparent);
}

.tag-accent {
    color: var(--text-accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.tag-muted {
    color: var(--text-muted);
    background: color-mix(in srgb, var(--highlight) 4%, transparent);
}

/* ============================================================
   JAX DATA
   Dashboard data-viz primitives: Stat Card, Gauge Bar, Sparkline.
   ============================================================ */

/* --- Stat Card --- */

.stat-card {
    padding: var(--space-4);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-top: 3px solid var(--border-color);
}

.stat-card-ok    { border-top-color: var(--color-ok); }
.stat-card-warn  { border-top-color: var(--color-warn); }
.stat-card-error { border-top-color: var(--color-error); }
.stat-card-accent { border-top-color: var(--accent); }

.stat-value {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-bold);
    color: var(--text-primary);
    line-height: var(--line-height-tight);
}

.stat-label {
    font-size: var(--font-size-sm);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    margin-top: var(--space-1);
}

.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: var(--space-4);
}

@media (max-width: 768px) {
    .stats-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* --- Gauge Bar --- */

.gauge {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.gauge-track {
    height: 8px;
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.gauge-meter {
    height: 100%;
    width: 100%;
    display: block;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
}

.gauge-meter::-webkit-progress-bar {
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
}

.gauge-meter::-webkit-progress-value {
    background: var(--accent);
    border-radius: var(--radius-sm);
}

.gauge-meter::-moz-progress-bar {
    background: var(--accent);
    border-radius: var(--radius-sm);
}

.gauge-ok .gauge-meter::-webkit-progress-value { background: var(--color-ok); }
.gauge-warn .gauge-meter::-webkit-progress-value { background: var(--color-warn); }
.gauge-error .gauge-meter::-webkit-progress-value { background: var(--color-error); }

.gauge-ok .gauge-meter::-moz-progress-bar { background: var(--color-ok); }
.gauge-warn .gauge-meter::-moz-progress-bar { background: var(--color-warn); }
.gauge-error .gauge-meter::-moz-progress-bar { background: var(--color-error); }

.gauge-label {
    display: flex;
    justify-content: space-between;
    font-size: var(--font-size-xs);
    color: var(--text-muted);
}

.gauge-lg .gauge-track { height: 12px; }

/* --- Sparkline --- */

.sparkline {
    display: flex;
    align-items: flex-end;
    gap: var(--space-1);
    height: 72px;
}

.sparkline-bar {
    flex: 1;
    min-width: var(--space-1);
    height: 100%;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
    writing-mode: vertical-lr;
    transform: rotate(180deg);
    opacity: 0.7;
    transition: opacity var(--transition-fast);
}

.sparkline-bar::-webkit-progress-bar {
    background: var(--bg-tertiary);
    border-radius: var(--radius-sm);
}

.sparkline-bar::-webkit-progress-value {
    background: var(--accent);
    border-radius: var(--radius-sm);
}

.sparkline-bar::-moz-progress-bar {
    background: var(--accent);
    border-radius: var(--radius-sm);
}

.sparkline-bar:hover {
    opacity: 1;
}

.sparkline-labels {
    display: flex;
    justify-content: space-between;
    font-size: var(--font-size-xs);
    color: var(--text-muted);
    margin-top: var(--space-1);
}

@media (prefers-reduced-motion: reduce) {
    .sparkline-bar { transition: none; }
}

/* ============================================================
   INLINE STAT ROW
   The stat card's flat sibling: a run of measured facts that
   belongs inside another component (a feed entry, a profile
   hero, a card footer) rather than owning its own panel.
   ============================================================ */

.stat-row {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: var(--space-2);
}

.stat-item {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border-subtle);
    min-width: 0;
}

.stat-item-value {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.stat-item-label {
    font-size: var(--font-size-xs);
    line-height: var(--line-height-tight);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
}

/* One-line variant: label and value on the same baseline. */
.stat-row-inline .stat-item {
    flex-direction: row;
    align-items: baseline;
    gap: var(--space-2);
}

.stat-row-inline .stat-item-label {
    order: -1;
}

.stat-item-accent { border-color: color-mix(in srgb, var(--accent) 45%, var(--border-subtle)); }
.stat-item-ok { border-color: color-mix(in srgb, var(--color-ok) 45%, var(--border-subtle)); }
.stat-item-warn { border-color: color-mix(in srgb, var(--color-warn) 45%, var(--border-subtle)); }
.stat-item-error { border-color: color-mix(in srgb, var(--color-error) 45%, var(--border-subtle)); }

/* ============================================================
   JAX FEED & PROFILE HERO
   The chronological list, and the object it belongs to.

   A feed is rules, not cards: entries are separated by a
   hairline so the page reads as one column of records rather
   than a wall of identical boxes. .feed-boxed is the opt-in
   for when the entries really are separate objects.
   ============================================================ */

/* --- Feed --- */

.feed {
    display: flex;
    flex-direction: column;
}

.feed-entry {
    padding-bottom: var(--space-6);
    margin-bottom: var(--space-6);
    border-bottom: 1px solid var(--border-subtle);
}

.feed-entry:last-child {
    padding-bottom: 0;
    margin-bottom: 0;
    border-bottom: none;
}

.feed-compact .feed-entry {
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-4);
}

.feed-boxed {
    gap: var(--space-3);
}

.feed-boxed .feed-entry {
    padding: var(--space-4);
    margin-bottom: 0;
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    transition: border-color var(--transition-fast);
}

.feed-boxed .feed-entry:hover:has(a:hover) {
    border-color: var(--accent);
}

/* --- Media-object entry --- */

.feed-entry-media {
    display: grid;
    grid-template-columns: minmax(120px, 200px) minmax(0, 1fr);
    gap: var(--space-4);
    align-items: start;
}

.feed-thumb {
    display: block;
    width: 100%;
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border: 1px solid var(--border-color);
    background: var(--bg-tertiary);
}

.feed-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    border-radius: var(--radius-none);
    margin: 0;
}

.feed-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}

/* --- Entry parts --- */

.feed-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    font-variant-numeric: tabular-nums;
}

.feed-meta > * + *::before {
    content: '/';
    color: var(--text-muted);
    margin-right: var(--space-2);
}

.feed-meta a {
    color: var(--text-secondary);
    text-decoration: none;
}

.feed-meta a:hover {
    color: var(--text-accent);
}

.feed-title {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-normal);
    text-transform: none;
    text-wrap: balance;
    overflow-wrap: anywhere;
    color: var(--text-primary);
    margin: 0;
}

.feed-title a {
    color: inherit;
    text-decoration: none;
}

.feed-title a:hover {
    color: var(--text-accent);
}

.feed-summary {
    max-width: var(--measure-tight);
    margin: 0;
    font-size: var(--font-size-base);
    line-height: var(--line-height-loose);
    color: var(--text-secondary);
    text-wrap: pretty;
}

.feed-tags,
.feed-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.feed-actions {
    margin-top: var(--space-1);
}

/* --- Profile hero --- */

.profile-hero {
    display: grid;
    grid-template-columns: minmax(220px, 380px) minmax(0, 1fr);
    border: 1px solid var(--border-color);
    background: var(--bg-secondary);
    box-shadow: none;
    margin-bottom: var(--space-8);
    overflow: hidden;
}

.hero-media {
    background: var(--bg-tertiary);
    display: grid;
    min-height: 180px;
}

.hero-media img,
.hero-media svg {
    width: 100%;
    height: 100%;
    max-height: 320px;
    object-fit: cover;
    display: block;
    border-radius: var(--radius-none);
    margin: 0;
}

.hero-media-contain img,
.hero-media-contain svg {
    object-fit: contain;
    padding: var(--space-6);
}

.hero-copy {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-3);
    padding: var(--space-6);
    min-width: 0;
}

.hero-title {
    font-size: clamp(var(--font-size-lg), 3vw, var(--font-size-display));
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-display);
    letter-spacing: var(--letter-spacing-tight);
    text-transform: none;
    text-wrap: balance;
    overflow-wrap: anywhere;
    color: var(--text-primary);
    margin: 0;
}

/* The classifier sits under the title, not above it: it is what
   the thing is, read after its name — not a label announcing it. */
.hero-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    font-variant-numeric: tabular-nums;
    margin-top: calc(-1 * var(--space-1));
}

.hero-meta > * + *::before {
    content: '\00B7';
    color: var(--text-muted);
    margin-right: var(--space-2);
}

.hero-meta .is-kind {
    color: var(--text-accent);
}

.hero-summary {
    max-width: var(--measure-tight);
    margin: 0;
    color: var(--text-secondary);
    line-height: var(--line-height-loose);
    text-wrap: pretty;
}

.hero-chips,
.hero-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

/* --- Compact hero (no media) --- */

.profile-hero-compact {
    grid-template-columns: 1fr;
}

.profile-hero-compact .hero-copy {
    padding: var(--space-5);
    gap: var(--space-2);
}

/* --- Responsive --- */

@media (max-width: 768px) {
    .feed-entry-media {
        grid-template-columns: 1fr;
        gap: var(--space-3);
    }

    .feed-thumb {
        aspect-ratio: 16 / 9;
    }

    .profile-hero {
        grid-template-columns: 1fr;
    }

    .hero-media img,
    .hero-media svg {
        max-height: 220px;
    }

    .hero-copy {
        padding: var(--space-4);
    }
}

/* ============================================================
   JAX PROSE
   Long-form reading: article header, measured body, figures,
   image rows, pullquotes, comparisons, full-bleed media.

   base.css already styles every element this uses. Prose only
   sets measure, rhythm, and the few compositions that plain
   HTML has no element for.
   ============================================================ */

/* --- Article header --- */

.article-header {
    padding-bottom: var(--space-5);
    border-bottom: 1px solid var(--border-color);
    margin-bottom: var(--space-8);
}

.article-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--text-muted);
    letter-spacing: var(--letter-spacing-wide);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
}

.article-meta > * + *::before {
    content: '/';
    color: var(--text-muted);
    margin-right: var(--space-2);
}

.article-meta a {
    color: var(--text-secondary);
    text-decoration: none;
}

.article-meta a:hover {
    color: var(--text-accent);
}

/* The one place the kit drops uppercase-and-track for size: an
   article title is read, not scanned. */
.article-title {
    font-size: clamp(var(--font-size-xl), 5vw, var(--font-size-display));
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-display);
    letter-spacing: var(--letter-spacing-tight);
    text-transform: none;
    text-wrap: balance;
    overflow-wrap: anywhere;
    color: var(--text-primary);
    margin: var(--space-3) 0 0;
}

.article-summary {
    max-width: var(--measure-tight);
    margin: var(--space-4) 0 0;
    font-size: var(--font-size-lg);
    line-height: var(--line-height);
    color: var(--text-secondary);
    text-wrap: pretty;
}

.article-hero {
    display: block;
    width: 100%;
    margin-top: var(--space-6);
    border: 1px solid var(--border-color);
    background: var(--bg-tertiary);
}

.article-hero img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-none);
    margin: 0;
}

/* --- Prose body --- */

.prose {
    max-width: var(--measure);
    line-height: var(--line-height-loose);
    color: var(--text-primary);
}

.prose > * + * {
    margin-top: var(--space-5);
}

.prose > p {
    margin-bottom: 0;
}

/* More air above a heading than below it — the gap belongs to the
   section it opens. */
.prose > h2 {
    margin-top: var(--space-12);
    margin-bottom: var(--space-4);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border-subtle);
}

.prose > h3 {
    margin-top: var(--space-8);
    margin-bottom: var(--space-3);
}

.prose > h2 + *,
.prose > h3 + * {
    margin-top: 0;
}

.prose > h2:first-child,
.prose > h3:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

.prose ul,
.prose ol {
    margin-bottom: 0;
}

.prose li {
    margin-bottom: var(--space-2);
}

.prose ol > li::marker {
    color: var(--text-accent);
    font-variant-numeric: tabular-nums;
}

.prose ul > li::marker {
    color: var(--text-muted);
}

.prose hr {
    margin: var(--space-10) 0;
}

.prose-wide { max-width: var(--max-width-lg); }
.prose-narrow { max-width: var(--measure-tight); }

/* --- Figure --- */

.prose-figure {
    margin: 0;
}

.prose-figure img,
.prose-figure video {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: var(--bg-tertiary);
    margin: 0;
}

.prose-figure figcaption,
.prose-caption {
    display: block;
    margin-top: var(--space-2);
    padding-left: var(--space-3);
    border-left: 1px solid var(--border-color);
    font-size: var(--font-size-sm);
    line-height: var(--line-height);
    color: var(--text-muted);
    text-wrap: pretty;
}

/* --- Image rows and contact sheets --- */

.prose-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-3);
    align-items: start;
}

.prose-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.prose-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.prose-row > * {
    min-width: 0;
}

.prose-sheet {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: var(--space-2);
}

.prose-sheet img {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: var(--bg-tertiary);
    margin: 0;
    transition: border-color var(--transition-fast);
}

.prose-sheet a:hover img,
.prose-sheet button:hover img {
    border-color: var(--accent);
}

.prose-sheet button {
    padding: 0;
    border: none;
    background: none;
    min-height: 0;
    display: block;
    width: 100%;
}

/* --- Pullquote --- */
/* base.css blockquote already owns the accent left rule; a pullquote
   is the other gesture — rules above and below, and size. */

.pullquote {
    margin: var(--space-8) 0;
    padding: var(--space-5) 0;
    border-top: 1px solid var(--accent);
    border-bottom: 1px solid var(--border-color);
    border-left: none;
    font-size: var(--font-size-lg);
    font-style: normal;
    line-height: var(--line-height);
    color: var(--text-primary);
    text-wrap: balance;
}

.pullquote p {
    margin: 0;
}

.pullquote cite,
.pullquote-source {
    display: block;
    margin-top: var(--space-3);
    font-size: var(--font-size-xs);
    font-style: normal;
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wide);
    color: var(--text-muted);
}

.pullquote cite::before,
.pullquote-source::before {
    content: '\2014\00A0';
    color: var(--text-muted);
}

/* --- Before / after --- */

.compare {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
}

.compare-item {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
    margin: 0;
}

.compare-item img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: var(--bg-tertiary);
    margin: 0;
}

.compare-label {
    order: -1;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--letter-spacing-wider);
    color: var(--text-secondary);
}

/* --- Full-bleed media --- */
/* Breaks the measure without breaking the container: the negative
   inline margin is capped so it never causes a horizontal scroll. */

.prose-bleed {
    width: auto;
    max-width: none;
    margin-inline: calc(-1 * min(var(--space-16), 4vw));
}

.prose-bleed img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-none);
}

/* --- Responsive --- */

@media (max-width: 768px) {
    .prose > h2 {
        margin-top: var(--space-10);
    }

    .prose-row-3 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .prose-sheet {
        grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
    }

    .compare {
        grid-template-columns: 1fr;
    }

    .prose-bleed {
        margin-inline: 0;
    }
}

/* ============================================================
   JAX DRAWER
   Slide-in side panel using native <dialog>.
   ============================================================ */

.drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto;
    margin: 0;
    max-height: 100vh;
    max-height: 100dvh;
    height: 100vh;
    height: 100dvh;
    width: 360px;
    max-width: 100%;
    border: none;
    border-left: 1px solid var(--border-color);
    border-radius: var(--radius-none);
    background: var(--bg-secondary);
    color: var(--text-primary);
    padding: 0;
}

.drawer[open] {
    display: flex;
    flex-direction: column;
    animation: jax-drawer-in var(--transition-normal);
}

.drawer::backdrop {
    background: color-mix(in srgb, var(--shadow) 50%, transparent);
}

/* --- Size variants --- */

.drawer-sm { width: 280px; }
.drawer-md { width: 360px; }
.drawer-lg { width: 480px; }

/* --- Internal structure --- */

.drawer-header {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-shrink: 0;
}

.drawer-title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-base);
    margin: 0;
}

.drawer-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: var(--space-1);
    min-width: var(--control-height-sm);
    min-height: var(--control-height-sm);
    font-size: var(--font-size-lg);
    line-height: 1;
}

.drawer-close:hover {
    color: var(--text-primary);
}

.drawer-body {
    padding: var(--space-4);
    flex: 1;
    overflow-y: auto;
}

.drawer-footer {
    padding: var(--space-3) var(--space-4);
    padding-bottom: max(var(--space-3), env(safe-area-inset-bottom));
    border-top: 1px solid var(--border-color);
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    flex-shrink: 0;
}

/* --- Animation --- */

@keyframes jax-drawer-in {
    from { transform: translateX(100%); }
    to   { transform: translateX(0); }
}

/* --- Mobile: full-width --- */

@media (max-width: 768px) {
    .drawer,
    .drawer-sm,
    .drawer-md,
    .drawer-lg {
        width: 100%;
    }

    .drawer-close {
        min-width: var(--touch-target-min);
        min-height: var(--touch-target-min);
    }
}

/* --- Reduced motion --- */

@media (prefers-reduced-motion: reduce) {
    .drawer { animation: none; }
}

/* ============================================================
   JAX TRANSITIONS
   Progressive enhancement view transitions + HTMX settle.
   ============================================================ */

/* --- View Transitions (browsers that support it) --- */

@view-transition {
    navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: var(--transition-view-duration);
    animation-timing-function: ease;
}

::view-transition-old(root) {
    animation-name: jax-view-out;
}

::view-transition-new(root) {
    animation-name: jax-view-in;
}

.site-navbar {
    view-transition-name: navbar;
}

@keyframes jax-view-out {
    to {
        opacity: 0;
        transform: translateY(8px);
    }
}

@keyframes jax-view-in {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* --- HTMX settle animation --- */

@keyframes jax-settle-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

.htmx-settling > * {
    animation: jax-settle-in var(--transition-normal);
}

/* --- Reduced motion kill switch --- */

@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation: none;
    }

    .htmx-settling > * {
        animation: none;
    }
}

/* ============================================================
   JAX UTILITIES
   Layout helpers and semantic utility classes.
   ============================================================ */

/* --- Display --- */

.flex { display: flex; }
.flex-row { display: flex; flex-direction: row; }
.flex-col { display: flex; flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1; }
.flex-none { flex: none; }
.grid { display: grid; }
.block { display: block; }
.inline { display: inline; }
.inline-flex { display: inline-flex; }
.display-none { display: none; }

/* --- Alignment --- */

.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.items-stretch { align-items: stretch; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-end { justify-content: flex-end; }
.justify-start { justify-content: flex-start; }
.self-center { align-self: center; }
.self-end { align-self: flex-end; }

/* --- Gap --- */

.gap-1 { gap: var(--space-1); }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.gap-6 { gap: var(--space-6); }
.gap-8 { gap: var(--space-8); }

/* --- Margin --- */

.m-0 { margin: 0; }
.m-1 { margin: var(--space-1); }
.m-2 { margin: var(--space-2); }
.m-4 { margin: var(--space-4); }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mt-8 { margin-top: var(--space-8); }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); }
.mb-8 { margin-bottom: var(--space-8); }
.ml-1 { margin-left: var(--space-1); }
.ml-2 { margin-left: var(--space-2); }
.ml-4 { margin-left: var(--space-4); }
.mr-1 { margin-right: var(--space-1); }
.mr-2 { margin-right: var(--space-2); }
.mr-4 { margin-right: var(--space-4); }
.mx-auto { margin-left: auto; margin-right: auto; }

/* --- Padding --- */

.p-0 { padding: 0; }
.p-1 { padding: var(--space-1); }
.p-2 { padding: var(--space-2); }
.p-3 { padding: var(--space-3); }
.p-4 { padding: var(--space-4); }
.p-6 { padding: var(--space-6); }
.p-8 { padding: var(--space-8); }
.pt-0 { padding-top: 0; }
.pt-2 { padding-top: var(--space-2); }
.pt-4 { padding-top: var(--space-4); }
.pb-2 { padding-bottom: var(--space-2); }
.pb-4 { padding-bottom: var(--space-4); }
.px-2 { padding-left: var(--space-2); padding-right: var(--space-2); }
.px-3 { padding-left: var(--space-3); padding-right: var(--space-3); }
.px-4 { padding-left: var(--space-4); padding-right: var(--space-4); }
.py-2 { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.py-3 { padding-top: var(--space-3); padding-bottom: var(--space-3); }
.py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); }

/* --- Text --- */

.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-xs { font-size: var(--font-size-xs); }
.text-sm { font-size: var(--font-size-sm); }
.text-base { font-size: var(--font-size-base); }
.text-lg { font-size: var(--font-size-lg); }
.text-xl { font-size: var(--font-size-xl); }
.leading-tight { line-height: var(--line-height-tight); }
.leading-normal { line-height: var(--line-height); }
.leading-loose { line-height: var(--line-height-loose); }
.text-uppercase { text-transform: uppercase; letter-spacing: var(--letter-spacing-wide); }
.text-bold { font-weight: var(--font-weight-bold); }
.text-semibold { font-weight: var(--font-weight-semibold); }
.text-normal { font-weight: var(--font-weight-normal); }
.text-nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-wrap { overflow-wrap: break-word; word-break: normal; }
.text-wrap-anywhere { overflow-wrap: anywhere; word-break: break-word; }
.text-break-spaces {
    white-space: break-spaces;
    overflow-wrap: anywhere;
    word-break: break-word;
}
.text-balance {
    text-wrap: balance;
    overflow-wrap: break-word;
}
.text-pretty {
    text-wrap: pretty;
    overflow-wrap: break-word;
}

@supports not (text-wrap: balance) {
    .text-balance {
        text-wrap: pretty;
    }
}

/* --- Content Width --- */

.content-xs { max-width: 48ch; }
.content-sm { max-width: 60ch; }
.content-md { max-width: 72ch; }
.content-lg { max-width: 84ch; }

/* Backward-compatible aliases */
.measure-sm { max-width: 58ch; }
.measure { max-width: 72ch; }
.measure-lg { max-width: 84ch; }

/* --- Vertical Rhythm --- */

.flow {
    --flow-space: var(--space-4);
}

.flow-xs { --flow-space: var(--space-1); }
.flow-sm { --flow-space: var(--space-2); }
.flow-lg { --flow-space: var(--space-6); }
.flow-xl { --flow-space: var(--space-8); }

.flow > * {
    margin: 0;
}

.flow > * + * {
    margin-top: var(--flow-space);
}

/* --- Text Colors --- */

.text-primary { color: var(--text-primary); }
.text-secondary { color: var(--text-secondary); }
.text-muted { color: var(--text-muted); }
.text-dim { color: var(--text-dim); }
.text-accent { color: var(--text-accent); }
.text-ok { color: var(--text-ok); }
.text-warn { color: var(--text-warn); }
.text-error { color: var(--text-error); }
.text-info { color: var(--text-info); }

/* --- Background Colors --- */

.bg-primary { background: var(--bg-primary); }
.bg-secondary { background: var(--bg-secondary); }
.bg-tertiary { background: var(--bg-tertiary); }

/* --- Width --- */

.w-full { width: 100%; }
.w-auto { width: auto; }
.max-w-sm { max-width: var(--max-width-sm); }
.max-w-md { max-width: var(--max-width-md); }
.max-w-lg { max-width: var(--max-width-lg); }
.max-w-xl { max-width: var(--max-width); }

/* --- Borders --- */

.border { border: 1px solid var(--border-color); }
.border-t { border-top: 1px solid var(--border-color); }
.border-b { border-bottom: 1px solid var(--border-color); }
.border-l { border-left: 1px solid var(--border-color); }
.border-r { border-right: 1px solid var(--border-color); }
.border-subtle { border-color: var(--border-subtle); }
.border-none { border: none; }

/* --- Overflow --- */

.overflow-auto { overflow: auto; }
.overflow-hidden { overflow: hidden; }
.overflow-x-auto { overflow-x: auto; }

/* --- Position --- */

.relative { position: relative; }
.absolute { position: absolute; }
.sticky { position: sticky; top: 0; }

/* --- Cursor --- */

.cursor-pointer { cursor: pointer; }
.cursor-default { cursor: default; }

/* --- Accessibility --- */

.sr-only,
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border-width: 0;
}

.sr-only-focusable:active,
.sr-only-focusable:focus,
.sr-only-focusable:focus-visible,
.visually-hidden-focusable:active,
.visually-hidden-focusable:focus,
.visually-hidden-focusable:focus-visible {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
}

.skip-link {
    border: 1px solid var(--accent);
    padding: var(--space-2) var(--space-3);
    background: var(--bg-secondary);
    color: var(--text-accent);
    text-decoration: none;
}

.skip-link:active,
.skip-link:focus,
.skip-link:focus-visible {
    position: fixed;
    top: max(var(--space-2), env(safe-area-inset-top));
    left: max(var(--space-2), env(safe-area-inset-left));
    z-index: 200;
    box-shadow: var(--shadow-focus);
}

/* --- Focus visibility for component links/buttons --- */

.navbar-link:focus-visible,
.sidebar-link:focus-visible,
.page-link:focus-visible,
.tab:focus-visible,
.segment:focus-visible,
.dialog-close:focus-visible,
.drawer-close:focus-visible,
.skip-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* --- High-contrast / forced-colors mode --- */

@media (forced-colors: active) {
    .btn,
    .tab,
    .navbar-link,
    .sidebar-link,
    .page-link,
    .badge,
    .tag,
    .alert,
    .card,
    .dialog,
    .drawer,
    .file-drop {
        border-color: CanvasText;
        forced-color-adjust: auto;
    }

    a:focus-visible,
    button:focus-visible,
    input:focus-visible,
    textarea:focus-visible,
    select:focus-visible,
    .navbar-link:focus-visible,
    .sidebar-link:focus-visible,
    .page-link:focus-visible,
    .tab:focus-visible,
    .segment:focus-visible {
        box-shadow: none;
        outline: 2px solid Highlight;
        outline-offset: 2px;
    }

    .badge,
    .tag {
        background: Canvas;
        color: CanvasText;
    }

    .alert {
        border-left-color: Highlight;
    }
}


/* ============================================================
   JAX SCHEME — ember
   Amber on ash. The original Jax accent, and the default bundle.

   Colour contract only. Pairs with tokens.css, which owns every
   non-colour token and the shadow/tint formulas that read these.

   Both modes live in one declaration: light-dark(light, dark).
   tokens.css sets color-scheme, so the mode resolves from
   [data-theme] or the OS preference with no second block to drift.

   Hand-tuned, unlike the generated schemes: these are the values
   the light palette was retoned to in the paper/ember pass.
   ============================================================ */

:root {
    --scheme: "ember";

    /* --- Backgrounds --- */
    --bg-primary: light-dark(#fbfaf7, #0f0f0f);
    --bg-secondary: light-dark(#f2efe8, #1a1a1a);
    --bg-tertiary: light-dark(#e8e5dd, #242424);

    /* --- Text --- */
    --text-primary: light-dark(#1f1c17, #e0e0e0);
    --text-secondary: light-dark(#57514a, #b0b0b0);
    --text-muted: light-dark(#6d665c, #888888);
    /* Dark dim sits just under muted: #777777 misses AA against the ash
       page (4.28:1) and --text-dim is gated like any other ink. */
    --text-dim: light-dark(#77705f, #7d7d7d);

    /* --- Borders --- */
    --border-color: light-dark(#d9d4c9, #333333);
    --border-subtle: light-dark(#eae6de, #2a2a2a);

    /* --- Accent --- */
    --accent: light-dark(#d1731f, #e67e22);
    --accent-hover: light-dark(#b9631a, #d35400);

    /* --- Accent as a solid fill ---
       --color-accent-solid is the value a LABEL sits on, so it is checked
       against --text-on-accent rather than against the page behind it.

       Both modes put the darkest neutral they have on an amber block. That
       is what keeps the light fill a true amber instead of a dark rust: a
       LIGHT label would force the fill down to about #b34b00 to clear AA,
       and at that point the accent has stopped being amber. Dark ink pins
       the fill inside a narrow luminance band (0.228-0.285 in light), which
       is exactly where ember lives once its value comes down for paper.

       The hover diverges by mode. The light fill already sits near the top
       of that band, so it brightens where dark deepens; deepening it would
       drop the ink below 4.5:1. */
    --color-accent-solid: light-dark(#d1731f, #e67e22);
    --color-accent-solid-hover: light-dark(#dd8129, #d35400);
    --text-on-accent: light-dark(#1f1c17, #0f0f0f);

    /* --- Semantic fills --- */
    --color-ok: light-dark(#2f8757, #2ecc71);
    --color-warn: light-dark(#b0790e, #f1c40f);
    --color-error: light-dark(#b84a3c, #e74c3c);
    --color-info: light-dark(#35719e, #3498db);

    /* --- Semantic ink --- */
    --text-accent: light-dark(#96430d, #e67e22);
    --text-ok: light-dark(#256b45, #2ecc71);
    --text-warn: light-dark(#7c5309, #f1c40f);
    --text-error: light-dark(#a03b2d, #ef6b5c);
    --text-info: light-dark(#275a82, #40a3e5);

    /* --- Tint sources --- */
    --highlight: light-dark(#3d3428, #ffffff);
    --shadow: light-dark(#3d3428, #000000);
}
