/*
 * Optimi stylesheet. Ported from the Astro build by scripts/port/port.mjs.
 * Generated 2026-08-29 - edit src/styles/ in the Astro repo, then re-run the port.
 *
 * One file, plain CSS, no build step (CLAUDE.md section 13): tokens, then fonts, then site
 * styles, concatenated in their original order. The Tailwind import is dropped; font URLs are
 * rewritten from node_modules paths to /fonts/.
 */

/*
 * Reset. The Astro build got this from Tailwind's preflight; dropping the Tailwind import for
 * the PHP port removed the utilities AND the reset with them, which is how the site ended up
 * with body{margin:8px} and box-sizing:content-box. A .container at width:100% plus 20px of
 * padding then computed to 384px inside a 360px viewport, and the whole page could be swiped
 * sideways. Measured in headless Chrome: viewport 360, document 412.
 *
 * Deliberately minimal: only what the layout depends on.
 */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { margin: 0; }
h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
/* :where(), so this has zero specificity. Without it the selector is (0,1,1) - an attribute
   selector - and it silently beat every rule that gave a classed list a margin: .cw__choices
   and .diagram__legend both asked for 12px and got nothing, and the old KROON lists lost their
   spacing the same way. A reset that outranks the styles written against it is a trap that
   springs every time someone adds a list with a class on it, and it springs quietly. */
:where(ul[class], ol[class]) { list-style: none; margin: 0; padding: 0; }
img, picture, video, canvas, svg { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; }

/*
 * Optimi design tokens - from docs/02-brand-guide.md (Brand Designer, 2026-08-28).
 * ONE light theme, no dark mode: Signal Yellow + Bone (what the brand guide calls Theme B).
 * Theme A (Acid Lime + Graphite) was a launch-time A/B and is gone - the owner chose B on
 * 2026-08-28, so the scales below ARE the B scales and there is no [data-theme] switch left.
 * Semantic tokens (--bg, --fg, --accent ...) are what components use; the scales are
 * here for reference and for the few places that need a specific step.
 * Contrast (WCAG 2.x): see the brand guide §3 tables. Rules that matter in code:
 *   - --accent is never text on light backgrounds; use --accent-ink for accent-coloured text.
 *   - --mid is decorative only (fails text contrast).
 */

:root {
  /* Graphite scale (Theme B: Bone) */
  --graphite-50: #f2f0e9;
  --graphite-100: #e8e5dc;
  --graphite-200: #d9d6cc;
  --graphite-300: #c1beb4;
  --graphite-400: #8c8a82;
  --graphite-500: #6a6862;
  --graphite-600: #5e5c56;
  --graphite-700: #3d3c38;
  --graphite-800: #262626;
  --graphite-900: #171717;
  --graphite-950: #0d0d0d;

  /* Signal Yellow scale (--accent is step 400) */
  --accent-50: #fff8c7;
  --accent-100: #fff08f;
  --accent-200: #fce85a;
  --accent-300: #f9e02e;
  --accent-400: #f5d90a;
  --accent-500: #d9be00;
  --accent-600: #b09a00;
  --accent-700: #6e5f00;
  --accent-800: #544800;
  --accent-900: #3a3200;
  --accent-950: #1f1b00;

  /* Semantic */
  --bg: var(--graphite-50);
  --fg: var(--graphite-900);
  --muted: var(--graphite-600);
  --muted-large: var(--graphite-500);
  --mid: var(--graphite-400);
  --line: var(--graphite-200);
  --surface: var(--graphite-100);
  --surface-dark: var(--graphite-800);
  --disabled: var(--graphite-300);
  --accent: var(--accent-400);
  --accent-hover: var(--accent-300);
  --accent-pressed: var(--accent-500);
  --accent-fg: var(--graphite-900);
  --accent-ink: var(--accent-700);
  --accent-wash: var(--accent-50);

  --success: #1f7a3f;
  --warning: #8a5a00;
  --danger: #b42318;
  --success-bg: #ddf2e3;
  --warning-bg: #f8ecd0;
  --danger-bg: #f8dad6;
}


:root {
  /* Typography (brand guide §4). Self-hosted @font-face in fonts.css; the "* Fallback" faces are metric-matched local fonts (no reflow on swap). */
  --font-heading: 'Inter Tight', 'Inter Tight Fallback', 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-sans: 'Inter', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  /* Fluid type scale: min at 360px -> max at 1440px */
  --text-display: clamp(2.75rem, 1.5rem + 5.5vw, 5.5rem);
  --text-h1: clamp(2.25rem, 1.4rem + 3.8vw, 4rem);
  --text-h2: clamp(1.75rem, 1.2rem + 2.4vw, 2.75rem);
  --text-h3: clamp(1.25rem, 1.05rem + 0.9vw, 1.625rem);
  --text-lead: clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
  --text-body: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  --text-small: clamp(0.875rem, 0.85rem + 0.1vw, 0.9375rem);
  --text-mono: clamp(0.8125rem, 0.78rem + 0.15vw, 0.875rem);
  --text-mono-num: clamp(2rem, 1.3rem + 3vw, 3.5rem);

  /* Geometry (creative direction: straight geometry) */
  --radius-sm: 4px;
  --radius-md: 8px;
  --container: 75rem;
  /* Measured, not guessed: 68ch rendered 86-87 characters because ch is the width of "0",
     which is narrower than Inter's average glyph. 54ch lands on the 68 this was named for. */
  --measure: 54ch;

  /* Spacing */
  --space-section: clamp(3.5rem, 2rem + 5vw, 6rem);
  --space-hero-top: clamp(2.5rem, 1rem + 4vw, 4rem);
  --header-h: 60px;

  /* Motion (UX §6): <= 400ms, ease-out, once */
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --dur-control: 150ms;
  --dur-fade: 240ms;
  --dur-draw: 400ms;
}

@media (min-width: 640px) {
  :root { --header-h: 64px; }
}
@media (min-width: 1024px) {
  :root { --header-h: 72px; }
}

/*
 * Self-hosted fonts (a11y/perf audit, docs/70-a11y-perf-audit.md). Replaces the Google Fonts <link>:
 * no third-party render-blocking CSS, no IP transfer to Google (AVG), files served from /_astro with
 * content hashes (Vite rewrites the node_modules URLs at build time).
 * Weights per brand guide §4: Inter Tight 500/600, Inter 400/500, JetBrains Mono 400/500. font-display: swap.
 * Subsets: latin (always) + latin-ext for Inter / Inter Tight, gated by unicode-range so the latin-ext
 * file is only fetched when a glyph needs it (œ, ł, ő ... and the wordmark's dotless i in Inter Tight 600).
 * JetBrains Mono is latin only (numbers, labels); a stray latin-ext glyph falls back to the local mono.
 * Do NOT import @fontsource's per-subset css (latin-400.css ...): those files carry no unicode-range and
 * every face would download on every page (measured: 10 files, +150 KiB, LCP +1 s on throttled mobile).
 */

/* Ranges as used by Google Fonts / @fontsource */
/* latin:     U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD */
/* latin-ext: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF */

/* Inter Tight 500 / 600 (headings, wordmark) */
@font-face { font-family: 'Inter Tight'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/inter-tight-latin-500-normal.woff2') format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Inter Tight'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/inter-tight-latin-ext-500-normal.woff2') format('woff2'); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'Inter Tight'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/inter-tight-latin-600-normal.woff2') format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Inter Tight'; font-style: normal; font-weight: 600; font-display: swap; src: url('/fonts/inter-tight-latin-ext-600-normal.woff2') format('woff2'); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* Inter 400 / 500 (body, UI) */
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/inter-latin-400-normal.woff2') format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/inter-latin-ext-400-normal.woff2') format('woff2'); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/inter-latin-500-normal.woff2') format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/inter-latin-ext-500-normal.woff2') format('woff2'); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* JetBrains Mono 400 / 500 (labels, numbers) - latin only */
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/jetbrains-mono-latin-400-normal.woff2') format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/jetbrains-mono-latin-500-normal.woff2') format('woff2'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }

/*
 * Metric-matched fallbacks: while the webfont loads, text renders in a local font scaled to the same
 * advance widths and vertical metrics, so the swap does not reflow the page (measured CLS 0.067 -> 0 on home).
 * Values: Inter vs Arial from the Fontaine/next-font tables; Inter Tight is Inter with tighter tracking;
 * JetBrains Mono (advance 0.6em) vs Consolas (0.55em).
 */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 107.12%;
  ascent-override: 90.44%;
  descent-override: 22.52%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Inter Tight Fallback';
  src: local('Arial'), local('Helvetica'), local('Liberation Sans');
  size-adjust: 102.5%;
  ascent-override: 94.5%;
  descent-override: 23.5%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'JetBrains Mono Fallback';
  src: local('Consolas'), local('Menlo'), local('DejaVu Sans Mono'), local('Courier New');
  size-adjust: 109%;
  ascent-override: 94%;
  descent-override: 28%;
  line-gap-override: 0%;
}

@import './fonts.css';

/* Map tokens to Tailwind utilities (bg-bg, text-fg, bg-accent, font-mono ...). */
@theme inline {
  --color-fg: var(--fg);
  --color-bg: var(--bg);
  --color-surface: var(--surface);
  --color-surface-dark: var(--surface-dark);
  --color-muted: var(--muted);
  --color-line: var(--line);
  --color-accent: var(--accent);
  --color-accent-ink: var(--accent-ink);
  --color-accent-fg: var(--accent-fg);
  --font-sans: var(--font-sans);
  --font-heading: var(--font-heading);
  --font-mono: var(--font-mono);
}

/* ------------------------------------------------------------------ base */
@layer base {
  html {
    color-scheme: light only;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--header-h) + 24px);
    -webkit-text-size-adjust: 100%;
  }
  body {
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-sans);
    font-size: var(--text-body);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }
  h1, h2, h3, h4 {
    font-family: var(--font-heading);
    font-weight: 600;
    text-wrap: balance;
    overflow-wrap: anywhere;
  }
  h1 { font-size: var(--text-h1); line-height: 1.05; letter-spacing: -0.025em; max-width: 24ch; }
  h2 { font-size: var(--text-h2); line-height: 1.1; letter-spacing: -0.02em; }
  h3 { font-size: var(--text-h3); line-height: 1.2; letter-spacing: -0.01em; font-weight: 500; }
  p { text-wrap: pretty; }
  a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }
  a:hover { text-decoration-thickness: 2px; }
  button { font: inherit; color: inherit; }
  code, kbd, samp, pre { font-family: var(--font-mono); }
  strong { font-weight: 600; }
  :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px var(--fg);
    border-radius: var(--radius-sm);
  }
  /* On graphite bands the --fg halo would be graphite-on-graphite; use the page background instead (review m-5). */
  .section--dark :focus-visible, .cta-band :focus-visible, .site-footer :focus-visible { box-shadow: 0 0 0 4px var(--bg); }
  /* #main is the skip link's destination and carries tabindex="-1" purely so it can receive
     that focus. It is the whole page, so the ring above drew a 4px halo around everything -
     the one focus the visitor could not miss was also the one that told them nothing. */
  #main:focus-visible { box-shadow: none; outline: none; }
  ::selection { background: var(--accent); color: var(--accent-fg); }
  [hidden] { display: none !important; }
}

/* ------------------------------------------------------------ utilities */
/* Last-resort guard: no replaced element may exceed its column, whatever markup width
   attribute it carries. One oversized image makes the whole page swipe sideways on a phone,
   and it is never obvious which one did it. */
img, svg, video, canvas, iframe { max-width: 100%; }

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: 20px;
}
@media (min-width: 640px) { .container { padding-inline: 32px; } }
@media (min-width: 1024px) { .container { padding-inline: 48px; } }
.measure { max-width: var(--measure); }

.mono {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.4;
}
.mono-label {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.mono-label--found::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}
.mono-num {
  font-family: var(--font-mono);
  font-size: var(--text-mono-num);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.lead { font-size: var(--text-lead); line-height: 1.45; max-width: var(--measure); }
.small { font-size: var(--text-small); }
.muted { color: var(--muted); }
.display { font-size: var(--text-display); line-height: 1; letter-spacing: -0.03em; max-width: 20ch; }
.dot::before {
  content: '';
  display: inline-block;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent);
  margin-right: 10px;
  vertical-align: middle;
}

/* ------------------------------------------------------------- buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 44px;
  padding: 0 20px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--text-body);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-control) ease-out, border-color var(--dur-control) ease-out, color var(--dur-control) ease-out;
}
@media (min-width: 640px) { .btn { min-height: 48px; } }
.btn:hover { text-decoration: none; }
.btn--primary { background: var(--accent); color: var(--accent-fg); }
.btn--primary:hover { background: var(--accent-hover); }
.btn--primary:active { background: var(--accent-pressed); }
.btn--secondary { background: transparent; color: var(--fg); border-color: var(--fg); }
.btn--secondary:hover { background: var(--surface); }
.btn--secondary:active { background: var(--graphite-200); }
.section--dark .btn--secondary { color: var(--bg); border-color: var(--bg); }
.section--dark .btn--secondary:hover { background: var(--graphite-700); }
.btn--ghost { background: transparent; padding-inline: 0; min-height: 0; text-decoration: underline; text-underline-offset: 3px; }
.btn--ghost:hover { text-decoration-thickness: 2px; }
.btn--block { width: 100%; }
.btn[disabled], .btn[aria-disabled='true'] { background: var(--disabled); color: var(--muted); border-color: var(--disabled); pointer-events: none; }
.btn__spinner { width: 16px; height: 16px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin 700ms linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------ sections */
.section { padding-block: var(--space-section); }
.section--tint { background: var(--surface); }
.section--dark { background: var(--surface-dark); color: var(--bg); }
.section--dark .muted, .section--dark .mono-label { color: var(--graphite-300); }
.section--hero { padding-top: var(--space-hero-top); padding-bottom: calc(var(--space-section) * .6); }
/* Two full section paddings meet under the hero - the hero's own bottom and the next
   section's top - and at 1920 they stack to 192px of empty bone between the intake button and
   the first paragraph. Neither is wrong on its own; together they are twice the gap the page
   asked for. .section--flush sets its own zero and keeps it. */
.section--hero + .section:not(.section--flush) { padding-top: calc(var(--space-section) * .7); }
/* The hand-written heroes put the h1 straight in .container, so neither `.prose h1` nor
   `.block--hero h1` reaches it and the reset's margin:0 stands - the lead sits on the
   descenders. `.block--hero` is emitted nowhere, so that rule cannot be the one that fixes it. */
.section--hero h1 { margin-bottom: 0.5em; }
.section--flush { padding-top: 0; }
.section + .section--dark, .section--dark + .section { border-top: 0; }
.section__head { max-width: var(--measure); margin-bottom: 40px; }
.section__head--wide { max-width: none; }
@media (max-width: 639px) { .section__head { margin-bottom: 32px; } }
.section__head h2 { margin-bottom: 0; }
.section__head .lead { margin-top: 16px; }
.hr { border: 0; border-top: 1px solid var(--line); }

/* --------------------------------------------------------------- prose */
.prose { max-width: var(--measure); }
/* A prose block is one measure wide - 613px - and the container it sits in is 75rem. Left
   aligned inside it, every text-only page ended up as a ribbon down the left edge with 900px
   of empty bone beside it at 1920. Centre it, but only when it is the ONLY thing in its
   container: the home hero, the article grid and the contact form all put a second column
   next to the prose, and those columns are the alignment. The text itself stays left aligned;
   this moves the column, not the words. Heroes are excluded: their copy is measured for the
   scrim and belongs at the left edge, beside the scene, not floating in the middle of it. */
.section:not(.section--hero) > .container > .prose:only-child { margin-inline: auto; }
.prose > * + * { margin-top: 1.1em; }
.prose > h2 { margin-top: 2.2em; }
.prose > h2 + * { margin-top: 0.8em; }
.prose > h3 { margin-top: 1.8em; }
.prose h1 { margin-bottom: 0.5em; }
.prose h1 + p { font-size: var(--text-lead); line-height: 1.45; }
.prose ul { list-style: disc; padding-left: 1.25em; }
.prose ol { list-style: decimal; padding-left: 1.4em; }
.prose li + li { margin-top: 0.45em; }
.prose li > p + p { margin-top: 0.5em; }
.prose blockquote { border-left: 3px solid var(--fg); padding: 4px 0 4px 20px; color: var(--muted); }
.prose table { width: 100%; border-collapse: collapse; font-size: var(--text-small); }
.prose th, .prose td { text-align: left; vertical-align: top; padding: 12px 12px 12px 0; border-bottom: 1px solid var(--line); }
.prose th { font-weight: 500; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.prose .table-wrap { overflow-x: auto; }
.prose pre { overflow-x: auto; font-size: var(--text-small); padding: 20px 24px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); line-height: 1.6; }
.prose code { font-size: 0.92em; }
.prose p > code:only-child { font-family: var(--font-mono); }
.prose p.mono-row {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  border-top: 1px solid var(--line);
  padding-top: 12px;
}
.prose p.mono-row code { font-size: inherit; }
.prose .mono-row + p { margin-top: 0.8em; }
.prose p.link-row a { font-weight: 500; }
.prose a { text-decoration: underline; }
.prose hr { border: 0; border-top: 1px solid var(--line); margin-block: 2em; }
.prose mark { background: var(--accent-wash); color: inherit; padding: 0 0.15em; }
.prose img, .prose svg { max-width: 100%; height: auto; }
.prose .prose-kicker { font-family: var(--font-mono); }
.prose > p:first-child strong:first-child { font-weight: 600; }
.summary-box {
  border-left: 3px solid var(--fg);
  padding: 16px 20px;
  background: var(--surface);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
.summary-box .mono-label { margin-bottom: 8px; }

/* ---------------------------------------------------- content blocks */
.block__label { margin-bottom: 12px; }
.block .prose h2:first-child { margin-top: 0; }
.block--hero h1 { margin-bottom: 0.4em; }
.block--hero .prose > p:first-of-type { font-size: var(--text-lead); line-height: 1.45; }
.block--display h1 { font-size: var(--text-display); line-height: 1; letter-spacing: -0.03em; max-width: 20ch; }
.block--display .prose > h1 + p { font-size: var(--text-lead); font-weight: 500; }
.block--display .prose > h1 + p + p { font-size: var(--text-body); color: var(--muted); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 16px 24px; align-items: center; margin-top: 32px; }
/* .prose a is (0,1,1) and outranks .btn (0,1,0), so a button placed in prose - the hero CTA -
   came out underlined. */
.prose .btn { text-decoration: none; }
.hero-grid { display: grid; gap: 40px; align-items: start; }
@media (min-width: 1024px) { .hero-grid { grid-template-columns: 7fr 5fr; gap: 64px; } }
.hero-grid .prose { max-width: none; }
/* Home hero (review M-4): text in column 1, the hero diagram in column 2 at desktop; stacked below the text on smaller screens. */
.hero-grid--home { gap: 32px; }
.hero-grid--home > .hero-grid__aside { order: 1; }
.hero-grid--home > .hero-grid__aside .diagram { margin-block: 0; }
@media (min-width: 1024px) {
  .hero-grid--home { grid-template-columns: 7fr 5fr; grid-auto-rows: max-content; align-items: start; }
  .hero-grid--home > :not(.hero-grid__aside) { grid-column: 1; }
  .hero-grid--home > .hero-grid__aside { grid-column: 2; grid-row: 1 / span 4; order: 0; justify-self: end; width: 100%; max-width: 26rem; }
  /* The vertical variant reads better in a narrow column than the shrunken horizontal one. */
  .hero-grid--home > .hero-grid__aside .diagram svg.diagram__desktop { display: none; }
  .hero-grid--home > .hero-grid__aside .diagram svg.diagram__mobile { display: block; max-width: none; }
}

/* Two-column block (home trust) */
@media (min-width: 1024px) {
  .block--two-col .prose { max-width: none; display: grid; grid-template-columns: 1fr 1fr; gap: 24px 64px; }
  .block--two-col .prose > * { margin-top: 0; grid-column: 1; }
  .block--two-col .prose > h2 { max-width: 20ch; }
  .block--two-col .prose > ul { grid-column: 2; grid-row: 1 / span 3; list-style: none; padding: 0; }
  .block--two-col .prose > p:last-child { grid-column: 1; }
}
.block--two-col .prose > ul { list-style: none; padding: 0; }
.block--two-col .prose > ul li { padding: 14px 0 14px 24px; position: relative; border-top: 1px solid var(--line); margin: 0; }
.block--two-col .prose > ul li:last-child { border-bottom: 1px solid var(--line); }
.block--two-col .prose > ul li::before { content: ''; position: absolute; left: 0; top: 24px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.section--dark .block--two-col .prose > ul li::before { background: var(--accent); }

/* Tables inside blocks that list rows (home services / sectors) -> row list */
.block--rows .prose { max-width: none; }
.block--rows .prose table { font-size: var(--text-body); }
.block--rows .prose thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.block--rows .prose td { padding: 18px 16px 18px 0; border-top: 1px solid var(--line); border-bottom: 0; }
.block--rows .prose tr:last-child td { border-bottom: 1px solid var(--line); }
.block--rows-3 .prose td:first-child { font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); letter-spacing: 0.06em; white-space: nowrap; width: 3.5rem; }
.block--rows .prose td a { font-family: var(--font-heading); font-weight: 500; font-size: var(--text-h3); text-decoration: none; letter-spacing: -0.01em; }
.block--rows .prose td a:hover { text-decoration: underline; }
.block--rows .prose td:last-child { color: var(--muted); }
.block--rows .prose tr:hover td { background: var(--surface); }
@media (max-width: 639px) {
  .block--rows-3 .prose tr { display: grid; grid-template-columns: 3rem 1fr; }
  .block--rows-3 .prose td { border: 0; padding: 4px 0; }
  .block--rows-3 .prose td:first-child { grid-row: 1 / span 2; padding-top: 18px; }
  .block--rows-3 .prose td:nth-child(2) { padding-top: 16px; }
  .block--rows-3 .prose td:last-child { padding-bottom: 16px; border-bottom: 1px solid var(--line); }
  .block--rows-3 .prose tr:first-child td:nth-child(2) { border-top: 1px solid var(--line); }
  .block--rows-2 .prose tr { display: grid; }
  .block--rows-2 .prose td { border: 0; padding: 4px 0; }
  .block--rows-2 .prose td:first-child { padding-top: 16px; border-top: 1px solid var(--line); }
  .block--rows-2 .prose td:last-child { padding-bottom: 16px; }
}
.block--rows-5 .prose td a { font-size: var(--text-body); }

/* Bullet lists of links with an outcome (home cases block, objections) */
.block--list .prose > ul { list-style: none; padding: 0; max-width: none; }
.block--list .prose > ul li { border-top: 1px solid var(--line); padding: 16px 0 16px 24px; position: relative; margin: 0; }
.block--list .prose > ul li:last-child { border-bottom: 1px solid var(--line); }
.block--list .prose > ul li::before { content: ''; position: absolute; left: 0; top: 26px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.block--list .prose > ul li a { font-weight: 500; }
.block--list .prose > ul li strong { display: block; font-family: var(--font-heading); font-size: var(--text-h3); font-weight: 500; margin-bottom: 4px; letter-spacing: -0.01em; }
.block--list .prose { max-width: none; }

/* --------------------------------------------------------- output block */
.output-block {
  border: 1px solid var(--fg);
  border-radius: var(--radius-md);
  padding: 24px;
  max-width: 44rem;
}
@media (min-width: 640px) { .output-block { padding: 32px; } }
.output-block__label { color: var(--muted); margin-bottom: 8px; }
.output-block__rows { list-style: none; padding: 0; margin: 0; }
.output-row { display: grid; grid-template-columns: minmax(6rem, auto) minmax(0, 1fr); gap: 8px 24px; align-items: baseline; padding: 16px 0; border-top: 1px solid var(--line); }
.output-row:first-child { border-top: 0; padding-top: 4px; }
.output-row__value { font-family: var(--font-mono); font-size: var(--text-mono-num); font-weight: 500; line-height: 1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--accent-ink); }
.output-row__label { font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.output-row__note { grid-column: 1 / -1; font-size: var(--text-small); color: var(--muted); font-family: var(--font-sans); text-transform: none; letter-spacing: 0; }
.output-block--dark, .section--dark .output-block { border-color: var(--graphite-700); }
.output-block--dark .output-row__value, .section--dark .output-row__value { color: var(--accent); }
.output-block--dark .output-row, .section--dark .output-row { border-color: var(--graphite-700); }
.output-block--dark .output-row__label, .section--dark .output-row__label, .section--dark .output-block__label, .section--dark .output-row__note { color: var(--graphite-300); }
.output-block--facts { max-width: none; border-color: var(--line); padding: 20px 24px; }
.output-block--facts .output-row { padding: 12px 0; grid-template-columns: minmax(7rem, 35%) 1fr; }
/* The facts panel carries sentences ("60 medewerkers · 38 chauffeurs · 4 op administratie"),
   not figures. At --text-h3 in mono, inside a 350px column, that wrapped to four lines with
   "4" and "op" orphaned on their own. The dark results panel is where the big numerals belong;
   this one is a labelled list and reads as one at lead size. */
.output-block--facts .output-row__value { font-size: var(--text-lead); line-height: 1.25; text-wrap: pretty; color: var(--fg); letter-spacing: 0; order: 2; }
.output-block--facts .output-row__label { order: 1; }
@media (max-width: 479px) { .output-row { grid-template-columns: 1fr; } .output-block--facts .output-row { grid-template-columns: 1fr; } }
.output-block--inline { max-width: none; }

/* Stat callout (single number) */
.stat { display: flex; flex-direction: column; gap: 6px; }
.stat__value { font-family: var(--font-mono); font-size: var(--text-mono-num); font-weight: 500; line-height: 1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--accent-ink); }
.stat__value small { font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin-left: 8px; font-weight: 500; }
.stat__label { font-size: var(--text-small); color: var(--muted); }
.section--dark .stat__value { color: var(--accent); }

/* ---------------------------------------------------------------- cards */
.cards { display: grid; gap: 24px; }
@media (min-width: 640px) { .cards--2, .cards--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg);
  padding: 24px;
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-control) ease-out;
  min-height: 100%;
}
/* Media slot (UX §4.10). Full-bleed: cancels the card's 24px padding on three sides and
   inherits the top corner radius. aspect-ratio reserves the box before the bytes arrive,
   which is what keeps CLS at 0 on card grids.

   DESKTOP ONLY (owner decision, 2026-08-28). On a phone a card is already a full-width block
   and a 16:9 image on top pushes the next card off the screen, so the grid becomes a slideshow
   of pictures instead of a list of things to read. Hidden below 1024px.

   This is a bandwidth decision as much as a layout one: the images are loading="lazy", and a
   lazy image inside display:none is never fetched, so phones download none of them at all
   rather than downloading and hiding them. */
.card__media { display: none; margin: -24px -24px 0; border-bottom: 1px solid var(--line); border-radius: calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0 0; overflow: hidden; }
@media (min-width: 1024px) { .card__media { display: block; } }
.card__media .content-image, .card__media img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
.content-image img { max-width: 100%; height: auto; }
/* Screen-wide photo band (render_band). Full-bleed 6:1 (halved from 3:1 on 2026-09-03, owner
   call) with the same bone multiply as the blog cards. aspect-ratio reserves the box so the page
   does not jump when the bytes land. Below 640px the box goes 4:1 and object-fit crops the
   sides; the accent and the mark sit on the midline, so both crops keep them. */
.band { position: relative; margin: 0; width: 100%; background: var(--surface); overflow: hidden; }
.band img { display: block; width: 100%; height: auto; aspect-ratio: 6 / 1; object-fit: cover; }
@media (max-width: 639px) { .band img { aspect-ratio: 4 / 1; } }
.band::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: var(--graphite-50); mix-blend-mode: multiply; }
/* Blog thumbnails carry the brand in the picture itself (generated 2026-09-03: bone, graphite,
   one Signal Yellow object each), so the CSS only nudges. A bone multiply pulls the highlights
   toward the page background and keeps a future stock photo from landing cold or too white;
   there is deliberately no desaturation, or the one yellow object would go grey with it. */
.cards--blog .card__media { position: relative; }
.cards--blog .card__media::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: var(--graphite-50); mix-blend-mode: multiply; }
.card:hover { border-color: var(--fg); text-decoration: none; }
.card:focus-visible { border-color: var(--fg); }
.card__eyebrow { color: var(--muted); }
.card__title { font-size: var(--text-h3); font-weight: 500; }
.card__text { color: var(--muted); }
.card__meta { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--line); color: var(--muted); display: flex; flex-wrap: wrap; gap: 6px 16px; }
.card__stats { display: flex; flex-wrap: wrap; gap: 12px 24px; margin-top: auto; padding-top: 12px; border-top: 1px solid var(--line); }
.card__stat { display: flex; flex-direction: column; gap: 2px; }
.card__stat b { font-family: var(--font-mono); font-size: var(--text-h3); font-weight: 500; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.card__stat span { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.card--large { padding: 32px; }
.card--large .card__title { font-size: var(--text-h2); }
.card--static:hover { border-color: var(--line); }
.card--ph { border-style: dashed; border-color: var(--mid); }
.card--ph:hover { border-color: var(--mid); }

/* --------------------------------------------------------------- row list */
.row-list { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--line); }
.row-list__item { border-bottom: 1px solid var(--line); }
.row-list__link { display: grid; grid-template-columns: 3.5rem minmax(0, 1fr) auto; gap: 8px 16px; align-items: baseline; padding: 18px 0; text-decoration: none; color: inherit; }
.row-list__link:hover { background: var(--surface); text-decoration: none; }
.row-list__index { font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); letter-spacing: 0.06em; }
.row-list__title { font-family: var(--font-heading); font-size: var(--text-h3); font-weight: 500; letter-spacing: -0.01em; }
.row-list__text { color: var(--muted); grid-column: 2; }
.row-list__arrow { grid-column: 3; grid-row: 1; color: var(--muted); }
@media (max-width: 639px) { .row-list__link { grid-template-columns: 3rem 1fr; } .row-list__arrow { display: none; } }

/* ---------------------------------------------------------- found list */
.found-list { list-style: none; padding: 0; margin: 0; max-width: var(--measure); }
.found-list li { position: relative; padding: 16px 0 16px 24px; border-top: 1px solid var(--line); }
.found-list li:last-child { border-bottom: 1px solid var(--line); }
.found-list li::before { content: ''; position: absolute; left: 0; top: 26px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.found-list strong { display: block; font-weight: 600; }
.found-list .mono { display: block; margin-top: 6px; color: var(--muted); }

/* ------------------------------------------------------------ definition */
.dl { max-width: var(--measure); }
.dl__row { padding: 16px 0; border-top: 1px solid var(--line); display: grid; gap: 4px 24px; }
.dl__row:last-child { border-bottom: 1px solid var(--line); }
.dl__k { font-weight: 500; }
.dl__v { color: var(--muted); }
@media (min-width: 768px) { .dl--cols .dl__row { grid-template-columns: minmax(10rem, 30%) 1fr; } }

/* ----------------------------------------------------------------- steps */
.steps { list-style: none; padding: 0; margin: 0; counter-reset: step; display: grid; gap: 24px; position: relative; }
@media (min-width: 640px) { .steps--3, .steps--4, .steps--5, .steps--6 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .steps--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .steps--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } .steps--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } .steps--6 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.steps__item { counter-increment: step; position: relative; padding-left: 0; }
.steps__node {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 56px; padding: 12px 16px;
  border: 1.5px solid var(--fg); border-radius: var(--radius-md); background: var(--bg);
  font-weight: 500; font-family: var(--font-heading); letter-spacing: -0.01em;
}
.steps__node::before { content: counter(step, decimal-leading-zero); font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; color: var(--muted); order: 2; }
.steps__item--optimised .steps__node { background: var(--fg); color: var(--bg); box-shadow: inset 3px 0 0 0 var(--accent); border-color: var(--fg); }
.steps__item--optimised .steps__node::before { color: var(--graphite-300); }
.steps__text { margin-top: 12px; color: var(--muted); font-size: var(--text-small); }
.steps__text b { color: var(--fg); font-weight: 600; }
@media (min-width: 1024px) {
  .steps__item:not(:last-child)::after { content: ''; position: absolute; top: 28px; right: -24px; width: 24px; height: 1.5px; background: var(--fg); }
  .steps--6 .steps__item:nth-child(3)::after { display: none; }
}

/* ----------------------------------------------------------------- facts */
.facts { border: 1px solid var(--line); border-radius: var(--radius-md); padding: 8px 24px; }
.facts__row { display: grid; grid-template-columns: minmax(7rem, 38%) minmax(0, 1fr); gap: 16px; padding: 12px 0; border-top: 1px solid var(--line); align-items: baseline; }
.facts__row:first-child { border-top: 0; }
.facts__k { color: var(--muted); }
.facts__v { font-family: var(--font-mono); font-weight: 500; font-size: var(--text-small); letter-spacing: 0.02em; }

/* ------------------------------------------------------------------- faq */
.faq { max-width: var(--measure); border-top: 1px solid var(--line); }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__q {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 20px 0; cursor: pointer; font-weight: 500; list-style: none;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { background: var(--surface); margin-inline: -12px; padding-inline: 12px; }
.faq__q::after { content: '+'; font-family: var(--font-mono); color: var(--muted); flex: none; font-size: 1.25rem; line-height: 1.2; transition: transform var(--dur-control) ease-out; }
.faq__item[open] .faq__q::after { transform: rotate(45deg); }
.faq__a { padding: 0 0 20px; max-width: 60ch; color: var(--fg); }
.faq__a p + p { margin-top: 0.8em; }

/* -------------------------------------------------------------- CTA band */
.cta-band { padding-block: clamp(4rem, 3rem + 4vw, 6rem); background: var(--surface-dark); color: var(--bg); }
.cta-band--light { background: var(--bg); color: var(--fg); border-top: 1px solid var(--fg); border-bottom: 1px solid var(--fg); }
.cta-band .mono-label { color: var(--graphite-300); }
.cta-band--light .mono-label { color: var(--muted); }
.cta-band h2 { max-width: 18ch; }
.cta-band p { max-width: 46ch; margin-top: 16px; font-size: var(--text-lead); line-height: 1.45; }
.cta-band__actions { margin-top: 32px; display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.cta-band__secondary { color: inherit; }

/* ------------------------------------------------------------ partner band
   A yellow strip of client logos under the services, running edge to edge while the label
   above it stays inside the container.

   The loop is two identical copies of the list in one flex track, slid by exactly -50%. That
   is only seamless if half the track width is one whole copy INCLUDING the gap that follows
   it, so the gap between copies is the list's own padding-right rather than a gap on the
   track: a track gap would leave half of one gap unaccounted for and the strip would jump by
   that much on every wrap.

   The logos are already black in the files - the SVGs carry fill:#171717 and the webp/png
   were re-rendered black on transparent - so nothing here recolours them. Only their height
   is normalised, and the width follows. */
.partner-band {
  padding-block: calc(var(--space-section) * 0.55);
  background: var(--accent);
  color: var(--fg);
  overflow: hidden;
  --partner-logo-h: 34px;
  --partner-gap: clamp(2.5rem, 1rem + 5vw, 5.5rem);
}
.partner-band .mono-label { color: var(--fg); margin-bottom: 24px; }
/* The fade at both ends, so a logo leaves the band rather than being cut off by it. */
.partner-band__viewport {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
}
.partner-band__track {
  display: flex;
  width: max-content;
  animation: partner-band-scroll 60s linear infinite;
}
.partner-band__viewport:hover .partner-band__track,
.partner-band__viewport:focus-within .partner-band__track { animation-play-state: paused; }
.partner-band__list {
  display: flex;
  align-items: center;
  gap: var(--partner-gap);
  padding-right: var(--partner-gap);
}
.partner-band__item { flex: none; }
/* height, not max-height. The five SVG logos carry a viewBox and no width/height attribute, so
   they have a ratio but no intrinsic size: as a flex item under max-height alone every one of
   them computed to 0x0 and five of the fourteen logos were simply absent from the band. A set
   height gives them something to scale from, and width:auto keeps the ratio. */
.partner-band__logo {
  height: var(--partner-logo-h);
  width: auto;
  /* A wordmark with no ascenders or descenders - Volvo - fills the full height and comes out
     240px wide, half again as wide as anything else. The cap gives it back its proportion;
     object-fit keeps the ratio rather than squashing it into the shorter box. */
  max-width: 190px;
  object-fit: contain;
}
@keyframes partner-band-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
/* No motion: one copy of the list, wrapped into a centred row. The band still says what it is
   there to say, it just says it all at once. */
@media (prefers-reduced-motion: reduce) {
  .partner-band__track { animation: none; width: 100%; }
  .partner-band__list[aria-hidden="true"] { display: none; }
  .partner-band__list {
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 32px;
    padding-right: 0;
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: 20px;
  }
}

/* ---------------------------------------------------------------- header */
.site-header { position: sticky; top: 0; z-index: 50; background: var(--bg); border-bottom: 1px solid var(--line); }
.site-header__inner { display: flex; align-items: center; justify-content: space-between; gap: 24px; min-height: var(--header-h); }
.site-header__brand { display: inline-flex; align-items: center; gap: 10px; color: var(--fg); text-decoration: none; }
.site-header__brand:hover { text-decoration: none; }

/* Brand lockup.
   The crown sits beside the wordmark at EVERY size, phone included. On scroll the header gets
   .is-compact and the crown slides out to the LEFT while its box collapses, so the wordmark
   slides across into the space rather than the whole lockup jumping.
   Exit is translateX, not translateY: leaving sideways reads as making room, leaving upwards
   reads as the header eating itself.
   Only opacity and transform animate; the collapsing width carries the layout change, which is
   scroll-driven, so nothing shifts on load. [data-header-ready] arms the transitions one frame
   in, so a restored scroll position snaps instead of animating. */
.brand__mark { width: 30px; height: auto; opacity: 1; transform: translateX(0); flex: none; }
.brand__stack { display: flex; flex-direction: column; justify-content: center; }
.brand__wordmark { display: block; width: 96px; height: auto; }
/* display:none, NOT max-height:0. The tagline is white-space:nowrap, so a collapsed-but-
   displayed element still contributes ~200px of intrinsic width to the flex column - which
   made the brand 200px wide on a 360px phone and pushed the menu button off the screen.
   Height was hidden; width was not. It is out of layout below 768px and back in at the
   breakpoint, where the transition it needs is defined. */
.brand__tagline {
  display: none; overflow: hidden; max-height: 0; opacity: 0; transform: translateY(6px);
  font-family: var(--font-mono); font-size: 10px; line-height: 1.4; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}

/* Scrolled: the crown leaves stage left and gives its width back. */
.site-header.is-compact .brand__mark,
body.menu-open .site-header .brand__mark {
  width: 0; opacity: 0; transform: translateX(-140%); margin-right: -10px;
}

@media (min-width: 768px) {
  .site-header:not(.is-compact) .brand__mark { width: 46px; }
  .site-header:not(.is-compact) .brand__wordmark { width: 156px; }
  .brand__tagline { display: block; }
  .site-header:not(.is-compact) .brand__tagline { max-height: 1.6em; opacity: 1; transform: translateY(0); margin-top: 3px; }
  .site-header:not(.is-compact) .site-header__inner { padding-block: 14px; }
}

/* The box stops moving; only the lockup inside it moves.
   --header-h was 72px at 1024 while the tall lockup needed 94.5, so the bar measured 94.5px at
   rest and 73px once collapsed - and a sticky header is in flow, so every collapse reflowed
   the whole document and pulled the page up under whoever was reading it. Raised here to the
   height the tall lockup actually occupies: .site-header__inner already carries
   `min-height: var(--header-h)`, so that one number holds the box still at both ends, and
   everything else that measures against the header (scroll-padding, the mobile panel, the
   sticky TOC) now measures against its real height. It has to come after the :root values near
   the top of the file. Below 768 the tagline is out of layout and the header never moved. */
@media (min-width: 768px) { :root { --header-h: 94px; } }

/* The transitions below still animate width and max-height, and that is deliberate: the crown
   collapsing its own width is what lets the wordmark slide across into the space, and no
   transform reproduces that. It is now confined to the header's own row. */
.site-header[data-header-ready] .brand__mark,
.site-header[data-header-ready] .brand__wordmark,
.site-header[data-header-ready] .brand__tagline,
.site-header[data-header-ready] .site-header__inner {
  transition: width var(--dur-fade) var(--ease), max-height var(--dur-fade) var(--ease),
    margin-top var(--dur-fade) var(--ease), padding-block var(--dur-fade) var(--ease),
    margin-right var(--dur-fade) var(--ease),
    opacity var(--dur-fade) var(--ease), transform var(--dur-fade) var(--ease);
}

/* The menu panel is fixed at --header-h, so a tall header would sit over its first link. */
body.menu-open .site-header .brand__mark { width: 0; opacity: 0; transform: translateY(-140%); }
body.menu-open .site-header .brand__wordmark { width: 96px; }
body.menu-open .site-header .brand__tagline { max-height: 0; opacity: 0; margin-top: 0; }
body.menu-open .site-header .site-header__inner { padding-block: 0; }

@media (prefers-reduced-motion: reduce) {
  .site-header .brand__mark, .site-header .brand__wordmark,
  .site-header .brand__tagline, .site-header .site-header__inner { transition: none !important; }
}
.nav { display: none; }
/* The gap gives way before the words do. Six items, a full brand lockup and a CTA do not fit
   at 1024 with a fixed 28px between them, and what gives first is the longest label wrapping
   to two lines - which drags the whole header taller. */
.nav__list { display: flex; align-items: center; gap: clamp(14px, 1.7vw, 28px); list-style: none; margin: 0; padding: 0; }
.nav__link, .nav-dd__summary, .util__cta { white-space: nowrap; }
.nav__link { position: relative; display: inline-flex; align-items: center; min-height: 44px; font-weight: 500; text-decoration: none; color: var(--fg); }
.nav__link:hover { color: var(--muted); text-decoration: none; }
.nav__link.is-active::after, .nav-dd.is-active > .nav-dd__summary::after { content: ''; position: absolute; left: 50%; bottom: 2px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); transform: translateX(-50%); }
.nav-dd { position: relative; }
.nav-dd__summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-weight: 500; position: relative; }
.nav-dd__summary::-webkit-details-marker { display: none; }
.nav-dd__summary:hover { color: var(--muted); }
.nav-dd__chevron { width: 10px; height: 10px; transition: transform var(--dur-control) ease-out; }
.nav-dd[open] .nav-dd__chevron { transform: rotate(180deg); }
.nav-dd__panel {
  position: absolute; top: calc(100% + 8px); left: -16px; width: 320px; z-index: 60;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 8px 0;
  animation: fade var(--dur-control) ease-out;
}
.nav-dd__panel .mono-label { padding: 10px 16px 4px; margin: 0; }
.nav-dd__panel ul { list-style: none; margin: 0; padding: 0; }
.nav-dd__panel a { display: flex; align-items: center; min-height: 44px; padding: 0 16px; text-decoration: none; font-weight: 500; }
.nav-dd__panel a:hover { background: var(--surface); }
.nav-dd__panel a.is-active { box-shadow: inset 3px 0 0 var(--accent); }
.nav-dd__panel hr { margin: 8px 0; }
.util { display: flex; align-items: center; gap: 12px; }
.util__cta { display: none; }
.menu-toggle { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; border: 1px solid var(--fg); border-radius: var(--radius-sm); background: transparent; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; padding: 0 12px; cursor: pointer; }
@media (min-width: 640px) { .util__cta { display: inline-flex; } }
@media (min-width: 1024px) { .nav { display: block; } .menu-toggle { display: none; } }

.mobile-nav { position: fixed; inset: var(--header-h) 0 0 0; z-index: 49; background: var(--bg); overflow-y: auto; padding: 24px 0 40px; animation: fade var(--dur-control) ease-out; }
.mobile-nav ul { list-style: none; margin: 0; padding: 0; }
.mobile-nav a { display: block; padding: 12px 0; font-size: var(--text-lead); font-weight: 500; text-decoration: none; }
.mobile-nav a.is-active { box-shadow: inset 3px 0 0 var(--accent); padding-left: 12px; }
.mobile-nav ul ul a { font-size: var(--text-body); font-weight: 400; padding: 8px 0 8px 16px; }
.mobile-nav hr { margin: 16px 0; }
.mobile-nav .mono-label { margin: 16px 0 4px; }
.mobile-nav__lang { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.mobile-nav__lang a { display: inline; padding: 8px 0; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; }
body.menu-open { overflow: hidden; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

/* Language picker */
.lang { position: relative; }
.lang__summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 6px; font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; }
.lang__summary::-webkit-details-marker { display: none; }
/* The flag, as the portal's switch shows it: a 3:2 box, the SVG fills it. */
.lang__flag { display: inline-block; flex: none; width: 18px; height: 12px; border-radius: 2px; overflow: hidden; box-shadow: 0 0 0 1px var(--line); line-height: 0; }
.lang__flag svg { display: block; width: 100%; height: 100%; }
.lang__panel { position: absolute; right: 0; top: calc(100% + 4px); z-index: 60; min-width: 11rem; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 6px 0; animation: fade var(--dur-control) ease-out; }
.lang__panel ul { list-style: none; margin: 0; padding: 0; }
.lang__panel a { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 16px; text-decoration: none; }
.lang__panel a:hover { background: var(--surface); }
.lang__panel a[aria-current]::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.lang--footer ul { display: flex; flex-wrap: wrap; gap: 4px 16px; list-style: none; margin: 0; padding: 0; }
.lang--footer a { display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
.lang--footer a:hover { text-decoration: underline; }

/* Theme toggle (test-only) */

/* Skip link */
.skip-link { position: absolute; left: 12px; top: -100px; z-index: 100; padding: 10px 16px; background: var(--accent); color: var(--accent-fg); border-radius: var(--radius-sm); font-weight: 500; }
.skip-link:focus { top: 12px; }

/* ------------------------------------ Curtain navigation (three pillars) */
/* The bar. Three pillar labels are about 120px wider than the six items they replaced, and the
   body size is 18px, so inside the 1200px container the row only fits with the labels at 1rem
   (the size the approved mockup rendered them at), 8px between the items and 12px of padding
   on the button. Measured in a browser with the site's own font files, not guessed: from 1200
   up the full "Plan een gratis intake" then has 65px to spare; below that the button says
   "Gratis intake", and between 1024 and 1139 the tagline is the one thing that still does not
   fit, so it goes. The lockup itself stays tall at every width. */
.cta-short { display: none; }
@media (min-width: 1024px) {
  .site-header__inner { gap: 12px; }
  .nav__list { gap: 8px; }
  .nav__link, .nav-dd__summary { font-size: 1rem; }
  .util__cta { padding-inline: 12px; }
}
@media (min-width: 1024px) and (max-width: 1199.98px) {
  .util__cta .cta-long { display: none; }
  .util__cta .cta-short { display: inline; }
}
@media (min-width: 1024px) and (max-width: 1139.98px) {
  .brand__tagline { display: none; }
  .site-header__inner { padding-inline: 20px; gap: 10px; }
  .nav__list { gap: 6px; }
}

/* A pillar control is a link that behaves like the dropdown summaries: same chevron, same
   turn when it is open. The dot under it (.is-active, shared with the other items) marks the
   pillar the page belongs to, and moves to the open pillar while the curtain is down. */
.nav__pillar { gap: 6px; }
.nav__pillar[aria-expanded="true"] .nav-dd__chevron { transform: rotate(180deg); }

/* The bar goes dark with the curtain: graphite ground, bone text, the light wordmark in place
   of the dark one. The crown is the same file in both states. #a9a79f is the muted text on
   graphite-800 (6.3:1); --muted is 2.5:1 there and the footer already avoids it the same way. */
.site-header { transition: background-color var(--dur-control) ease-out, color var(--dur-control) ease-out, border-color var(--dur-control) ease-out; }
.site-header.is-curtain { background: var(--surface-dark); color: var(--bg); border-bottom-color: rgba(242, 240, 233, 0.15); }
.site-header.is-curtain .site-header__brand, .site-header.is-curtain .nav__link, .site-header.is-curtain .nav-dd__summary { color: var(--bg); }
.site-header.is-curtain .nav__link:hover, .site-header.is-curtain .nav-dd__summary:hover, .site-header.is-curtain .brand__tagline { color: #a9a79f; }
.site-header.is-curtain .util__cta { color: var(--bg); border-color: var(--bg); }
.site-header.is-curtain .util__cta:hover { background: rgba(242, 240, 233, 0.08); }
.site-header.is-curtain :focus-visible { box-shadow: 0 0 0 4px var(--bg); }
.brand__wordmark--light { display: none; }
.site-header.is-curtain .brand__wordmark--dark { display: none; }
.site-header.is-curtain .brand__wordmark--light { display: block; }

/* The curtain itself: the viewport under the bar, wiped open from the top edge. It is inside
   the sticky header, so it hangs off the bar wherever the page is scrolled to. The wipe is a
   clip-path, not a height, so nothing inside it reflows while it opens. */
.curtain {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 55;
  height: calc(100vh - var(--header-h)); display: none; flex-direction: column;
  background: var(--surface-dark); color: var(--bg); overflow: hidden;
  clip-path: inset(0 0 100% 0);
}
.curtain:not([hidden]) { display: flex; }
.curtain.is-in { clip-path: inset(0 0 -40px 0); }
.curtain:focus, .curtain:focus-visible { outline: none; box-shadow: none; }
@media (prefers-reduced-motion: no-preference) { .curtain { transition: clip-path var(--dur-draw) var(--ease); } }
/* Below 1024 the mobile panel is the only navigation; the curtain has no business there even if
   a wide window was narrowed with it open. */
@media (max-width: 1023.98px) { .curtain { display: none !important; } }
/* Two-ring focus on the dark ground: the ring in the text colour, the halo in the accent. */
.curtain :focus-visible { outline: 2px solid currentColor; outline-offset: 2px; box-shadow: 0 0 0 4px var(--accent); }

/* One pillar at a time. Head (the category row) and foot (the startpunten) hold still; the
   body between them scrolls only if a screen is too short for the tallest category. */
.curtain__pillar { flex: 1; min-height: 0; display: flex; flex-direction: column; animation: fade 120ms ease-out; }
.curtain__head { flex: none; border-bottom: 1px solid rgba(242, 240, 233, 0.15); }
.curtain__row { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-block: 6px; }
.curtain__tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 0 clamp(14px, 1.7vw, 28px); }
.curtain__tab {
  position: relative; display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0;
  font-weight: 500; font-size: var(--text-small); white-space: nowrap; color: #a9a79f; cursor: pointer;
  box-shadow: inset 0 0 0 0 var(--accent);
  transition: color var(--dur-control) ease-out, box-shadow var(--dur-control) ease-out;
}
.curtain__tab:hover { color: var(--bg); box-shadow: inset 0 -2px 0 0 var(--accent); }
.curtain__tab[aria-selected="true"] { color: var(--bg); }
.curtain__tab[aria-selected="true"]::after { content: ''; position: absolute; left: 50%; bottom: 2px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); transform: translateX(-50%); }
.curtain__count, .curtain__glabel, .curtain__label { font-family: var(--font-mono); font-size: 12px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #a9a79f; }
.curtain__close { flex: none; width: 44px; height: 44px; border: 1px solid rgba(242, 240, 233, 0.15); border-radius: var(--radius-sm); font-size: 22px; line-height: 1; cursor: pointer; }
.curtain__close:hover { background: rgba(242, 240, 233, 0.08); }
.curtain__body { flex: 1; min-height: 0; overflow-y: auto; }
/* --sec-h is the tallest category of the open pillar, measured once by js/site.js, so the foot
   stays where it is when the reader moves between categories. */
.curtain__panels { min-height: var(--sec-h, 0); }

/* A category: photo, title, blurb and hub link in a 240px column, its groups as equal columns
   beside it, four across and three under 1200. */
.curtain__cat { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 48px; padding: 24px 0 28px; animation: fade 120ms ease-out; }
.curtain__band { width: 100%; height: 110px; object-fit: cover; border-radius: var(--radius-sm); }
.curtain__title { margin: 12px 0 0; font-family: var(--font-heading); font-weight: 600; font-size: 22px; letter-spacing: -0.02em; line-height: 1.15; color: var(--bg); }
.curtain__side > .curtain__title:first-child { margin-top: 0; }
.curtain__blurb { margin: 8px 0 0; font-size: var(--text-small); color: #a9a79f; }
.curtain__hub { display: inline-flex; margin-top: 12px; font-weight: 500; font-size: var(--text-small); text-decoration: none; color: var(--bg); border-bottom: 1px solid var(--accent); padding-bottom: 1px; }
.curtain__hub:hover { text-decoration: none; border-bottom-width: 2px; }
.curtain__groups { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px; align-items: start; }
@media (max-width: 1199.98px) { .curtain__groups { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.curtain__group { min-width: 0; }
.curtain__glabel { margin: 0 0 4px; }
.curtain__item {
  display: flex; align-items: center; min-height: 40px; padding: 0 0 0 10px;
  border-bottom: 1px solid rgba(242, 240, 233, 0.15); box-shadow: inset 0 0 0 0 var(--accent);
  text-decoration: none; color: var(--bg); font-size: var(--text-small); font-weight: 500; line-height: 1.25;
  transition: box-shadow var(--dur-control) ease-out;
}
.curtain__item:hover { box-shadow: inset 2px 0 0 0 var(--accent); text-decoration: none; }

/* The foot: this pillar's startpunten as accent-outlined pills, then the whole catalogue. */
.curtain__foot { flex: none; border-top: 1px solid rgba(242, 240, 233, 0.15); }
.curtain__row--foot { justify-content: flex-end; flex-wrap: wrap; gap: 10px 14px; padding-block: 12px; }
.curtain__label { margin-right: 4px; }
.curtain__pill { display: inline-flex; align-items: center; min-height: 34px; padding: 0 14px; border: 1px solid var(--accent); border-radius: 999px; color: var(--accent); font-size: var(--text-small); font-weight: 500; text-decoration: none; transition: background-color var(--dur-control) ease-out, color var(--dur-control) ease-out; }
.curtain__pill:hover { background: var(--accent); color: var(--accent-fg); text-decoration: none; }
.curtain__all { margin-left: 12px; font-weight: 500; text-decoration: none; color: var(--bg); border-bottom: 1px solid rgba(242, 240, 233, 0.15); }
.curtain__all:hover { border-bottom-color: var(--accent); text-decoration: none; }

@media (prefers-reduced-motion: reduce) {
  .site-header, .curtain, .curtain__pillar, .curtain__cat, .curtain__tab, .curtain__item, .curtain__pill { transition: none !important; animation: none !important; }
}

/* The same tree in the mobile panel: a pillar folds open to its categories, a category to its
   groups and the hub link. Native <details>, so it works with scripts off. */
.mobile-nav__pillar > summary, .mobile-nav__cat > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 10px; }
.mobile-nav__pillar > summary::-webkit-details-marker, .mobile-nav__cat > summary::-webkit-details-marker { display: none; }
.mobile-nav__pillar > summary { padding: 12px 0; font-size: var(--text-lead); font-weight: 500; }
.mobile-nav__cat > summary { padding: 10px 0 10px 16px; font-size: var(--text-body); font-weight: 500; }
.mobile-nav__pillar > summary .nav-dd__chevron, .mobile-nav__cat > summary .nav-dd__chevron { margin-left: auto; flex: none; }
.mobile-nav details[open] > summary .nav-dd__chevron { transform: rotate(180deg); }
.mobile-nav__count { font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; color: var(--muted); }
.mobile-nav__cat-body { padding: 0 0 8px 16px; }
.mobile-nav__cat-body .mono-label { margin: 8px 0 0; }
.mobile-nav .mobile-nav__hub { font-size: var(--text-body); padding: 10px 0 4px 16px; }

/* ---------------------------------------------------------------- footer */
.site-footer { background: var(--surface-dark); color: var(--bg); margin-top: auto; }
.site-footer a { color: inherit; opacity: 0.85; text-decoration: none; }
/* a11y: --muted (#5f645f) on the dark band is 2.49:1; graphite-300 is 8.4:1 in both themes. */
.site-footer .muted { color: var(--graphite-300); }
/* a11y: --muted (#5f645f) on the dark band is 2.49:1; graphite-300 is 8.4:1 in both themes. */
.site-footer .muted { color: var(--graphite-300); }
.site-footer a:hover { opacity: 1; text-decoration: underline; }
.footer-top { padding-block: 56px 40px; display: grid; gap: 40px; }
.footer-top .wordmark { color: var(--bg); }
.footer-grid { display: grid; gap: 32px; }
@media (min-width: 640px) { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .footer-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.footer-col .mono-label { color: var(--graphite-300); }
.footer-col--contact { order: -1; }
@media (min-width: 1024px) { .footer-col--contact { order: 0; } }
.footer-trust { padding: 24px 0; border-top: 1px solid var(--graphite-700); display: grid; gap: 8px; }
@media (min-width: 768px) { .footer-trust { grid-template-columns: auto 1fr; gap: 24px; } }
.footer-trust .mono-label { margin: 0; color: var(--graphite-300); }
.footer-trust p { font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.04em; color: var(--graphite-300); }
.footer-bottom { padding: 20px 0 32px; border-top: 1px solid var(--graphite-700); display: flex; flex-direction: column; gap: 12px; font-size: var(--text-small); color: var(--graphite-300); }
@media (min-width: 1024px) { .footer-bottom { flex-direction: row; justify-content: space-between; align-items: center; } }
.footer-bottom ul { display: flex; flex-wrap: wrap; gap: 6px 16px; list-style: none; margin: 0; padding: 0; }

/* ------------------------------------------------------------ breadcrumb */
.breadcrumb { padding-top: 24px; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.04em; color: var(--muted); }
.breadcrumb ol { display: flex; flex-wrap: wrap; gap: 6px 10px; list-style: none; margin: 0; padding: 0; align-items: center; }
.breadcrumb a { text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; color: var(--fg); }
.breadcrumb [aria-current] { color: var(--fg); }
@media (max-width: 639px) { .breadcrumb li:not(.breadcrumb__parent) { display: none; } .breadcrumb__parent::before { content: '← '; } .breadcrumb__parent .sep { display: none; } }

/* ------------------------------------------------------------ pagination */
.pagination ul { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; list-style: none; margin: 40px 0 0; padding: 0; font-family: var(--font-mono); font-size: var(--text-mono); }
.pagination a, .pagination span { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; padding: 0 12px; border-radius: var(--radius-sm); text-decoration: none; }
.pagination [aria-current] { background: var(--fg); color: var(--bg); }
.pagination a:hover { background: var(--surface); }

/* --------------------------------------------------------------- diagram */
/* Nothing may exceed its column. A single overflowing child makes the whole page
   horizontally scrollable on a phone, and it is never obvious which child did it. */
.prose > *, .prose svg, .prose img, .prose pre, .prose table { max-width: 100%; }
.diagram { width: 100%; max-width: 60rem; }
.diagram svg { width: 100%; height: auto; display: block; color: var(--fg); }
.diagram svg.diagram__desktop { display: none; }
/* 22rem (352px) was wider than a 360px phone has after the container padding (320px), so
   the page could be swiped sideways. The SVG carries a viewBox, so it scales on its own;
   the cap only needs to stop it growing past its natural size on a wide phone. */
.diagram svg.diagram__mobile { display: block; width: 100%; max-width: min(22rem, 100%); }
@media (min-width: 640px) { .diagram svg.diagram__desktop { display: block; } .diagram svg.diagram__mobile { display: none; } }
.prose:has(> .diagram) { max-width: none; }
/* ...but only the diagram needs the width. Without this the paragraphs in the same chunk
   run to 134 characters at 1920 - the one place --measure was not doing its job. */
.prose:has(> .diagram) > :not(.diagram) { max-width: var(--measure); }
.prose > .diagram { margin-block: 2.5rem; }
.diagram figcaption { margin-top: 12px; }
.diagram__legend { display: flex; flex-wrap: wrap; gap: 8px 24px; margin-top: 12px; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.04em; color: var(--muted); list-style: none; padding: 0; }
.diagram__legend li { display: inline-flex; align-items: center; gap: 8px; }
.diagram__legend i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; }
.diagram__legend .lg-bottleneck { border: 2px solid var(--accent); }
.diagram__legend .lg-optimised { background: var(--fg); box-shadow: inset 2px 0 0 var(--accent); }
.diagram__legend .lg-human { border: 1.5px solid var(--fg); border-radius: 50%; }
.diagram--muted svg { color: var(--mid); }

/* ---------------------------------------------------------------- chips */
.chips { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0 0 32px; padding: 0; }
.chip { min-height: 44px; padding: 0 14px; display: inline-flex; align-items: center; border: 1px solid var(--fg); border-radius: 999px; background: transparent; font-size: var(--text-small); font-weight: 500; cursor: pointer; transition: background-color var(--dur-control) ease-out, color var(--dur-control) ease-out; }
.chip[aria-pressed='true'] { background: var(--fg); color: var(--bg); }
.chip:hover { background: var(--surface); }
.chip[aria-pressed='true']:hover { background: var(--fg); }

/* --------------------------------------------------------- case routes */
/* The cases index. Six cards would have said six companies; the page is one route walked six
   times - situatie, knelpunt, oplossing, resultaat - so the repetition is the argument: same
   shape, same order, different numbers. The four cells are the process-diagram nodes laid
   flat, joined by the same hairline-and-arrowhead the diagrams use, and the knelpunt cell
   carries the diagrams' 2px accent ring. A reader who has seen one case page already knows how
   to read this one. Markup: render_case_routes() in pages/page-parts.php. */

/* The thesis is the section's whole argument, so it is set as a sentence and given room
   rather than trimmed to a headline. */
.route-thesis { max-width: 26ch; }
@media (min-width: 900px) { .route-thesis { max-width: 34ch; } }

.filter { display: grid; gap: 4px; margin-bottom: 48px; }
.filter .chips { margin-bottom: 0; }
.filter__count { font-family: var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; font-size: var(--text-mono); }

.routes { display: grid; gap: var(--space-section); }
.route {
  display: block;
  color: inherit;
  text-decoration: none;
  border-top: 1px solid var(--fg);
  padding-top: 18px;
}
/* .route is display:block, which beats the UA rule for [hidden]. */
.route[hidden] { display: none; }
.route:hover { text-decoration: none; }

.route__head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px 24px; align-items: baseline; margin-bottom: 24px; }
.route__meta { font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 0; display: flex; flex-wrap: wrap; gap: 0 8px; }
.route__sep { color: var(--mid); }
.route__go { font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.route:hover .route__go { color: var(--fg); }
.route__title { grid-column: 1 / -1; font-family: var(--font-heading); font-weight: 600; font-size: var(--text-h3); line-height: 1.25; letter-spacing: -0.01em; margin: 0; max-width: 46ch; text-wrap: balance; }
.route:hover .route__title { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
@media (max-width: 639px) { .route__go { display: none; } .route__head { grid-template-columns: minmax(0, 1fr); } }

.route__steps { display: grid; gap: 26px; grid-template-columns: minmax(0, 1fr); }
.step {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg);
  padding: 16px 18px;
}
.step__label { font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 0 0 10px; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.step__text { font-size: var(--text-small); line-height: 1.5; margin: 0; text-wrap: pretty; }

/* The knelpunt cell takes the ring straight off the process diagrams: 2px of accent, and the
   word in accent ink. The border grows by 1px, so the padding gives that pixel back and the
   four cells keep one inner edge. The label doubles as the diagrams' dg-tag - the word is
   already there, so it is coloured rather than repeated beside itself. */
.step--bottleneck { border: 2px solid var(--accent); padding: 15px 17px; }
.step--bottleneck .step__label { color: var(--accent-ink); }

.step--result { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); display: flex; flex-direction: column; }
.step--result .step__label { color: var(--accent-800); }
.result__value { font-family: var(--font-heading); font-weight: 600; font-size: clamp(1.75rem, 1.05rem + 2vw, 2.5rem); line-height: 1.05; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; margin: 2px 0 8px; }
.result__label { font-size: var(--text-small); line-height: 1.4; margin: 0; color: var(--accent-900); }
.result__more { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid rgba(23, 23, 23, 0.22); font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent-800); }
/* The hint sits on the bottom edge, whatever the cell's neighbours make of it. */
.step--result .result__more { margin-top: auto; }

/* Desktop: four cells in a row, a hairline across each gap with an arrowhead at its far end -
   the diagrams' dg-conn, horizontal. */
@media (min-width: 900px) {
  .route__steps { grid-template-columns: repeat(3, minmax(0, 1fr)) 1.05fr; gap: 40px; }
  .step::after {
    content: ''; position: absolute; top: 50%; right: -40px;
    width: 40px; height: 1px; background: var(--mid);
  }
  .step::before {
    content: ''; position: absolute; top: 50%; right: -10px;
    width: 6px; height: 6px;
    border-top: 1px solid var(--mid); border-right: 1px solid var(--mid);
    transform: translateY(-50%) rotate(45deg);
  }
  .step:last-child::after, .step:last-child::before { content: none; }
}

/* Phone: the same route turned ninety degrees. One spine down the left edge, a tick into each
   cell, an arrowhead on the spine between them. */
@media (max-width: 899px) {
  .route__steps { position: relative; padding-left: 28px; gap: 28px; }
  .route__steps::before {
    content: ''; position: absolute; left: 9px; top: 24px; bottom: 24px;
    width: 1px; background: var(--mid);
  }
  .step::after {
    content: ''; position: absolute; left: -19px; top: 24px;
    width: 19px; height: 1px; background: var(--mid);
  }
  .step--bottleneck::after { left: -20px; width: 20px; }
  .step:not(:last-child)::before {
    content: ''; position: absolute; left: -22px; bottom: -20px;
    width: 6px; height: 6px;
    border-right: 1px solid var(--mid); border-bottom: 1px solid var(--mid);
    transform: rotate(45deg);
  }
}

/* The strip is one link, so the focus ring is drawn around the whole run rather than around
   whichever cell the caret happens to be in. */
.route:focus-visible { outline: 2px solid var(--fg); outline-offset: 8px; }

/* ---------------------------------------------------------- placeholders */
/* Placeholder link target (entity.ts still holds [BOOKING-LINK] etc.): visibly not a link, token in mono. */
.ph-link { color: var(--muted); border-bottom: 1px dashed currentColor; }
.ph-link code { font-size: var(--text-mono); letter-spacing: 0.04em; }
.ph-box { border: 1px dashed var(--mid); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; color: var(--muted); background: var(--surface); }
.ph-box--map { aspect-ratio: 16 / 9; max-width: 44rem; }
.ph-box--photo { aspect-ratio: 1; width: 100%; max-width: 10rem; }
.ph-logos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; list-style: none; margin: 12px 0 0; padding: 0; }
@media (min-width: 640px) { .ph-logos { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.ph-logos li { height: 40px; }
.ph-logos .ph-box { height: 100%; border-radius: var(--radius-sm); }
.testimonial-ph { border: 1px dashed var(--mid); border-radius: var(--radius-md); padding: 24px 28px; max-width: var(--measure); }
.testimonial-ph blockquote { font-size: var(--text-lead); color: var(--muted); border: 0; padding: 0; margin: 12px 0; }
.testimonial-ph footer { font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); letter-spacing: 0.04em; }

/* ----------------------------------------------------------------- team */
.team-card { display: grid; gap: 16px; border: 1px solid var(--line); border-radius: var(--radius-md); padding: 24px; }
.team-card__role { color: var(--muted); }
.team-card__bio { font-size: var(--text-small); }

/* ----------------------------------------------------------------- form */
.form { display: grid; gap: 20px; max-width: 40rem; }
.form__row { display: grid; gap: 20px; }
@media (min-width: 640px) { .form__row--2 { grid-template-columns: 1fr 1fr; } }
.field { display: grid; gap: 6px; }
.field__label { font-weight: 500; font-size: var(--text-small); }
.field__input {
  width: 100%; min-height: 48px; padding: 0 14px; font: inherit; color: inherit;
  background: var(--bg); border: 1px solid var(--fg); border-radius: var(--radius-sm);
  transition: border-color var(--dur-control) ease-out, box-shadow var(--dur-control) ease-out;
}
textarea.field__input { min-height: 8rem; padding-block: 12px; resize: vertical; }
select.field__input { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--fg) 50%), linear-gradient(135deg, var(--fg) 50%, transparent 50%); background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%; background-size: 6px 6px; background-repeat: no-repeat; padding-right: 40px; }
.field__input[aria-invalid='true'] { border-color: var(--danger); box-shadow: inset 0 0 0 1px var(--danger); }
.field__hint { font-size: var(--text-small); color: var(--muted); }
.field__error { display: flex; gap: 8px; align-items: flex-start; font-size: var(--text-small); color: var(--danger); }
.field__error::before { content: '!'; flex: none; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid currentColor; font-family: var(--font-mono); font-size: 11px; display: inline-flex; align-items: center; justify-content: center; margin-top: 2px; }
.segmented { display: inline-grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--fg); border-radius: var(--radius-sm); overflow: hidden; }
.segmented label { position: relative; display: flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 16px; cursor: pointer; font-weight: 500; text-align: center; }
.segmented label + label { border-left: 1px solid var(--fg); }
.segmented input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.segmented label:has(input:checked) { background: var(--fg); color: var(--bg); }
.segmented label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
.form__legend { font-size: var(--text-small); color: var(--muted); }
/* The form sat directly on the page ground with nothing marking where it began or ended. A
   surface gives it edges, which is most of what "this is the thing to fill in" needs. */
.form--card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 28px; }
@media (min-width: 640px) { .form--card { padding: 36px 40px; } }
.form--card .field__input { background: var(--bg); }
/* Submit and its reassurance travel together: the line belongs at the moment of deciding, not
   a screen above it. */
.form__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; }
.form__reassurance { margin: 0; flex: 1 1 22ch; }
.field__check { display: flex; gap: 12px; align-items: flex-start; cursor: pointer; font-size: var(--text-small); }
.field__check input { flex: none; width: 20px; height: 20px; margin-top: 2px; accent-color: var(--fg); cursor: pointer; }
.field__check input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.field__check input[aria-invalid='true'] { outline: 2px solid var(--danger); outline-offset: 2px; }
.form__status { font-weight: 500; }
.form__status--error { color: var(--danger); }
.form-success { border: 1px solid var(--fg); border-radius: var(--radius-md); padding: 32px; max-width: 40rem; }
.form-success h2 { margin-top: 8px; }
.form-success p { margin-top: 12px; max-width: 46ch; }
.honeypot { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }

/* ------------------------------------------------------------ article */
.article-grid { display: grid; gap: 40px; }
/* Grid items default to min-width:auto, so .prose could not shrink below the widest thing in
   it and a wide table pushed its min-content out to the track - which defeated
   `.prose .table-wrap { overflow-x: auto }` and gave six blog posts up to 155px of sideways
   scroll at 390. The table scrolls inside its wrapper again with this. */
.article-grid > * { min-width: 0; }
@media (min-width: 1024px) { .article-grid { grid-template-columns: 3fr 7fr 2fr; gap: 48px; } .article-grid > .prose { grid-column: 2; } .article-grid > .toc { grid-column: 1; grid-row: 1; } }
.toc { font-size: var(--text-small); }
/* Two labels, one shown at a time: the <summary> opens the list below 1024, the mono label
   titles the sticky rail above it. */
.toc > .mono-label { margin: 0; }
@media (max-width: 1023px) { .toc > .mono-label { display: none; } }
@media (min-width: 1024px) { .toc { position: sticky; top: calc(var(--header-h) + 24px); align-self: start; } .toc summary { display: none; } .toc[open] > *, .toc > * { display: block; } }
/* `.toc[open] > *` above is (0,2,0) and outranks the `.toc summary` rule beside it, putting the
   summary back on screen at desktop so the label reads twice. Matching [open] and naming the
   element makes this (0,2,1), which is the only way to win; `.toc > summary` alone is (0,1,1)
   and loses. The second selector covers the collapsed state, where `.toc > *` is (0,1,0). */
@media (min-width: 1024px) { .toc[open] > summary, .toc > summary { display: none; } }
.toc summary { list-style: none; cursor: pointer; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); padding: 12px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.toc summary::-webkit-details-marker { display: none; }
.toc summary::after { content: ' +'; }
.toc[open] summary::after { content: ' −'; }
.toc ol { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 4px; }
.toc li.depth-3 { padding-left: 12px; }
.toc a { display: block; padding: 4px 0 4px 12px; text-decoration: none; border-left: 3px solid transparent; color: var(--muted); }
.toc a:hover { color: var(--fg); }
.toc a.is-current { border-left-color: var(--fg); color: var(--fg); }
.article-meta { display: flex; flex-wrap: wrap; gap: 6px 16px; }

/* ---------------------------------------------------------------- aanpak */
/* /aanpak/ and nothing else. The page argues one thing twice: a board of five step columns
   crossed by three lanes, and under it the same five steps as stations on a spine. Both are
   built from the same entries in content/nl/pages/aanpak.php, and neither introduces a colour
   or a font - the nodes are the process diagrams' nodes, the rail is the .toc above, and the
   Jij/Wij ledger is .facts with its value column put back into running text. */

/* ------------------------------------------------------------- the board */
.swim { margin-top: 8px; }
.swim__caption { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
/* The board is wider than a phone and narrower than the container, so it scrolls inside its own
   box rather than making the page scroll sideways.

   position:relative is load-bearing, not decoration. The visually-hidden caption and the
   CONTROLE screen-reader text are position:absolute; without a positioned ancestor their
   containing block is the page itself, so they sit at their static position out past the right
   edge and the scroll container never clips them - which makes the whole page scroll sideways
   on a phone even though the board is already scrolling on its own. */
.swim__scroll { position: relative; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; }
.swim__scroll:focus-visible { outline: 2px solid var(--fg); outline-offset: 4px; }
.swim__hint { display: none; font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 0 0 14px; }
@media (max-width: 1023px) { .swim__hint { display: block; } }
/* border-spacing 0 with padding on the cells: a collapsed table and a sticky column disagree
   about where the border lives, and the gaps between nodes are the thing that makes three lanes
   read as three lanes. */
.swim__table { width: 100%; min-width: 54rem; table-layout: fixed; border-collapse: separate; border-spacing: 0; }
/* 54rem is the widest the board can be and still fit the container at desktop. Below that it
   scrolls anyway, so it can afford to be wider - and it has to be, or the columns get narrower
   than a Dutch compound noun. */
@media (max-width: 1023px) { .swim__table { min-width: 62rem; } }
.swim__labcol { width: 12%; }
/* Sticky row labels: on a phone the lane you are reading has to stay named while the five steps
   slide past under it. The left edge is drawn once, so the sticky column has a visible boundary
   while it occludes. */
.swim-corner, .swim-lane {
  position: sticky; left: 0; z-index: 2;
  background: var(--bg); text-align: left; vertical-align: top;
  padding: 0 20px 14px 0; min-width: 7.5rem;
  border-right: 1px solid var(--line);
}
.swim-corner { z-index: 3; }
.swim-corner span, .swim-lane__k {
  display: block; font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
}
.swim-lane__k { color: var(--fg); }
.swim-lane__v { display: block; margin-top: 4px; font-size: var(--text-small); line-height: 1.35; color: var(--muted); }
.swim-lane--stop .swim-lane__k { color: var(--accent-ink); }

.swim-step { text-align: left; vertical-align: bottom; padding: 12px 12px 14px 12px; border-top: 1.5px solid var(--fg); }
.swim-step__n { display: block; font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.08em; color: var(--muted); font-variant-numeric: tabular-nums; }
.swim-step__name { display: block; margin-top: 6px; font-family: var(--font-heading); font-weight: 600; font-size: var(--text-h3); line-height: 1.15; letter-spacing: -0.02em; color: var(--fg); }
.swim-step__dur {
  display: inline-block; margin-top: 10px; padding: 3px 9px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface);
  font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500;
  letter-spacing: 0.04em; color: var(--muted); white-space: nowrap;
}

/* height:1px lets the node stretch to the tallest cell in its lane, so the three lanes keep flat
   top and bottom edges across all five columns. */
.swim-cell { vertical-align: top; padding: 0 12px 14px 12px; height: 1px; }
.swim-node {
  height: 100%; box-sizing: border-box;
  display: flex; flex-direction: column;
  padding: 13px 14px;
  border-radius: var(--radius-md);
  font-size: var(--text-small); line-height: 1.45;
}
/* Dutch compounds ("leverancierswijzigingen") are wider than a 130px column, and a word that
   leaves its node is the one bug a board like this cannot have. */
.swim-node__t { margin: 0; text-wrap: pretty; overflow-wrap: break-word; }
/* Jij: the diagrams' plain node - your process, drawn as it is. */
.swim-node--jij { border: 1.5px solid var(--fg); background: var(--bg); color: var(--fg); }
/* Wij: the diagrams' optimised node, accent bar and all. */
.swim-node--wij { border: 1.5px solid var(--fg); background: var(--fg); color: var(--bg); box-shadow: inset 3px 0 0 0 var(--accent); }
/* Wat je hebt: the one lane that is neither of us, so it gets the one colour. */
.swim-node--stop {
  border: 1px solid var(--accent); background: var(--accent-wash); color: var(--fg);
  font-family: var(--font-heading); font-weight: 600; font-size: var(--text-body);
  line-height: 1.3; letter-spacing: -0.01em;
}
/* The CONTROLE marker sits on the bottom edge of the node, where the diagrams hang it off the
   optimised path. */
.swim-ctrl {
  margin: 12px 0 0; padding-top: 10px; margin-top: auto;
  border-top: 1px solid var(--graphite-700);
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500;
  letter-spacing: 0.06em; color: var(--graphite-300);
}
.swim-ctrl svg { flex: none; display: block; }
.swim-ctrl__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
/* Two more swatches for the diagrams' legend: the plain node and the deliverable. */
.swim__legend { margin-top: 20px; }
.diagram__legend .lg-jij { border: 1.5px solid var(--fg); }
.diagram__legend .lg-stop { border: 1px solid var(--accent); background: var(--accent-wash); }

/* ---------------------------------------------------------- the timeline */
.journey { display: grid; gap: 48px; }
@media (min-width: 1024px) { .journey { grid-template-columns: minmax(0, 1fr) 13rem; gap: 72px; align-items: start; } }
/* One spine, two measurements: the node's diameter and the gutter it sits in. Everything else
   is derived from those two, so the phone and the desktop are the same drawing at two sizes
   rather than two drawings. */
.journey__spine { --node: 44px; --gutter: 62px; position: relative; display: grid; gap: 30px; }
@media (min-width: 640px) { .journey__spine { --node: 56px; --gutter: 80px; gap: 36px; } }
.journey__spine::before { content: ''; position: absolute; left: calc(var(--node) / 2 - 1px); top: 0; bottom: 0; width: 2px; background: var(--line); }
/* An end-cap, so the rule stops on a mark instead of just running out. */
.journey__end {
  position: absolute; left: calc(var(--node) / 2); bottom: 0;
  width: 9px; height: 9px; transform: translate(-50%, 50%) rotate(45deg);
  background: var(--bg); border: 2px solid var(--line);
}

.station { position: relative; padding-left: var(--gutter); }
.station__node {
  position: absolute; left: 0; top: 0;
  width: var(--node); height: var(--node); margin: 0;
  border: 2px solid var(--line); border-radius: 50%; background: var(--bg);
  display: grid; place-items: center;
  font-family: var(--font-mono); font-weight: 500; font-size: var(--text-mono);
  letter-spacing: 0.04em; color: var(--muted); font-variant-numeric: tabular-nums;
  transition: background-color var(--dur-control) var(--ease), border-color var(--dur-control) var(--ease), color var(--dur-control) var(--ease);
}
/* Filled as you arrive. --accent is never text on light, so the numeral inside switches to
   --accent-fg (graphite) rather than staying muted. */
.station.is-current .station__node { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.station__body { max-width: var(--measure); }
/* The duration badge is set against the node, so the first line of a station reads
   "01 | 30 MIN" straight across the spine. */
.station__dur { display: flex; align-items: center; min-height: var(--node); margin: 0; }
.station__dur-txt {
  display: inline-flex; align-items: center;
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px; background: var(--bg);
  font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.station.is-current .station__dur-txt { border-color: var(--fg); color: var(--fg); }
/* An h2 at --text-h2 would out-shout the numerals it is meant to sit beside; the station title
   takes the h3 step and keeps the h2's weight. */
.station__title { font-size: var(--text-h3); font-weight: 600; letter-spacing: -0.015em; margin: 6px 0 0; }
.station__p { margin-top: 14px; }
.station__more { margin-top: 16px; font-weight: 500; }
.station__more a { text-decoration: underline; text-underline-offset: 3px; }

/* .facts, with the value column returned to running text: these are sentences, not
   measurements, and mono at --text-small would set them as data. */
.facts--ledger { margin-top: 22px; padding: 4px 20px; background: var(--bg); }
.facts--ledger .facts__row { grid-template-columns: minmax(3rem, 22%) minmax(0, 1fr); gap: 20px; }
.facts--ledger .facts__k { font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; }
.facts--ledger .facts__v { font-family: var(--font-sans); font-weight: 400; font-size: var(--text-small); line-height: 1.5; letter-spacing: 0; margin: 0; }

/* The way out, between every pair of stations. */
.gate { position: relative; padding-left: calc(var(--node) / 2 + 26px); margin: 0; display: flex; align-items: center; min-height: 24px; }
.gate__glyph {
  position: absolute; left: calc(var(--node) / 2); top: 50%; transform: translate(-50%, -50%);
  width: 24px; height: 24px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg); color: var(--mid);
}
.gate__text { font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: var(--mid); }

/* Phone and tablet get no sticky rail: the spine already says where you are, and a fixed column
   at that width costs more than it returns. */
.journey__nav { display: none; }
@media (min-width: 1024px) {
  .journey__nav { display: block; }
  .journey__nav a { display: flex; gap: 12px; padding: 7px 0 7px 14px; }
  .journey__nav a.is-current { border-left-color: var(--accent); }
  .jn__n { font-family: var(--font-mono); font-size: var(--text-mono); font-variant-numeric: tabular-nums; color: var(--mid); }
  .journey__nav a.is-current .jn__n { color: var(--accent-ink); }
}

/* ------------------------------------------------------ principles, roles */
/* Four tiles, hairline-separated rather than four bordered cards: they are one list, and four
   separate boxes would say four separate things. */
.tenets { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-md); overflow: hidden; list-style: none; margin: 0; padding: 0; }
@media (min-width: 768px) { .tenets { grid-template-columns: 1fr 1fr; } }
.tenet { background: var(--bg); padding: 26px 24px 28px; }
@media (min-width: 768px) { .tenet { padding: 32px; } }
/* The fourth principle is the one the gates on the spine draw, so it carries the same accent
   mark the site puts on an optimised step. */
.tenet--stop { box-shadow: inset 3px 0 0 var(--accent); }
.tenet__n { font-family: var(--font-mono); font-size: var(--text-mono); font-weight: 500; letter-spacing: 0.06em; color: var(--muted); margin: 0 0 14px; }
.tenet__head { font-family: var(--font-heading); font-weight: 600; font-size: var(--text-h3); line-height: 1.2; letter-spacing: -0.01em; margin: 0; }
.tenet__body { margin: 10px 0 0; color: var(--muted); font-size: var(--text-small); line-height: 1.55; }

/* The five people, as chips under the paragraph that names them. .chip is a filter button
   everywhere else on the site; here it is a link, so it gives back the link colour. */
.chips--roles { margin: 24px 0 0; }
.chip--role { text-decoration: none; color: inherit; border-color: var(--line); }
.chip--role:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); text-decoration: none; }

/* ---------------------------------------------------------------- motion */
/* Only when JS is present (html.js) and motion is allowed; otherwise everything is in its end state. */
@media (prefers-reduced-motion: no-preference) {
  html.js [data-motion] { opacity: 0; transform: translateY(12px); transition: opacity var(--dur-fade) var(--ease), transform var(--dur-fade) var(--ease); }
  html.js [data-motion].is-visible { opacity: 1; transform: none; }
  html.js [data-motion='stagger'] > * { opacity: 0; transform: translateY(12px); transition: opacity var(--dur-fade) var(--ease), transform var(--dur-fade) var(--ease); }
  html.js [data-motion='stagger'] { opacity: 1; transform: none; }
  html.js [data-motion='stagger'].is-visible > * { opacity: 1; transform: none; }
  html.js [data-motion='stagger'].is-visible > :nth-child(2) { transition-delay: 60ms; }
  html.js [data-motion='stagger'].is-visible > :nth-child(3) { transition-delay: 120ms; }
  html.js [data-motion='stagger'].is-visible > :nth-child(n + 4) { transition-delay: 180ms; }
  html.js [data-motion='marker'] { opacity: 1; transform: none; }
  html.js [data-motion='marker'] li::before { transform: scale(0.6); opacity: 0; transition: transform var(--dur-control) var(--ease), opacity var(--dur-control) var(--ease); }
  html.js [data-motion='marker'].is-visible li::before { transform: scale(1); opacity: 1; }
  /* Diagram draw-in (M1): connectors, then bottleneck ring, then optimised path. */
  html.js [data-motion='diagram'] { opacity: 1; transform: none; }
  html.js [data-motion='diagram'] .dg-conn { stroke-dasharray: 1; stroke-dashoffset: 1; }
  html.js [data-motion='diagram'] .dg-ring, html.js [data-motion='diagram'] .dg-tag, html.js [data-motion='diagram'] .dg-dot { opacity: 0; }
  html.js [data-motion='diagram'] .dg-opt { stroke-dasharray: 1; stroke-dashoffset: 1; }
  html.js [data-motion='diagram'].is-visible .dg-conn { animation: draw var(--dur-draw) var(--ease) forwards; }
  html.js [data-motion='diagram'].is-visible .dg-ring, html.js [data-motion='diagram'].is-visible .dg-tag, html.js [data-motion='diagram'].is-visible .dg-dot { animation: appear 200ms var(--ease) 400ms forwards; }
  html.js [data-motion='diagram'].is-visible .dg-opt { animation: draw 300ms var(--ease) 600ms forwards; }
}
@keyframes draw { to { stroke-dashoffset: 0; } }
@keyframes appear { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  .btn__spinner { animation: none; border-right-color: currentColor; opacity: 0.5; }
}

/* Footer wordmark: the lockup with its navy letters redrawn in bone, because navy vanishes on
   the graphite band. Same box as before, so nothing around it moves. */
.site-footer__brand { display: inline-block; }
.site-footer__brand .wordmark { display: block; width: 150px; height: auto; }


/* ---------------------------------------------------------------- hero scene
   js/hero3d.js paints a slow scene behind the hero copy. Everything here is inert until that
   script marks the element [data-hero3d-on], so a page with no WebGL, reduced motion or a
   metered connection is exactly what it was before.

   Everything below is drawn from measured numbers, not from guessed percentages. js/site.js
   stamps two more custom properties on the hero section beside --hero-band: --cb, the bottom
   edge of the copy, and --cr, the right edge of its LONGEST LINE, taken from the line boxes
   rather than the block box - the h1 is 24ch wide whatever the words are, so its box says
   nothing about where the text ends. The stops this replaced (46/64/82 across, 26/48/70 down)
   were one guess applied to thirty-six pages of unequal length: on a short heading the bone ran
   a third of the band past the last word, on a long one it let go mid-sentence.

   Three things follow. Desktop: the scrim holds bone 32px past the end of the longest line and
   releases over the 160px after that. Phone: the copy runs the full width, so there is no column to
   hold it off with - the canvas is confined to the strip below the copy instead, and the two
   never overlap at all. Everywhere: the last 22% of the band masks the canvas away, so the
   scene dissolves into the section's own bone rather than being cut off at the seam with the
   next section. The mask sits on the canvas and the scroll-away transform on the wrapper, so
   the fade travels with the picture instead of being smeared by it.

   The entry fade lives on the canvas and the scroll fade on the wrapper, deliberately: one is
   a 600ms transition, the other tracks the scrollbar, and a scroll-driven value on a
   transitioned property lags behind the finger by the length of the transition. */
.hero3d { position: absolute; top: 0; left: 0; right: 0; bottom: auto; z-index: 0;
  height: var(--hero-band, 100%); pointer-events: none; overflow: hidden; will-change: transform; }
.hero3d canvas { display: block; width: 100%; height: 100%;
  opacity: 0; transition: opacity 600ms var(--ease);
  -webkit-mask-image: var(--scene-mask); mask-image: var(--scene-mask);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%; }
.hero3d[data-hero3d-on] canvas { opacity: var(--hero3d-alpha, 1); }

/* The copy sits over the scene and has to win - always, at every point in the animation, not
   on average. This scrim is the whole of that job: the canvas renders at full strength
   (--hero3d-alpha is 1) and the bone background is held over the column the text occupies,
   releasing only where the text has ended. Dimming the canvas as well was two half-measures -
   the copy still had a moving object under it and the scene was too faint to be worth loading. */
.section--hero[data-hero] { position: relative; }
.section--hero:has(.hero3d) { z-index: 0;
  /* The bottom fade, on every hero. --hero-band is the height the scene occupies, so the last
     22% of it is the same distance whether the canvas is the whole band (desktop, and the home
     page) or the strip under the copy (phone) - one edge, computed from one number. */
  --scene-mask: linear-gradient(180deg, #000 0,
    #000 calc(100% - 0.22 * var(--hero-band, 100%)), rgba(0,0,0,0) 100%); }
.section--hero:has(.hero3d) > .container { position: relative; z-index: 2; }
.section--hero:has(.hero3d)::after { content: ''; position: absolute; top: 0; left: 0; right: 0;
  height: var(--hero-band, 100%); z-index: 1; pointer-events: none;
  background: linear-gradient(90deg, var(--bg) 0, var(--bg) calc(var(--cr, 55%) + 32px),
    rgba(242,240,233,0) calc(var(--cr, 55%) + 192px)); }
/* The home hero is the one page with something already living beside the copy - the intake
   diagram - and the scene competes with it rather than with the copy. It is the one page that
   is not given the treatment below: the scene is asked to sit left of centre (the 'b'
   parameter, pages/hero3d.php), the canvas stays the whole band at every width, and the scrim
   never fully lets go - it thins to a veil under the aside instead of clearing to nothing.
   Measured all the same: it holds bone to the end of the longest line in the text column, and
   the aside is excluded from the measurement (js/site.js) so one column cannot decide the
   other's edge. Confining the canvas here, as the other pages do, would put it behind the
   diagram - which on a phone sits directly under the copy - so it keeps a veil instead. */
.section--hero:has(.hero3d):has(.hero-grid--home)::after {
  background: linear-gradient(90deg, var(--bg) 0, var(--bg) calc(var(--cr, 55%) + 32px),
    rgba(242,240,233,.72) calc(var(--cr, 55%) + 160px), rgba(242,240,233,.72) 100%); }
@media (max-width: 860px) {
  /* Narrow: the copy runs the full width, so there is no column beside it to hold the scene off
     with, and every horizontal scrim is a compromise between hiding the picture and letting it
     under the words. So the picture moves. The canvas is confined to the strip below the copy -
     top at --cb, height whatever is left of the band - and the copy has nothing behind it at
     all. The band is given a floor first, because a hero that is copy plus a 90px sliver has
     nowhere to put a scene.

     The canvas is ~390x200 there, which is the narrow branch in js/hero3d.js: the scenes reframe
     for a small picture rather than being the desktop one shrunk.

     The mask arrives over the first 60px, so the picture emerges from the bone instead of
     starting at a seam, and leaves over the band's last 22% like every other hero. The ::after
     covers the same arrival - bone to nothing over the 84px below the strip's top edge - as
     belt and braces: the mask alone was enough everywhere it was tested, but a hard line under
     the lead is the one artefact that would be worth having seen. */
  .section--hero:has(.hero3d) { min-height: 560px; }
  .section--hero.hero--only:has(.hero3d) { padding-bottom: 88px; }
  .section--hero:has(.hero3d):not(:has(.hero-grid--home)) .hero3d {
    top: calc(var(--cb, 55%) + 16px); left: 0; right: 0;
    height: calc(var(--hero-band, 100%) - var(--cb, 0px) - 16px); }
  .section--hero:has(.hero3d):not(:has(.hero-grid--home)) {
    --scene-mask: linear-gradient(180deg, rgba(0,0,0,0) 0, #000 60px,
      #000 calc(100% - 0.22 * var(--hero-band, 100%)), rgba(0,0,0,0) 100%); }
  .section--hero:has(.hero3d)::after {
    background: linear-gradient(180deg, var(--bg) 0, var(--bg) calc(var(--cb, 55%) + 16px),
      rgba(242,240,233,0) calc(var(--cb, 55%) + 100px)); }
  /* Home again: the diagram is stacked under the copy at this width, so what is below --cb is
     not empty strip, it is the aside. The veil carries on to the bottom of the band. */
  .section--hero:has(.hero3d):has(.hero-grid--home)::after {
    background: linear-gradient(180deg, var(--bg) 0, var(--bg) calc(var(--cb, 55%) + 24px),
      rgba(242,240,233,.72) calc(var(--cb, 55%) + 144px), rgba(242,240,233,.72) 100%); }
}
@media (prefers-reduced-motion: reduce) { .hero3d { display: none; } }

/* Scroll-away. js/site.js marks the hero band .hero-lift and sets --hero-p from 0 to 1 over
   that band's height; it lifts and fades as it goes, passing behind the sticky header
   (z-index 50) rather than being cut off by it. The scene travels further than the copy, so
   the two separate slightly on the way out. Nothing here changes layout - the hero occupies
   the space it always did - and with no script, or under reduced motion, nothing is marked,
   --hero-p is never set, and all of it is inert. */
.section--hero[data-hero] .hero-lift {
  transform: translate3d(0, calc(var(--hero-p, 0) * -56px), 0);
  /* The lift is band-wide; the fade is not. --hero-y is the raw scroll position, --lift-a the
     point at which this element's own top goes under the header and --lift-b the distance it
     fades over, both set once by the script. Below --lift-a the numerator is negative and
     opacity resolves above 1, which the property clamps for us - so a paragraph the reader is
     still looking at cannot be dimmed, whatever the rest of the band is doing. */
  opacity: calc(1 - (var(--hero-y, 0) - var(--lift-a, 0)) / var(--lift-b, 1)); }
.section--hero[data-hero] .hero3d {
  transform: translate3d(0, calc(var(--hero-p, 0) * -96px), 0);
  opacity: calc((1 - var(--hero-p, 0)) * 1.5); }

/* ------------------------------------------------------------- booking */
/* pages/booking-widget.php + js/booking.js: the Cal.com calendar on /gratis-intake/. Two panes on a wide
   screen, stacked on a phone. The month grid is a 7-column grid rather than a table because
   nothing here is tabular data to read across - each cell is its own control. */
.bk { margin-top: 24px; }
.bk__nojs, .bk__fallback { border: 1px solid var(--line); border-radius: var(--radius-md); padding: 24px; max-width: 40rem; }
.bk__fallback-title { font-weight: 600; }
.bk__fallback p + p { margin-top: 12px; }
.bk__fallback-actions { display: flex; flex-wrap: wrap; gap: 12px; }

.bk__status { margin-bottom: 16px; font-size: var(--text-small); color: var(--muted); }
.bk__status--error { color: var(--danger); font-weight: 500; }

.bk__grid { display: grid; gap: 32px; align-items: start; }
@media (min-width: 768px) { .bk__grid { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); gap: 40px; } }

.bk__monthbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.bk__monthname { font-family: var(--font-heading); font-weight: 600; font-size: var(--text-h3); }
.bk__nav {
  width: 40px; height: 40px; flex: none; cursor: pointer;
  border: 1px solid var(--fg); border-radius: var(--radius-sm); background: transparent;
  transition: background-color var(--dur-control) ease-out;
}
.bk__nav:hover { background: var(--surface); }
.bk__nav:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
.bk__nav[disabled] { color: var(--disabled); border-color: var(--line); cursor: default; background: none; }

.bk__weekdays, .bk__days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.bk__weekdays { margin-bottom: 4px; font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); text-align: center; }
.bk__day {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1; min-height: 40px; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-variant-numeric: tabular-nums; background: var(--bg); color: var(--fg); cursor: pointer;
  transition: background-color var(--dur-control) ease-out, border-color var(--dur-control) ease-out;
}
.bk__day:hover { border-color: var(--fg); background: var(--surface); }
.bk__day:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
/* A day with nothing free is shown, not removed: the shape of the month stays readable, and a
   grid that reflows as availability changes is much harder to scan. Shown means readable -
   --disabled is 1.85:1 on bone, so the date was gone, not dimmed. --muted-large clears 4.5:1
   and the border does the dimming instead: present, but a shade lighter than a day you can
   click, so the two are still told apart at a glance. */
.bk__day--empty { color: var(--muted-large); border-color: var(--graphite-100); cursor: default; }
.bk__day--blank { border-color: transparent; }
/* The placeholder month the page prints before the API answers. Flat, quiet and exactly the
   size of a real cell, so nothing moves when the days arrive. */
.bk__day--skeleton { background: var(--surface); border-color: transparent; pointer-events: none; }
.bk__day.is-chosen { background: var(--fg); border-color: var(--fg); color: var(--bg); font-weight: 600; }

.bk__times-head { font-weight: 600; margin-bottom: 12px; }
.bk__timelist { display: grid; grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr)); gap: 8px; }
.bk__time {
  min-height: 44px; padding: 0 12px; cursor: pointer;
  border: 1px solid var(--fg); border-radius: var(--radius-sm); background: var(--bg);
  font-variant-numeric: tabular-nums; font-weight: 500;
  transition: background-color var(--dur-control) ease-out, color var(--dur-control) ease-out;
}
.bk__time:hover { background: var(--fg); color: var(--bg); }
.bk__time:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }

.bk__form { margin-top: 8px; }
.bk__chosen { font-weight: 600; padding-bottom: 16px; border-bottom: 1px solid var(--line); }
.bk__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }
.bk__done-when { font-weight: 600; }
.bk__done:focus { outline: none; }
.bk__tz { margin-top: 20px; }

/* ---------------------------------------------------- floating contact widget */
/* pages/contact-widget.php: a launcher that opens a greeting and a list of ways to get in
   touch. Built on <details> so it opens with no JavaScript; js/site.js only adds Esc and
   outside-click, shared with the header menus.

   z-index 45 sits below the mobile menu, which marks this inert. WhatsApp green is deliberate
   and is not a site token: the mark is recognised before it is read, which is the only reason
   the launcher works at 56px. */
.cw {
  /* The only green left is WhatsApp's own, on the WhatsApp chip, where it is the brand mark
     doing the work. Everything else that used to borrow it - the launcher, and our own primary
     CTA - is back in the site's palette. */
  --cw-green: #25d366;
  --cw-edge: 16px;                 /* how far off the edge he stands, at every station */
  /* How high off the bottom edge the two bottom-edge route stations stand. On a phone that is
     72px rather than the 16px used at rest: a bar used to sit along the bottom of the page and
     he lifted over it, and that lifted height is where he has stood at those stations since.
     The bar is gone, but he is not moving down onto the copy at the foot of the screen. At
     1024 and up the lift was never applied and this is simply the resting inset. */
  --cw-bottom: 72px;
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 45;
  transition: bottom var(--dur-control) ease-out;
}
@media (min-width: 1024px) { .cw { right: 24px; bottom: 24px; --cw-edge: 24px; --cw-bottom: 24px; } }
/* The mobile menu is a full-screen panel; a button floating over it would be the only thing on
   top. site.js marks it inert at the same time, so this is the visual half of that. */
body.menu-open .cw { display: none; }

.cw__root { position: relative; }
.cw__root > summary { list-style: none; }
.cw__root > summary::-webkit-details-marker { display: none; }
.cw__sronly {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The launcher is the mascot himself - no button, no plate, no circle. He is cut out against
   the page, so the only thing anchoring him to it is the shadow below, which is drawn here
   rather than baked into the images: a shadow that is part of the sprite cannot stay still
   while he moves, and it would have to be redrawn for every pose. */
.cw__launcher {
  --cw-buddy: 72px;                      /* his height; the sprite is 261x288 */
  position: relative;
  display: block;
  width: calc(var(--cw-buddy) * 261 / 288);
  height: var(--cw-buddy);
  margin-left: auto;
  cursor: pointer;
}
@media (min-width: 1024px) { .cw__launcher { --cw-buddy: 88px; } }
.cw__launcher::after {
  content: "";
  position: absolute;
  left: 14%;
  right: 14%;
  bottom: -2px;
  height: 8%;
  border-radius: 50%;
  background: rgb(0 0 0 / 16%);
  filter: blur(3px);
}
.cw__launcher:focus-visible { outline: 2px solid var(--fg); outline-offset: 6px; border-radius: 8px; }

/* On a graphite band he is the same value as the page behind him and reduces to the yellow
   bits - antenna, eye ring, ears - floating on black. js/buddy.js sets this class only when a
   dark section actually contains him, so it never fires on bone, where it would read as a
   glow. A drop-shadow rather than a plate behind him: it follows his alpha, so it lifts the
   silhouette without giving him back the button we took away. The floor shadow inverts too -
   a black shadow on near-black is nothing at all.
   Carlos asked for it always, not only on the dark band, so the base glow is warm and quiet -
   it separates him from the bone without reading as a light source - and the dark band gets a
   brighter, cooler one where it is doing real work. */
.cw__pose { filter: drop-shadow(0 1px 1px rgba(255, 253, 249, .9)) drop-shadow(0 0 7px rgba(255, 253, 249, .55)); }
.cw--dark .cw__pose { filter: drop-shadow(0 0 2px rgba(255, 255, 255, .5)) drop-shadow(0 0 9px rgba(255, 255, 255, .34)); }
.cw--dark .cw__launcher::after { background: rgb(255 255 255 / 14%); }

.cw__pose {
  display: block;
  width: 100%;
  height: 100%;
  /* He pivots on his feet, so the squash in js/buddy.js compresses him downwards onto the
     shadow instead of shrinking him towards his middle. */
  transform-origin: 50% 92%;
  /* Three effects share this transform and none may clobber the others, so all of them are
     composed from custom properties: the hover lift belongs to CSS, the squash belongs to
     js/buddy.js, and the flip belongs to whichever station he is standing at. Written as one
     inline transform, whichever set it last would win and the rest would silently stop working.
     That is not hypothetical - it is why the slot was built this way, and it is what made the
     flip a one-line addition rather than a redesign.
     --cw-flip is -1 only where he stands to the LEFT of the page: he points and looks up-left,
     which gestures into the copy from the right-hand side and at the browser chrome from the
     left. Unset everywhere else, so this is inert on all 46 other pages. */
  transform: translateY(var(--cw-lift, 0))
             scale(calc(var(--cw-sx, 1) * var(--cw-flip, 1)), var(--cw-sy, 1));
}
@media (prefers-reduced-motion: no-preference) {
  /* buddy.js overrides the duration and easing per phase of the squash; it never has to touch
     the transform itself. With motion reduced there is no transition here at all, so the same
     script produces an instant pose change and no squash. */
  .cw__pose { transition: transform var(--dur-control) ease-out; }
  .cw__launcher:hover .cw__pose { --cw-lift: -3px; }
}

.cw__panel {
  position: absolute;
  right: 0;
  /* Measured off the launcher rather than fixed at 68px: he is taller than the button was,
     and he is two different heights across the breakpoint. */
  bottom: calc(100% + 12px);
  width: min(330px, calc(100vw - 32px));
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg);
  box-shadow: 0 10px 30px rgb(0 0 0 / 18%);
}

/* Greeting: avatar beside a speech bubble, the way a message from them would look. */
.cw__greeting { display: flex; align-items: flex-start; gap: 10px; }
.cw__avatar { flex: none; width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.cw__bubble {
  flex: 1;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 2px var(--radius-md) var(--radius-md) var(--radius-md);
  background: var(--surface);
  font-size: var(--text-small);
  line-height: 1.5;
}
.cw__bubble p { margin: 0; }
.cw__from { margin-bottom: 6px !important; }
.cw__role { color: var(--muted); font-weight: 400; }
.cw__ask { color: var(--muted); }

/* Choices, aligned right so they read as the visitor's side of the exchange. */
.cw__choices { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.cw__chip {
  display: inline-block;
  max-width: 100%;
  padding: 9px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg);
  color: var(--fg);
  font-size: var(--text-small);
  line-height: 1.35;
  text-align: right;
  text-decoration: none;
  transition: border-color var(--dur-control) ease-out, background-color var(--dur-control) ease-out;
}
.cw__chip:hover { border-color: var(--fg); background: var(--surface); }
.cw__chip:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
/* Our own primary CTA, in our own colour. It was wearing WhatsApp's green, which made the one
   choice we most want taken look like a WhatsApp button that goes to the intake page instead.
   This is .btn--primary, which is what the same call to action looks like everywhere else on
   the site - the header, every hero, the CTA bands. */
.cw__chip--primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}
.cw__chip--primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* The WhatsApp chip earns the brand colour; the rest stay neutral so one choice leads. */
.cw__chip[data-channel="whatsapp"] { border-color: var(--cw-green); }
.cw__chip[data-channel="whatsapp"]:hover { background: var(--cw-green); border-color: var(--cw-green); color: #0b2e18; }

.cw__foot { margin: 12px 0 0; font-size: var(--text-mono); color: var(--muted); text-align: right; }

/* What he says. A sibling of the <details>, positioned against .cw, so it is independent of
   whether the panel is open - and it is hidden the moment the panel opens, because the panel
   says everything the bubble was about to. */
.cw__say {
  position: absolute;
  right: 0;
  bottom: calc(100% + 10px);
  width: min(300px, calc(100vw - 32px));
  padding: 14px 16px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md) var(--radius-md) 2px var(--radius-md);
  background: var(--bg);
  box-shadow: 0 10px 30px rgb(0 0 0 / 14%);
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
}
.cw__say.is-open { opacity: 1; transform: none; pointer-events: auto; }
@media (prefers-reduced-motion: no-preference) {
  .cw__say { transition: opacity var(--dur-fade) var(--ease), transform var(--dur-fade) var(--ease); }
}
.cw__say-text {
  margin: 0 0 12px;
  font-size: var(--text-small);
  line-height: 1.5;
  /* The height is reserved for three lines from the outset. The text is typed in one character
     at a time, and a box that grew with it would push the chip down under the reader's cursor
     line by line - the one animation on this widget nobody asked for. */
  min-height: calc(1.5em * 3);
  /* Dragging across a bubble that is about to disappear selects it instead; there is nothing
     here worth copying that the page does not already say. */
  user-select: none;
}
/* The cursor, only while characters are still arriving. */
.cw__say.is-typing .cw__say-text::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 1px;
  background: var(--accent-ink, currentColor);
  vertical-align: -0.15em;
  animation: cw-caret 1s steps(2, start) infinite;
}
@keyframes cw-caret { to { visibility: hidden; } }
@media (prefers-reduced-motion: reduce) { .cw__say.is-typing .cw__say-text::after { display: none; } }

.cw__say-cta { display: inline-block; text-align: left; }

.cw__say-close {
  position: absolute;
  top: 6px;
  right: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.cw__say-close:hover { color: var(--fg); background: var(--surface); }

/* On a phone there is no margin for him to speak into, so the bubble lands on the copy. It was
   300px in a 390px viewport - 77% of the screen - and covered six lines of body text; measured,
   not estimated. Nothing here makes that go away, because at this width every position is on
   top of something. What it does is cost fewer lines: the width comes in so it stops spanning
   the screen, and the padding and type come down because HEIGHT is what covers lines. Six
   becomes about four.
   The 3-line reservation on .cw__say-text stays. It is what stops the box growing under the
   reader while the text types itself in, and trading that away would be a worse phone
   experience than the one it fixes. He is bottom-anchored and the reading band is 20-30% from
   the top, so he is already about as far from the eye as this width allows, and he keeps his
   close button. */
@media (max-width: 560px) {
  .cw__say { width: min(280px, calc(100vw - 88px)); padding: 12px 13px; }
  .cw__say-text { font-size: 14.5px; margin-bottom: 10px; }
  .cw__chip.cw__say-cta { padding: 7px 13px; }
}

@media (prefers-reduced-motion: no-preference) {
  .cw__root[open] .cw__panel { animation: cw-in var(--dur-fade) var(--ease); }
}
@keyframes cw-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }


/* --------------------------------------------------------------- THE ROUTE
   On a page with a route he leaves the corner and stands beside what he is talking about. The
   old /kroon/ page had one (top left at the hero, centre right for the products); no page has
   one today. A route is authored in the page's content file, and js/buddy.js puts
   cw--path on the widget only when the page has one, the viewport is at least 1280px wide,
   motion is allowed and he has not been dismissed twice. Without that class every rule below
   is dead, which is the state on all 46 other pages.

   The breakpoint lives in the SCRIPT, not here, so the two cannot drift apart: there is no
   width query in this block. Positions are ordinary insets rather than transforms, so a resize
   re-places him with no script running.

   This block sits at the end of the widget section on purpose. The panel's open animation is
   set with the `animation` shorthand a few rules above, so the station variants have to come
   after it for `animation-name` to override the keyframe and nothing else.

   There are two routes and the content file authors both, because a phone is not a small
   desktop: the wide one circles through the middle of each edge, and the narrow one keeps to
   the four corners, since the middle of a phone screen is where the words are. Which one is in
   force is the only thing the viewport width decides.

   Centring is done with auto margins against opposite edges rather than by subtracting half
   his width, because he is 88px tall on a desktop and 72px on a phone and there is no reason
   for this block to know either number.

   The three bottom stations take their inset from --cw-bottom rather than the base one, which
   is how they stay clear of the copy at the foot of a phone screen: see that property in the
   .cw rule above for why the two differ. It is one custom property and not a width query here,
   so this block still knows nothing about the viewport. */
.cw.cw--path.is-tl { top: calc(var(--header-h) + 16px); bottom: auto; left: var(--cw-edge); right: auto; }
.cw.cw--path.is-tr { top: calc(var(--header-h) + 16px); bottom: auto; right: var(--cw-edge); left: auto; }
.cw.cw--path.is-tc { top: calc(var(--header-h) + 16px); bottom: auto; left: 0; right: 0; margin-inline: auto; width: max-content; }
.cw.cw--path.is-cl { top: 0; bottom: 0; margin-block: auto; height: max-content; left: var(--cw-edge); right: auto; }
.cw.cw--path.is-cr { top: 0; bottom: 0; margin-block: auto; height: max-content; right: var(--cw-edge); left: auto; }
.cw.cw--path.is-bl { top: auto; bottom: var(--cw-bottom); left: var(--cw-edge); right: auto; }
.cw.cw--path.is-br { top: auto; bottom: var(--cw-bottom); right: var(--cw-edge); left: auto; }
/* Off dead centre by 16px, towards the picture and away from the text. On the graphite band
   the text column's longest inked line ends exactly where a truly centred buddy begins - no
   overlap, but no slack either, and one long Dutch compound would close it. The gutter is
   64px and he is 80px, so he cannot sit inside it whatever we do; this buys clearance on the
   side where it costs something to lose. */
.cw.cw--path.is-bc { top: auto; bottom: var(--cw-bottom); left: 32px; right: 0; margin-inline: auto; width: max-content; }

/* He points and looks up and to his left. Standing on the left of the page that gestures at
   the browser chrome, so on both left-hand stations he is mirrored and faces the copy instead.
   In the middle and on the right he is left as drawn. The pose transform composes from custom
   properties, which is what makes this one declaration rather than a second set of sprites. */
.cw.cw--path.is-tl .cw__pose,
.cw.cw--path.is-cl .cw__pose,
.cw.cw--path.is-bl .cw__pose { --cw-flip: -1; }

/* The bubble hangs off whichever side has room, and the 2px corner is the tail, so it always
   points back at him. */
/* Standing on the left he speaks to his right, and the 2px corner is the tail. */
.cw.cw--path.is-tl .cw__say,
.cw.cw--path.is-cl .cw__say,
.cw.cw--path.is-bl .cw__say {
  left: calc(100% + 10px); right: auto; top: 0; bottom: auto;
  width: min(260px, calc(100vw - 100px));
  border-radius: 2px var(--radius-md) var(--radius-md) var(--radius-md);
}
/* And on the right, to his left. */
.cw.cw--path.is-tr .cw__say,
.cw.cw--path.is-cr .cw__say,
.cw.cw--path.is-br .cw__say {
  right: calc(100% + 10px); left: auto; top: 0; bottom: auto;
  width: min(260px, calc(100vw - 100px));
  border-radius: var(--radius-md) 2px var(--radius-md) var(--radius-md);
}
/* Along the top he speaks downwards, along the bottom upwards - into the page either way,
   never off the edge of it. */
.cw.cw--path.is-tc .cw__say {
  top: calc(100% + 10px); bottom: auto; left: 50%; right: auto; translate: -50% 0;
  width: min(260px, calc(100vw - 32px));
  border-radius: var(--radius-md);
}
.cw.cw--path.is-bc .cw__say {
  bottom: calc(100% + 10px); top: auto; left: 50%; right: auto; translate: -50% 0;
  width: min(260px, calc(100vw - 32px));
  border-radius: var(--radius-md);
}
/* The open transition animates `transform`, so the centring uses `translate` instead and the
   two never fight. */
.cw.cw--path.is-tc .cw__say.is-open,
.cw.cw--path.is-bc .cw__say.is-open { transform: none; }

/* The panel opens where he stands rather than sending him home first - being teleported to the
   corner at the moment you reach for him is worse than a menu in an unusual place. Downwards
   at both stations, because at the top there is nothing above him and at centre-right a panel
   opening upwards runs under the header on a short viewport. */
/* Downwards from anywhere in the upper half, upwards from the lower half - so it never runs
   off the viewport or under the header. The left-hand stations align it to his left edge. */
.cw.cw--path.is-tl .cw__panel,
.cw.cw--path.is-tc .cw__panel,
.cw.cw--path.is-tr .cw__panel,
.cw.cw--path.is-cl .cw__panel,
.cw.cw--path.is-cr .cw__panel { top: calc(100% + 12px); bottom: auto; }
.cw.cw--path.is-cl .cw__panel,
.cw.cw--path.is-cr .cw__panel { max-height: calc(50vh - 64px); overflow-y: auto; }
.cw.cw--path.is-tl .cw__panel,
.cw.cw--path.is-cl .cw__panel,
.cw.cw--path.is-bl .cw__panel { left: 0; right: auto; }
/* Travel. js/buddy.js writes a transform that puts him back where he just was, then clears it,
   and this transition carries him to the new station - so the movement is one compositor-only
   property and the stations stay plain insets. The transform lives on .cw and never on
   .cw__pose, whose own transform is the shared lift/squash/flip slot; that separation is what
   lets him be hovered, squashed and mirrored while in flight. */
@media (prefers-reduced-motion: no-preference) {
  /* Slow enough to be a journey rather than a jump: you should be able to watch him cross the
     page and know where he is going before he gets there. Must match TRAVEL_MS in buddy.js. */
  .cw.cw--path { transition: bottom var(--dur-control) ease-out, transform 1100ms cubic-bezier(.33, .05, .2, 1); }
  .cw__launcher::after { transition: opacity 200ms ease-out; }
  /* His shadow belongs to the floor, so it lets go while he is off it. */
  .cw.is-moving .cw__launcher::after { opacity: 0; }
  .cw.cw--path .cw__root[open] .cw__panel { animation-name: cw-in-down; }
}
@keyframes cw-in-down { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* Printing a page should not print a contact button. */
@media print { .cw { display: none; } }


/* ===== team =====================================================
   /team/ - the ring on bone, the matrix on tint. The page makes one
   argument twice: the drawing arranges the six roles around your
   process and frames the one you can always reach, and the grid under
   it says the same six roles against the five steps, so a node in the
   drawing links straight into the row that answers it.

   Tokens only, no new colours, no new faces. The ring reuses the
   process diagrams' own marks - dg-node, dg-ring, dg-dot, dg-tag,
   dg-conn, dg-opt - which already have their rules and their draw-in
   further up this file; what is below is the two things those rules
   do not cover: a node that is a link, and the matrix.
   ================================================================= */

/* --------------------------------------------------------------- orbit */
/* The figure is wider than the measure and narrower than the container,
   and it is the one thing on the page that wants to be centred. */
.orbit-wrap { margin-bottom: 48px; }
@media (min-width: 640px) { .orbit-wrap { margin-bottom: 64px; } }
/* margin-inline rather than a flex centre: as a flex item the figure sizes
   to its content and collapses to about half the track it is entitled to. */
.diagram--orbit { max-width: 60rem; margin-inline: auto; }
.diagram--orbit .diagram__legend { justify-content: center; }

/* The ring itself. It is not a connector - nothing travels along it - so it
   takes --line rather than currentColor and stays behind the nodes, which
   are filled and mask it into six arcs. */
.dg-orbit { color: var(--line); }

/* A node is a link, so it has to look like one under the cursor and under a
   keyboard. The hover is the surface step, not a colour change: these six
   are peers and none of them may look selected. */
.orb-node { cursor: pointer; }
.orb-box { transition: fill var(--dur-control) var(--ease), stroke-width var(--dur-control) var(--ease); }
.orb-node:hover .orb-box { fill: var(--surface); stroke-width: 2.5; }
.orb-node:focus { outline: none; }
.orb-node:focus-visible .orb-box { fill: var(--accent-wash); stroke: var(--accent-700); stroke-width: 3; }
.orb-t { pointer-events: none; }

/* Draw-in, on top of the shipped diagram sequence: the ring sweeps first,
   then the five hairlines, then the accent line and the frame/tag/dot the
   shipped rules already handle. Same guard, so reduced motion still gets the
   finished drawing with no animation at all. */
@media (prefers-reduced-motion: no-preference) {
  html.js [data-motion='diagram'] .dg-orbit { stroke-dasharray: 1; stroke-dashoffset: 1; }
  html.js [data-motion='diagram'].is-visible .dg-orbit { animation: draw 800ms var(--ease) forwards; }
  html.js [data-motion='diagram'].is-visible .dg-conn { animation-delay: 300ms; }
}

/* -------------------------------------------------------------- matrix */
/* The site's own table manner - hairline rows, mono uppercase heads - with
   the step names turned onto their side of the grid. It stays a real table,
   so a screen reader still reads "Delivery lead, Managed AI, ja". */
.mx-wrap { overflow-x: auto; }
.mx {
  width: 100%;
  min-width: 46rem;
  border-collapse: collapse;
}
.mx__vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.mx__corner { border-bottom: 2px solid var(--fg); }
.mx__step {
  vertical-align: bottom;
  text-align: left;
  padding: 0 10px 12px;
  width: 11%;
  border-bottom: 2px solid var(--fg);
}
.mx__n {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--mid);
  margin-bottom: 6px;
}
.mx__stepname {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  text-wrap: balance;
}
/* The row header carries what the card used to: the role, and under it the
   one line about what that role does. So it wraps - hence the width rather
   than white-space. */
.mx__role {
  text-align: left;
  vertical-align: middle;
  color: var(--fg);
  padding: 16px 24px 16px 14px;
  width: 45%;
  border-bottom: 1px solid var(--line);
  transition: background var(--dur-control) var(--ease), box-shadow var(--dur-control) var(--ease);
}
.mx__rolename {
  display: block;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: var(--text-body);
  letter-spacing: -0.01em;
}
/* The weight has to be stated: this lives in a <th>, which is bold by
   default, and a bold bio outshouts the role it belongs to. */
.mx__bio {
  display: block;
  font-family: var(--font-sans);
  font-weight: 400;
  margin-top: 4px;
  font-size: var(--text-small);
  line-height: 1.5;
  color: var(--muted);
  text-wrap: pretty;
}
/* Same mono tag, same --accent-ink, as the tag over the framed node in the
   diagram - it is one mark used twice, not two marks that happen to agree. */
.mx__tag {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink);
  margin-bottom: 5px;
}
.mx tbody tr:last-child .mx__role,
.mx tbody tr:last-child .mx__cell { border-bottom: none; }
.mx__cell {
  text-align: center;
  vertical-align: middle;
  padding: 14px 10px;
  border-bottom: 1px solid var(--line);
}
/* Off is a small grey dot rather than an empty cell: an absence you can see
   is an absence you can trust, and it keeps the columns readable while the
   grid is being scrolled sideways. */
.mx__dot {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--graphite-300);
  vertical-align: middle;
}
.mx__cell.is-on .mx__dot {
  width: 14px; height: 14px;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-600);
}
/* Hovering a row dims nothing and highlights the row it is on - useful on a
   46rem-wide grid where the role label can be far from the last column. */
.mx tbody tr:hover .mx__cell,
.mx tbody tr:hover .mx__role { background: var(--accent-wash); }

.mx__note {
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  letter-spacing: 0.03em;
  color: var(--muted);
}
.mx__dot--key { width: 14px; height: 14px; background: var(--accent); box-shadow: 0 0 0 1px var(--accent-600); flex: none; }

/* On a phone the whole table is inside a horizontal scroller, so the only
   question is how the fixed width is divided. The bio needs more of it than
   the dots do - a role column under about 22rem breaks that one sentence
   into five lines - so the header column takes a larger share here, not a
   smaller one, and the table gets 4rem more to give. */
@media (max-width: 640px) {
  .mx { min-width: 44rem; }
  .mx__role { width: 52%; padding: 14px 16px 14px 12px; }
  .mx__step { width: 9.6%; }
}

/* ------------------------------------------------------- landing on a row */
/* Arriving from a node. The wash is held for as long as the hash is there, so
   a reader who clicked "Adoptietrainer" can see which of six rows answered,
   and the 3px accent edge on the header is the same inset the diagram puts
   down the side of the core. scroll-margin lives on the row and on its cells
   because a <tr> is not a block box everywhere; either way the sticky header
   does not land on top of the answer. */
.mx tbody tr,
.mx tbody tr > * { scroll-margin-top: calc(var(--header-h) + 32px); }
.mx tbody tr:target .mx__cell,
.mx tbody tr:target .mx__role { background: var(--accent-wash); }
.mx tbody tr:target .mx__role { box-shadow: inset 3px 0 0 var(--accent); }
.mx tbody tr:target .mx__rolename { color: var(--fg); }

/* ---------------------------------------------------------------------- VEILIGHEID & AVG
   /veiligheid-en-avg/. The page used to be nine H2s in one column of prose, which reads as
   nine equal claims. It is not nine things: it is one picture of where the data goes, four
   statements about how we actually work, three external instruments we hold ourselves to,
   and a summary. So it is set in those registers.

   Register one, the route: the dataflow figure, using the diagram parts that already exist
   above - this block only adds the swatch for the branch that is refused and the heading the
   figure sits under.

   Register two, the practice: four tiles on tint, 2x2. The tiles differ because the claims
   differ - "wat we nooit doen" is the only refusal on the page and is the only graphite tile,
   and "een mens aan de knop" is the promise everything else rests on, so it carries the accent.

   Register three, the frameworks: three equal-height hairline columns, identical treatment
   because they are genuinely parallel; the eyebrow says which kind of instrument each is.

   Register four, the summary: a checklist, because that is what it is - eight commitments you
   can tick off against the four tiles above.

   Tokens only, no new colours. */

/* A section tag, so the registers announce themselves without another h2 competing with the
   ones inside the tiles. */
.secttag { margin: 0 0 clamp(20px, 2vw, 30px); }

/* ---- the flow */
.section--flow { padding-top: clamp(2.5rem, 2rem + 2vw, 4rem); }
.flow { display: grid; gap: clamp(32px, 2vw + 20px, 48px); }
/* The diagram is the widest thing on the page and it is allowed to be: it is the argument, not
   an illustration of one. Centred, so the loop sits under the H1 rather than off to one side. */
.flow__fig { max-width: 62rem; margin-inline: auto; width: 100%; }
.flow__fig .diagram__legend { justify-content: flex-start; }
@media (min-width: 640px) { .flow__fig .diagram__legend { justify-content: center; } }
/* One more legend swatch: the branch that is refused. Dashed, grey, no fill - exactly how it is
   drawn. The other three - lg-jij, lg-optimised, lg-human - are already above. */
.diagram__legend .lg-never { border: 1.5px dashed var(--mid); }

/* The figure gets a heading of its own so the section announces itself the way the other three
   do. The mono tag sits tight above it rather than carrying the section alone, so the label and
   the h2 read as one header. */
.secttag--tight { margin-bottom: 10px; }
.flow__h {
  font-family: var(--font-heading);
  font-size: var(--text-h2);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 clamp(24px, 2.4vw, 36px);
}

/* ---- the four tiles */
.tiles { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-md); overflow: hidden; }
@media (min-width: 900px) { .tiles { grid-template-columns: 1fr 1fr; } }

.tile {
  background: var(--bg);
  padding: 26px 22px 30px;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
@media (min-width: 640px) { .tile { padding: 34px 32px 38px; } }

.tile__label {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 16px;
}
/* The one marker on the page. "Een mens aan de knop" is the claim the other three depend on, so
   its label is set as a filled chip rather than a quiet caption. */
.tile__label--mark {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  gap: 8px;
  background: var(--accent);
  color: var(--accent-fg);
  padding: 4px 12px;
  border-radius: 999px;
}
.tile__label--mark::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-fg);
}

.tile__title {
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-wrap: balance;
  margin: 0 0 14px;
}
.tile__body { font-size: var(--text-small); line-height: 1.6; color: var(--muted); flex: 1; min-width: 0; }
.tile__body > * + * { margin-top: 14px; }
.tile__note { color: var(--mid); }

/* The supplier table. Same rules as .prose table, set one step smaller because it is evidence
   inside a tile rather than the tile's own argument. */
.tile__tablewrap { overflow-x: auto; }
.tile__table { width: 100%; border-collapse: collapse; font-size: var(--text-mono); line-height: 1.45; }
.tile__table th, .tile__table td { text-align: left; vertical-align: top; padding: 9px 14px 9px 0; border-bottom: 1px solid var(--line); }
.tile__table td:last-child, .tile__table th:last-child { padding-right: 0; }
.tile__table th {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mid);
  border-bottom-color: var(--fg);
  white-space: nowrap;
}
.tile__table td { color: var(--muted); }
.tile__table td:first-child { color: var(--fg); font-weight: 500; }
.tile__table tr:last-child td { border-bottom: 0; }
/* Every row in the last column says the same thing in different words. Setting it in mono makes
   that visible down the column instead of leaving it to be read four times. */
.eu { font-family: var(--font-mono); font-size: 0.94em; color: var(--fg); }

/* ---- the graphite tile */
.tile--never { background: var(--surface-dark); color: var(--bg); }
.tile--never .tile__label { color: var(--graphite-300); }
.tile--never .tile__body { color: var(--graphite-200); }

/* The five refusals spread over whatever height the tile beside it sets, so the graphite panel
   reads as a full ledger rather than a short list with a pool of empty ink under it. */
.tile--never .tile__body { display: flex; }
.never { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; flex: 1; align-content: space-between; }
.never li {
  display: grid;
  grid-template-columns: 15px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  padding: 12px 0;
  border-top: 1px solid var(--graphite-700);
}
.never li:first-child { border-top: 0; padding-top: 0; }
.never__mark { color: var(--graphite-400); margin-top: 3px; flex: none; }

/* ---- the accent tile */
.tile--control { box-shadow: inset 0 3px 0 var(--accent); }

/* ---- the three frameworks */
/* Three columns of equal height. They are alternatives to one another in kind, not steps, so
   nothing is numbered and nothing leads. */
.pillars { display: grid; gap: 20px; align-items: stretch; }
@media (min-width: 1024px) { .pillars { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; } }

.pillar {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg);
  padding: 26px 24px 30px;
  min-width: 0;
}
@media (min-width: 640px) { .pillar { padding: 32px 30px 36px; } }

/* The eyebrow sits above a hairline that runs the full width of the card, so the three kinds
   line up across the row and can be compared at a glance. */
.pillar__kind {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-ink);
  margin: 0 0 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.pillar__title {
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.015em;
  text-wrap: balance;
  margin: 0 0 16px;
}
.pillar__body { font-size: var(--text-small); line-height: 1.6; color: var(--muted); }
.pillar__body a { text-decoration: underline; text-underline-offset: 3px; }
.pillar__list { list-style: none; margin: 0; padding: 0; }
.pillar__list li { padding: 12px 0; border-top: 1px solid var(--line); }
.pillar__list li:first-child { border-top: 0; padding-top: 0; }
.pillar__list strong { color: var(--fg); font-weight: 500; }

/* ---- samengevat */
.sum { max-width: 62rem; }
.sum__title { font-size: var(--text-h2); font-weight: 600; letter-spacing: -0.02em; margin: 0 0 clamp(20px, 2vw, 28px); }

.sum__list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.sum__row {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  column-gap: 14px;
  row-gap: 4px;
  align-items: start;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
@media (min-width: 720px) {
  .sum__row { grid-template-columns: 24px minmax(9rem, 12rem) minmax(0, 1fr); column-gap: 20px; align-items: baseline; }
}

.sum__tick {
  grid-row: 1;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--accent-300);
}
@media (min-width: 720px) { .sum__tick { align-self: center; } }

.sum__k {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg);
}
.sum__v { font-size: var(--text-small); line-height: 1.55; color: var(--muted); }
@media (max-width: 719px) { .sum__v { grid-column: 2; } }

.sum__ask { margin-top: 26px; font-size: var(--text-body); color: var(--fg); max-width: var(--measure); }
/* ===== over optimi =====================================================
   /over-optimi/ - pages/about.php + pages/about-parts.php.

   The page's argument is that a company is a system before it is an AI problem, so the facts
   come first as facts (the case pages' .output-block--facts beside the H1, which needs no rule
   of its own here), the central paragraph is drawn in the site's own diagram language instead
   of only being read, and the two lists that were four rules and five promises are set side by
   side as one ruled pair - a numbered column of how we work against a checked column of what
   you get. Tokens only, no new colours. */

/* -------------------------------------------------------------- hero */
/* The eyebrow is inside .prose, because a hero with a facts panel beside it has to stay
   exactly two grid items. .prose spaces its children at 1.1em, which is a paragraph gap
   between a label and the heading it labels. */
.section--hero .prose > .mono-label + h1 { margin-top: 0; }

/* ------------------------------------------------------------- system */
/* The paragraph and its drawing are one unit: the reader should be able to look from a sentence
   to the box it names without scrolling. The figure takes the wider track, because the labels
   inside it are real text. */
.system { display: grid; gap: 32px; }
@media (min-width: 1024px) {
  .system { grid-template-columns: 5fr 7fr; gap: 56px; align-items: start; }
}
.system__copy { max-width: var(--measure); }
.system__copy h2 { margin-bottom: 20px; }
.system__figure .diagram { max-width: none; margin: 0; }
@media (min-width: 1024px) { .system__figure { position: sticky; top: 96px; } }

/* ------------------------------------------------------------ sectors */
.sectors { margin-top: 32px; }
.sectors .card { background: var(--bg); }
.sectors .card__eyebrow { margin-bottom: 0; }
.sectors .card__meta { font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; }

/* --------------------------------------------------------------- duo */
/* Two lists, one rhythm. They are different in kind - an ordered method and an unordered set of
   promises - so the marker column is what separates them, not a border or a tint. */
.duo { display: grid; gap: 56px; }
@media (min-width: 900px) { .duo { grid-template-columns: 1fr 1fr; gap: 64px; align-items: start; } }
.duo__col { min-width: 0; }
.duo__more { margin-top: 20px; font-size: var(--text-small); }
.duo__more a { text-decoration: underline; text-underline-offset: 3px; }

.ruled { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--fg); }
.ruled__item {
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}
.ruled__mark {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  line-height: 1.6;
}
.ruled__text { margin: 0; line-height: 1.6; }
.ruled__text strong { font-weight: 500; }
.ruled--checks .ruled__text { color: var(--muted); }
/* The check is the only accent in this pair, and it belongs to the promises: --accent is never
   text, so it is drawn, not written. */
.ruled__check { display: block; line-height: 0; padding-top: 3px; }

/* ------------------------------------------------------------- place */
.place { display: grid; gap: 24px; }
@media (min-width: 900px) { .place { grid-template-columns: 5fr 7fr; gap: 64px; align-items: start; } }
.place__body { max-width: var(--measure); }
.place__contact { margin-top: 16px; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.04em; }
.place__contact a { text-decoration: underline; text-underline-offset: 3px; }
.place__sep { color: var(--mid); padding-inline: 6px; }

/* ------------------------------------------------------------- roles */
/* .chips--roles and .chip--role are already set above, for /aanpak/, where the chips are links.
   Here they are labels - the one link to /team/ is in the sentence over them - so they lose the
   pointer, take the muted ink a secondary list has, and do not answer a hover they cannot act
   on. Selected on the element, so the links on /aanpak/ keep every bit of what they had. */
span.chip--role { cursor: default; color: var(--muted); }
span.chip--role:hover { background: transparent; color: var(--muted); border-color: var(--line); }
.legal-line { margin-top: 28px; font-family: var(--font-mono); font-size: var(--text-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }

/* /juridisch/ and /en/legal/ (pages/legal.php): three document cards, then the company details. */
.legal-version { margin-top: 12px; color: var(--muted); }
.legal-doc__actions { margin-top: auto; padding-top: 12px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; }
.legal-doc__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.legal-company { max-width: 40rem; margin-top: 16px; }
.legal-company .facts__v { overflow-wrap: anywhere; }
.legal-company .facts__v a { text-decoration: underline; text-underline-offset: 3px; }
/* ===== veelgestelde vragen ===== */
/* /veelgestelde-vragen/ - the finder and the one list under it. See pages/faq-parts.php.
 *
 * The page used to be two accordions under two prose headings, and the two halves were drawn in
 * two visual languages, so no bar over them could make them feel like one list - they were not
 * one list. Here they are: fifteen rows, one ground, one grid, one set of hairlines that line up
 * with the toolbar's own. The difference between an objection and a practical question survives
 * inside the row - 01-05 answer in the dark Optimi panel and stay open, 06-15 keep their first
 * sentence and the "+" - but it is a difference in what a row contains, not in what kind of
 * thing it is.
 *
 * Tokens only, no new colours, and .chip and .field__input are the site's own. */

/* ------------------------------------------------------------ toolbar */
/* No box: hairline top and bottom on the page's own ground, so it reads as a control strip
   belonging to the page rather than a widget sitting on it. Its inner container shares the
   list's left and right edge, which is what lets the bar and the list read as one table. */

.finderwrap { margin-top: 0; }

.fbar {
  position: sticky;
  /* Under the site header, never over it: .site-header is z-index 50, so 30 leaves the header -
     and its 60/z-index mobile menu - on top. */
  top: var(--header-h);
  z-index: 30;
  background: var(--bg);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-control) var(--ease);
}
/* Set by js/site.js once the bar has actually detached. */
.fbar[data-stuck] { border-bottom-color: var(--line); }

.fbar__inner {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-block: 10px;
}
@media (min-width: 1024px) {
  .fbar__inner {
    flex-direction: row;
    align-items: center;
    gap: 20px;
    padding-block: 8px;
    min-height: 60px;
  }
  .fbar__search { flex: 1 1 240px; min-width: 190px; }
  .fbar__chipscroll { flex: 0 1 auto; }
}

.fbar__search { position: relative; display: block; }
.fbar__icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  color: var(--muted);
  pointer-events: none;
}
.fbar__input {
  min-height: 44px;
  padding-left: 42px;
  padding-right: 42px;
  border-color: var(--line);
  background: var(--surface);
}
.fbar__input:hover { border-color: var(--mid); }
.fbar__input:focus-visible { border-color: var(--fg); }
.fbar__input::-webkit-search-cancel-button { display: none; }
.fbar__input::placeholder { color: var(--muted); }

.fbar__clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}
.fbar__clear:hover { background: var(--bg); color: var(--fg); }

.fbar__chipscroll {
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  margin-inline: -20px;
  padding-inline: 20px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.fbar__chipscroll::-webkit-scrollbar { display: none; }
@media (min-width: 640px) { .fbar__chipscroll { margin-inline: -32px; padding-inline: 32px; } }
@media (min-width: 1024px) { .fbar__chipscroll { margin-inline: 0; padding-inline: 0; } }

.fbar__chips { flex-wrap: nowrap; margin: 0; gap: 8px; }
.fbar__chips > li { flex: none; }
.fbar__chips .chip { white-space: nowrap; }

.chip__n {
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  color: var(--mid);
  font-variant-numeric: tabular-nums;
}
.chip[aria-pressed='true'] .chip__n { color: var(--graphite-400); }

/* The count and the open/close toggle are a hairline strip directly under the bar. There is one
   list, so there is one place to say how many rows are in it and one control that opens the ten
   rows that can open. */
.fbar__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px 20px;
  padding-top: 10px;
  padding-bottom: 10px;
}
.fbar__count {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.fbar__toggle {
  border: 0;
  background: transparent;
  padding: 2px 0;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--fg);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.fbar__toggle:hover { color: var(--muted); }

/* ---------------------------------------------------------- no result */
.noresult {
  max-width: var(--measure);
  margin-top: 32px;
  padding: 28px 24px;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
}
.noresult__line { margin: 0; font-family: var(--font-heading); font-weight: 600; font-size: var(--text-h3); letter-spacing: -0.01em; }
.noresult__hint { margin: 8px 0 0; color: var(--muted); font-size: var(--text-small); }
.noresult__ask { margin-top: 24px; padding-top: 22px; border-top: 1px solid var(--line); }
.noresult__ask h2 { margin-top: 0; font-size: var(--text-h3); }

/* ------------------------------------------------------------ one list */
/* The list starts where the count strip ends, with no section padding above it, so its top
   hairline continues the bar's own. Bar, strip and list share the container, so all three
   hairlines start and stop at the same x. */
.onelist-section { padding-top: 0; }

.onelist {
  --ngut: 2.75rem;
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}
@media (min-width: 900px) { .onelist { --ngut: 3.5rem; } }

/* One grid for every row on the page, divider included. Phone and tablet: numeral and question
   on one line, answer below across the full width. Desktop: numeral, a question column of about
   a third, and the answer in what is left. */
.grow {
  display: grid;
  grid-template-columns: var(--ngut) minmax(0, 1fr);
  align-items: start;
  padding: 22px 0;
  border-bottom: 1px solid var(--line);
}
@media (min-width: 900px) {
  .grow {
    grid-template-columns: var(--ngut) minmax(0, 34%) minmax(0, 1fr);
    column-gap: clamp(20px, 3vw, 44px);
    padding: 30px 0;
  }
}
@media (min-width: 1400px) { .grow { padding: 34px 0; } }

.grow__n {
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  color: var(--mid);
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
  padding-top: 4px;
  transition: color var(--dur-control) var(--ease);
}
.grow:hover .grow__n { color: var(--fg); }

.grow__q { min-width: 0; }
.grow__eyebrow {
  display: block;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.grow__qt {
  display: block;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: var(--text-h3);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--fg);
  text-wrap: balance;
}

/* Phone and tablet: the answer drops under the pair and takes the whole row, numeral column
   included, because a 2.75rem indent on a 390px screen costs the panel a word a line for
   nothing. */
.grow__a { grid-column: 1 / -1; margin-top: 14px; min-width: 0; }
@media (min-width: 900px) {
  .grow__a { grid-column: 3; margin-top: 0; }
}

/* The Optimi panel: graphite with bone text and the accent as the same 3px inset the stylesheet
   gives an optimised node. It is the answer cell rather than a bubble in a pair, so it has no
   tail and needs none - the row it sits in is the pairing. */
.panel {
  background: var(--surface-dark);
  color: var(--bg);
  border-radius: var(--radius-sm);
  box-shadow: inset 3px 0 0 var(--accent);
  padding: 18px 20px 20px;
}
@media (min-width: 640px) { .panel { padding: 20px 24px 22px; } }
.panel__who {
  margin: 0 0 8px;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--graphite-300);
}
.panel__t { margin: 0; color: var(--graphite-100); font-size: var(--text-body); line-height: 1.65; }

/* 06-15: first sentence on the row, the rest behind the site's "+" marker. */
.grow__d { min-width: 0; }
.grow__sum {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  list-style: none;
  cursor: pointer;
}
.grow__sum::-webkit-details-marker { display: none; }
.grow__sum::after {
  content: '+';
  flex: none;
  margin-left: auto;
  font-family: var(--font-mono);
  color: var(--muted);
  font-size: 1.25rem;
  line-height: 1.1;
  transition: transform var(--dur-control) ease-out;
}
.grow__d[open] > .grow__sum::after { transform: rotate(45deg); }
.grow__sum:hover .grow__lede { color: var(--fg); }

.grow__lede {
  display: block;
  margin: 0;
  max-width: 62ch;
  color: var(--muted);
  font-size: var(--text-small);
  line-height: 1.6;
  transition: color var(--dur-control) var(--ease);
}
.grow__rest { padding-top: 12px; }
.grow__rest p { margin: 0; max-width: 62ch; }

/* A divider is a row: same hairline, same left edge, mono and muted, and thin enough that it
   separates rather than announces. */
.grow--group {
  display: block;
  padding: 16px 0 10px;
  font-family: var(--font-mono);
  font-size: var(--text-mono);
  font-weight: 500;
  color: var(--mid);
}
.grow--group:hover .grow__n { color: var(--mid); }
.grow__glabel { letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.grow__gnote { text-transform: lowercase; letter-spacing: 0.02em; }
.grow__gnote::before { content: ' \b7  '; }

/* -------------------------------------------------------------- filter */
/* A hidden row is gone, not faded: the animation runs first and the attribute lands at the end
   of it, so nothing collapsed is left taking up a gap. .grow sets display:grid, which is why
   [hidden] has to be said again here. */
[data-item][hidden], [data-group-head][hidden], [data-faq-list][hidden], [data-closing][hidden] { display: none; }

/* -------------------------------------------------------------- closing */
.closing__inner { display: grid; gap: 28px; }
@media (min-width: 900px) {
  .closing__inner { grid-template-columns: minmax(0, 1fr) auto; gap: 48px; align-items: center; }
}
.closing__text { max-width: var(--measure); }
.closing__text h2 { margin-bottom: 12px; }
.closing__text p { margin: 0; }
.closing__actions { margin-top: 0; }


/* ===== the Optimi suite ===========================================
   /optimi/ and its product pages (pages/optimi.php, pages/product.php,
   pages/optimi-parts.php). The section is the software line, so it
   keeps the line's salmon; everything else is the site's own: the
   header, the type, the buttons, the container, the straight
   geometry. What is new is the picture: a webshop with the products
   working in it, the cards with a live demo each, and the same
   product in four shops.

   About the two salmons: #f2907c does everything visual and never
   carries text on bone (2.0:1). Text that has to be salmon uses
   --suite-ink, the same hue at 6.9:1. Black on the salmon fill is
   7.9:1, which is why the buttons keep black type.
   ================================================================= */
.theme-optimi {
  --suite-300: #f7ab9b;
  --suite-400: #f2907c;   /* the colour */
  --suite-500: #e07561;
  --suite-ink: #9b3a26;   /* the same hue, dark enough to be read */
  --suite-wash: #fdf0ec;
  --on-dark-muted: #a9a79f;

  --accent: var(--suite-400);
  --accent-hover: var(--suite-300);
  --accent-pressed: var(--suite-500);
  --accent-ink: var(--suite-ink);
  --accent-wash: var(--suite-wash);
  --accent-fg: var(--graphite-900);
}
.theme-optimi .mono-label { color: var(--suite-ink); }
.theme-optimi :focus-visible { outline-color: var(--suite-ink); }

/* The nav item: the odd one out, permanently. The word takes the ink and a salmon square
   marks it, left of the word, so it never reads as a second "you are here". */
.nav__link--optimi { font-family: var(--font-mono); font-weight: 500; letter-spacing: .08em; color: #9b3a26; }
.nav__link--optimi:hover { color: #7d2d1d; }
.nav__link--optimi::before { content: ''; position: absolute; left: 0; bottom: 3px; width: 7px; height: 7px; background: #f2907c; }
.nav__link--optimi.is-active::after { background: #9b3a26; }
.mobile-nav .nav__link--optimi { color: #9b3a26; }
.site-header.is-curtain .nav__link--optimi { color: #f2907c; }
.site-header.is-curtain .nav__link--optimi::before { background: var(--accent); }

.o-actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.o-note { margin-top: 16px; font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); max-width: 90ch; }
.o-caret { display: inline-block; width: 2px; height: 1.05em; margin-left: 1px; background: currentColor; vertical-align: -0.15em; animation: o-blink 1s steps(1) infinite; flex: none; }
@keyframes o-blink { 50% { opacity: 0; } }

/* ---- browser chrome: the three dots and an address, on every mock window */
.o-chrome { display: flex; align-items: center; gap: 6px; padding: 9px 12px; border-bottom: 1px solid #eeece6;
  font-family: var(--font-mono); font-size: .7rem; color: var(--graphite-400); }
.o-chrome i { width: 9px; height: 9px; border-radius: 50%; background: #e2ded4; flex: none; }
.o-chrome span { margin-left: 8px; background: #f4f2ec; border-radius: var(--radius-sm); padding: 3px 9px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* ---- the small search demo (cards, screens, the hero's webshop) */
.o-search { width: min(92%, 360px); display: grid; gap: 8px; }
.o-search__box { display: flex; align-items: center; min-height: 42px; padding: 8px 12px; background: #fff; color: var(--graphite-900);
  border: 2px solid var(--graphite-900); border-radius: var(--radius-md); font-size: .95rem; font-weight: 500; overflow: hidden; white-space: nowrap; }
.o-search__res { list-style: none; margin: 0; padding: 5px; display: grid; gap: 3px; background: #fff; color: var(--graphite-900);
  border-radius: var(--radius-md); box-shadow: 0 6px 18px rgba(23,23,23,.12); font-size: .84rem; }
.o-search__res li { display: flex; justify-content: space-between; gap: 10px; padding: 6px 8px; border-radius: var(--radius-sm); }
.o-search__res li:first-child { background: #f3f1ea; }
.o-search__price { font-family: var(--font-mono); font-size: .76rem; color: var(--graphite-500); white-space: nowrap; }
.o-spinimg { display: block; width: auto; height: 100%; max-height: 190px; mix-blend-mode: multiply; }

/* The suite's blocks follow each other closely: each one is a self-contained card or band with
   its own frame, so the site's full section rhythm between them reads as empty bone. */
.section.theme-optimi:not(.section--hero) { padding-block: calc(var(--space-section) * .55); }

/* ================================================== the suite page */
/* ---- hero: the copy beside a webshop with every product working in it */
.o-hero { background: var(--suite-wash); }
.o-hero__grid { display: grid; gap: 40px; align-items: center; }
@media (min-width: 960px) { .o-hero__grid { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 56px; } }
.o-hero h1 { max-width: 14ch; margin: 0; }
.o-hero .lead { margin-top: 20px; color: var(--muted); }
.o-hero .o-actions { margin-top: 28px; }
.o-stats { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 26px; font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); }
.o-stats b { color: var(--fg); font-weight: 500; }

.o-shop { position: relative; min-width: 0; background: #fff; color: var(--graphite-900); border-radius: var(--radius-md);
  box-shadow: 0 30px 60px -30px rgba(155,58,38,.45), 0 0 0 1px rgba(23,23,23,.06); }
.o-shop__head { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; padding: 14px 16px; border-bottom: 1px solid #f0eee8; }
.o-shop__head b { font-family: var(--font-heading); font-weight: 600; }
.o-shop__search { position: relative; flex: 1; min-width: 180px; }
.o-shop__search .o-search__box { min-height: 38px; font-size: .88rem; padding: 6px 10px; }
.o-shop__drop { position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 2; }
.o-shop__body { display: grid; gap: 16px; padding: 16px; }
@media (min-width: 560px) { .o-shop__body { grid-template-columns: 1fr 1fr; } }
.o-shop__img { position: relative; aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; background: #ebe8e0; border-radius: var(--radius-md); }
.o-shop__img .o-spinimg { width: 88%; height: auto; max-height: none; }
.o-shop__badge { position: absolute; left: 8px; bottom: 8px; padding: 3px 7px; background: #fff; border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: .66rem; }
.o-shop__info { display: grid; gap: 8px; align-content: start; font-size: .88rem; }
.o-shop__name { font-family: var(--font-heading); font-weight: 600; font-size: 1.05rem; }
.o-shop__price { font-family: var(--font-heading); font-weight: 600; font-size: 1.1rem; }
.o-shop__sub { color: var(--graphite-500); }
.o-shop__cart { padding: 9px; background: var(--graphite-900); color: #fff; border-radius: var(--radius-md); text-align: center; font-weight: 500; }
.o-shop__slot { padding: 10px; border: 1.5px dashed #c9c4b7; border-radius: var(--radius-md); text-align: center;
  font-family: var(--font-mono); font-size: .7rem; color: var(--graphite-400); }
/* The tags: each product names itself where it works. Hung off the window's edges on a wide
   screen; tucked inside on a narrow one, where an overhang would push the page sideways. */
.o-tag { position: absolute; z-index: 3; display: flex; align-items: center; gap: 6px; padding: 6px 10px;
  background: var(--graphite-900); color: var(--bg); border-radius: 999px; white-space: nowrap;
  font-family: var(--font-mono); font-size: .68rem; box-shadow: 0 6px 14px rgba(23,23,23,.18); }
.o-tag::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--suite-400); }
.o-tag--1 { top: -14px; right: 24px; }
.o-tag--2 { left: -12px; top: 52%; }
.o-tag--next { right: -10px; bottom: 22px; background: #fff; color: var(--graphite-500); border: 1px dashed #b9b3a5; box-shadow: none; }
.o-tag--next::before { background: transparent; border: 1.5px dashed #b9b3a5; }
@media (max-width: 959px) { .o-tag--1 { right: 8px; } .o-tag--2 { left: 8px; } .o-tag--next { display: none; } }

/* ---- the product cards */
.o-cards { display: grid; gap: 18px; margin-top: 32px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.o-card { display: grid; gap: 16px; align-content: start; min-width: 0; padding: 22px; background: #fbfaf6; border: 1px solid var(--line); border-radius: var(--radius-md); }
.o-card__top { display: flex; justify-content: space-between; align-items: start; gap: 10px; }
.o-card__name { font-weight: 600; font-size: var(--text-h3); }
.o-card__name a { text-decoration: none; }
.o-card__name a:hover { text-decoration: underline; }
.o-badge { padding: 5px 9px; border-radius: 999px; white-space: nowrap; font-family: var(--font-mono); font-size: .7rem; }
.o-badge--open { background: var(--suite-400); color: var(--graphite-900); }
.o-badge--closed { border: 1px solid var(--suite-ink); color: var(--suite-ink); }
.o-card__demo { height: 210px; display: grid; place-items: center; overflow: hidden; background: var(--surface); border-radius: var(--radius-md); }
.o-facts { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; font-size: var(--text-small); }
.o-facts li { position: relative; padding-left: 18px; }
.o-facts li::before { content: ''; position: absolute; left: 0; top: .55em; width: 8px; height: 8px; background: var(--suite-400); }
.o-card__price { display: grid; gap: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.o-card__big { font-family: var(--font-heading); font-weight: 600; font-size: 1.25rem; }
.o-card__small { font-family: var(--font-mono); font-size: .74rem; color: var(--muted); }
.o-card--next { background: transparent; border: 1.5px dashed var(--graphite-300); }
.o-card__demo--next { background: repeating-linear-gradient(135deg, var(--surface) 0 10px, transparent 10px 20px); }
.o-card__demo--next span { padding: 6px 10px; background: var(--bg); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: .76rem; color: var(--muted); }
.o-card__note { color: var(--muted); font-size: var(--text-small); }

/* ---- the tailored band: graphite, the one dark block before the stack builder */
.o-band { display: grid; gap: 28px; align-items: center; padding: clamp(24px, 4vw, 40px); background: var(--graphite-900); color: var(--bg); border-radius: var(--radius-md); }
@media (min-width: 860px) { .o-band { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); } }
.o-band .mono-label { color: var(--suite-300); }
.o-band__lead { margin-top: 12px; color: var(--on-dark-muted); max-width: 48ch; }
.o-band__points { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 6px; }
.o-band__points li::before { content: '→ '; color: var(--suite-400); font-family: var(--font-mono); }
.o-band__offer { display: grid; gap: 12px; padding: 22px; background: var(--graphite-800); border-radius: var(--radius-md); }
.o-band__big { font-family: var(--font-heading); font-weight: 600; font-size: 2rem; line-height: 1; letter-spacing: -.03em; }
.o-band__then { font-family: var(--font-mono); font-size: .78rem; color: var(--on-dark-muted); }
.o-band :focus-visible { box-shadow: 0 0 0 4px var(--bg); }

/* ---- build your stack */
.o-stack { display: grid; gap: 16px; padding: clamp(20px, 3vw, 28px); background: #fbfaf6; border: 1px solid var(--line); border-radius: var(--radius-md); }
.o-stack__title { font-size: var(--text-h3); font-weight: 600; }
.o-stack__intro { color: var(--muted); }
.o-stack__rows { display: grid; gap: 8px; }
.o-pick { display: grid; grid-template-columns: auto 1fr; gap: 10px 14px; align-items: center; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius-md); cursor: pointer; }
@media (min-width: 640px) { .o-pick { grid-template-columns: auto 1fr auto; } }
.o-pick input { width: 20px; height: 20px; accent-color: #f2907c; }
.o-pick__name small { display: block; color: var(--muted); font-size: var(--text-small); }
.o-pick select { grid-column: 1 / -1; min-height: 40px; padding: 6px 8px; border: 1px solid var(--graphite-300); border-radius: var(--radius-sm); background: #fff; font-size: .9rem; max-width: 100%; }
@media (min-width: 640px) { .o-pick select { grid-column: auto; } }
.o-pick--next { border-style: dashed; color: var(--muted); cursor: default; }
.o-pick__box { width: 20px; height: 20px; border: 1.5px dashed var(--graphite-300); border-radius: var(--radius-sm); }
.o-stack__total { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; padding-top: 14px; border-top: 1px solid var(--line); }
.o-stack__total small { display: block; font-family: var(--font-mono); font-size: .74rem; color: var(--muted); }
.o-stack__total b { font-family: var(--font-heading); font-weight: 600; font-size: 1.8rem; font-variant-numeric: tabular-nums; }
/* Adding up is the script's job; without it there is nothing to add up, only the price list. */
html:not(.js) .o-stack__total p { display: none; }

/* ---- what every product has in common */
.o-family { list-style: none; margin: 28px 0 0; padding: 0; display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-md); overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); }
.o-family li { display: grid; gap: 4px; align-content: start; padding: 18px; background: var(--bg); }
.o-family b { font-family: var(--font-heading); font-weight: 600; }
.o-family span { color: var(--muted); font-size: var(--text-small); }

/* ---- the same product, on every platform */
.o-runs { display: grid; gap: 22px; padding: clamp(20px, 3.5vw, 32px); background: var(--graphite-900); color: var(--bg); border-radius: var(--radius-md); }
.o-runs .mono-label { color: var(--suite-300); }
.o-runs__head { display: flex; flex-wrap: wrap; gap: 18px; align-items: end; justify-content: space-between; }
.o-runs__title { max-width: 18ch; }
.o-runs__body { margin-top: 10px; color: var(--on-dark-muted); max-width: 46ch; }
.o-switch { display: inline-flex; flex-wrap: wrap; gap: 4px; padding: 4px; background: var(--graphite-800); border-radius: 999px; }
.o-switch button { padding: 8px 14px; border-radius: 999px; font-family: var(--font-mono); font-size: .76rem; color: var(--on-dark-muted); cursor: pointer; }
.o-switch button[aria-pressed="true"] { background: var(--suite-400); color: var(--graphite-900); }
.o-runs :focus-visible { box-shadow: 0 0 0 4px var(--bg); }
.o-screens { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
.o-screen { display: flex; flex-direction: column; min-width: 0; background: #fff; color: var(--graphite-900); border-radius: var(--radius-md); overflow: hidden; }
.o-screen__in { flex: 1; display: flex; flex-direction: column; }
.o-screen__bar { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid #f0eee8; }
.o-screen__bar b { font-family: var(--font-heading); font-weight: 600; font-size: .86rem; }
.o-pf { padding: 4px 7px; border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: .64rem; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; }
.o-pf--woocommerce { background: #efe7f6; color: #5b3a86; }
.o-pf--shopify { background: #e6f2e8; color: #2c6b3c; }
.o-pf--magento { background: #fbe9e1; color: #a2401a; }
.o-pf--own { background: var(--graphite-900); color: var(--bg); }
.o-screen__body { position: relative; flex: 1; display: grid; place-items: center; min-height: 190px; padding: 12px; background: #fff; }
.o-screen__body .o-search { width: 100%; }
.o-screen__body .o-search__res { box-shadow: none; border: 1px solid #ece9e1; font-size: .76rem; }
.o-screen__body .o-search__box { font-size: .84rem; min-height: 36px; padding: 6px 10px; }
.o-screen__body .o-spinimg { max-height: 150px; }
.o-screen__in--coming .o-screen__body > :not(.o-roadmap) { opacity: .5; filter: grayscale(.6); }
.o-screen__in--tailored { outline: 2px dashed var(--graphite-500); outline-offset: -2px; }
.o-roadmap { position: absolute; right: 10px; top: 10px; padding: 3px 7px; background: var(--accent-400); color: var(--graphite-900); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: .64rem; }
.o-screen__st { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-top: 1px solid #f0eee8; font-family: var(--font-mono); font-size: .72rem; color: var(--graphite-500); }
.o-mark { width: 11px; height: 11px; border-radius: 50%; flex: none; }
.o-mark--live { background: var(--suite-400); animation: o-ping 2.4s ease-out infinite; }
.o-mark--early { border: 2px solid var(--suite-400); animation: o-ping 2.4s ease-out infinite; }
.o-mark--coming { border: 2px dashed var(--graphite-400); }
.o-mark--tailored { width: 9px; height: 9px; margin-inline: 1px; border: 2px solid var(--graphite-900); border-radius: 1px; transform: rotate(45deg); }
@keyframes o-ping { 0% { box-shadow: 0 0 0 0 rgba(242,144,124,.6); } 70% { box-shadow: 0 0 0 9px rgba(242,144,124,0); } 100% { box-shadow: 0 0 0 0 rgba(242,144,124,0); } }
.o-runs__more { display: grid; gap: 12px; padding-top: 20px; border-top: 1px solid var(--graphite-700); }
.o-runs__more p { color: var(--on-dark-muted); font-size: var(--text-small); }
.o-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.o-chips li { display: inline-flex; align-items: center; gap: 7px; padding: 6px 12px; border: 1px solid var(--graphite-700); border-radius: 999px; font-family: var(--font-mono); font-size: .74rem; color: var(--graphite-100); }
.o-chips li::before { content: ''; width: 7px; height: 7px; border: 1.5px solid var(--suite-300); transform: rotate(45deg); }
.o-runs__foot { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; justify-content: space-between; }
.o-tally { display: flex; flex-wrap: wrap; gap: 6px 20px; font-family: var(--font-mono); font-size: .8rem; color: var(--on-dark-muted); }
.o-tally b { color: var(--bg); font-weight: 500; }

/* ================================================= a product page */
.o-phero { background: var(--suite-wash); text-align: center; }
.o-phero > .container { display: grid; justify-items: center; gap: 22px; }
.o-phero h1 { max-width: 14ch; margin: 0; font-size: var(--text-display); line-height: 1; }
.o-phero .lead { color: var(--muted); }
.o-offerline { display: flex; flex-wrap: wrap; gap: 14px 18px; align-items: center; justify-content: center; }
.o-offerline p { font-family: var(--font-mono); font-size: var(--text-mono); color: var(--muted); }
.o-offerline b { color: var(--fg); font-weight: 500; }

/* ---- the big search: one box, cycling through the kinds of search it understands */
.o-big { width: min(100%, 720px); text-align: left; }
.o-big__box { display: flex; align-items: center; min-height: 72px; padding: 16px 20px; background: #fff; color: var(--graphite-900);
  border: 3px solid var(--graphite-900); border-radius: var(--radius-md); box-shadow: 8px 8px 0 var(--suite-400);
  font-size: clamp(1.1rem, 2.6vw, 1.6rem); font-weight: 500; overflow: hidden; white-space: nowrap; }
.o-big__why { margin: 14px 0 8px; font-family: var(--font-mono); font-size: .76rem; letter-spacing: .1em; color: var(--muted); }
.o-big__res { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; min-height: 160px; align-content: start; }
.o-big__res li { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 11px 14px; background: #fff; color: var(--graphite-900); border: 1px solid #e2ded4; border-radius: var(--radius-md); font-weight: 500; }
.o-big__res li > span:first-child { display: flex; align-items: center; min-width: 0; }
.o-swatch { width: 26px; height: 26px; margin-right: 12px; border-radius: var(--radius-sm); flex: none; }

/* ---- the 360 hero: two photos, an arrow, the viewer */
.o-stage { width: min(100%, 820px); display: grid; gap: 16px; align-items: center; grid-template-columns: 1fr 1fr; text-align: left; }
.o-inputs { grid-column: 1 / -1; display: grid; gap: 10px; grid-template-columns: 1fr 1fr; }
.o-inputs figure { display: grid; gap: 4px; padding: 8px; background: #fff; border: 1px solid #e2ded4; border-radius: var(--radius-md); }
.o-inputs img { width: 100%; height: auto; background: #ebe8e0; border-radius: var(--radius-sm); }
.o-inputs figcaption { font-family: var(--font-mono); font-size: .68rem; color: var(--graphite-500); }
.o-arrow { display: none; font-family: var(--font-mono); font-size: 1.6rem; color: var(--suite-ink); text-align: center; }
.o-viewer { grid-column: 1 / -1; position: relative; aspect-ratio: 1; overflow: hidden; background: #ececea; color: var(--graphite-900);
  border: 3px solid var(--graphite-900); border-radius: var(--radius-md); box-shadow: 8px 8px 0 var(--suite-400);
  touch-action: pan-y; user-select: none; -webkit-user-select: none; }
@media (min-width: 680px) {
  .o-stage { grid-template-columns: 150px 40px minmax(0, 1fr); }
  .o-inputs { grid-column: auto; grid-template-columns: 1fr; }
  .o-arrow { display: block; }
  .o-viewer { grid-column: auto; }
}
.o-viewer__still, .o-viewer canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.o-viewer__still { object-fit: contain; }
.o-viewer canvas { cursor: grab; }
.o-viewer canvas:active { cursor: grabbing; }
.o-viewer.is-ready .o-viewer__still { visibility: hidden; }
.o-viewer__gal { position: absolute; left: 12px; top: 10px; font-family: var(--font-mono); font-size: .68rem; color: var(--graphite-500); pointer-events: none; }
.o-viewer__hint { position: absolute; left: 12px; bottom: 12px; padding: 5px 9px; background: var(--graphite-900); color: var(--bg); border-radius: 999px;
  font-family: var(--font-mono); font-size: .7rem; pointer-events: none; transition: opacity var(--dur-fade) var(--ease); }
.o-viewer.is-touched .o-viewer__hint { opacity: 0; }
.o-viewer__deg { position: absolute; right: 12px; bottom: 12px; font-family: var(--font-mono); font-size: .72rem; color: var(--suite-ink); font-variant-numeric: tabular-nums; pointer-events: none; }
.o-viewer__load { position: absolute; left: 12px; right: 12px; bottom: 40px; height: 2px; background: var(--line); pointer-events: none; }
.o-viewer__load span { display: block; height: 100%; width: 0; background: var(--suite-400); transition: width var(--dur-fade) var(--ease); }
.o-viewer.is-ready .o-viewer__load { display: none; }
.o-dial { width: min(100%, 820px); display: flex; align-items: center; gap: 12px; font-family: var(--font-mono); font-size: .72rem; color: var(--muted); }
.o-dial input { flex: 1; accent-color: #f2907c; }
html:not(.js) .o-dial { display: none; }

/* ---- the blocks */
.o-block h2 { margin-bottom: 28px; max-width: 24ch; }
.o-proof { display: grid; gap: 28px; align-items: center; }
@media (min-width: 900px) { .o-proof { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); } }
.o-proof__n { font-family: var(--font-heading); font-weight: 600; font-size: clamp(3rem, 2rem + 4vw, 4.5rem); line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.o-proof__text { margin-top: 14px; color: var(--muted); max-width: 46ch; }
.o-proof img { width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius-md); }
.o-steps { list-style: none; margin: 0; padding: 0; counter-reset: o-step; display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); }
.o-steps li { display: grid; gap: 6px; align-content: start; padding: 18px; background: #fbfaf6; border: 1px solid var(--line); border-radius: var(--radius-md); }
.o-steps li::before { counter-increment: o-step; content: counter(o-step); font-family: var(--font-heading); font-weight: 600; font-size: 2.2rem; line-height: 1; color: var(--suite-ink); }
.o-steps b { font-family: var(--font-heading); font-weight: 600; }
.o-steps span { color: var(--muted); font-size: var(--text-small); }
.o-promise { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-md); overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); }
.o-promise li { display: grid; gap: 6px; align-content: start; padding: 20px; background: var(--bg); }
.o-promise__big { font-family: var(--font-heading); font-weight: 600; font-size: 2rem; line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.o-promise span:last-child { color: var(--muted); font-size: var(--text-small); }
.o-prices { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); }
.o-price { display: grid; gap: 6px; align-content: start; padding: 16px; background: #fff; border: 1px solid var(--line); border-radius: var(--radius-md); }
.o-price--free { outline: 3px solid var(--suite-400); outline-offset: -3px; }
.o-price h3 { font-size: 1.05rem; font-weight: 600; }
.o-price__amount { font-family: var(--font-heading); font-weight: 600; font-size: 1.8rem; line-height: 1; font-variant-numeric: tabular-nums; }
.o-price__line { font-family: var(--font-mono); font-size: .72rem; color: var(--muted); }
.o-price__each { font-family: var(--font-mono); font-size: .78rem; color: var(--suite-ink); }
.o-calc { display: grid; gap: 12px; margin-top: 16px; padding: 20px; background: #fff; border: 1px solid var(--line); border-radius: var(--radius-md); }
.o-calc label { font-weight: 500; }
.o-calc__row { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.o-calc input { flex: 1; min-width: 200px; accent-color: #f2907c; }
.o-calc output { font-family: var(--font-heading); font-weight: 600; font-size: 1.6rem; font-variant-numeric: tabular-nums; min-width: 3ch; text-align: right; }
.o-calc__result { font-family: var(--font-mono); font-size: .76rem; color: var(--muted); min-height: 1.4em; }
html:not(.js) .o-calc { display: none; }
.o-feats { margin-top: 20px; display: grid; gap: 10px; }
.o-feats p { font-weight: 500; }
.o-feats ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.o-feats li { padding: 6px 12px; background: #fff; border: 1px solid var(--line); border-radius: 999px; font-size: var(--text-small); }
.page-product .breadcrumb { padding-bottom: 8px; background: #fdf0ec; }
.o-back { margin-top: 20px; font-family: var(--font-mono); font-size: var(--text-mono); }
.o-back a { color: var(--suite-ink); }

/* ---- the bar that stays: what it is, and the one button. The right padding keeps the button
   clear of the contact buddy, who lives in the bottom-right corner. */
.o-sticky { position: sticky; bottom: 0; z-index: 5; display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; justify-content: space-between;
  padding: 10px 104px calc(10px + env(safe-area-inset-bottom, 0px)) 20px; background: var(--graphite-900); color: var(--bg); }
.o-sticky p { font-family: var(--font-mono); font-size: .8rem; }
.o-sticky__btn { min-height: 40px; }
.o-sticky :focus-visible { box-shadow: 0 0 0 4px var(--bg); }

@media (prefers-reduced-motion: reduce) {
  .o-caret, .o-mark--live, .o-mark--early { animation: none; }
}
