/* ==========================================================================
   Hexfolio6 — design tokens
   Dark is the default ground; light is the same markup with inverted ramps.
   Both sets are always defined, so nothing depends on a media query alone.
   ========================================================================== */

:root {
  /* — ground — */
  --color-bg: #1A1423;
  --color-text: #FBEFEA;
  --color-surface: #251B33;
  --color-surface-2: #372549;
  --color-border: #4E3352;
  --color-section: #372549;

  /* — accent (Deep Puce) — */
  --color-accent: #B75D69;
  --color-accent-100: #F3DCDE;
  --color-accent-200: #E4BCC1;
  --color-accent-300: #D48F98;
  --color-accent-400: #C67480;
  --color-accent-500: #B75D69;
  --color-accent-600: #9E4C58;
  --color-accent-700: #7F3D48;
  --color-accent-800: #5A2C36;
  --color-accent-900: #3A1F27;

  /* — neutrals — */
  --color-neutral-100: #F5E4DE;
  --color-neutral-200: #EACDC2;
  --color-neutral-300: #DFC5C9;
  --color-neutral-400: #B08F9C;
  --color-neutral-500: #94707F;
  --color-neutral-600: #774C60;
  --color-neutral-700: #4E3352;
  --color-neutral-800: #372549;
  --color-neutral-900: #241A31;

  /* — secondary accent, for a second tag colour — */
  --color-accent-2: #8C6A8E;
  --color-accent-2-100: #EFE1EF;
  --color-accent-2-800: #4A3550;
  --color-accent-2-900: #2E2135;

  --color-divider: color-mix(in srgb, var(--color-text) 16%, transparent);

  /* — type — */
  --font-heading: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-heading-weight: 500;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* The sk:: mark. Its own token because it follows neither role: it is set
     in the body face — Inter's round colon is the one the mark is drawn
     around — while every other heading keeps --font-heading. */
  --font-mark: var(--font-body);
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* — space: the deck's 2.8px unit — */
  --space-1: 2.8px;
  --space-2: 5.6px;
  --space-3: 8.4px;
  --space-4: 11.2px;
  --space-6: 16.8px;
  --space-8: 22.4px;
  --space-12: 33.6px;
  --space-16: 44.8px;
  --space-20: 56px;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;

  --shadow-sm: 0 0 0 1px var(--color-border);
  --shadow-md: 0 0 0 1px var(--color-neutral-700), 0 6px 18px rgba(0, 0, 0, 0.55);
  --shadow-lg: 0 0 0 1px var(--color-neutral-600), 0 16px 40px rgba(0, 0, 0, 0.65);

  /* — measure — */
  --width-page: 1280px;
  --width-prose: 680px;
  --width-sidebar: 240px;

  color-scheme: dark;
}

/* Light — Almond Silk ground, Shadow Grey ink, accent unchanged.
   The ramps invert: step 100 is the darkest value in light mode, so every
   component that reaches for neutral-500 keeps meaning "quiet text". */
.light-tokens, :root[data-theme="light"] {
  --color-bg: #FBF4F0;
  --color-text: #241A31;
  --color-surface: #FFFFFF;
  --color-surface-2: #F3E7E4;
  --color-border: #E3D2D4;
  --color-section: #F0DFDB;

  --color-accent: #B75D69;
  --color-accent-100: #3A1F27;
  --color-accent-200: #5A2C36;
  --color-accent-300: #7F3D48;
  --color-accent-400: #9E4C58;
  --color-accent-500: #B75D69;
  --color-accent-600: #C67480;
  --color-accent-700: #D48F98;
  --color-accent-800: #F0DFDB;
  --color-accent-900: #F8EFEC;

  --color-neutral-100: #241A31;
  --color-neutral-200: #372549;
  --color-neutral-300: #4E3352;
  --color-neutral-400: #774C60;
  --color-neutral-500: #8B6B77;
  --color-neutral-600: #C9AEB4;
  --color-neutral-700: #DDC5C8;
  --color-neutral-800: #F0DFDB;
  --color-neutral-900: #F5EAE7;

  --color-accent-2: #7A5A7C;
  --color-accent-2-100: #33253A;
  --color-accent-2-800: #EDE2ED;
  --color-accent-2-900: #F6F0F6;

  --color-divider: color-mix(in srgb, var(--color-text) 14%, transparent);

  --shadow-sm: 0 0 0 1px var(--color-border);
  --shadow-md: 0 1px 2px rgba(36, 26, 49, 0.06), 0 6px 18px rgba(36, 26, 49, 0.08);
  --shadow-lg: 0 1px 3px rgba(36, 26, 49, 0.08), 0 16px 40px rgba(36, 26, 49, 0.12);

  color-scheme: light;
}

/* No-JS / "system" preference: the same light ramp, applied only when the
   reader has not made an explicit choice. The inline head script normally
   stamps data-theme before first paint, so this is the fallback path. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --color-bg: #FBF4F0;
    --color-text: #241A31;
    --color-surface: #FFFFFF;
    --color-surface-2: #F3E7E4;
    --color-border: #E3D2D4;
    --color-section: #F0DFDB;

    --color-accent-100: #3A1F27;
    --color-accent-200: #5A2C36;
    --color-accent-300: #7F3D48;
    --color-accent-400: #9E4C58;
    --color-accent-600: #C67480;
    --color-accent-700: #D48F98;
    --color-accent-800: #F0DFDB;
    --color-accent-900: #F8EFEC;

    --color-neutral-100: #241A31;
    --color-neutral-200: #372549;
    --color-neutral-300: #4E3352;
    --color-neutral-400: #774C60;
    --color-neutral-500: #8B6B77;
    --color-neutral-600: #C9AEB4;
    --color-neutral-700: #DDC5C8;
    --color-neutral-800: #F0DFDB;
    --color-neutral-900: #F5EAE7;

    --color-accent-2: #7A5A7C;
    --color-accent-2-100: #33253A;
    --color-accent-2-800: #EDE2ED;
    --color-accent-2-900: #F6F0F6;

    --color-divider: color-mix(in srgb, var(--color-text) 14%, transparent);
    --shadow-sm: 0 0 0 1px var(--color-border);
    --shadow-md: 0 1px 2px rgba(36, 26, 49, 0.06), 0 6px 18px rgba(36, 26, 49, 0.08);
    --shadow-lg: 0 1px 3px rgba(36, 26, 49, 0.08), 0 16px 40px rgba(36, 26, 49, 0.12);

    color-scheme: light;
  }
}
/* ==========================================================================
   Base — reset, typography, the fading rule
   ========================================================================== */

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

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

/* The search overlay and the JS-gated buttons are toggled through the `hidden`
   property, but their component classes set `display` — and any author rule
   beats the user-agent [hidden] default whatever its specificity. Without this
   the overlay renders open on load and never closes. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin: 0 0 var(--space-2);
}
h1 { font-size: 42px; }
h2 { font-size: 32px; }
h3 { font-size: 25px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; }

p { margin: 0 0 var(--space-3); }

a { color: var(--color-accent-300); text-underline-offset: 3px; }
a:hover { color: var(--color-accent-200); }

img { display: block; max-width: 100%; height: auto; }
figure { margin: 0; }
figcaption {
  font-size: 11px;
  margin-top: var(--space-1);
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}

code, pre, kbd, samp { font-family: var(--font-mono); }

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* Rules fade to transparent at both ends instead of stopping cleanly — the
   Hexfolio6 signature. 48px of ramp an end. Box outlines and short accent
   marks stay solid. */
.hr, hr {
  height: 1px;
  border: 0;
  margin: var(--space-4) 0;
  background: linear-gradient(to right,
    transparent, var(--color-divider) 48px,
    var(--color-divider) calc(100% - 48px), transparent);
}

/* The accent mark that sits under every page title. */
.rule-accent {
  height: 1px;
  width: 180px;
  border: 0;
  margin: 0;
  background: linear-gradient(90deg, var(--color-accent), transparent);
}

.text-muted { color: color-mix(in srgb, var(--color-text) 55%, transparent); }

.kicker {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-accent-300);
}

/* Small-caps section label: "LATEST WRITING", "NEWS", "SELECTED PROJECTS". */
.eyebrow {
  font-family: var(--font-heading);
  font-size: 13px;
  font-weight: var(--font-heading-weight);
  letter-spacing: 0.17em;
  text-transform: uppercase;
  margin: 0;
}
.eyebrow-sm {
  font-size: 10px;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--color-neutral-500);
}

/* Date / read-time line under a heading. */
.meta-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-neutral-500);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: var(--space-4); top: -100px; z-index: 100;
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-sm);
  background: var(--color-surface); color: var(--color-text);
  box-shadow: var(--shadow-md); text-decoration: none;
}
.skip-link:focus { top: var(--space-4); }

@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;
  }
}
/* ==========================================================================
   Prose — everything rendered from Markdown lives inside .prose
   ========================================================================== */

/* Element selectors inside .prose are wrapped in :where() so they contribute
   no specificity. A component class that lands in the middle of an article —
   the CV timeline, a callout, an embedded post list — then wins on its own
   class, without every component needing a defensive override. The component
   sheets are concatenated after this one so equal-specificity ties go their
   way too. */
.prose {
  font-size: 16.5px;
  line-height: 1.75;
  color: var(--color-neutral-200);
}
/* Reset first, then let the flow rule own the spacing. Both selectors weigh
   (0,1,0) — :where() adds nothing — so whichever comes last wins outright.
   With `margin: 0` last, no two paragraphs in an article were ever spaced. */
.prose :where(p) { margin: 0; }
.prose > * + * { margin-top: var(--space-6); }
.prose :where(h2, h3, h4) { color: var(--color-text); }
.prose :where(h2) { font-size: 24px; margin-top: var(--space-12); }
.prose :where(h3) { font-size: 20px; margin-top: var(--space-8); }
.prose :where(h4) { font-size: 17px; margin-top: var(--space-8); }
.prose :where(h2, h3):first-child { margin-top: 0; }

/* Heading anchors appear on hover, never in print or on touch. */
.prose .anchor {
  margin-left: var(--space-3); font-size: 0.7em; text-decoration: none;
  color: var(--color-accent); opacity: 0; transition: opacity 120ms ease;
}
.prose .anchor::before { content: "#"; }
.prose :is(h2, h3, h4):hover .anchor, .prose .anchor:focus-visible { opacity: 1; }

.prose :where(ul, ol) { margin: 0; padding-left: var(--space-8); }
.prose :where(li + li) { margin-top: var(--space-2); }
.prose :where(li)::marker { color: var(--color-accent); }

.prose :where(blockquote) {
  margin: 0; padding: var(--space-2) 0 var(--space-2) var(--space-6);
  border-left: 2px solid var(--color-accent);
  color: var(--color-neutral-300); font-style: italic;
}
.prose :where(blockquote p + p) { margin-top: var(--space-4); }

.prose :not(pre) > code {
  font-size: 0.86em; padding: 2px 5px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-2);
  color: var(--color-accent-200);
}

.prose :where(figure figcaption) {
  font-size: 12px; margin-top: var(--space-3); color: var(--color-neutral-500);
}
.prose :where(img) { border-radius: var(--radius-md); }

.prose .table-wrap { margin-block: var(--space-8); }

.prose :where(kbd) {
  font-size: 0.8em; padding: 2px 6px;
  border: 1px solid var(--color-border); border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--color-surface); color: var(--color-text);
}

.prose :where(abbr) { text-decoration-style: dotted; cursor: help; }
.prose :where(mark) { background: var(--color-accent-800); color: var(--color-accent-100); padding: 0 3px; }

.prose :where(dl) { margin: 0; }
.prose :where(dt) { font-family: var(--font-heading); font-weight: var(--font-heading-weight); color: var(--color-text); }
.prose :where(dd) { margin: 0 0 var(--space-4); color: var(--color-neutral-300); }

.footnotes { font-size: 14px; color: var(--color-neutral-300); }
.footnotes hr { margin-bottom: var(--space-8); }

/* ── code blocks ──────────────────────────────────────────────────────── */
/* The chrome around a fence: a filename strip and a copy button, matching
   the mockup's wg0.conf block. */
.code-block {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-neutral-900);
}
.code-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.code-name {
  font-size: 10px; letter-spacing: 0.17em; text-transform: uppercase;
  color: var(--color-neutral-400);
}
.copy-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font: inherit; font-size: 11px; color: var(--color-neutral-300);
  background: transparent;
  border: 1px solid var(--color-border); border-radius: 4px;
  padding: 2px 8px; cursor: pointer;
}
.copy-btn:hover { border-color: var(--color-accent); color: var(--color-accent-200); }
.copy-btn[data-copied="true"] { color: var(--color-accent-300); border-color: var(--color-accent); }
.copy-btn::after { content: attr(data-label); }
.copy-btn[data-copied="true"]::after { content: attr(data-label-done); }
.copy-btn .check { display: none; }
.copy-btn[data-copied="true"] .check { display: block; }
.copy-btn[data-copied="true"] .copy { display: none; }

.code-block pre {
  margin: 0; padding: var(--space-4);
  font-size: 12.5px; line-height: 1.8;
  color: var(--color-neutral-200);
  overflow-x: auto;
  background: transparent;
}
.code-block pre code { background: none; padding: 0; font-size: inherit; color: inherit; }

/* Line numbers, when a fence asks for them. Chroma splits the gutter and the
   code into two table cells, each with its own <pre> — so the block's padding
   has to be redistributed or it lands twice. */
.code-block .lntable { border-collapse: collapse; width: 100%; }
.code-block .lntable td { padding: 0; vertical-align: top; }
.code-block .lntable .lntd:first-child { width: 1%; }
.code-block .lntable pre { padding-inline: 0; }
.code-block .lntable .lntd:first-child pre { padding-left: var(--space-4); }
.code-block .lntable .lntd:last-child pre { padding-right: var(--space-4); }
.code-block .lnt {
  display: inline-block; min-width: 2ch; text-align: right;
  color: var(--color-neutral-600); user-select: none;
  padding-right: var(--space-4);
}
/* The highlight strip has to reach the full width of the row, which means
   painting it on the line wrapper rather than on the token span. */
.code-block .hl { display: block; background: color-mix(in srgb, var(--color-accent) 14%, transparent); }

/* ── callouts ─────────────────────────────────────────────────────────── */
.note {
  display: flex; gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  border: 1px solid var(--color-border);
  border-left: 2px solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  font-size: 14.5px; line-height: 1.7;
  color: var(--color-neutral-200);
}
.note svg { flex: none; color: var(--color-accent); margin-top: 3px; }
.note-body { min-width: 0; }
.note-body > :first-child { margin-top: 0; }
.note-body > :last-child { margin-bottom: 0; }
.note-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent-300); margin: 0 0 var(--space-2);
}
.note-body p + p { margin-top: var(--space-3); }
.note-warning { border-left-color: #D89A5B; }
.note-warning svg, .note-warning .note-title { color: #D89A5B; }
.note-danger { border-left-color: #C4566A; }
.note-danger svg, .note-danger .note-title { color: #C4566A; }
.note-success { border-left-color: #6FA88A; }
.note-success svg, .note-success .note-title { color: #6FA88A; }

/* ── terminal transcript ──────────────────────────────────────────────── */
.terminal {
  border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  background: var(--color-neutral-900);
  padding: var(--space-4);
  font-family: var(--font-mono); font-size: 12.5px; line-height: 1.8;
  color: var(--color-neutral-200); overflow-x: auto;
}
.terminal .prompt { color: var(--color-accent); user-select: none; }
.terminal .out { color: var(--color-neutral-400); }

/* ── link card ────────────────────────────────────────────────────────── */
.link-card {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-4);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  background: var(--color-surface);
  text-decoration: none; color: inherit;
}
.link-card:hover { border-color: var(--color-accent); color: inherit; }
.link-card .host { font-size: 10px; letter-spacing: 0.17em; text-transform: uppercase; color: var(--color-accent-300); }
.link-card .title { font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 17px; }
.link-card .desc { font-size: 13px; color: var(--color-neutral-300); }

/* ── figure grid ──────────────────────────────────────────────────────── */
.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-4); }
/* ==========================================================================
   Components — the Hexfolio6 kit, plain CSS on plain HTML
   ========================================================================== */

/* — buttons — */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer; text-decoration: none;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 14px; line-height: 1.2; color: var(--color-text);
  background: transparent; border: 1px solid transparent;
  padding: var(--space-2) calc(var(--space-3) * 1.2);
  border-radius: var(--radius-md);
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.btn svg { display: block; flex: none; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { color: var(--color-accent); border-color: var(--color-accent); }
.btn-primary:hover { color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.btn-primary:active { background: color-mix(in srgb, var(--color-accent) 22%, transparent); }
.btn-secondary { border-color: var(--color-divider); color: var(--color-text); }
.btn-secondary:hover { color: var(--color-text); background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.btn-ghost { color: var(--color-accent); padding-inline: var(--space-3); }
.btn-ghost:hover { color: var(--color-accent); background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
.btn-ghost:active { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
.btn-icon { width: 36px; height: 36px; padding: 0; }
.btn-block { width: 100%; margin-top: var(--space-2); }
.btn-row { display: flex; gap: var(--space-4); flex-wrap: wrap; align-items: center; }

/* — forms — */
.field { display: block; }
.field > label {
  display: block; font-size: 12px; margin-bottom: 5px;
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
.input {
  width: 100%; min-height: 36px; padding: 6px 10px; font: inherit;
  font-size: 14px; color: var(--color-text); caret-color: var(--color-accent);
  background: var(--color-surface);
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.input::placeholder { color: var(--color-neutral-500); }
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; }
textarea.input { min-height: 90px; resize: vertical; }

.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.radio input, .seg-opt input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.radio .dot {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 1.5px solid var(--color-divider);
}
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot {
  border-color: var(--color-accent); background: var(--color-accent);
  box-shadow: inset 0 0 0 4px var(--color-bg);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.seg {
  display: inline-flex; overflow: hidden;
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; font-size: 13px; cursor: pointer;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt:has(input:checked) { color: var(--color-accent); box-shadow: inset 0 0 0 1px var(--color-accent); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* — cards — */
.card {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4); border-radius: var(--radius-md);
  background: var(--color-surface);
}
.card-kicker {
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-accent);
}
.card-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 17px; line-height: 1.2; margin: 0;
}
.card-title a { color: inherit; text-decoration: none; }
.card-title a:hover { color: var(--color-accent-300); }
.card-body { margin: 0; font-size: 13px; opacity: 0.8; flex: 1; }
.card-meta {
  display: flex; align-items: center; gap: var(--space-4); font-size: 11px;
  color: color-mix(in srgb, var(--color-text) 50%, transparent);
}
.card-meta .with-icon { display: inline-flex; align-items: center; gap: 4px; }
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* A card that is entirely a link. */
a.card { text-decoration: none; color: inherit; transition: box-shadow 140ms ease; }
a.card:hover { color: inherit; box-shadow: var(--shadow-md); }

/* — tags — */
.tag {
  display: inline-flex; align-items: center; font-size: 11px;
  letter-spacing: 0.02em; padding: 3px 10px;
  border-radius: calc(var(--radius-md) * 0.75);
  text-decoration: none;
}
.tag-accent { background: var(--color-accent-800); color: var(--color-accent-100); }
.tag-accent-2 { background: var(--color-accent-2-800); color: var(--color-accent-2-100); }
.tag-neutral { background: var(--color-neutral-800); color: var(--color-neutral-100); }
.tag-outline { border: 1px solid var(--color-accent); color: var(--color-accent); }
a.tag-neutral:hover { background: var(--color-neutral-700); color: var(--color-neutral-100); }
a.tag-outline:hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); color: var(--color-accent); }
a.tag-accent:hover { color: var(--color-accent-100); }
.tag-count { opacity: 0.6; margin-left: 5px; }
.tag-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* — tables — */
.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
  text-align: left; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
  padding: var(--space-2); border-bottom: 1px solid transparent;
}
.table td { padding: var(--space-2); border-bottom: 1px solid transparent; }
/* Row rules are row-level strips so the end-fade spans the row, not each cell. */
.table thead tr {
  background: linear-gradient(to right,
    transparent, var(--color-divider) 48px,
    var(--color-divider) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px;
}
.table tbody tr {
  background: linear-gradient(to right,
    transparent, color-mix(in srgb, var(--color-text) 8%, transparent) 48px,
    color-mix(in srgb, var(--color-text) 8%, transparent) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px;
}
.table tbody tr:hover {
  background:
    linear-gradient(color-mix(in srgb, var(--color-text) 4%, transparent),
                    color-mix(in srgb, var(--color-text) 4%, transparent)) no-repeat 0 0 / 100% 100%,
    linear-gradient(to right,
      transparent, color-mix(in srgb, var(--color-text) 8%, transparent) 48px,
      color-mix(in srgb, var(--color-text) 8%, transparent) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px;
}
.table-wrap { overflow-x: auto; }

/* — dialog — */
.dialog-backdrop {
  position: fixed; inset: 0; z-index: 50; display: grid; place-items: start center;
  padding: var(--space-4);
  background: color-mix(in srgb, var(--color-neutral-900) 62%, transparent);
  backdrop-filter: blur(3px);
}
.dialog {
  width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4); border-radius: var(--radius-lg);
  background: var(--color-surface); box-shadow: var(--shadow-lg);
}
.dialog-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 20px; margin: 0;
}
.dialog-body { font-size: 14px; opacity: 0.85; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }

/* — timeline: the dot-and-line spine shared by /news/ and the CV — */
.timeline { display: flex; flex-direction: column; }
.timeline-row { display: flex; gap: var(--space-6); padding-bottom: var(--space-8); }
.timeline-row:last-child { padding-bottom: 0; }
.timeline-when {
  flex: none; width: 110px; display: flex; flex-direction: column;
  align-items: flex-end; gap: var(--space-2); padding-top: 2px;
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-neutral-400);
}
.timeline-when .time { letter-spacing: 0; text-transform: none; color: var(--color-neutral-500); }
.timeline-spine { flex: none; display: flex; flex-direction: column; align-items: center; width: 11px; }
.timeline-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--color-neutral-600); margin-top: 6px;
}
.timeline-row:first-child .timeline-dot { background: var(--color-accent); }
.timeline-line { flex: 1; width: 1px; background: var(--color-border); }
.timeline-row:last-child .timeline-line { display: none; }
.timeline-body { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.timeline-body > p { margin: 0; font-size: 15.5px; line-height: 1.7; color: var(--color-text); }
.timeline-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 17px; margin: 0;
}
/* CV variant: a year label rather than a date-and-time stack. */
.timeline-cv .timeline-when { width: 88px; align-items: flex-start; }
.timeline-cv .timeline-body { gap: var(--space-2); }
.timeline-cv .timeline-body > p { font-size: 14px; line-height: 1.6; color: var(--color-neutral-300); }

/* — drawers: <details> given the card treatment —
   The disclosure triangle is suppressed and replaced by the caret on the
   right, which rotates on open. Height is not animated: <details> has no
   interoperable way to do it, and a half-drawn drawer reads worse than an
   honest jump. */
.drawer {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  overflow: hidden;
}
.drawer + .drawer { margin-top: var(--space-3); }
.drawer[open] { border-color: var(--color-neutral-600); }

.drawer-summary {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  cursor: pointer; list-style: none;
  transition: background 120ms ease;
}
.drawer-summary::-webkit-details-marker { display: none; }
.drawer-summary:hover { background: var(--color-surface-2); }
.drawer-summary:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: -2px;
}

.drawer-icon {
  flex: none; display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  background: var(--color-surface-2); color: var(--color-accent-300);
}
/* Tinted from the accent rather than a fixed step of it, so the open tile
   separates from the card on either ground. */
.drawer[open] .drawer-icon {
  background: color-mix(in srgb, var(--color-accent) 18%, transparent);
  color: var(--color-accent);
}

.drawer-head { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.drawer-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 16px; line-height: 1.25; color: var(--color-text);
  margin: 0;   /* it may be an <hN> on a page built out of drawers */
}
.drawer-lede {
  font-size: 13px; line-height: 1.5; color: var(--color-neutral-400);
  text-wrap: pretty;
}
.drawer-meta {
  flex: none; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--color-neutral-500);
}
.drawer-marker {
  flex: none; display: grid; place-items: center;
  color: var(--color-neutral-500);
  transition: transform 160ms ease, color 120ms ease;
}
.drawer[open] .drawer-marker { transform: rotate(90deg); color: var(--color-accent-300); }
.drawer-summary:hover .drawer-marker { color: var(--color-accent-300); }

/* The divider fades at both ends, like the .hr and the table rules. */
.drawer-body {
  padding: var(--space-6);
  background: linear-gradient(to right,
    transparent, color-mix(in srgb, var(--color-text) 13%, transparent) 10%,
    color-mix(in srgb, var(--color-text) 13%, transparent) 90%, transparent)
    no-repeat top / 100% 1px;
  /* No fill-mode: if the animation never runs the body is simply visible,
     rather than stuck at the keyframe's opacity: 0. */
  animation: drawer-open 180ms ease;
}
.drawer-body > :last-child { margin-bottom: 0; }
@keyframes drawer-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* — reading progress —
   Sits at the foot of the site header, covering its border, so it reads as
   part of the page rather than as browser chrome. It carries a faint track:
   without one the bar is invisible until you have read some of the article,
   which makes it easy to miss that it is there at all.

   The fill is scaled rather than resized — a transform costs the compositor
   nothing, a width change costs a layout on every scroll frame. */
.reading-progress {
  position: absolute; left: 0; right: 0; bottom: -1px;
  height: 3px; z-index: 1; pointer-events: none;
  background: color-mix(in srgb, var(--color-text) 9%, transparent);
}
.reading-progress > span {
  display: block; height: 100%;
  transform: scaleX(0); transform-origin: left;
  background: linear-gradient(90deg, var(--color-accent-600), var(--color-accent));
}
[dir="rtl"] .reading-progress > span { transform-origin: right; }
/* ==========================================================================
   Layout — shell, header, footer, page grids
   ========================================================================== */

.site {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.site-main { flex: 1 0 auto; }

.container {
  width: 100%;
  max-width: var(--width-page);
  margin-inline: auto;
  padding-inline: var(--space-6);
}
@media (min-width: 768px) {
  .container { padding-inline: var(--space-8); }
}

/* Vertical rhythm of a page: the design stacks blocks at 2× space-8. */
.page-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
  padding-block: var(--space-16) var(--space-20);
}
.page-body--tight { gap: var(--space-12); }

/* Content + right rail, as on /blog/, a post, a project and /about/. */
.with-rail {
  display: flex;
  gap: var(--space-16);
  align-items: flex-start;
}
.with-rail > .rail-main { flex: 1 1 0; min-width: 0; }
.with-rail > .rail-side {
  flex: 0 0 var(--width-sidebar);
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
}
.rail-main--prose { max-width: var(--width-prose); }
.rail-main--wide { max-width: 720px; }
.rail-side--wide { flex-basis: 280px; }
.rail-side--contact { flex-basis: 320px; }

.rail-block { display: flex; flex-direction: column; gap: var(--space-4); }
.rail-block a { font-size: 13px; text-decoration: none; line-height: 1.5; }

/* Page heading: title, accent mark, standfirst. */
.page-head { display: flex; flex-direction: column; gap: var(--space-4); }
.page-head h1 { font-size: 40px; line-height: 1.05; margin: 0; }
.page-head .lede { font-size: 15px; line-height: 1.6; color: var(--color-neutral-300); margin: 0; }

/* Section: eyebrow on the left, "All posts →" on the right. */
.section { display: flex; flex-direction: column; gap: var(--space-6); }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4);
}
.section-more { font-size: 12px; text-decoration: none; white-space: nowrap; }

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

/* ── header ───────────────────────────────────────────────────────────── */

.site-header {
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  position: sticky;
  top: 0;
  z-index: 30;
}
.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-6);
  padding-block: var(--space-4);
}

.brand {
  display: flex; align-items: baseline; gap: var(--space-3);
  flex: none; text-decoration: none; color: inherit;
}
.brand:hover { color: inherit; }
.brand-mark {
  font-family: var(--font-mark); font-weight: var(--font-heading-weight);
  font-size: 22px; line-height: 0.9; color: var(--color-text);
}
.brand-mark i { font-style: normal; color: var(--color-accent); }
.brand-name {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--color-neutral-400);
}

.nav-main {
  display: flex; align-items: center; gap: var(--space-6);
  flex: 1 1 auto; justify-content: center;
}
.nav-item {
  display: flex; flex-direction: column; gap: 5px; align-items: center;
  text-decoration: none;
}
.nav-item span {
  font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-neutral-400);
}
.nav-item::after {
  content: ""; width: 100%; height: 1px; background: transparent;
}
.nav-item:hover span { color: var(--color-text); }
.nav-item[aria-current="page"] span { color: var(--color-text); }
.nav-item[aria-current="page"]::after {
  background: linear-gradient(90deg, var(--color-accent), transparent);
}

.header-tools { display: flex; align-items: center; gap: var(--space-4); flex: none; }

.search-trigger {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 5px var(--space-3); min-width: 150px;
  border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  background: transparent; color: var(--color-neutral-400);
  font: inherit; font-size: 12px; cursor: pointer;
}
.search-trigger:hover { border-color: var(--color-neutral-500); }
.search-trigger .label { flex: 1; text-align: left; }
.search-trigger kbd {
  font-size: 10px; padding: 1px 5px; font-family: var(--font-body);
  border: 1px solid var(--color-border); border-radius: 3px;
}

.lang-switch { display: flex; align-items: center; gap: var(--space-2); font-size: 11px; letter-spacing: 0.1em; }
.lang-switch a { color: var(--color-neutral-500); text-decoration: none; }
.lang-switch a:hover { color: var(--color-neutral-300); }
.lang-switch .is-current { color: var(--color-text); }
.lang-switch .sep { color: var(--color-neutral-600); }

.theme-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none;
  border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  background: transparent; color: var(--color-accent-300); cursor: pointer;
}
.theme-toggle:hover { border-color: var(--color-accent); }
/* Only one of the two glyphs shows; which one depends on the active theme.
   Keyed on the attribute, not the class: the header, the mobile header, the
   mobile menu and the splash each style their button differently, and every
   one of them must hide the glyph for the theme that is already active. */
[data-theme-toggle] .icon-sun { display: none; }
:root[data-theme="light"] [data-theme-toggle] .icon-sun { display: block; }
:root[data-theme="light"] [data-theme-toggle] .icon-moon { display: none; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) [data-theme-toggle] .icon-sun { display: block; }
  :root:not([data-theme="dark"]) [data-theme-toggle] .icon-moon { display: none; }
}

/* Mobile header controls — hidden on desktop. */
.header-mobile { display: none; align-items: center; gap: var(--space-4); }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  color: var(--color-neutral-300);
}
.icon-btn:hover { color: var(--color-text); }

/* ── footer ───────────────────────────────────────────────────────────── */

.site-footer {
  background: var(--color-bg);
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-16);
}
.footer-inner {
  display: flex; flex-direction: column; gap: var(--space-6);
  padding-block: var(--space-12) var(--space-8);
}
.footer-cols {
  display: flex; flex-wrap: wrap; gap: var(--space-12);
  justify-content: space-between;
}
.footer-brand { display: flex; flex-direction: column; gap: var(--space-3); max-width: 280px; }
.footer-brand .brand-mark { font-size: 26px; }
.footer-brand .tagline {
  font-size: 10px; letter-spacing: 0.17em; text-transform: uppercase;
  color: var(--color-neutral-400); line-height: 1.7;
}
.footer-col { display: flex; flex-direction: column; gap: var(--space-3); }
.footer-col a { font-size: 13px; text-decoration: none; }
.footer-social {
  display: grid; grid-template-columns: auto auto; gap: var(--space-2) var(--space-6);
}
.footer-social a { display: flex; align-items: center; gap: var(--space-2); }
.footer-bottom {
  display: flex; flex-wrap: wrap; gap: var(--space-4); justify-content: space-between;
  padding-top: var(--space-4); border-top: 1px solid var(--color-border);
  font-size: 11px; color: var(--color-neutral-500);
}

/* ── small utilities ──────────────────────────────────────────────────── */
/* Enough to keep the layouts free of inline style attributes. */
.stack { display: flex; flex-direction: column; }
.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); }
.gap-12 { gap: var(--space-12); }
.gap-16 { gap: var(--space-16); }
.measure { max-width: var(--width-prose); }
.self-start { align-self: flex-start; }
/* ==========================================================================
   Pages — hero, listings, post, news, projects, contact, search, 404
   ========================================================================== */

/* ── home hero ────────────────────────────────────────────────────────── */
.hero { display: flex; gap: var(--space-16); align-items: flex-start; }
.hero-text {
  flex: 1 1 0; min-width: 0; max-width: 640px;
  display: flex; flex-direction: column; gap: var(--space-6);
}
.hero-text h1 {
  font-size: 56px; line-height: 1.02; letter-spacing: -0.01em; margin: 0;
}
.hero-text .lede {
  font-size: 16px; line-height: 1.65; color: var(--color-neutral-300);
  text-wrap: pretty; margin: 0;
}
.hero-text .rule-accent { width: 220px; }
.hero-aside { flex: 0 0 300px; display: flex; flex-direction: column; gap: var(--space-4); }
.hero-portrait {
  border-radius: var(--radius-md); overflow: hidden;
  width: 300px; height: 340px; object-fit: cover;
}
.hero-note { font-size: 11px; color: var(--color-neutral-500); line-height: 1.6; }

/* ── post listings ────────────────────────────────────────────────────── */
/* Compact form used in the home column. */
.post-brief {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding-bottom: var(--space-6); border-bottom: 1px solid var(--color-border);
}
.post-brief:last-child { padding-bottom: 0; border-bottom: 0; }
.post-brief h3 { font-size: 18px; line-height: 1.25; margin: 0; }
.post-brief h3 a { color: inherit; text-decoration: none; }
.post-brief h3 a:hover { color: var(--color-accent-300); }
.post-brief p { font-size: 13px; color: var(--color-neutral-300); line-height: 1.6; margin: 0; }
.post-brief .meta-line { gap: var(--space-3); }

/* Full form used on /blog/. */
.post-entry {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding-bottom: var(--space-8); border-bottom: 1px solid var(--color-border);
}
.post-entry:last-child { padding-bottom: 0; border-bottom: 0; }
.post-entry h2 { font-size: 24px; line-height: 1.2; margin: 0; }
.post-entry h2 a { color: inherit; text-decoration: none; }
.post-entry h2 a:hover { color: var(--color-accent-300); }
.post-entry p { font-size: 14px; line-height: 1.6; color: var(--color-neutral-300); margin: 0; }
.post-entry .tag-row { margin-top: var(--space-2); }
.post-list { display: flex; flex-direction: column; gap: var(--space-8); }
.post-list--brief { gap: var(--space-6); }

/* "Series · 2 of 4" pill that rides in a meta line. */
.series-pill {
  color: var(--color-accent-300);
  border: 1px solid var(--color-accent-700);
  border-radius: 3px; padding: 1px 6px; text-decoration: none;
}
a.series-pill:hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }

/* ── single post ──────────────────────────────────────────────────────── */
.post-header { display: flex; flex-direction: column; gap: var(--space-6); }
.post-header h1 { font-size: 44px; line-height: 1.05; letter-spacing: -0.01em; margin: 0; }
.breadcrumb {
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-neutral-500);
}
.breadcrumb a { text-decoration: none; }

/* The banner that says which part of a series you are reading. */
.series-banner {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-left: 2px solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}
.series-banner .part {
  font-size: 10px; letter-spacing: 0.17em; text-transform: uppercase;
  color: var(--color-accent-300);
}
.series-banner .prev { font-size: 13px; color: var(--color-neutral-300); }

.post-nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-6);
  padding-top: var(--space-8); margin-top: var(--space-6);
  border-top: 1px solid var(--color-border);
}
.post-nav a { font-size: 13px; text-decoration: none; }
.post-nav a:only-child:last-child { margin-left: auto; }

/* Series index in the right rail: the current part is plain text. */
.series-list { display: flex; flex-direction: column; gap: var(--space-3); font-size: 13px; line-height: 1.5; }
.series-list a { text-decoration: none; }
.series-list .is-current { color: var(--color-text); }
.series-list .is-draft { color: var(--color-neutral-500); }

/* Table of contents. */
.toc { font-size: 13px; line-height: 1.6; }
.toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.toc ul ul { padding-left: var(--space-4); margin-top: var(--space-2); }
.toc a { text-decoration: none; color: var(--color-neutral-300); }
.toc a:hover { color: var(--color-accent-300); }

/* ── news ─────────────────────────────────────────────────────────────── */
.news-page { max-width: 760px; }
.news-tags { display: flex; gap: var(--space-2); }

/* ── projects ─────────────────────────────────────────────────────────── */
.project-filters { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.project-card { min-height: 200px; }
.project-card .tag-row { margin-top: auto; }
.glance {
  display: grid; grid-template-columns: auto 1fr;
  gap: var(--space-3) var(--space-4); font-size: 13px;
}
.glance dt { color: var(--color-neutral-500); margin: 0; }
.glance dd { margin: 0; }

/* Caret-led feature list on a project page. */
.feature-list { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.feature-list li {
  display: flex; gap: var(--space-3);
  font-size: 15.5px; line-height: 1.7; color: var(--color-neutral-200);
}
.feature-list svg { flex: none; color: var(--color-accent); margin-top: 6px; }

/* ── about ────────────────────────────────────────────────────────────── */
.about-portrait { border-radius: var(--radius-md); width: 280px; height: 320px; object-fit: cover; }

/* ── contact ──────────────────────────────────────────────────────────── */
.contact-form { display: flex; flex-direction: column; gap: var(--space-6); }
.contact-note { font-size: 12px; color: var(--color-neutral-500); }
.contact-links { display: flex; flex-direction: column; gap: var(--space-4); }
.contact-link {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--color-border); border-radius: var(--radius-sm);
  text-decoration: none; color: inherit;
}
.contact-link:hover { border-color: var(--color-accent); color: inherit; }
.contact-link svg { flex: none; color: var(--color-accent-300); }
.contact-link .value { font-size: 14px; color: var(--color-text); }
.contact-link .label { font-size: 11px; color: var(--color-neutral-500); }

/* ── search ───────────────────────────────────────────────────────────── */
.search-panel {
  width: min(760px, 100%);
  display: flex; flex-direction: column; gap: var(--space-6);
  padding: var(--space-12) var(--space-8);
  margin-top: 8vh;
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  background: var(--color-bg); box-shadow: var(--shadow-lg);
  max-height: 80vh;
}
.search-box {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-accent); border-radius: var(--radius-sm);
  background: var(--color-surface);
}
.search-box svg { flex: none; color: var(--color-accent-300); }
.search-box input {
  flex: 1; min-width: 0; font: inherit; font-size: 16px;
  background: transparent; border: 0; color: var(--color-text);
  caret-color: var(--color-accent);
}
.search-box input:focus { outline: none; }
.search-status {
  font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-neutral-500);
}
.search-results { display: flex; flex-direction: column; gap: var(--space-6); overflow-y: auto; }
.search-hit {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding-bottom: var(--space-6); border-bottom: 1px solid var(--color-border);
  text-decoration: none; color: inherit;
}
.search-hit:last-child { padding-bottom: 0; border-bottom: 0; }
.search-hit:hover { color: inherit; }
.search-hit .kind {
  font-size: 10px; letter-spacing: 0.17em; text-transform: uppercase;
  color: var(--color-accent-300);
}
.search-hit h3 { font-size: 19px; margin: 0; }
.search-hit p { font-size: 13.5px; line-height: 1.6; color: var(--color-neutral-300); margin: 0; }
.search-hit mark {
  color: var(--color-text); background: var(--color-accent-800); padding: 0 3px;
}
.search-hit[aria-selected="true"] { outline: 2px solid var(--color-accent); outline-offset: 6px; border-radius: var(--radius-sm); }

/* ── 404 ──────────────────────────────────────────────────────────────── */
.notfound {
  display: flex; flex-direction: column; gap: var(--space-6);
  min-height: 55vh; justify-content: center; max-width: 480px;
}
.notfound h1 { font-size: 64px; line-height: 0.9; margin: 0; }
.notfound .rule-accent { width: 160px; }
.notfound p { font-size: 16px; line-height: 1.7; color: var(--color-neutral-300); margin: 0; }

/* ── archive ──────────────────────────────────────────────────────────── */
.archive-years { font-size: 13px; color: var(--color-neutral-300); line-height: 1.9; }
.archive-years a { text-decoration: none; }

/* Home page news column: a compact form of the /news/ spine, where only the
   newest note carries the accent dot. */
.news-brief { display: flex; gap: var(--space-4); }
.news-brief > .timeline-dot { width: 6px; height: 6px; margin-top: 6px; flex: none; }
.news-brief:first-of-type > .timeline-dot { background: var(--color-accent); }
.news-brief > div { display: flex; flex-direction: column; gap: var(--space-2); }
.news-brief-text { font-size: 14px; line-height: 1.6; color: var(--color-neutral-200); }
.news-brief-text p:last-child { margin-bottom: 0; }

/* Series term page: a numbered index down the left of each entry. */
.series-index-row { display: flex; gap: var(--space-6); align-items: flex-start; }
.series-index-row > .series-index-num { flex: none; width: 28px; padding-top: 4px; }
.series-index-row > .series-index-body { flex: 1 1 0; min-width: 0; }
.series-index-row--upcoming { opacity: 0.55; }

.series-banner-text { display: flex; flex-direction: column; gap: 2px; }

.project-lede { font-size: 17px; line-height: 1.7; color: var(--color-neutral-200); margin: 0; }
.rule-accent--wide { width: 200px; }
.rule-accent--full { width: 100%; }

.prose-sm { font-size: 15.5px; }
.rail-main--narrow { max-width: 520px; }
.form-actions { display: flex; align-items: center; gap: var(--space-6); flex-wrap: wrap; }
.pagination-count { font-size: 12px; }
.search-trigger--block { min-width: 0; }
.search-trigger--block .label { font-size: 14px; }

/* ── services ─────────────────────────────────────────────────────────── */
.drawer-stack { display: flex; flex-direction: column; gap: var(--space-3); }
.drawer-stack .drawer + .drawer { margin-top: 0; }

.service-body { display: flex; flex-direction: column; gap: var(--space-6); }
/* A drawer is as wide as the page; running text is not. Everything meant to
   be read stops at the same measure as the rest of the site, and the rows of
   tags below it are free to run the full width. */
.service-body > .prose,
.service-glance { max-width: var(--width-prose); }
.service-glance { display: flex; flex-direction: column; gap: var(--space-3); }

.service-terms {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3) var(--space-6);
}
.service-rate { font-size: 12px; color: var(--color-neutral-400); }

.service-more {
  display: inline-flex; align-items: center; gap: var(--space-2);
  align-self: flex-start;
  font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent-300); text-decoration: none;
}
.service-more:hover { color: var(--color-accent-200); }
.service-more svg { transition: transform 140ms ease; }
.service-more:hover svg { transform: translateX(3px); }

/* The closing ask, set apart from the drawers by the section ground. */
.service-cta {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-8); flex-wrap: wrap;
  padding: var(--space-8) var(--space-12);
  border-radius: var(--radius-lg);
  background: var(--color-section);
}
.service-cta-text { flex: 1 1 340px; min-width: 0; }
.service-cta h2 { font-size: 22px; margin: 0 0 var(--space-2); }
.service-cta p {
  margin: 0; font-size: 14px; line-height: 1.6;
  color: var(--color-neutral-300); text-wrap: pretty; max-width: 56ch;
}
.service-cta .btn { flex: none; }

/* Back to /services/ from a single service. */
.rail-back {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: 12px; color: var(--color-neutral-400); text-decoration: none;
}
.rail-back:hover { color: var(--color-accent-300); }

/* ── contact card ─────────────────────────────────────────────────────── */
/* Avatar, name, links, a paragraph. Built by _partials/card-body.html and
   used by the splash; the base styles stay variant-free so a future page can
   drop the same card into ordinary site chrome. */
.id-card {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--space-6);
  width: min(560px, 100%);
  padding: var(--space-12) var(--space-12) var(--space-16);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
  text-align: center;
}
.id-avatar {
  width: 132px; height: 132px;
  border-radius: 50%; object-fit: cover;
  background: var(--color-surface-2);
  box-shadow: 0 0 0 1px var(--color-border);
}
.id-head { display: flex; flex-direction: column; gap: var(--space-2); }
.id-head h1 { font-size: 30px; margin: 0; }
.id-role {
  margin: 0; font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-neutral-400);
}
/* Centred, so the accent rule fades from both ends rather than one. */
.id-card .rule-accent {
  width: 140px;
  background: linear-gradient(90deg, transparent, var(--color-accent), transparent);
}

.id-links { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3); }
.id-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 6px var(--space-4);
  border: 1px solid var(--color-border); border-radius: 999px;
  font-size: 13px; color: var(--color-neutral-200); text-decoration: none;
  transition: border-color 120ms ease, color 120ms ease;
}
.id-link:hover { border-color: var(--color-accent); color: var(--color-text); }
.id-link svg { flex: none; color: var(--color-accent-300); }

.id-bio { text-wrap: pretty; max-width: 46ch; }
.id-cta { justify-content: center; }

/* ── splash: the bare entry point ─────────────────────────────────────── */
/* layouts/splash.html is a whole document of its own — no site header, no
   site footer. The background sits behind everything; the card floats. */
.splash-body { background: var(--color-bg); }
.splash {
  position: relative;
  min-height: 100vh;    /* fallback for engines without dvh */
  min-height: 100dvh;
  display: grid; grid-template-rows: auto 1fr auto;
  gap: var(--space-6);
  padding: var(--space-6) var(--space-8) var(--space-8);
}
.splash > *:not(.splash-bg) { position: relative; }

/* `isolation` keeps every blend below — the soft-light glow, the duotone —
   inside the background layer, so nothing reaches the card or the body. It
   also means the ground has to be painted here: an isolated group blends
   against transparency, and multiply against nothing is a no-op that leaves
   the tone layer's own colour showing wherever the photo is masked away. */
.splash-bg {
  position: absolute; inset: 0; overflow: hidden;
  isolation: isolate; background: var(--color-bg);
}
.splash-bg > * { position: absolute; inset: 0; }

/* The stock background: a node-and-link constellation drawn in the accent,
   which therefore follows the light/dark switch with everything else. */
.splash-mesh { color: var(--color-accent); opacity: 0.5; }
.splash-mesh svg { width: 100%; height: 100%; display: block; }

.splash-glow {
  background:
    radial-gradient(900px 640px at 14% 6%,
      color-mix(in srgb, var(--color-accent) 20%, transparent), transparent 68%),
    radial-gradient(780px 580px at 88% 92%,
      color-mix(in srgb, var(--color-accent-2) 18%, transparent), transparent 70%);
}
/* Quietest where the card sits, so the text keeps its contrast. */
.splash-scrim {
  background: radial-gradient(62% 56% at 50% 46%,
    color-mix(in srgb, var(--color-bg) 84%, transparent) 0%,
    color-mix(in srgb, var(--color-bg) 46%, transparent) 100%);
}

/* ── splash: photo backgrounds ────────────────────────────────────────── */
/* A photo enters through `background` in front matter and is graded by the
   `splashStyle` it is given. Every treatment reads the same four variables,
   which is also where light mode is answered: a photograph that carries dark
   text has to be lifted, not dimmed. `splashFocus` overrides --splash-focus
   per page for images whose subject is not in the middle. */
/* --splash-ink is the colour a treatment pushes the photo toward and
   --splash-clear is the neutral element of --splash-blend, so a gradient
   between the two reads as "grade by this much". Laying translucent plum over
   a bright sky only ever makes mauve fog; multiplying it makes dusk. Light
   mode runs the same gradient through screen, which lifts instead. */
.splash-bg--photo {
  --splash-sat: 0.95;
  --splash-con: 1.06;
  --splash-bri: 0.82;
  --splash-blend: multiply;
  --splash-ink: #1A1423;
  --splash-clear: #ffffff;
  --splash-veil: 88%;
  --splash-veil-top: 62%;
  --splash-veil-mid: 34%;
  --splash-foot: 72%;
  --splash-head: 58%;
  --splash-bri: var(--photo-dim, 0.82);
}
:root[data-theme="light"] .splash-bg--photo {
  --splash-sat: 0.72;
  --splash-con: 0.94;
  --splash-bri: var(--photo-dim-light, 0.94);
  --splash-blend: screen;
  --splash-ink: #FBF4F0;
  --splash-clear: #000000;
  --splash-veil: 44%;
  --splash-veil-top: 24%;
  --splash-veil-mid: 9%;
  --splash-foot: 52%;
  --splash-head: 38%;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .splash-bg--photo {
    --splash-sat: 0.72;
    --splash-con: 0.94;
    --splash-bri: var(--photo-dim-light, 0.94);
    --splash-blend: screen;
    --splash-ink: #FBF4F0;
    --splash-clear: #000000;
    --splash-veil: 44%;
    --splash-veil-top: 24%;
    --splash-veil-mid: 9%;
    --splash-foot: 52%;
    --splash-head: 38%;
  }
}

.splash-photo-wrap { overflow: hidden; }
/* Absolute, so the <noscript> copy lands on top of the src-less element the
   picker fills in rather than stacking underneath it. */
.splash-photo {
  position: absolute; inset: 0;
  width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: var(--splash-focus, 50% 50%);
  filter: saturate(var(--splash-sat)) contrast(var(--splash-con)) brightness(var(--splash-bri));
}
/* Over a photograph the constellation is a signature, not a background of its
   own, so it thins out — and it rises above the scrim, which is there to calm
   the picture rather than the strokes. */
/* --photo-mesh is the per-image escape hatch: a busy or near-black photograph
   swallows the strokes at the opacity an empty sky wants. */
.splash-bg--photo .splash-mesh {
  opacity: var(--photo-mesh, 0.46); z-index: 2; color: var(--color-accent-200);
}
:root[data-theme="light"] .splash-bg--photo .splash-mesh {
  color: var(--color-accent-300); opacity: var(--photo-mesh, 0.5);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .splash-bg--photo .splash-mesh {
    color: var(--color-accent-300); opacity: var(--photo-mesh, 0.5);
  }
}
/* Soft-light so the glow colours the photograph rather than sitting on it;
   held well back, because over a sunrise it turns the sky to fog. */
.splash-bg--photo .splash-glow { mix-blend-mode: soft-light; opacity: 0.5; }

/* cinematic — the photo as shot, graded down and weighted at the foot of the
   frame the way a title card is. The default when a photo is set. */
.splash-bg--cinematic .splash-tone {
  mix-blend-mode: var(--splash-blend);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--splash-ink) var(--splash-veil-top), var(--splash-clear)) 0%,
    color-mix(in srgb, var(--splash-ink) var(--splash-veil-mid), var(--splash-clear)) 42%,
    color-mix(in srgb, var(--splash-ink) var(--splash-veil), var(--splash-clear)) 100%);
}
.splash-bg--cinematic .splash-scrim {
  background: radial-gradient(66% 58% at 50% 48%,
    color-mix(in srgb, var(--color-bg) 46%, transparent) 0%,
    transparent 100%);
}

/* duotone — greyscale, then remapped onto two brand colours: multiply drops
   the picture onto the highlight, lighten floors the shadows. The card then
   sits on something that is unmistakably part of the palette. */
.splash-bg--duotone {
  --duo-lo: #120D19;                    /* where the shadows land */
  --duo-hi: var(--color-accent-600);    /* where the highlights land */
}
:root[data-theme="light"] .splash-bg--duotone { --duo-lo: #6E3A45; --duo-hi: #FDF8F6; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .splash-bg--duotone { --duo-lo: #6E3A45; --duo-hi: #FDF8F6; }
}
.splash-bg--duotone .splash-photo-wrap { background: var(--duo-hi); isolation: isolate; }
.splash-bg--duotone .splash-photo {
  filter: grayscale(1) contrast(1.2) brightness(0.98);
  mix-blend-mode: multiply;
}
.splash-bg--duotone .splash-tone { background: var(--duo-lo); mix-blend-mode: lighten; }
.splash-bg--duotone .splash-glow { mix-blend-mode: overlay; opacity: 0.55; }
.splash-bg--duotone .splash-scrim {
  background: radial-gradient(64% 58% at 50% 46%,
    color-mix(in srgb, var(--color-bg) 58%, transparent) 0%,
    color-mix(in srgb, var(--color-bg) 14%, transparent) 100%);
}
.splash-bg--duotone .splash-mesh { opacity: 0.4; color: var(--color-accent-100); }

/* horizon — the photo is a band at the foot of the page that dissolves
   upward into the flat ground, with the accent drawn along the waterline.
   The mesh runs the full height, so the two read as one field. */
.splash-bg--horizon .splash-photo-wrap {
  top: 34%; bottom: 0;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 26%, #000 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 26%, #000 100%);
}
.splash-bg--horizon { --splash-bri: 0.78; }
.splash-bg--horizon .splash-photo { object-position: var(--splash-focus, 50% 72%); }
/* The waterline: a hairline that fades out before either edge, so it reads as
   a horizon and not as a border. */
.splash-bg--horizon .splash-tone {
  background:
    linear-gradient(90deg, transparent 0%,
      color-mix(in srgb, var(--color-accent-300) 85%, transparent) 18%,
      color-mix(in srgb, var(--color-accent-300) 85%, transparent) 82%, transparent 100%)
      no-repeat 0 34% / 100% 1.5px,
    linear-gradient(180deg, transparent 58%,
      color-mix(in srgb, var(--color-bg) 58%, transparent) 100%);
}
.splash-bg--horizon .splash-scrim {
  background: radial-gradient(56% 44% at 50% 40%,
    color-mix(in srgb, var(--color-bg) 66%, transparent) 0%,
    transparent 100%);
}

/* aperture — the picture frames the page and is masked away under the card,
   so the card floats on clean ground and the photo never fights the text.
   An inverse vignette; the mesh crosses the whole field. */
/* The mask has to run all the way to the corners: a gradient that reaches
   full opacity early draws a visible ring across an even sky. */
.splash-bg--aperture .splash-photo-wrap {
  -webkit-mask-image: radial-gradient(96% 104% at 50% 46%, transparent 4%,
    rgba(0, 0, 0, 0.35) 46%, rgba(0, 0, 0, 0.82) 76%, #000 100%);
          mask-image: radial-gradient(96% 104% at 50% 46%, transparent 4%,
    rgba(0, 0, 0, 0.35) 46%, rgba(0, 0, 0, 0.82) 76%, #000 100%);
}
.splash-bg--aperture { --splash-bri: 0.72; }
.splash-bg--aperture .splash-tone {
  mix-blend-mode: var(--splash-blend);
  background: radial-gradient(96% 104% at 50% 46%,
    var(--splash-clear) 30%,
    color-mix(in srgb, var(--splash-ink) 60%, var(--splash-clear)) 100%);
}
.splash-bg--aperture .splash-scrim { background: none; }
.splash-bg--aperture .splash-mesh { opacity: 0.4; }

/* grain — the photo pushed nearly to monochrome and dusted with film noise,
   which is the one treatment that hides a source's own compression. */
.splash-bg--grain { --splash-sat: 0.18; --splash-con: 1.2; --splash-bri: 0.86; }
:root[data-theme="light"] .splash-bg--grain { --splash-sat: 0.16; --splash-con: 1.06; --splash-bri: 1.1; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .splash-bg--grain { --splash-sat: 0.16; --splash-con: 1.06; --splash-bri: 1.1; }
}
.splash-bg--grain .splash-tone {
  mix-blend-mode: var(--splash-blend);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--splash-ink) 72%, var(--splash-clear)) 0%,
    color-mix(in srgb, var(--splash-ink) 38%, var(--splash-clear)) 44%,
    color-mix(in srgb, var(--splash-ink) 84%, var(--splash-clear)) 100%);
}
/* On the scrim rather than the tone: the tone carries a blend mode of its own,
   which would trap the noise underneath it. */
.splash-bg--grain .splash-scrim::after {
  content: ""; position: absolute; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
  opacity: 0.26; mix-blend-mode: overlay;
}
.splash-bg--grain .splash-scrim {
  background: radial-gradient(62% 56% at 50% 46%,
    color-mix(in srgb, var(--color-bg) 50%, transparent) 0%,
    transparent 100%);
}
.splash-bg--grain .splash-mesh { opacity: 0.4; }

/* slats — the photo read through louvres of the ground colour, closing up
   toward the edges. A rack front, if you like; the picture is present without
   ever being the thing you look at. */
.splash-bg--slats { --splash-sat: 0.8; --splash-bri: 0.55; }
:root[data-theme="light"] .splash-bg--slats { --splash-sat: 0.62; --splash-bri: 1.16; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .splash-bg--slats { --splash-sat: 0.62; --splash-bri: 1.16; }
}
.splash-bg--slats .splash-tone {
  background:
    repeating-linear-gradient(90deg,
      transparent 0 26px,
      color-mix(in srgb, var(--color-bg) 46%, transparent) 26px 27px,
      color-mix(in srgb, var(--color-bg) 58%, transparent) 27px 33px,
      color-mix(in srgb, var(--color-bg) 46%, transparent) 33px 34px),
    linear-gradient(90deg,
      color-mix(in srgb, var(--color-bg) 80%, transparent) 0%,
      transparent 30%, transparent 70%,
      color-mix(in srgb, var(--color-bg) 80%, transparent) 100%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--color-bg) 55%, transparent) 0%,
      transparent 34%, transparent 62%,
      color-mix(in srgb, var(--color-bg) 72%, transparent) 100%);
}
.splash-bg--slats .splash-scrim {
  background: radial-gradient(58% 54% at 50% 47%,
    color-mix(in srgb, var(--color-bg) 62%, transparent) 0%,
    color-mix(in srgb, var(--color-bg) 12%, transparent) 100%);
}
.splash-bg--slats .splash-mesh { opacity: 0.3; }

/* The nav and the language switch sit outside the card, with nothing but the
   photograph behind them — and a photograph is the one background whose
   contrast cannot be predicted. Both ends therefore get a little ground back,
   laid over whatever the treatment did and under the mesh. Stated after every
   variant so it holds whichever one is in play. */
/* Both ends are eased rather than linear. A two-stop alpha ramp is linear in
   coverage but not in what the eye reads, so its far end arrives as a visible
   edge — the shorter the run, the harder that edge. The stops below follow an
   ease-out curve, and the head gets the longer run of the two because it has
   less to hide behind. */
.splash-bg--photo .splash-scrim::before {
  --head: color-mix(in srgb, var(--color-bg) var(--splash-head, 58%), transparent);
  --foot: color-mix(in srgb, var(--color-bg) var(--splash-foot, 72%), transparent);
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg,
      var(--head) 0%,
      color-mix(in srgb, var(--head) 93%, transparent) 4.8%,
      color-mix(in srgb, var(--head) 78%, transparent) 9.6%,
      color-mix(in srgb, var(--head) 58%, transparent) 14.4%,
      color-mix(in srgb, var(--head) 37%, transparent) 19.2%,
      color-mix(in srgb, var(--head) 21%, transparent) 23%,
      color-mix(in srgb, var(--head) 8%, transparent) 27.2%,
      transparent 32%),
    linear-gradient(0deg,
      var(--foot) 0%,
      color-mix(in srgb, var(--foot) 93%, transparent) 3.6%,
      color-mix(in srgb, var(--foot) 78%, transparent) 7.2%,
      color-mix(in srgb, var(--foot) 58%, transparent) 10.8%,
      color-mix(in srgb, var(--foot) 37%, transparent) 14.4%,
      color-mix(in srgb, var(--foot) 21%, transparent) 17.3%,
      color-mix(in srgb, var(--foot) 8%, transparent) 20.4%,
      transparent 24%);
}

/* A slow drift, so the page is alive without ever asking to be watched.
   Off for anyone who has asked for less motion. */
@media (prefers-reduced-motion: no-preference) {
  .splash-bg--cinematic .splash-photo,
  .splash-bg--grain .splash-photo,
  .splash-bg--duotone .splash-photo {
    animation: splash-drift 52s ease-in-out infinite alternate;
    will-change: transform;
  }
}
@keyframes splash-drift {
  from { transform: scale(1.04); }
  to   { transform: scale(1.12) translate3d(-1.4%, -1%, 0); }
}

.splash-tools {
  display: flex; justify-content: flex-end; align-items: center;
  gap: var(--space-6);
}
.splash-main { display: grid; place-items: center; padding-block: var(--space-8); }

/* Glass, because the point of this page is that it is not the site.

   The trade that makes it read as frosted rather than as a tinted panel: take
   opacity out of the fill and put it into the blur. A thinner fill alone would
   just make the text fight the photograph; at this blur radius the backdrop is
   diffuse enough that it stays a ground, and the saturation push is what keeps
   the colour behind it alive instead of grey. */
.id-card--glass {
  /* No border, and deliberately not --shadow-lg either: on the dark ground
     that token leads with `0 0 0 1px var(--color-neutral-600)`, a hard ring
     that would outline the pane just as plainly as the border did. Only the
     soft drop is wanted here, so it is stated rather than inherited. */
  --glass-drop: 0 18px 48px rgba(0, 0, 0, 0.5);
  background: color-mix(in srgb, var(--color-surface) 54%, transparent);
  backdrop-filter: blur(30px) saturate(1.7);
  -webkit-backdrop-filter: blur(30px) saturate(1.7);
  border: 0;
  /* A specular rim: bright along the top edge, barely there along the bottom,
     which is what reads as a lit pane rather than a hole. It sits inside the
     shape, so the card still has no outline. */
  box-shadow:
    var(--glass-drop),
    inset 0 1px 0 color-mix(in srgb, var(--color-text) 24%, transparent),
    inset 0 -1px 0 color-mix(in srgb, var(--color-text) 7%, transparent);
}
/* Dark text needs more to sit on than light text does, so the light ground
   keeps a little more fill — and a shadow that reads on a pale ground. */
:root[data-theme="light"] .id-card--glass {
  background: color-mix(in srgb, var(--color-surface) 64%, transparent);
  --glass-drop: 0 18px 48px rgba(36, 26, 49, 0.16);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .id-card--glass {
    background: color-mix(in srgb, var(--color-surface) 64%, transparent);
    --glass-drop: 0 18px 48px rgba(36, 26, 49, 0.16);
  }
}
.id-card--glass .id-link {
  background: color-mix(in srgb, var(--color-bg) 22%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.splash-foot {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
}
.splash-nav, .splash-legal {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--space-2) var(--space-6);
}
.splash-nav a {
  font-size: 12px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--color-neutral-400); text-decoration: none;
}
.splash-nav a:hover { color: var(--color-accent-300); }
.splash-legal a { font-size: 11px; color: var(--color-neutral-600); text-decoration: none; }
.splash-legal a:hover { color: var(--color-neutral-400); }
/* ==========================================================================
   Syntax highlighting (Chroma) — hues chosen inside the mauve family so a
   code block reads as part of the page rather than a pasted screenshot.
   ========================================================================== */

.code-block .chroma { background: transparent; color: var(--color-neutral-200); }

:root {
  --syn-comment: #94707F;
  --syn-keyword: #D48F98;
  --syn-string:  #C9A47A;
  --syn-number:  #D8A0AE;
  --syn-name:    #E4BCC1;
  --syn-type:    #B7A4CE;
  --syn-punct:   #B08F9C;
  --syn-attr:    #C67480;
  --syn-err:     #E06C75;
  --syn-ins:     #6FA88A;
  --syn-del:     #C4566A;
}
:root[data-theme="light"] {
  --syn-comment: #8B6B77;
  --syn-keyword: #7F3D48;
  --syn-string:  #7A5A2C;
  --syn-number:  #96455C;
  --syn-name:    #6B3F73;
  --syn-type:    #4E3352;
  --syn-punct:   #774C60;
  --syn-attr:    #9E4C58;
  --syn-err:     #B3384A;
  --syn-ins:     #3F7A5E;
  --syn-del:     #A63D50;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --syn-comment: #8B6B77;
    --syn-keyword: #7F3D48;
    --syn-string:  #7A5A2C;
    --syn-number:  #96455C;
    --syn-name:    #6B3F73;
    --syn-type:    #4E3352;
    --syn-punct:   #774C60;
    --syn-attr:    #9E4C58;
    --syn-err:     #B3384A;
    --syn-ins:     #3F7A5E;
    --syn-del:     #A63D50;
  }
}

.chroma .c, .chroma .ch, .chroma .cm, .chroma .c1, .chroma .cs,
.chroma .cp, .chroma .cpf { color: var(--syn-comment); font-style: italic; }

.chroma .k, .chroma .kc, .chroma .kd, .chroma .kn, .chroma .kp,
.chroma .kr { color: var(--syn-keyword); }
.chroma .kt { color: var(--syn-type); }

.chroma .s, .chroma .sa, .chroma .sb, .chroma .sc, .chroma .dl, .chroma .sd,
.chroma .s2, .chroma .se, .chroma .sh, .chroma .si, .chroma .sx, .chroma .sr,
.chroma .s1, .chroma .ss { color: var(--syn-string); }

.chroma .m, .chroma .mb, .chroma .mf, .chroma .mh, .chroma .mi, .chroma .il,
.chroma .mo { color: var(--syn-number); }

.chroma .n, .chroma .nx, .chroma .py, .chroma .nl { color: var(--color-neutral-200); }
.chroma .na { color: var(--syn-attr); }
.chroma .nb, .chroma .bp { color: var(--syn-type); }
.chroma .nc, .chroma .nn { color: var(--syn-type); }
.chroma .nd, .chroma .ni { color: var(--syn-attr); }
.chroma .ne, .chroma .nf, .chroma .fm { color: var(--syn-name); }
.chroma .nt { color: var(--syn-keyword); }
.chroma .nv, .chroma .vc, .chroma .vg, .chroma .vi, .chroma .vm { color: var(--syn-name); }

.chroma .o, .chroma .ow { color: var(--syn-punct); }
.chroma .p { color: var(--syn-punct); }

.chroma .err { color: var(--syn-err); }
.chroma .gd { color: var(--syn-del); }
.chroma .gi { color: var(--syn-ins); }
.chroma .ge { font-style: italic; }
.chroma .gs { font-weight: 600; }
.chroma .gu { color: var(--syn-comment); }
.chroma .gh { color: var(--color-text); font-weight: 600; }
/* ==========================================================================
   Mobile — the header collapses to mark + search + mode + menu; the right
   rails move below the content they annotate.
   ========================================================================== */

/* Full-screen menu panel, hidden until the toggle opens it. */
.mobile-menu { display: none; }
.mobile-menu[data-open="true"] {
  display: flex; flex-direction: column; gap: var(--space-6);
  position: fixed; inset: 0; z-index: 40;
  background: var(--color-bg);
  overflow-y: auto;
}
.mobile-menu-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--color-border);
}
.mobile-menu-body {
  display: flex; flex-direction: column; gap: var(--space-6);
  padding: var(--space-12) var(--space-6) var(--space-8);
}
.mobile-nav {
  display: flex; flex-direction: column; gap: var(--space-6);
  font-family: var(--font-heading); font-size: 22px; font-weight: var(--font-heading-weight);
}
.mobile-nav a { color: var(--color-neutral-400); text-decoration: none; }
.mobile-nav a[aria-current="page"] { color: var(--color-text); }
.mobile-menu-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
}
.mobile-menu-foot .lang-switch { font-size: 13px; }
.mode-label {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: 12px; color: var(--color-neutral-400);
  background: transparent; border: 0; cursor: pointer; font-family: inherit;
}
.mode-label svg { color: var(--color-accent-300); }
.mobile-menu .hr { margin: 0; background: linear-gradient(90deg, var(--color-border), transparent); }

.social-row { display: flex; gap: var(--space-6); }
.social-row a { color: var(--color-neutral-300); }
.social-row a:hover { color: var(--color-accent-300); }

/* Home page social strip, mobile only. */
.home-social {
  display: none; gap: var(--space-6); justify-content: center;
  padding-top: var(--space-8); border-top: 1px solid var(--color-border);
}

@media (max-width: 1080px) {
  .with-rail { flex-direction: column; gap: var(--space-12); }
  .with-rail > .rail-side { flex-basis: auto; width: 100%; }
  .with-rail > .rail-side,
  .rail-side--wide, .rail-side--contact { flex-basis: auto; }
  .rail-main--prose, .rail-main--wide { max-width: none; }
  .with-rail > .rail-side { flex-direction: row; flex-wrap: wrap; gap: var(--space-12); }
  .with-rail > .rail-side > * { flex: 1 1 240px; }
}

@media (max-width: 900px) {
  .hero { flex-direction: column-reverse; gap: var(--space-8); }
  .hero-aside { flex-basis: auto; width: 100%; }
  .hero-portrait { width: 100%; max-width: 340px; height: auto; aspect-ratio: 300 / 340; }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 820px) {
  /* Desktop nav, search field and language pair give way to icon buttons. */
  .nav-main, .header-tools { display: none; }
  .header-mobile { display: flex; }
  .site-header { position: static; }

  /* The header stops sticking here, so a bar anchored to it would scroll out
     of sight the moment you started reading. Pin it to the viewport instead,
     and give it another pixel: at the very top of a phone screen a hairline
     disappears. Below the mobile menu (40) so an open menu still covers it. */
  .reading-progress {
    position: fixed; top: 0; bottom: auto; height: 4px; z-index: 35;
  }

  .page-body { gap: var(--space-12); padding-block: var(--space-12) var(--space-16); }
  .grid-2 { grid-template-columns: 1fr; gap: var(--space-12); }
  .grid-3 { grid-template-columns: 1fr; }

  h1 { font-size: 34px; }
  .page-head h1 { font-size: 32px; }
  .hero-text h1 { font-size: 32px; line-height: 1.08; }
  .hero-text .kicker { font-size: 10px; }
  .hero-text .rule-accent { width: 140px; }
  .post-header h1 { font-size: 32px; }
  .prose { font-size: 16px; }

  .home-social { display: flex; }

  .timeline-row { gap: var(--space-4); }
  .timeline-when { width: 76px; }
  .timeline-cv .timeline-when { width: 72px; }

  .footer-cols { gap: var(--space-8); }
  .footer-brand { max-width: none; flex-basis: 100%; }
  .footer-col { flex: 1 1 140px; }

  .search-panel { margin-top: 0; padding: var(--space-8) var(--space-6); max-height: 100vh; height: 100%; border: 0; border-radius: 0; }
  .dialog-backdrop { padding: 0; }

  /* No room beside the title for both the lede and the availability note.
     The note is repeated on the service's own page; the lede is not. */
  .drawer-meta { display: none; }
  .service-cta { flex-direction: column; align-items: stretch; }
}

@media (max-width: 520px) {
  .news-page .timeline-row { flex-direction: column; gap: var(--space-2); }
  .news-page .timeline-spine { flex-direction: row; width: auto; gap: var(--space-3); align-items: center; }
  .news-page .timeline-line { display: none; }
  .news-page .timeline-when {
    flex-direction: row; align-items: center; gap: var(--space-3); width: auto;
  }
  .news-page .timeline-row { padding-bottom: var(--space-8); border-bottom: 1px solid var(--color-border); }
  .news-page .timeline-row:last-child { border-bottom: 0; }

  .post-nav { flex-direction: column; align-items: flex-start; gap: var(--space-4); }
  .btn-row > .btn { flex: 1 1 auto; }

  .drawer-summary { gap: var(--space-3); padding: var(--space-4); }
  .drawer-body { padding: var(--space-4); }
  .service-cta { padding: var(--space-6); }

  .splash { padding: var(--space-4) var(--space-4) var(--space-6); gap: var(--space-4); }
  .splash-nav { gap: var(--space-2) var(--space-4); }
  .splash-nav a { font-size: 11px; }

  /* A card that nearly fills a phone leaves the photo only the margins, so
     every treatment pulls back: the masked ones widen their opening and the
     graded ones lean harder on the scrim. */
  /* Relative to the per-image value rather than a flat number, so an image
     that needed stronger strokes keeps them here too. */
  .splash-bg--photo .splash-mesh { opacity: calc(var(--photo-mesh, 0.46) * 0.8); }
  .splash-bg--horizon .splash-photo-wrap { top: 46%; }
  .splash-bg--horizon .splash-tone {
    background:
      linear-gradient(180deg, transparent 0 45.8%,
        color-mix(in srgb, var(--color-accent) 55%, transparent) 45.8% 46%,
        transparent 46%),
      linear-gradient(180deg, transparent 55%,
        color-mix(in srgb, var(--color-bg) 62%, transparent) 100%);
  }
  /* Wider and taller than the frame, as on the desktop mask: a gradient that
     tops out before the corners draws a visible arc across an even sky. */
  .splash-bg--aperture .splash-photo-wrap {
    -webkit-mask-image: radial-gradient(150% 72% at 50% 46%, transparent 2%,
      rgba(0, 0, 0, 0.4) 44%, #000 100%);
            mask-image: radial-gradient(150% 72% at 50% 46%, transparent 2%,
      rgba(0, 0, 0, 0.4) 44%, #000 100%);
  }
  .splash-bg--aperture .splash-tone {
    background: radial-gradient(150% 72% at 50% 46%,
      var(--splash-clear) 26%,
      color-mix(in srgb, var(--splash-ink) 60%, var(--splash-clear)) 100%);
  }
  .splash-bg--slats .splash-tone {
    background: repeating-linear-gradient(90deg,
      transparent 0 22px,
      color-mix(in srgb, var(--color-bg) 82%, transparent) 22px 30px);
  }

  .id-card { padding: var(--space-8) var(--space-6) var(--space-12); gap: var(--space-4); }
  .id-avatar { width: 104px; height: 104px; }
  .id-head h1 { font-size: 26px; }
}

/* ── print ────────────────────────────────────────────────────────────── */
@media print {
  .site-header, .site-footer, .mobile-menu, .dialog-backdrop, .post-nav, .anchor { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .prose { color: #000; }
  a { color: #000; }
  .prose a::after { content: " (" attr(href) ")"; font-size: 0.8em; color: #555; }
  /* drawer.js opens every drawer before printing; this just tidies them. */
  .drawer { border: 0; page-break-inside: avoid; }
  .drawer-body { animation: none; background: none; padding-inline: 0; }
  .drawer-summary { padding-inline: 0; }
  .drawer-marker, .drawer-icon { display: none; }
}
