/* =========================================================================
   milkycode — mission-control cinematic
   Committed dark palette: nocturnal blue-black surface, cyan as signal,
   amber as the single action colour.
   ========================================================================= */

@layer reset, tokens, base, layout, components, motion;

/* ------------------------------------------------------------------ reset */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; }
  html { -webkit-text-size-adjust: 100%; }
  body { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
  img, picture, canvas, svg, video { display: block; max-width: 100%; }
  input, button, textarea, select { font: inherit; color: inherit; }
  button { background: none; border: 0; cursor: pointer; }
  a { color: inherit; }
  ul, ol { list-style: none; padding: 0; }
  :where(h1, h2, h3, h4) { font-weight: inherit; }
}

/* ----------------------------------------------------------------- tokens */
@layer tokens {
  :root {
    /* surface ramp */
    --void:        oklch(0.135 0.024 248);
    --void-deep:   oklch(0.105 0.020 250);
    --surface:     oklch(0.185 0.026 245);
    --raise:       oklch(0.232 0.028 243);

    /* text ramp — every step ≥4.5:1 on --void */
    --ink:         oklch(0.970 0.008 230);
    --mute:        oklch(0.800 0.020 232);
    --faint:       oklch(0.660 0.024 232);

    /* signal */
    --signal:      oklch(0.815 0.130 218);
    --signal-dim:  oklch(0.640 0.115 224);
    --signal-glow: oklch(0.815 0.130 218 / 0.28);

    /* action */
    --amber:       oklch(0.805 0.155 68);
    --amber-dim:   oklch(0.640 0.130 66);

    /* one tint every photograph is graded through */
    --duotone:     oklch(0.45 0.052 234);

    /* corner radius: one value for controls, cards and every image frame */
    --radius: 15px;
    --radius-sm: 8px;

    --line:        oklch(0.97 0.01 230 / 0.10);
    --line-strong: oklch(0.97 0.01 230 / 0.22);

    /* type — Teko carries every display voice, Kanit every reading voice */
    --display: 'Teko', 'Archivo Narrow', 'Oswald', system-ui, sans-serif;
    --sans: 'Kanit', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', 'Menlo', monospace;

    --step--1: clamp(0.8rem, 0.775rem + 0.12vw, 0.875rem);
    --step-0:  clamp(1rem, 0.965rem + 0.18vw, 1.125rem);
    --step-1:  clamp(1.25rem, 1.18rem + 0.34vw, 1.5rem);
    --step-2:  clamp(1.6rem, 1.46rem + 0.68vw, 2.05rem);
    --step-3:  clamp(2rem, 1.75rem + 1.9vw, 3.5rem);
    --step-4:  clamp(2.5rem, 2.05rem + 3.4vw, 5rem);
    --step-5:  clamp(3.5rem, 2.5rem + 4.8vw, 6rem);
    --step-6:  clamp(2.75rem, 1.9rem + 7.4vw, 7.2rem);

    /* rhythm */
    --gutter: clamp(1.25rem, 4.2vw, 4.5rem);
    --measure: 68ch;
    --section-y: clamp(5.5rem, 11vw, 11rem);
    --shell: 1480px;
    /* full-bleed strips align their contents to the shell column */
    --edge: max(var(--gutter), (100% - var(--shell)) / 2);

    /* motion */
    --e-expo: cubic-bezier(0.16, 1, 0.3, 1);
    --e-quart: cubic-bezier(0.25, 1, 0.35, 1);
    --e-inout: cubic-bezier(0.65, 0, 0.35, 1);

    /* z-scale */
    --z-canvas: 0;
    --z-content: 10;
    --z-sticky: 40;
    --z-nav: 60;
    --z-overlay: 80;

    color-scheme: dark;
  }
}

/* ------------------------------------------------------------------- base */
@layer base {
  html { scroll-behavior: smooth; }

  body {
    background: var(--void);
    color: var(--mute);
    font-family: var(--sans);
    font-size: var(--step-0);
    font-weight: 300;
    line-height: 1.72;                 /* light-on-dark bump, plus Kanit's x-height */
    font-synthesis-weight: none;
    overflow-x: clip;
  }

  /* The grain + vignette that keeps large dark fields from banding. */
  body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: var(--z-overlay);
    pointer-events: none;
    opacity: 0.42;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.30'/%3E%3C/svg%3E");
    mix-blend-mode: overlay;
  }

  /* Teko is drawn condensed and tall: it wants no negative tracking, a hair
     of positive instead, and a line-height that respects its Devanagari-sized
     ascenders — umlauts sit high in this face. */
  /* All-caps Teko: caps carry no descenders but the umlaut marks sit higher
     than the cap line, so the leading opens up a little rather than closing. */
  h1, h2, h3, .display {
    font-family: var(--display);
    font-weight: 600;
    text-transform: uppercase;
    line-height: 0.75;
    letter-spacing: 0;
    color: var(--ink);
    text-wrap: balance;
  }

  h1 { font-size: var(--step-6); font-weight: 700; }
  h2 { font-size: var(--step-4); }
  h3 { font-size: var(--step-3); font-weight: 500; line-height: 1; }

  p { text-wrap: pretty; }
  p.lead { font-size: var(--step-1); line-height: 1.55; color: var(--mute); max-width: 30ch; }

  ::selection { background: var(--signal); color: oklch(0.12 0.02 245); }

  :focus-visible {
    outline: 2px solid var(--signal);
    outline-offset: 3px;
    border-radius: 2px;
  }

  .skip {
    position: absolute; left: 1rem; top: -4rem; z-index: 999;
    background: var(--signal); color: oklch(0.13 0.02 245);
    padding: 0.6rem 1rem; font: 500 var(--step--1)/1 var(--mono);
    transition: top 0.2s var(--e-quart);
  }
  .skip:focus { top: 1rem; }

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

/* ----------------------------------------------------------------- layout */
@layer layout {
  .shell {
    width: min(100% - var(--gutter) * 2, var(--shell));
    margin-inline: auto;
  }
  .shell-wide { width: min(100% - var(--gutter), 1760px); margin-inline: auto; }

  section { position: relative; }
  .band { padding-block: var(--section-y); }

  .rule {
    height: 1px;
    background: var(--line);
    border: 0;
  }

  /* A section head that isn't the uppercase-eyebrow reflex: the label sits
     inline with the rule, as a survey marker, not a kicker. */
  .marker {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: clamp(1rem, 2.4vw, 2.2rem);
    margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
  }
  .marker__id {
    font: 500 var(--step--1)/1 var(--mono);
    color: var(--signal);
    letter-spacing: 0.06em;
    white-space: nowrap;
  }
  .marker__id::before {
    content: '';
    display: inline-block;
    width: 6px; height: 6px;
    margin-right: 0.7em;
    background: var(--signal);
    box-shadow: 0 0 10px var(--signal);
    transform: rotate(45deg);
    vertical-align: middle;
  }
  .marker__line { height: 1px; background: linear-gradient(90deg, var(--line-strong), transparent); }
  /* A third column for the score; the rule between still takes the slack. */
  .marker--rated { grid-template-columns: auto minmax(1.5rem, 1fr) auto; }
}

/* ------------------------------------------------------------- components */
@layer components {

  /* ---------------------------------------------------------------- nav */
  /* The bar spans the viewport; its row sits on the same column as the
     content, so logo and menu line up with every section below. */
  .nav {
    position: fixed;
    inset: 0 0 auto;
    z-index: var(--z-nav);
    padding-block: clamp(0.85rem, 1.6vw, 1.35rem);
    transition: padding 0.5s var(--e-quart);
  }
  /* The frosted backing lives on a pseudo-element, never on .nav itself.
     backdrop-filter turns an element into a containing block for fixed-position
     descendants — with it on .nav, the mobile sheet (position: fixed; inset: 0)
     resolved against the 136px bar instead of the viewport and opened as a
     stub. Only once scrolled, because that is when the filter switched on. */
  .nav::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: oklch(0.135 0.024 248 / 0.72);
    backdrop-filter: blur(18px) saturate(1.3);
    border-bottom: 1px solid var(--line);
    opacity: 0;
    transition: opacity 0.5s var(--e-quart);
  }
  .nav[data-stuck='true']::before,
  .nav[data-panel='true']::before { opacity: 1; }
  .nav__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: clamp(1rem, 2vw, 2rem);
  }
  .nav[data-stuck='true'] { padding-block: 0.6rem; }

  /* The wordmark is the supplied artwork, white on transparent — never
     re-typeset, and never dimmed: on hover it tilts and grows instead. */
  .brand {
    display: inline-flex;
    align-items: center;
    flex: none;
    text-decoration: none;
  }
  .brand img {
    /* Two independent factors, multiplied — the bar's scroll-shrink and the
       hover growth both live on `transform`, so a single rule for each would
       cancel the other out whenever they are active together. */
    --logo-shrink: 1;
    --logo-grow: 1;
    --logo-tilt: 0deg;

    display: block;
    width: auto;
    height: clamp(28px, 3vw, 38px);       /* ≈200px wide at full size */
    /* the artwork is bitmap: never let it render above its native density */
    max-width: 100%;
    /* Pivots on its own left edge, which is where it is anchored in the bar:
       it grows and tilts to the right without shifting off its alignment. */
    transform-origin: left center;
    /* scale, not height — animating a layout property would reflow the bar */
    transform: rotate(var(--logo-tilt)) scale(calc(var(--logo-shrink) * var(--logo-grow)));
    transition: transform 0.5s var(--e-expo);
  }
  .nav[data-stuck='true'] .brand img { --logo-shrink: 0.9; }
  .brand--lg img { height: clamp(34px, 4vw, 46px); }
  .brand:hover img,
  .brand:focus-visible img { --logo-grow: 1.05; --logo-tilt: -2.5deg; }

  .nav__links { display: flex; align-items: center; gap: clamp(1rem, 2.1vw, 2.1rem); }
  .nav__link {
    position: relative;
    font-family: var(--display);
    /* The ramp shifted down 0.1rem at both ends; the vw term follows so the
       clamp still hands over at the same viewport widths it used to. */
    font-size: clamp(1.05rem, 1.35vw, 1.3rem);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.085em;      /* condensed caps need real air */
    color: var(--mute);
    text-decoration: none;
    padding-block: 0.35rem;
    transition: color 0.3s var(--e-quart);
  }
  .nav__link::after {
    content: '';
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 1px;
    background: var(--signal);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.45s var(--e-expo);
  }
  .nav__link:hover { color: var(--ink); }
  .nav__link:hover::after { transform: scaleX(1); transform-origin: left; }

  /* ------------------------------------------------------------ mega menu */
  .menu {
    display: flex;
    align-items: center;
    gap: clamp(0.9rem, 1.9vw, 2rem);
  }
  /* static, so the panels position against the fixed bar and can span it */
  .menu__item { position: static; }

  .menu__trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    cursor: pointer;
  }
  .menu__chev { transition: transform 0.4s var(--e-expo); flex: none; }
  .menu__item[data-open='true'] .menu__chev { transform: rotate(180deg); }
  .menu__item[data-open='true'] > .nav__link { color: var(--ink); }
  .menu__item[data-open='true'] > .nav__link::after { transform: scaleX(1); transform-origin: left; }

  .mega {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: -1;
    background: oklch(0.135 0.024 248 / 0.72);
    backdrop-filter: blur(18px) saturate(1.3);
    border-block: 1px solid var(--line);
    opacity: 0;
    pointer-events: none;
    transform: translateY(-12px);
    transition: opacity 0.36s var(--e-quart), transform 0.5s var(--e-expo);
  }
  /* Focus and the a11y tree are handled by `inert`, toggled in JS — a
     `visibility` transition makes the open state ambiguous mid-flight. */
  .menu__item[data-open='true'] .mega {
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }
  /* Before JS runs (or with it off) hover and focus still open the panels, so
     the menu is never a dead end. Once .js-motion exists, JS owns the state. */
  html:not(.js-motion) .menu__item--panel:hover .mega,
  html:not(.js-motion) .menu__item--panel:focus-within .mega {
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }
  .mega__inner { padding-block: clamp(1.8rem, 3vw, 2.8rem) clamp(1.6rem, 2.6vw, 2.4rem); }

  .mega__grid {
    display: grid;
    gap: clamp(1.6rem, 3.4vw, 3.4rem);
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  }
  .mega__grid--products { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }

  /* The news column is a different kind of content from the product cards, so
     it gets its own ground and a rule rather than just more gap. */
  .mega__col--news {
    padding-left: clamp(1.4rem, 2.6vw, 2.6rem);
    border-left: 1px solid var(--line);
    position: relative;
  }
  .mega__col--news::before {
    content: '';
    position: absolute;
    inset: calc(var(--radius) * -1) calc(var(--radius) * -1) calc(var(--radius) * -1) 0;
    z-index: -1;
    border-radius: 0 var(--radius) var(--radius) 0;
    background: oklch(0.815 0.13 218 / 0.045);
    pointer-events: none;
  }
  .mfeat__meta {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
  }

  .mega__label {
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.08em;
    color: var(--signal);
    margin-bottom: 1.2rem;
  }

  .mega__list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.15rem 1.4rem; }
  .mega__list a {
    display: grid;
    gap: 0.2rem;
    padding: 0.6rem 0.7rem;
    margin-inline: -0.7rem;
    text-decoration: none;
    transition: background-color 0.3s var(--e-quart);
  }
  .mega__list a:hover, .mega__list a:focus-visible { background: oklch(0.815 0.13 218 / 0.07); }
  .mega__name {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    letter-spacing: 0.05em;
    line-height: 0.9;
    font-size: 1.2rem;
    color: var(--ink);
    transition: color 0.3s var(--e-quart);
  }
  .mega__list a:hover .mega__name { color: var(--signal); }
  .mega__desc { font-size: var(--step--1); font-weight: 500; color: var(--mute); line-height: 1.5; }

  .mfeat {
    display: grid;
    grid-template-columns: minmax(0, 130px) minmax(0, 1fr);
    gap: 1rem;
    align-items: center;
    text-decoration: none;
    border: 1px solid var(--line);
    padding: 0.7rem;
    transition: border-color 0.4s var(--e-quart), background-color 0.4s var(--e-quart);
  }
  .mfeat:hover, .mfeat:focus-visible {
    border-color: oklch(0.815 0.13 218 / 0.34);
    background: oklch(0.815 0.13 218 / 0.05);
  }
  .mfeat__frame { position: relative; overflow: hidden; isolation: isolate; aspect-ratio: 16 / 10; }
  .mfeat__frame img { width: 100%; height: 100%; object-fit: cover; filter: saturate(0.3) brightness(0.62) contrast(1.12); transition: transform 1s var(--e-expo); }
  .mfeat__frame::before { content: ''; position: absolute; inset: 0; z-index: 1; background: var(--duotone); mix-blend-mode: color; }
  .mfeat:hover .mfeat__frame img { transform: scale(1.08); }
  .mfeat__body { display: grid; gap: 0.35rem; }
  .mfeat__title {
    font-family: var(--display); text-transform: uppercase;
    font-weight: 600; letter-spacing: 0.05em;
    line-height: 0.9; font-size: 1.15rem; color: var(--ink);
  }
  .mfeat__text { font-size: var(--step--1); font-weight: 500; color: var(--mute); line-height: 1.5; }

  .mega__more {
    display: inline-flex; align-items: center; gap: 0.55rem;
    margin-top: 1rem;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--mute);
    text-decoration: none;
    transition: color 0.3s;
  }
  .mega__more svg { transition: transform 0.45s var(--e-expo); }
  .mega__more:hover { color: var(--signal); }
  .mega__more:hover svg { transform: translateX(5px); }

  /* products */
  .pcards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(0.8rem, 1.6vw, 1.4rem); }
  .pcard {
    position: relative;
    display: grid;
    align-content: start;
    gap: 0.5rem;
    padding: clamp(1.1rem, 1.8vw, 1.5rem);
    border: 1px solid var(--line);
    text-decoration: none;
    overflow: hidden;
    transition: border-color 0.4s var(--e-quart), background-color 0.4s var(--e-quart), transform 0.5s var(--e-expo);
  }
  .pcard::after {
    content: '';
    position: absolute; inset: auto 0 0 0; height: 1px;
    background: var(--signal);
    transform: scaleX(0); transform-origin: left;
    transition: transform 0.6s var(--e-expo);
  }
  .pcard:hover, .pcard:focus-visible {
    border-color: oklch(0.815 0.13 218 / 0.34);
    background: oklch(0.815 0.13 218 / 0.05);
    transform: translateY(-3px);
  }
  .pcard:hover::after, .pcard:focus-visible::after { transform: scaleX(1); }
  .pcard__mark {
    display: grid; place-items: center;
    width: 38px; height: 38px;
    border: 1px solid oklch(0.815 0.13 218 / 0.34);
    color: var(--signal);
    font: 500 1.05rem/1 var(--mono);
    margin-bottom: 0.4rem;
  }
  .pcard__name {
    font-family: var(--display); text-transform: uppercase;
    font-weight: 600; letter-spacing: 0.05em;
    line-height: 0.9; font-size: 1.4rem; color: var(--ink);
    transition: color 0.3s var(--e-quart);
  }
  .pcard:hover .pcard__name { color: var(--signal); }
  .pcard__desc { font-size: var(--step--1); font-weight: 500; color: var(--mute); line-height: 1.55; }
  .pcard__go {
    display: inline-flex; align-items: center; gap: 0.5rem;
    margin-top: 0.5rem;
    font: 500 var(--step--1)/1 var(--mono); letter-spacing: 0.06em;
    color: var(--mute);
  }
  .pcard__go svg { transition: transform 0.45s var(--e-expo); }
  .pcard:hover .pcard__go svg { transform: translateX(5px); }

  /* the compact dropdown */
  .menu__item--drop { position: relative; }
  .mega--drop {
    left: 0; right: auto;
    top: 100%;
    margin-top: clamp(0.85rem, 1.6vw, 1.35rem);
    width: min(20rem, calc(100vw - 2 * var(--gutter)));
    border: 1px solid var(--line);
  }
  .nav[data-stuck='true'] .mega--drop { margin-top: 0.6rem; }
  .mega--drop .mega__inner { padding: 0.6rem; }
  .drop { display: grid; }
  .drop a {
    display: block;
    padding: 0.72rem 0.85rem;
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    letter-spacing: 0.05em;
    line-height: 1;
    font-size: 1.1rem;
    color: var(--mute);
    text-decoration: none;
    transition: color 0.3s var(--e-quart), background-color 0.3s var(--e-quart);
  }
  .drop a:hover, .drop a:focus-visible { color: var(--signal); background: oklch(0.815 0.13 218 / 0.07); }

  /* The header CTA sits inside the menu row, so it follows the menu's voice
     rather than the body buttons'. */
  .nav .btn {
    font-family: var(--display);
    font-size: clamp(1.05rem, 1.3vw, 1.25rem);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    /* The body line-height of 1.65 was adding ~13px of dead space above and
       below a single line of caps — that, not the type size, is what made the
       button read as oversized. */
    line-height: 1;
    gap: 0.5rem;
    /* asymmetric on purpose: measured off the rendered caps, this is what
       lands the ink optically centred rather than metrically centred */
    padding: 0.695rem 1rem 0.465rem;
  }
  .nav .btn__arrow { width: 11px; }

  .nav__toggle { display: none; }

  /* --------------------------------------------------------------- button */
  .btn {
    --btn-fg: var(--ink);
    --btn-line: var(--line-strong);
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.85rem 1.4rem;
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--btn-fg);
    text-decoration: none;
    border: 1px solid var(--btn-line);
    background: transparent;
    overflow: hidden;
    isolation: isolate;
    transition: color 0.35s var(--e-quart), border-color 0.35s var(--e-quart);
  }
  .btn::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--btn-fill, var(--ink));
    transform: translateY(101%);
    transition: transform 0.5s var(--e-expo);
  }
  .btn:hover, .btn:focus-visible { color: var(--btn-hover-fg, oklch(0.13 0.02 245)); }
  .btn:hover::before, .btn:focus-visible::before { transform: translateY(0); }
  .btn__arrow { transition: transform 0.45s var(--e-expo); }
  .btn:hover .btn__arrow { transform: translateX(4px); }

  .btn--action {
    --btn-line: oklch(0.805 0.155 68 / 0.55);
    --btn-fg: var(--amber);
    --btn-fill: var(--amber);
    --btn-hover-fg: oklch(0.16 0.03 60);
  }
  .btn--ghost { --btn-line: var(--line); --btn-fg: var(--mute); }

  /* ----------------------------------------------------------------- hero */
  /* The scroll stop: the section is taller than the viewport, the stage pins
     inside it, and everything below only starts moving once the flight is over.
     No scroll hijacking — the page keeps its own scrollbar and its own speed. */
  .hero {
    position: relative;
    height: 420svh;
    --hero-p: 0;
    --hero-out: 0;
  }
  .hero__stage {
    position: sticky;
    top: 0;
    height: 100svh;
    display: grid;
    grid-template-columns: minmax(0, 1fr);   /* nowrap children must not widen it */
    grid-template-rows: 1fr auto;
    isolation: isolate;
    overflow: clip;
  }
  /* the scene dissolves into the page ground as the flight ends */
  .hero__stage::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 5;
    pointer-events: none;
    background: var(--void);
    opacity: calc(var(--hero-out) * 0.46);
  }
  .hero__canvas {
    position: absolute;
    inset: 0;
    z-index: var(--z-canvas);
    width: 100%;
    height: 100%;
    background: var(--void-deep);
  }
  /* Legibility scrim — the type sits west, so the scrim leans west too. */
  .hero__scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
      linear-gradient(100deg, oklch(0.105 0.02 250 / 0.94) 0%,
                              oklch(0.105 0.02 250 / 0.72) 32%,
                              oklch(0.105 0.02 250 / 0.12) 62%,
                              transparent 84%),
      linear-gradient(to top, oklch(0.105 0.02 250 / 0.72) 2%, oklch(0.105 0.02 250 / 0.2) 30%, transparent 46%);
  }
  .hero__inner {
    position: relative;
    z-index: var(--z-content);
    min-width: 0;
    display: flex;
    align-items: center;                       /* optically centred between the
                                                  fixed header and the readout */
    padding-block: clamp(5.5rem, 11vh, 8rem) clamp(2.5rem, 5vh, 4.5rem);
    padding-inline: var(--edge);
  }
  .hero__copy {
    width: min(100%, 62ch);
    will-change: transform, opacity;
    opacity: calc(1 - var(--hero-out));
    transform: translate3d(0, calc(var(--hero-out) * -8vh), 0);
  }

  /* Three chapters share one cell, so the block never resizes as they swap.
     Only stacked once JS is running — without it they read as three sections. */
  .js-motion .hero__copy { display: grid; grid-template-rows: auto auto; }
  .js-motion .hero__step {
    grid-area: 1 / 1;
    align-self: center;
    opacity: 0;
    transform: translate3d(0, 3.5vh, 0);
    pointer-events: none;
    transition: opacity 0.75s var(--e-quart), transform 0.9s var(--e-expo);
  }
  .js-motion .hero__step[data-active] {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }
  /* Chapters that have already passed leave upward, not back down. */
  .js-motion .hero__step[data-past] { transform: translate3d(0, -3.5vh, 0); }

  .hero__step + .hero__step { margin-top: clamp(2rem, 5vh, 4rem); }
  .js-motion .hero__step + .hero__step { margin-top: 0; }

  .hero__title--sub {
    display: block;
    max-width: 20ch;
    font-size: var(--step-5);
  }
  .hero__title em {
    font-style: normal;
    color: var(--signal);
    text-shadow: 0 0 42px var(--signal-glow);
  }

  .hero__facts {
    display: grid;
    gap: 0.55rem;
    margin-top: clamp(1.4rem, 3vh, 2.2rem);
    padding-top: clamp(1.2rem, 2.4vh, 1.8rem);
    border-top: 1px solid var(--line);
    max-width: 44ch;
  }
  .hero__facts li {
    display: grid;
    grid-template-columns: 2.6rem 1fr;
    align-items: baseline;
    gap: 0.6rem;
    font-size: var(--step--1);
    color: var(--mute);
  }
  .hero__facts b {
    font-family: var(--display);
    font-size: 1.7rem;
    font-weight: 600;
    line-height: 0.9;
    letter-spacing: 0.02em;
    color: var(--signal);
  }

  /* Chapter markers — and the only way to reach chapter 3 without scrolling. */
  .hero__dots {
    display: flex;
    gap: 0.55rem;
    margin-top: clamp(2rem, 5vh, 3.5rem);
  }
  .js-motion .hero__dots { grid-row: 2; }
  .hero__dots button {
    display: block;
    padding-block: 13px;          /* invisible tap target, 28px + rule */
    width: 34px;
    transition: width 0.55s var(--e-expo);
  }
  .hero__dots button::before {
    content: '';
    display: block;
    height: 2px;
    border-radius: 2px;
    background: var(--line-strong);
    transition: background-color 0.5s var(--e-quart);
  }
  .hero__dots button:hover::before { background: var(--mute); }
  .hero__dots li[data-active] button { width: 54px; }
  .hero__dots li[data-active] button::before { background: var(--signal); }

  /* The headline is a composition, not a paragraph: two full-width words, then
     a baseline row where the secondary discipline sits small beside "und". */
  .hero__title {
    display: grid;
    justify-items: start;
    max-width: 12ch;
  }
  .hero__line { display: block; }
  .hero__row {
    display: flex;
    align-items: flex-end;
    gap: clamp(0.9rem, 2.2vw, 2.2rem);
  }
  .hero__and { order: 2; }
  .hero__side {
    order: 1;
    align-self: flex-end;
    font-size: 0.27em;
    line-height: 1.06;
    letter-spacing: 0.05em;
    color: var(--signal);
    padding-bottom: 0.5em;
    padding-right: 1.4em;
    border-bottom: 1px solid var(--line-strong);
    text-shadow: 0 0 40px var(--signal-glow);
  }
  .hero__side span { display: block; }
  .hero__badge {
    order: 3;
    align-self: center;
    display: grid;
    place-items: center;
    width: clamp(68px, 7.2vw, 98px);
    aspect-ratio: 1;
    border-radius: 50%;
    border: 1px solid oklch(0.815 0.13 218 / 0.38);
    background: oklch(0.815 0.13 218 / 0.07);
    flex: none;
  }
  .hero__badge span {
    writing-mode: vertical-rl;
    white-space: nowrap;          /* the circle is the frame, not the measure */
    font: 500 clamp(0.58rem, 0.72vw, 0.72rem)/1 var(--mono);
    letter-spacing: 0.08em;
    text-transform: none;
    color: var(--signal);
  }

  .hero__lead {
    margin-top: clamp(1.6rem, 3vw, 2.6rem);
    max-width: 46ch;
    font-size: var(--step-1);
    line-height: 1.5;
    color: var(--mute);
  }
  .hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem;
    margin-top: clamp(2rem, 3.6vw, 3rem);
  }

  /* telemetry strip along the base of the hero */
  .telemetry {
    position: relative;
    z-index: var(--z-content);
    min-width: 0;
    opacity: calc(1 - var(--hero-out));
    display: flex;
    align-items: center;
    gap: clamp(1rem, 3vw, 3rem);
    padding: 0.85rem var(--edge);
    border-top: 1px solid var(--line);
    background: oklch(0.105 0.02 250 / 0.5);
    backdrop-filter: blur(8px);
    font: 400 var(--step--1)/1 var(--mono);
    color: var(--faint);
    letter-spacing: 0.02em;
    overflow: hidden;
    white-space: nowrap;
  }
  .telemetry span b { color: var(--signal); font-weight: 500; }
  .telemetry__scroll { margin-left: auto; display: inline-flex; align-items: center; gap: 0.6rem; color: var(--mute); }
  .telemetry__scroll { opacity: calc(1 - var(--hero-p) * 2.4); }
  .telemetry__scroll i {
    display: block; width: 1px; height: 14px;
    background: linear-gradient(var(--signal), transparent);
    animation: trickle 1.9s var(--e-inout) infinite;
  }
  .telemetry__hide { display: none; }

  /* ------------------------------------------------------------- manifest */
  .manifest { padding-block: clamp(5rem, 10vw, 9rem) clamp(4rem, 7vw, 6.5rem); }
  .manifest__text {
    font-family: var(--display);
    font-size: var(--step-3);
    font-weight: 500;
    line-height: 1.04;
    letter-spacing: 0.004em;
    color: var(--ink);
    max-width: 28ch;
  }
  .manifest__grid {
    display: grid;
    gap: clamp(2rem, 5vw, 5rem);
    grid-template-columns: 1fr;
  }
  @media (min-width: 60rem) {
    .manifest__grid { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); align-items: end; }
  }
  .manifest__aside p { max-width: 46ch; }
  .manifest__aside p + p { margin-top: 1.2rem; }
  .word { display: inline-block; }

  /* ------------------------------------------------------------ practices */
  .practice { display: grid; gap: clamp(2rem, 4vw, 4rem); grid-template-columns: 1fr; }
  @media (min-width: 62rem) {
    .practice { grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); align-items: start; }
  }

  .practice__stage {
    position: sticky;
    top: clamp(6rem, 14vh, 9rem);
    aspect-ratio: 4 / 5;
    border: 1px solid var(--line);
    overflow: hidden;
    isolation: isolate;
    background: var(--void-deep);
  }
  .practice__stage img {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    opacity: 0;
    transform: scale(1.08);
    filter: saturate(0.32) brightness(0.56) contrast(1.14);
    transition: opacity 0.85s var(--e-quart), transform 1.4s var(--e-expo);
  }
  .practice__stage img[data-active] { opacity: 1; transform: scale(1); }
  .practice__stage::before {
    content: '';
    position: absolute; inset: 0; z-index: 1;
    background: var(--duotone);
    mix-blend-mode: color;
  }
  .practice__stage::after {
    content: '';
    position: absolute; inset: 0; z-index: 2;
    background: linear-gradient(to top, oklch(0.105 0.02 250 / 0.8), transparent 55%),
                radial-gradient(120% 80% at 80% 10%, oklch(0.815 0.13 218 / 0.10), transparent 60%);
  }
  .practice__count {
    position: absolute;
    left: 1rem; bottom: 0.9rem;
    z-index: 2;
    font: 500 var(--step--1)/1 var(--mono);
    color: var(--signal);
    letter-spacing: 0.08em;
  }

  .practice__list { border-top: 1px solid var(--line); }
  .practice__item { border-bottom: 1px solid var(--line); }
  .practice__btn {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: baseline;
    gap: clamp(0.9rem, 2vw, 1.8rem);
    width: 100%;
    padding: clamp(1.3rem, 2.4vw, 2rem) 0;
    text-align: left;
    color: var(--mute);
    transition: color 0.35s var(--e-quart);
  }
  .practice__btn:hover, .practice__item[data-open='true'] .practice__btn { color: var(--ink); }
  .practice__tag {
    font: 400 var(--step--1)/1 var(--mono);
    color: var(--faint);
    white-space: nowrap;
    transition: color 0.35s var(--e-quart);
  }
  .practice__item[data-open='true'] .practice__tag { color: var(--signal); }
  .practice__name {
    font-family: var(--display);
    font-size: clamp(1.6rem, 3vw, 2.6rem);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.024em;
    line-height: 1.02;
    color: currentColor;
  }
  .practice__plus {
    width: 13px; height: 13px;
    position: relative;
    align-self: center;
    flex: none;
  }
  .practice__plus::before, .practice__plus::after {
    content: ''; position: absolute; inset: 50% 0 auto; height: 1px;
    background: currentColor; transition: transform 0.5s var(--e-expo), opacity 0.3s;
  }
  .practice__plus::after { transform: rotate(90deg); }
  .practice__item[data-open='true'] .practice__plus::after { transform: rotate(0deg); opacity: 0; }

  /* Open by default: with JS off every service still reads in full. The
     accordion is layered on top by .js-motion, not baked into the markup. */
  .practice__panel {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows 0.6s var(--e-expo);
  }
  .js-motion .practice__item:not([data-open='true']) .practice__panel { grid-template-rows: 0fr; }
  .practice__panel > div { overflow: hidden; }
  .practice__panel p { max-width: 54ch; padding-bottom: 1.1rem; }
  .practice__panel ul {
    display: flex; flex-wrap: wrap; gap: 0.45rem;
    padding-bottom: clamp(1.3rem, 2.4vw, 2rem);
  }
  .practice__panel li {
    font: 400 var(--step--1)/1 var(--mono);
    color: var(--faint);
    border: 1px solid var(--line);
    padding: 0.42rem 0.7rem;
  }
  .practice__mobile { display: none; }

  /* ---------------------------------------------------------------- craft */
  .craft { background: linear-gradient(var(--void), var(--void-deep) 40%, var(--void)); }
  .craft > .band { padding-bottom: clamp(3rem, 5vw, 5rem); }
  .work { padding-top: clamp(4rem, 7vw, 7rem); padding-bottom: var(--section-y); }
  .chapter {
    display: grid;
    gap: clamp(1.8rem, 4vw, 4rem);
    grid-template-columns: 1fr;
    align-items: center;
    padding-block: clamp(3rem, 7vw, 6rem);
    border-top: 1px solid var(--line);
  }
  @media (min-width: 58rem) {
    .chapter { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .chapter:nth-child(even) .chapter__media { order: -1; }
  }
  .chapter__media {
    position: relative;
    aspect-ratio: 16 / 11;
    overflow: hidden;
    isolation: isolate;
    border: 1px solid var(--line);
    background: var(--void-deep);
  }
  .chapter__media::before {
    content: ''; position: absolute; inset: 0; z-index: 1;
    background: var(--duotone);
    mix-blend-mode: color;
  }
  .chapter__media img {
    position: absolute;
    inset: -9% 0;
    width: 100%; height: 118%;
    object-fit: cover;
    filter: saturate(0.32) brightness(0.58) contrast(1.14);
    will-change: transform;
  }
  .chapter__media::after {
    content: ''; position: absolute; inset: 0; z-index: 2;
    background: linear-gradient(200deg, oklch(0.815 0.13 218 / 0.16), transparent 54%),
                linear-gradient(to top, oklch(0.105 0.02 250 / 0.62), transparent 62%);
  }
  .chapter__no {
    font: 500 var(--step--1)/1 var(--mono);
    color: var(--signal);
    letter-spacing: 0.12em;
    margin-bottom: 1.1rem;
    display: block;
  }
  .chapter h3 { margin-bottom: 1rem; }
  .chapter p { max-width: 46ch; }
  .chapter dl {
    display: flex; flex-wrap: wrap; gap: 1.4rem clamp(2rem, 4vw, 3.5rem);
    margin-top: clamp(1.5rem, 3vw, 2.2rem);
    padding-top: 1.4rem;
    border-top: 1px solid var(--line);
  }
  .chapter dt { font: 400 var(--step--1)/1.3 var(--mono); color: var(--faint); margin-bottom: 0.4rem; }
  .chapter dd {
    font-family: var(--display); font-size: var(--step-2); font-weight: 500;
    letter-spacing: 0.006em; line-height: 1; color: var(--ink);
  }

  /* -------------------------------------------------------------- showcase */
  .work__head {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    justify-content: space-between; gap: 1.5rem;
    margin-bottom: clamp(2rem, 4vw, 3.4rem);
  }
  /* Fallback shape (no JS, reduced motion, phones): a native swipe rail. */
  .rail {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--edge);
    scrollbar-width: none;
  }
  .rail::-webkit-scrollbar { display: none; }
  .rail__track {
    display: flex;
    gap: clamp(1rem, 2vw, 1.8rem);
    width: max-content;
    padding-inline: var(--edge);
    padding-bottom: 1.5rem;
  }
  .rail[data-dragging='true'] { cursor: grabbing; scroll-snap-type: none; }
  .rail[data-dragging='true'] a { pointer-events: none; }

  /* Pinned shape: the section holds while vertical scroll drives the track
     sideways. Transform only — the rail itself never scrolls. */
  .work.is-pinned .work__stage {
    position: sticky;
    top: 0;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: clamp(5.5rem, 11vh, 8rem) clamp(1.5rem, 4vh, 3rem);
  }
  .work.is-pinned { padding-block: 0; }
  .work.is-pinned .rail {
    overflow: hidden;
    scroll-snap-type: none;
    cursor: default;
  }
  .work.is-pinned .rail__track {
    transform: translate3d(calc(var(--rail-x, 0px) * -1), 0, 0);
    will-change: transform;
  }

  .project {
    position: relative;
    flex: 0 0 auto;
    width: clamp(250px, 30vw, 400px);
    scroll-snap-align: start;
    text-decoration: none;
    color: inherit;
    display: block;
  }
  /* Pinned: the card is sized off the viewport height so head, rail and
     progress always fit one screen; width follows from the aspect ratio. */
  .work.is-pinned .project {
    width: auto;
    height: clamp(280px, 47vh, 520px);
  }
  .work.is-pinned .project__frame { height: 100%; width: auto; }

  .project__frame {
    position: relative;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    isolation: isolate;
    border: 1px solid var(--line);
    background: var(--void-deep);
  }
  /* The duotone is the *hover* state here, not the resting one: the projects
     show themselves in their own colours and take the house tint only when
     you reach for them. (The cards on referenzen.html keep the older reading,
     where the grade lifts on hover — they are a browsable index rather than a
     showcase.) */
  .project__frame::before {
    content: ''; position: absolute; inset: 0; z-index: 1;
    background: var(--duotone);
    mix-blend-mode: color;
    opacity: 0;
    transition: opacity 0.8s var(--e-quart);
  }
  .project:hover .project__frame::before,
  .project:focus-visible .project__frame::before { opacity: 1; }
  .project__frame img {
    width: 100%; height: 100%;
    object-fit: cover;
    filter: saturate(0.8) brightness(0.82) contrast(1.04);
    transform: scale(1.04);
    transition: transform 1.1s var(--e-expo), filter 0.8s var(--e-quart);
  }
  .project:hover .project__frame img,
  .project:focus-visible .project__frame img {
    transform: scale(1.11);
    filter: saturate(0.34) brightness(0.6) contrast(1.12);
  }
  .project__frame::after {
    content: '';
    position: absolute; inset: 0; z-index: 2;
    background: linear-gradient(to top, oklch(0.105 0.02 250 / 0.9) 4%, transparent 52%);
  }

  .project__badge {
    position: absolute; top: 0.85rem; left: 0.85rem; z-index: 3;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--signal);
    background: oklch(0.105 0.02 250 / 0.6);
    border: 1px solid oklch(0.815 0.13 218 / 0.3);
    padding: 0.32rem 0.55rem;
  }
  .project__caption {
    position: absolute; inset: auto 0 0; z-index: 3;
    padding: 1rem;
    display: grid; gap: 0.35rem;
  }
  .project__name {
    font-family: var(--display);
    font-size: clamp(1.3rem, 1.9vw, 1.7rem);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0;
    line-height: 0.8;
    color: var(--ink);
  }
  .project__meta { font: 400 var(--step--1)/1.4 var(--mono); color: var(--faint); }
  .project__result {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    font-size: var(--step--1);
    color: var(--mute);
    transition: grid-template-rows 0.55s var(--e-expo), opacity 0.4s var(--e-quart);
  }
  .project__result > span { overflow: hidden; }
  .project:hover .project__result, .project:focus-visible .project__result {
    grid-template-rows: 1fr; opacity: 1;
  }
  .project__result b { color: var(--amber); font-weight: 600; }

  .rail__nav { display: flex; gap: 0.5rem; }
  .rail__btn {
    width: 44px; height: 44px;
    display: grid; place-items: center;
    border: 1px solid var(--line-strong);
    color: var(--mute);
    transition: color 0.3s, border-color 0.3s, background-color 0.3s, opacity 0.3s;
  }
  .rail__btn:hover:not(:disabled) {
    color: var(--ink);
    border-color: var(--signal);
    background: oklch(0.815 0.13 218 / 0.08);
  }
  .rail__btn:disabled { opacity: 0.3; cursor: not-allowed; }

  /* Context on the left, position on the right, the rule carrying both. */
  .rail__meter {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: clamp(0.7rem, 1.8vw, 1.5rem);
    margin-inline: var(--edge);
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
  }
  .rail__count { white-space: nowrap; }
  .rail__pct {
    color: var(--signal);
    font-weight: 500;
    min-width: 5ch;
    text-align: right;
    font-variant-numeric: tabular-nums;
  }

  .rail__progress {
    height: 1px;
    background: var(--line);
    position: relative;
  }
  .rail__progress i {
    position: absolute; inset: 0 0 0 0;
    background: var(--signal);
    transform-origin: left;
    transform: scaleX(var(--rail-p, 0.12));
    transition: transform 0.2s linear;
  }

  /* ------------------------------------------------------------- lifestyle */
  .field {
    position: relative;
    min-height: clamp(30rem, 78vh, 46rem);
    display: grid;
    align-items: end;
    overflow: clip;
    isolation: isolate;
  }
  .field__bg { position: absolute; inset: -12% 0; z-index: -2; isolation: isolate; }
  .field__bg img {
    width: 100%; height: 100%;
    object-fit: cover;
    filter: saturate(0.3) brightness(0.5) contrast(1.16);
    will-change: transform;
  }
  .field__bg::after {
    content: ''; position: absolute; inset: 0;
    background: var(--duotone);
    mix-blend-mode: color;
  }
  .field::before {
    content: ''; position: absolute; inset: 0; z-index: -1;
    background:
      linear-gradient(to top, var(--void) 2%, oklch(0.105 0.02 250 / 0.6) 45%, oklch(0.105 0.02 250 / 0.35)),
      radial-gradient(80% 60% at 20% 90%, oklch(0.815 0.13 218 / 0.12), transparent 70%);
  }
  .field__body { padding: var(--gutter); padding-bottom: clamp(2.5rem, 6vw, 5rem); }
  .field__quote {
    font-family: var(--display);
    font-size: var(--step-3);
    font-weight: 500;
    line-height: 1.05; letter-spacing: 0.005em;
    color: var(--ink);
    max-width: 24ch;
  }
  .field__facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1.5rem clamp(1rem, 3vw, 3rem);
    margin-top: clamp(2rem, 4vw, 3.4rem);
    padding-top: 1.4rem;
    border-top: 1px solid var(--line-strong);
  }
  .field__facts dt { font: 400 var(--step--1)/1.3 var(--mono); color: var(--faint); }
  .field__facts dd { margin-top: 0.35rem; color: var(--ink); font-weight: 500; }

  /* ------------------------------------------------------------- voices */
  .voices { display: grid; gap: clamp(2rem, 4vw, 3.5rem); }
  @media (min-width: 62rem) {
    .voices { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); align-items: start; }
  }
  .quote--lead blockquote {
    font-family: var(--display);
    font-size: var(--step-3);
    font-weight: 400;
    line-height: 1.08; letter-spacing: 0.005em;
    color: var(--ink);
    max-width: 26ch;
  }
  .quote--lead blockquote::before {
    content: '„';
    color: var(--signal);
    margin-right: 0.06em;
  }
  .quote__by {
    display: flex; align-items: center; gap: 0.9rem;
    margin-top: clamp(1.6rem, 3vw, 2.4rem);
  }
  .quote__by img {
    width: 46px; height: 46px;
    object-fit: cover;
    border-radius: 50%;
    filter: saturate(0.6) brightness(1.12) contrast(1.02);
    border: 1px solid var(--line-strong);
  }
  .quote__by cite { font-style: normal; color: var(--ink); font-weight: 500; display: block; }
  .quote__by span { font: 400 var(--step--1)/1.4 var(--mono); color: var(--faint); }

  .quote--small { border-top: 1px solid var(--line); padding-top: 1.6rem; }
  .quote--small + .quote--small { margin-top: 2rem; }
  .quote--small blockquote { color: var(--mute); max-width: 42ch; }
  .quote--small .quote__by { margin-top: 1.1rem; }
  .quote--small .quote__by img { width: 34px; height: 34px; }

  /* clients marquee */
  .marquee {
    overflow: hidden;
    border-block: 1px solid var(--line);
    padding-block: clamp(1.2rem, 2.4vw, 1.9rem);
    margin-top: var(--section-y);
    mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  }
  .marquee__track {
    display: flex;
    gap: clamp(2.5rem, 6vw, 6rem);
    width: max-content;
    animation: slide 42s linear infinite;
  }
  .marquee:hover .marquee__track { animation-play-state: paused; }
  .marquee__item {
    font-family: var(--display);
    font-size: clamp(1.3rem, 2.6vw, 2rem);
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--faint);
    white-space: nowrap;
    transition: color 0.3s;
  }
  .marquee__item:hover { color: var(--ink); }

  /* ----------------------------------------------------------------- blog */
  .blog__head {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    justify-content: space-between; gap: 1.5rem;
    margin-bottom: clamp(2rem, 4vw, 3.4rem);
  }
  .blog__all {
    display: inline-flex; align-items: center; gap: 0.6rem;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--mute);
    text-decoration: none;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--line-strong);
    transition: color 0.35s var(--e-quart), border-color 0.35s var(--e-quart);
  }
  .blog__all svg { transition: transform 0.45s var(--e-expo); }
  .blog__all:hover { color: var(--signal); border-color: var(--signal); }
  .blog__all:hover svg { transform: translateX(5px); }

  .blog {
    display: grid;
    gap: clamp(1.1rem, 2vw, 1.8rem);
    grid-template-columns: 1fr;
  }
  @media (min-width: 62rem) {
    .blog { grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); }
    .post--lead { grid-row: span 3; }
  }

  .post {
    position: relative;
    --mx: 50%;
    --my: 50%;
    isolation: isolate;
  }
  /* A soft light that follows the pointer across the card it is over. It sits
     1px outside the card, so its corner has to be 1px rounder to stay
     concentric — otherwise the gradient squares off past the card's corner. */
  .post::before {
    content: '';
    position: absolute;
    inset: -1px;
    z-index: 0;
    pointer-events: none;
    border-radius: calc(var(--radius) + 1px);
    background: radial-gradient(280px circle at var(--mx) var(--my),
                                oklch(0.815 0.13 218 / 0.16), transparent 68%);
    opacity: 0;
    transition: opacity 0.55s var(--e-quart);
  }
  .post:hover::before, .post:focus-within::before { opacity: 1; }

  .post__link {
    position: relative;
    z-index: 1;
    display: grid;
    height: 100%;
    text-decoration: none;
    color: inherit;
    border: 1px solid var(--line);
    background: oklch(0.155 0.024 246 / 0.5);
    overflow: hidden;
    transition: border-color 0.5s var(--e-quart), background-color 0.5s var(--e-quart);
  }
  .post:hover .post__link, .post:focus-within .post__link {
    border-color: oklch(0.815 0.13 218 / 0.34);
    background: oklch(0.19 0.028 244 / 0.6);
  }

  .post__frame {
    position: relative;
    display: block;
    overflow: hidden;
    isolation: isolate;
    background: var(--void-deep);
  }
  /* Same reading as the project rail: the cover shows itself, and the house
     tint arrives on hover rather than sitting on it at rest. */
  .post__frame img {
    width: 100%; height: 100%;
    object-fit: cover;
    filter: saturate(0.75) brightness(0.8) contrast(1.04);
    transform: scale(1.03);
    transition: transform 1.2s var(--e-expo), filter 0.8s var(--e-quart);
  }
  .post__frame::before {
    content: ''; position: absolute; inset: 0; z-index: 1;
    background: var(--duotone);
    mix-blend-mode: color;
    opacity: 0;
    transition: opacity 0.8s var(--e-quart);
  }
  .post__frame::after {
    content: ''; position: absolute; inset: 0; z-index: 2;
    background: linear-gradient(to top, oklch(0.105 0.02 250 / 0.72) 2%, transparent 55%);
  }
  .post:hover .post__frame img, .post:focus-within .post__frame img {
    transform: scale(1.1);
    filter: saturate(0.34) brightness(0.6) contrast(1.12);
  }
  .post:hover .post__frame::before, .post:focus-within .post__frame::before { opacity: 1; }

  .post__cat {
    position: absolute; top: 0.85rem; left: 0.85rem; z-index: 3;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--signal);
    background: oklch(0.105 0.02 250 / 0.62);
    border: 1px solid oklch(0.815 0.13 218 / 0.3);
    padding: 0.32rem 0.55rem;
  }

  .post__body {
    display: grid;
    align-content: start;
    gap: 0.55rem;
    padding: clamp(0.95rem, 1.6vw, 1.35rem);
  }
  .post__meta { font: 400 var(--step--1)/1 var(--mono); color: var(--faint); }
  .post__title {
    font-family: var(--display);
    font-weight: 500;
    text-transform: uppercase;
    line-height: 0.78;
    letter-spacing: 0;
    color: var(--ink);
    font-size: clamp(1.35rem, 2.1vw, 1.75rem);
    transition: color 0.35s var(--e-quart);
    text-wrap: balance;
  }
  .post:hover .post__title, .post:focus-within .post__title { color: var(--signal); }

  .post__excerpt { color: var(--mute); max-width: 46ch; }
  .post__more {
    display: inline-flex; align-items: center; gap: 0.6rem;
    margin-top: 0.4rem;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--faint);
    transition: color 0.35s var(--e-quart);
  }
  .post__more svg { transition: transform 0.45s var(--e-expo); }
  .post:hover .post__more { color: var(--signal); }
  .post:hover .post__more svg { transform: translateX(5px); }

  /* the lead runs as a poster; the others as a compact media row */
  .post--lead .post__frame { aspect-ratio: 16 / 9; }
  .post--lead .post__title { font-size: clamp(1.75rem, 3.1vw, 2.7rem); }

  .post:not(.post--lead) .post__link { grid-template-columns: minmax(96px, 34%) 1fr; }
  .post:not(.post--lead) .post__frame { height: 100%; min-height: 94px; }
  .post:not(.post--lead) .post__body { align-content: center; }

  @media (max-width: 34rem) {
    .post:not(.post--lead) .post__link { grid-template-columns: 1fr; }
    .post:not(.post--lead) .post__frame { aspect-ratio: 16 / 9; height: auto; }
    .post:not(.post--lead) .post__body { align-content: start; }
  }

  /* ------------------------------------------------------------------ cta */
  .cta {
    position: relative;
    overflow: clip;
    isolation: isolate;
    border-top: 1px solid var(--line);
  }
  .cta__canvas { position: absolute; inset: 0; z-index: -1; width: 100%; height: 100%; opacity: 0.75; }
  .cta::before {
    content: ''; position: absolute; inset: 0; z-index: -1;
    background: radial-gradient(72% 95% at 50% 122%, oklch(0.815 0.13 218 / 0.24), transparent 72%);
  }
  .cta__body { text-align: center; display: grid; justify-items: center; gap: 1.6rem; }
  /* `min-width: min-content` is the guard, not decoration: 15ch is measured on
     Teko's very narrow zero, so a long compound like SOFTWAREENTWICKLUNG is far
     wider than the box it is given. The line then overflows to the right and
     the heading reads as though it is not centred. min-width beats max-width in
     the cascade, so the box can never be narrower than its longest word while
     15ch still governs where shorter headings wrap. */
  .cta h2 { max-width: 15ch; min-width: min-content; }
  .cta p { max-width: 48ch; }
  .cta__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; justify-content: center; margin-top: 0.8rem; }
  .cta__direct {
    display: flex; flex-wrap: wrap; gap: clamp(1rem, 3vw, 2.6rem); justify-content: center;
    margin-top: clamp(2rem, 4vw, 3rem);
    padding-top: 1.6rem;
    border-top: 1px solid var(--line);
    width: min(100%, 46rem);
    font: 400 var(--step--1)/1.5 var(--mono);
    color: var(--faint);
  }
  .cta__direct a { color: var(--mute); text-decoration: none; border-bottom: 1px solid var(--line-strong); padding-bottom: 2px; transition: color 0.3s, border-color 0.3s; }
  .cta__direct a:hover { color: var(--signal); border-color: var(--signal); }

  /* --------------------------------------------------------------- footer */
  /* the top padding used to come from the status strip that sat here */
  /* The same reading as the review tile: a touch lighter where it meets the
     page, settling darker towards the base, so the footer sits on the page
     rather than being a hole cut in it. */
  .foot {
    border-top: 1px solid var(--line);
    padding-block: clamp(3rem, 6vw, 5rem) 2rem;
    background: linear-gradient(180deg,
      oklch(0.190 0.026 245 / 0.55) 0%,
      oklch(0.160 0.024 246 / 0.30) 38%,
      oklch(0.105 0.020 250 / 0.55) 100%);
  }
  .foot__grid {
    display: grid;
    gap: clamp(2rem, 4vw, 3rem);
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  }
  .foot__brand { grid-column: 1 / -1; }
  @media (min-width: 62rem) { .foot__brand { grid-column: span 2; } }
  .foot__brand p { max-width: 34ch; margin-top: 1.1rem; color: var(--faint); }
  .foot h4 {
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--faint);
    margin-bottom: 1.1rem;
  }
  .foot__grid li + li { margin-top: 0.6rem; }
  .foot__grid .foot__social li + li { margin-top: 0; }   /* it is a row, not a list */
  .foot a { text-decoration: none; color: var(--mute); transition: color 0.3s; }
  .foot a:hover { color: var(--signal); }
  .foot__base {
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: space-between; gap: 1rem;
    margin-top: clamp(2.5rem, 5vw, 4rem);
    padding-top: 1.4rem;
    border-top: 1px solid var(--line);
    font: 400 var(--step--1)/1.5 var(--mono);
    color: var(--faint);
  }
  .foot__base ul { display: flex; flex-wrap: wrap; align-items: center; gap: 1.2rem; }
  .foot__top a {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    color: var(--mute);
    transition: color 0.3s var(--e-quart), border-color 0.3s var(--e-quart), transform 0.45s var(--e-expo);
  }
  .foot__top a:hover { color: var(--signal); border-color: var(--signal); transform: translateY(-2px); }

  /* Icon-only here — the network names already appear in the contact card,
     and each link carries an aria-label so the icon is not the only cue. */
  .foot__social { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.3rem; }
  .foot__social a {
    display: grid;
    place-items: center;
    width: 38px; height: 38px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--faint);
    transition: color 0.3s var(--e-quart), border-color 0.3s var(--e-quart), transform 0.45s var(--e-expo);
  }
  .foot__social a:hover { color: var(--signal); border-color: var(--signal); transform: translateY(-2px); }
  .foot__social svg { width: 17px; height: 17px; }

  /* scroll progress */
  .progress {
    position: fixed; inset: 0 0 auto; height: 2px;
    z-index: var(--z-nav);
    background: var(--signal);
    transform-origin: left;
    transform: scaleX(var(--p, 0));
    box-shadow: 0 0 12px var(--signal-glow);
  }
}

/* ----------------------------------------------------------------- motion */
@layer motion {
  @keyframes ping {
    0%   { box-shadow: 0 0 0 0 var(--signal-glow); }
    70%  { box-shadow: 0 0 0 12px oklch(0.815 0.13 218 / 0); }
    100% { box-shadow: 0 0 0 0 oklch(0.815 0.13 218 / 0); }
  }
  @keyframes trickle {
    0%   { transform: translateY(-100%); opacity: 0; }
    35%  { opacity: 1; }
    100% { transform: translateY(100%); opacity: 0; }
  }
  @keyframes slide { to { transform: translateX(calc(-50% - clamp(1.25rem, 3vw, 3rem))); } }

  /* Reveals are opt-in: the class only exists once JS has run, so a page
     with JS off (or a headless render) ships fully visible. */
  .js-motion [data-reveal] {
    opacity: 0;
    transform: translate3d(0, 22px, 0);
    transition: opacity 0.9s var(--e-quart), transform 1s var(--e-expo);
    transition-delay: var(--d, 0ms);
  }
  .js-motion [data-reveal='shown'] { opacity: 1; transform: none; }

  /* Blog cards get a second layer: the frame wipes open and the photograph
     settles out of an over-scale. Enhances the reveal, never gates it. */
  .js-motion .post[data-reveal] .post__frame { clip-path: inset(0 0 100% 0); }
  .js-motion .post[data-reveal] .post__frame img { transform: scale(1.18); }
  .js-motion .post[data-reveal='shown'] .post__frame {
    clip-path: inset(0 0 0 0);
    transition: clip-path 1.15s var(--e-expo) var(--d, 0ms);
  }
  .js-motion .post[data-reveal='shown'] .post__frame img {
    transform: scale(1.03);
    transition: transform 1.6s var(--e-expo) var(--d, 0ms);
  }
  /* The reveal rule above pins the settled scale — it outweighs the hover rule
     in @layer components on both specificity and layer order, so the teaser
     cards have never actually zoomed. The hover has to be restated here, at
     the same weight, to land at all. */
  .js-motion .post[data-reveal='shown']:hover .post__frame img,
  .js-motion .post[data-reveal='shown']:focus-within .post__frame img {
    transform: scale(1.1);
  }

  .js-motion .hero__copy [data-lift] {
    opacity: 0;
    transform: translate3d(0, 26px, 0);
    transition: opacity 1s var(--e-quart), transform 1.1s var(--e-expo);
    transition-delay: var(--d, 0ms);
  }
  .js-motion.is-ready .hero__copy [data-lift] { opacity: 1; transform: none; }

  /* --t is written per word by the scroll pipeline; no transition, because
     the value itself is already continuous with scroll position. */
  .js-motion .word {
    --t: 0;
    opacity: calc(0.16 + 0.84 * var(--t));
    color: color-mix(in oklab, var(--faint), var(--ink) calc(var(--t) * 100%));
  }

  @media print {
    .hero { min-height: 62vh; }
    .js-motion [data-reveal],
    .js-motion .hero__copy [data-lift],
    .js-motion .word { opacity: 1 !important; transform: none !important; }
  }

  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .hero { height: auto; }
    .hero__stage { position: static; min-height: 100svh; height: auto; }
    .hero__stage::after { display: none; }
    /* Without the pin there is no scroll to advance chapters, so all three
       stack as ordinary copy and the markers have nothing to mark. */
    .js-motion .hero__copy { display: block; }
    .js-motion .hero__step {
      opacity: 1 !important;
      transform: none !important;
      pointer-events: auto;
    }
    .hero__step + .hero__step { margin-top: clamp(2rem, 5vh, 4rem); }
    .hero__dots { display: none; }
    .hero__copy, .telemetry, .telemetry__scroll { opacity: 1 !important; transform: none !important; }
    *, *::before, *::after {
      animation-duration: 0.001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.001ms !important;
      scroll-behavior: auto !important;
    }
    .js-motion [data-reveal],
    .js-motion .hero__copy [data-lift],
    .js-motion .word { opacity: 1 !important; transform: none !important; }
    .js-motion .post .post__frame { clip-path: none !important; }
    .js-motion .post .post__frame img { transform: none !important; }
    .marquee__track { animation: none; }
  }
}

/* ------------------------------------------------------------ responsive */
@media (max-width: 61.99rem) {
  .practice__stage { display: none; }
  .practice__mobile {
    display: block;
    position: relative;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    isolation: isolate;
    border: 1px solid var(--line);
    margin-bottom: 1.3rem;
  }
  .practice__mobile::before {
    content: ''; position: absolute; inset: 0; z-index: 1;
    background: var(--duotone);
    mix-blend-mode: color;
  }
  .practice__mobile img {
    width: 100%; height: 100%; object-fit: cover;
    filter: saturate(0.32) brightness(0.58) contrast(1.14);
  }
  .practice__mobile::after {
    content: ''; position: absolute; inset: 0; z-index: 2;
    background: linear-gradient(to top, oklch(0.105 0.02 250 / 0.7), transparent 62%);
  }
}

@media (max-width: 26rem) {
  .telemetry {
    padding-inline: 1rem;
    gap: 0.8rem;
    font-size: 0.72rem;
  }
}

@media (max-width: 52rem) {
  /* The sheet scrolls underneath the logo row. ::before cannot mask it — it
     sits at z-index -1, below the sheet — so the mask is ::after, which as the
     last child wins the tie at the same z-index. The wordmark and the close
     button are lifted one step above it. */
  .nav[data-open='true']::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: var(--z-nav);
    background: oklch(0.105 0.02 250);
    pointer-events: none;
  }
  .nav[data-open='true'] .brand {
    position: relative;
    z-index: calc(var(--z-nav) + 1);
  }

  /* three chapters need room to breathe even on a phone */
  .hero { height: 330svh; }

  .nav__links {
    position: fixed;
    inset: 0;
    z-index: var(--z-nav);
    flex-direction: column;
    justify-content: flex-start;
    align-items: flex-start;
    gap: 0.4rem;
    /* the panels make this taller than the screen: it scrolls itself */
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 5.5rem var(--gutter) 3rem;
    background: oklch(0.105 0.02 250 / 0.97);
    backdrop-filter: blur(20px);
    transform: translateY(-100%);
    visibility: hidden;
    transition: transform 0.6s var(--e-expo), visibility 0.6s;
  }
  /* The sheet is a column flexbox that overflows once a panel opens, and flex
     items shrink by default — which squashed the CTA from 48px to 31px the
     moment anything expanded. Nothing in here may shrink; the sheet scrolls. */
  .nav__links > * { flex: none; }
  .nav[data-open='true'] .nav__links { transform: translateY(0); visibility: visible; }
  .nav .brand { position: relative; z-index: calc(var(--z-nav) + 1); }
  /* --step-3 shifted down 0.25rem; the ramp is otherwise identical. */
  .nav__links .nav__link {
    font-size: clamp(1.75rem, 1.5rem + 1.9vw, 3.25rem);
    font-weight: 500;
    letter-spacing: 0.05em;
    color: var(--ink);
  }

  .menu { flex-direction: column; align-items: stretch; gap: 0; width: 100%; }
  .menu__item { border-bottom: 1px solid var(--line); }
  .menu__item--drop { position: static; }
  /* The row, not the word. `.nav__link` is a bare <a>, so it is inline by
     default — `width` and `padding-block` were both inert on it, while the
     <button> triggers next to it are inline-block and took them. That one
     difference made the plain items both tighter and only tap-able on their
     text. Laying every row out the same way fixes both at once. */
  .menu__trigger,
  .menu__item > .nav__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding-block: 0.75rem;
  }
  .menu__chev { width: 12px; height: 8px; }

  .mega, .mega--drop {
    position: static;
    width: auto;
    margin-top: 0;
    opacity: 1;
    pointer-events: auto;
    transform: none;
    background: none;
    backdrop-filter: none;
    border: 0;
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.5s var(--e-expo);
  }
  .menu__item[data-open='true'] .mega { grid-template-rows: 1fr; }
  /* padding has to live on the content, not on the collapsing row — otherwise
     the closed panel keeps its padding and leaks a strip of the first item */
  .mega__inner { overflow: hidden; padding: 0; }
  .mega__grid { padding-block: 1.15rem 1.4rem; }
  .mega--drop .drop { padding-block: 0.8rem 1.4rem; }
  .mega__grid, .mega__grid.shell { grid-template-columns: 1fr; width: 100%; gap: 1.4rem; }
  .mega__col--news {
    padding: 1.4rem 0 0;
    border-left: 0;
    border-top: 1px solid var(--line);
  }
  .mega__col--news::before { display: none; }
  .mega__list { grid-template-columns: 1fr; }
  .mega__list a { margin-inline: 0; padding-inline: 0; }
  .pcards { grid-template-columns: 1fr; }
  .mfeat { grid-template-columns: minmax(0, 110px) minmax(0, 1fr); }
  /* 0.72rem of padding left these rows at 41px — under the 44px a thumb
     wants. The other two lists in the sheet already clear it. */
  .drop a { padding-inline: 0; padding-block: 0.85rem; font-size: 1.15rem; }
  /* Line-height 1 on a mono caption leaves a 13px tap target. On a pointer
     that is fine; under a thumb it is not. */
  .mega__more { padding-block: 1rem; margin-top: 0.3rem; }
  .mega__label { margin-bottom: 0.8rem; }
  .nav__links .btn { margin-top: 1.6rem; padding: 0.95rem 1.4rem 0.85rem; }

  .nav__toggle {
    display: grid;
    position: relative;
    z-index: calc(var(--z-nav) + 1);
    width: 42px; height: 42px;
    place-items: center;
    align-content: center;      /* rows must hug the bars, not stretch */
    gap: 7px;
    border: 1px solid var(--line-strong);
  }
  .nav__toggle i {
    width: 17px; height: 1px; background: var(--ink);
    transition: transform 0.45s var(--e-expo), opacity 0.3s;
  }
  .nav[data-open='true'] .nav__toggle i:nth-of-type(1) { transform: translateY(4px) rotate(45deg); }
  .nav[data-open='true'] .nav__toggle i:nth-of-type(2) { transform: translateY(-4px) rotate(-45deg); }

  .telemetry__hide { display: none !important; }
  .hero__inner { padding-bottom: 1rem; }
}

@media (min-width: 52.01rem) { .telemetry__hide { display: inline; } }

/* =========================================================================
   Corner radius
   One 15px value across controls, cards and image frames. Avatars and the
   hero badge stay circular; full-bleed bands (telemetry, marquee, studio)
   stay square because a rounded edge-to-edge band reads as a mistake.
   ========================================================================= */
@layer components {
  /* controls */
  .btn,
  .rail__btn,
  .nav__toggle { border-radius: var(--radius); }

  /* image frames */
  .project__frame,
  .chapter__media,
  .practice__stage,
  .practice__mobile { border-radius: var(--radius); }

  /* cards — the parent clips its own frame, so the image follows the corner */
  .post__link,
  .mfeat { border-radius: var(--radius); }

  /* nested frame: inner radius steps down by the card's padding */
  .mfeat__frame { border-radius: var(--radius-sm); }

  /* product cards in the mega menu are cards like any other */
  .pcard { border-radius: var(--radius); }

  /* every hover target in the menu system shares one corner, so the two
     panel types do not read as different components */
  .mega__list a,
  .drop a { border-radius: var(--radius); }

  /* the skip link is invisible until it is focused, but it is still a box */
  .skip { border-radius: var(--radius-sm); }

  /* small boxes: chips, tags, the product glyph */
  .practice__panel li,
  .pcard__mark,
  .post__cat,
  .project__badge { border-radius: var(--radius-sm); }

  /* menu panels hang off a square bar, so only the loose corners round */
  .mega,
  .mega--drop { border-radius: 0 0 var(--radius) var(--radius); }
}

@media (max-width: 52rem) {
  /* the mobile menu is a full-height sheet, not a hanging panel */
  .mega, .mega--drop { border-radius: 0; }
}

/* =========================================================================
   Mono labels run in caps
   JetBrains Mono is only ever used for labels, tags and telemetry here, and
   those all read as caps. Deliberately excluded: addresses, dates, tech names
   and the copyright line, where casing carries meaning or aids reading.
   ========================================================================= */
@layer components {
  .marker__id,          /* Leistungen · Handwerk · Referenzen · Stimmen · Blog */
  .mega__label,         /* "Was wir bauen" */
  .mega__more,
  .pcard__go,
  .post__more,
  .post__cat,
  .project__badge,
  .hero__badge span,
  .foot h4,
  .chapter dt,
  .field__facts dt { text-transform: uppercase; }

  /* caps need a touch more tracking than the lowercase they replace */
  .chapter dt,
  .field__facts dt,
  .foot h4 { letter-spacing: 0.08em; }
}

/* =========================================================================
   Pinned rail on short viewports
   The stage has to hold nav clearance + a three-line heading + the cards +
   the meter on one screen. Below ~820px tall that no longer fits, and the
   meter is the first thing to drop under the fold — so the cards and the
   head spacing give way instead, not the readout.
   ========================================================================= */
@media (min-width: 48rem) and (max-height: 51rem) {
  .work.is-pinned .project { height: clamp(210px, 38vh, 520px); }
  .work.is-pinned .work__head { margin-bottom: clamp(1rem, 2.4vh, 2rem); }
  .work.is-pinned .work__head h2 { font-size: clamp(1.9rem, 3.4vw, 2.6rem); }
  .work.is-pinned .marker { margin-bottom: clamp(1rem, 2.4vh, 2rem); }
}

/* =========================================================================
   Sub-pages
   ========================================================================= */
@layer components {

  /* ------------------------------------------------------------ page hero */
  .phero {
    position: relative;
    overflow: clip;
    isolation: isolate;
    padding-block: clamp(9rem, 18vh, 14rem) clamp(3rem, 6vw, 5rem);
    border-bottom: 1px solid var(--line);
  }
  .phero__glow {
    position: absolute;
    inset: -40% -10% auto -30%;
    height: 120%;
    z-index: -1;
    background:
      radial-gradient(55% 60% at 22% 30%, oklch(0.815 0.13 218 / 0.16), transparent 68%),
      radial-gradient(40% 50% at 78% 12%, oklch(0.805 0.155 68 / 0.07), transparent 70%);
    pointer-events: none;
  }
  .crumbs {
    display: flex;
    gap: 0.7rem;
    margin-bottom: clamp(1.6rem, 3vw, 2.6rem);
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
  }
  .crumbs a { text-decoration: none; transition: color 0.3s var(--e-quart); }
  .crumbs a:hover { color: var(--signal); }
  .crumbs [aria-current] { color: var(--mute); }

  .phero__title {
    font-family: var(--display);
    font-weight: 600;
    text-transform: uppercase;
    font-size: var(--step-5);
    line-height: 0.96;
    letter-spacing: 0.022em;
    color: var(--ink);
    max-width: 20ch;
    text-wrap: balance;
  }
  .phero__title em {
    font-style: normal;
    color: var(--signal);
    text-shadow: 0 0 44px var(--signal-glow);
  }
  .phero__lead {
    margin-top: clamp(1.4rem, 2.6vw, 2rem);
    max-width: 56ch;
    font-size: var(--step-1);
    line-height: 1.55;
    color: var(--mute);
  }
  .phero__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: clamp(2rem, 4vw, 2.8rem); }

  /* ------------------------------------------- the reel (referenzen.html) */
  /* A tilted plane of project screenshots filling the right of the hero and
     running in under the copy. Behind .phero__glow rather than in front of it,
     so the house light falls over the shots and ties them into the page
     instead of sitting as a bright rectangle on top of it. */
  .phero--refs { --reel-gap: clamp(0.8rem, 1.4vw, 1.15rem); }
  .reel {
    position: absolute;
    z-index: -2;                    /* .phero__glow is -1 and paints over this */
    inset: 0 -16% 0 28%;
    pointer-events: none;
    /* One mask layer doing both jobs: opaque over the right of the hero and
       falling away in every direction, so the plane dissolves into the page on
       the left where the headline is and never meets an edge anywhere else.

       The vertical numbers are not free. Each column is clipped at the plane's
       top and bottom, and the tilt turns those clips into diagonal cuts — which
       show wherever the mask is still painting. Centred at 50% with a 58% radius
       against a 86% transparent stop, the fade reaches zero exactly at both
       edges (0.5 / 0.58 = 0.86), so the cuts land where nothing is drawn. Moving
       the centre off 50% brings one of them back.

       Prefixed first, standard last — see the note on .wcol. */
    -webkit-mask: radial-gradient(60% 58% at 66% 50%, #000 5%, oklch(0 0 0 / 0.34) 46%, transparent 86%);
    mask: radial-gradient(60% 58% at 66% 50%, #000 5%, oklch(0 0 0 / 0.34) 46%, transparent 86%);
    opacity: 0.5;
  }
  /* The tilt. rotate + the two skews is what makes a flat grid read as a plane
     lying back in space; a plain rotate only spins it in the picture plane. */
  .reel__plane {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--reel-gap);
    height: 100%;
    transform: rotate(-15deg) skewX(10deg) skewY(10deg) scale(1.02);
    transform-origin: 60% 50%;
  }
  /* Each column is its own window; the run inside it carries two identical
     sets and travels exactly -50%, so the spacing has to live on the cards as
     a margin and not as a gap on the run — a gap leaves half a gap of drift at
     the loop point. Same construction and same reason as the wall. */
  .reel__col { overflow: hidden; }
  .reel__run { display: flex; flex-direction: column; }
  .reel__card {
    display: block;
    margin-bottom: var(--reel-gap);
    aspect-ratio: var(--ar, 16 / 10);
    overflow: hidden;
    border: 1px solid oklch(1 0 0 / 0.10);
    border-radius: calc(var(--radius) * 0.7);
    background: oklch(0.19 0.026 245);
    box-shadow: 0 18px 40px oklch(0.04 0.02 250 / 0.5);
  }
  .reel__card img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    /* The same resting grade the rail and the reference cards use, pushed one
       step further: this is background, and it must not out-contrast the H1. */
    filter: saturate(0.5) brightness(0.62) contrast(1.06);
  }

  /* The copy needs a ground of its own once the plane runs underneath it. */
  .phero--refs .shell { position: relative; }
  .phero--refs .phero__title,
  .phero--refs .phero__lead { text-shadow: 0 2px 18px oklch(0.06 0.02 250 / 0.85); }

  /* Narrow: the plane moves fully behind the copy and drops right back, because
     there is no longer a right-hand half to give it.

     Columns are dropped rather than squeezed. A set has to be at least as tall
     as its column or the loop shows a gap at the seam, and a card's height
     follows its width — four columns at 390px makes cards 75px tall and five of
     them no longer fill the hero. Fewer, wider columns keep the set tall. */
  @media (max-width: 62rem) {
    .reel { inset: 0 -34% 0 2%; opacity: 0.26; }
    .reel__plane {
      grid-template-columns: repeat(3, minmax(0, 1fr));
      transform: rotate(-13deg) skewX(8deg) skewY(8deg) scale(1.3);
    }
    .reel__col:nth-child(n + 4) { display: none; }
  }
  @media (max-width: 40rem) {
    .reel__plane {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      transform: rotate(-13deg) skewX(8deg) skewY(8deg) scale(1.42);
    }
    .reel__col:nth-child(n + 3) { display: none; }
  }


  .facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: clamp(1.4rem, 3vw, 2.6rem);
    margin-top: clamp(3rem, 6vw, 4.5rem);
    padding-top: clamp(1.6rem, 3vw, 2.2rem);
    border-top: 1px solid var(--line);
  }
  .fact__num {
    font-family: var(--display);
    font-weight: 600;
    font-size: clamp(2.2rem, 4.4vw, 3.4rem);
    line-height: 0.9;
    letter-spacing: 0.02em;
    color: var(--signal);
  }
  .fact__label {
    margin-top: 0.5rem;
    font: 400 var(--step--1)/1.45 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
  }

  /* ---------------------------------------------------------------- story */
  .story { display: grid; gap: clamp(1.6rem, 4vw, 4rem); }
  @media (min-width: 62rem) {
    .story { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); align-items: start; }
    .story__lead { position: sticky; top: clamp(7rem, 15vh, 10rem); }
  }
  .story__lead h2 { max-width: 12ch; }
  .story__body p { max-width: 62ch; }
  .story__body p + p { margin-top: 1.3rem; }

  /* ------------------------------------------------------------- services */
  /* Eight services — same rule as the perks grid. */
  .svcs {
    display: grid;
    gap: 1px;
    grid-template-columns: minmax(0, 1fr);
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
  }
  @media (min-width: 44rem) { .svcs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (min-width: 78rem) { .svcs { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  .svc {
    background: var(--void);
    padding: clamp(1.4rem, 2.6vw, 2rem);
    transition: background-color 0.45s var(--e-quart);
  }
  .svc:hover { background: oklch(0.185 0.026 245 / 0.55); }
  .svc__name {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.2rem, 2vw, 1.5rem);
    letter-spacing: 0.04em;
    line-height: 1;
    color: var(--ink);
    margin-bottom: 0.65rem;
  }
  .svc__text { font-size: var(--step--1); color: var(--mute); line-height: 1.6; }

  /* --------------------------------------------------------------- clients */
  /* Set apart from the page ground so the block reads as its own surface. */
  .clients {
    background:
      linear-gradient(180deg, oklch(0.185 0.026 245 / 0.5), oklch(0.185 0.026 245 / 0.18));
    border-block: 1px solid var(--line);
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  .clients__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
  }
  .clients__intro,
  .client {
    background: oklch(0.145 0.024 247);
    display: grid;
    place-items: center;
    min-height: clamp(110px, 14vw, 150px);
    padding: 1.2rem;
  }
  .clients__intro {
    place-items: start;
    align-content: center;
    gap: 0.7rem;
    grid-column: span 2;
    padding: clamp(1.4rem, 2.6vw, 2rem);
  }
  @media (max-width: 34rem) { .clients__intro { grid-column: span 1; } }
  .clients__intro h2 { font-size: clamp(1.5rem, 3.2vw, 2.1rem); line-height: 1; }
  .clients__intro p { font-size: var(--step--1); color: var(--faint); }

  .client__logo { display: block; width: 100%; max-width: 200px; }
  .client__logo svg {
    width: 100%;
    height: auto;
    /* The artwork is full colour; brightness(0) crushes it to black and
       invert(1) lifts that to pure white — one filter, any colour in. */
    filter: brightness(0) invert(1);
    opacity: 0.45;
    transition: filter 0.45s var(--e-quart), opacity 0.45s var(--e-quart), transform 0.6s var(--e-expo);
  }
  .client:hover .client__logo svg,
  .client:focus-within .client__logo svg {
    filter: none;
    opacity: 1;
    transform: scale(1.05);
  }

  /* --------------------------------------------------------------- values */
  .values {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
    gap: clamp(1.6rem, 3vw, 2.6rem);
  }
  .value {
    position: relative;
    padding-top: 1.4rem;
    border-top: 1px solid transparent;
  }
  .value__no {
    display: block;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.12em;
    color: var(--signal);
    margin-bottom: 1rem;
  }
  .value h3 { margin-bottom: 0.7rem; }
  .value p { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }

  /* ------------------------------------------------- blog, three abreast */
  .blog--row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
    gap: clamp(1rem, 2vw, 1.6rem);
  }
  .blog--row .post .post__link { grid-template-columns: 1fr; }
  .blog--row .post .post__frame { aspect-ratio: 16 / 10; height: auto; min-height: 0; }
  .blog--row .post .post__body { align-content: start; }
}

/* =========================================================================
   Blog index
   ========================================================================= */
@layer components {
  .phero--blog { padding-block: clamp(8rem, 16vh, 12rem) clamp(2rem, 4vw, 3.5rem); }

  /* ------------------------------------------------------- the opened core */
  /* The scene spans the hero *and* the filter row, because the chips are what
     drives it. Scoped to its own class names rather than reusing
     `.phero__stage` — that one is sticky by design for the pinned scenes, and
     this hero is deliberately not pinned: a blog index should get you to the
     posts, not hold you for a camera move. */
  /* One value for the gap on both sides of the closing rule, so the status line
     and the first row of posts sit off it by the same amount. Splitting this
     into a margin here and a padding there is how the grid ended up welded to
     the line. Declared on both because `.bposts` is the wrapper's sibling, not
     its child — a custom property on `.bhero` alone never reaches it. */
  .bhero,
  .bposts { --bhero-gap: clamp(2rem, 3.6vw, 3rem); }

  .bhero {
    position: relative;
    isolation: isolate;
    overflow: clip;
    border-bottom: 1px solid var(--line);
  }
  .bhero .phero--blog { border-bottom: 0; }
  .bhero__stage {
    position: absolute;
    inset: 0;
    z-index: -2;
    pointer-events: none;
  }
  /* The canvas draws with a transparent backdrop, so this gradient is both the
     fallback with no JS and the scene's own ambient ground. */
  .bhero__canvas {
    display: block;
    width: 100%;
    height: 100%;
    background:
      radial-gradient(30% 34% at 72% 40%, oklch(0.58 0.13 222 / 0.26), transparent 70%),
      radial-gradient(13% 15% at 72% 40%, oklch(0.82 0.10 210 / 0.20), transparent 74%),
      linear-gradient(#04101b 0%, #05141f 54%, #030a11 100%);
  }
  .bhero__veil {
    position: absolute;
    inset: 0;
    background:
      linear-gradient(100deg,
        oklch(0.105 0.02 250 / 0.90) 0%,
        oklch(0.105 0.02 250 / 0.70) 34%,
        oklch(0.105 0.02 250 / 0.22) 62%,
        transparent 84%),
      linear-gradient(to bottom,
        oklch(0.105 0.02 250 / 0.5) 0%,
        transparent 24%,
        transparent 74%,
        oklch(0.125 0.023 249 / 0.72) 100%);
  }
  .bhero__filters {
    position: relative;
    padding-block: 0 var(--bhero-gap);
  }
  /* The status line's own margin would stack on top of that padding and make
     the two sides of the rule unequal again. */
  .bhero .filters__status { margin-bottom: 0; }
  /* The band below holds only the grid. Its section padding is replaced by the
     mirrored gap — the full `--section-y` would open a hole instead. */
  .bposts { padding-block-start: var(--bhero-gap); }

  /* Unpressed chips are transparent, which is fine over a flat band and not
     over a moving scene. The pressed state keeps its solid cyan. */
  .bhero .chip:not([aria-pressed='true']) {
    background: oklch(0.155 0.024 246 / 0.66);
  }

  @media (max-width: 61.99rem) {
    /* Full-width copy, no free column: the object drops to the middle (set in
       core.js) and the veil carries the text band. */
    .bhero__veil {
      background:
        linear-gradient(to bottom,
          oklch(0.105 0.02 250 / 0.42) 0%,
          oklch(0.105 0.02 250 / 0.78) 20%,
          oklch(0.105 0.02 250 / 0.74) 58%,
          oklch(0.115 0.022 249 / 0.84) 82%,
          oklch(0.125 0.023 249 / 0.9) 100%);
    }
  }

  /* ------------------------------------------------------------- filters */
  .filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1.2rem;
  }
  .chip {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.6rem 0.95rem 0.5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: 1.05rem;
    line-height: 1;
    letter-spacing: 0.05em;
    color: var(--mute);
    transition: color 0.3s var(--e-quart), border-color 0.3s var(--e-quart),
                background-color 0.3s var(--e-quart);
  }
  .chip__n {
    font: 400 0.66rem/1 var(--mono);
    letter-spacing: 0.04em;
    color: var(--faint);
    transition: color 0.3s var(--e-quart);
  }
  .chip:hover:not(:disabled) { color: var(--ink); border-color: var(--line-strong); }
  .chip[aria-pressed='true'] {
    color: oklch(0.16 0.03 240);
    background: var(--signal);
    border-color: var(--signal);
  }
  .chip[aria-pressed='true'] .chip__n { color: oklch(0.16 0.03 240 / 0.7); }
  .chip:disabled { opacity: 0.35; cursor: not-allowed; }

  .filters__status {
    font: 400 var(--step--1)/1.5 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    margin-bottom: clamp(1.6rem, 3vw, 2.4rem);
  }

  /* ---------------------------------------------------------------- grid */
  /* 400px min lands on three columns at full width, so six per page fills two
     complete rows instead of leaving a ragged half-row. */
  .bgrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 400px), 1fr));
    gap: clamp(1rem, 2vw, 1.6rem);
  }
  .bempty {
    padding: clamp(2.5rem, 6vw, 4rem) 0;
    text-align: center;
    color: var(--faint);
  }

  .bpost { position: relative; }
  .bpost__link {
    display: grid;
    grid-template-rows: auto 1fr;
    height: 100%;
    text-decoration: none;
    color: inherit;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.5);
    overflow: hidden;
    transition: border-color 0.5s var(--e-quart), background-color 0.5s var(--e-quart),
                transform 0.6s var(--e-expo);
  }
  .bpost:hover .bpost__link {
    border-color: oklch(0.815 0.13 218 / 0.34);
    background: oklch(0.19 0.028 244 / 0.6);
    transform: translateY(-4px);
  }
  .bpost__frame {
    position: relative;
    display: block;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    isolation: isolate;
    background: var(--void-deep);
  }
  .bpost__frame img {
    width: 100%; height: 100%;
    object-fit: cover;
    filter: saturate(0.75) brightness(0.78) contrast(1.04);
    transform: scale(1.03);
    transition: transform 1.2s var(--e-expo), filter 0.8s var(--e-quart);
  }
  /* This one had no transition at all — it only ever sat at full strength.
     It needs one now that it has two states to move between. */
  .bpost__frame::before {
    content: ''; position: absolute; inset: 0; z-index: 1;
    background: var(--duotone); mix-blend-mode: color;
    opacity: 0;
    transition: opacity 0.8s var(--e-quart);
  }
  /* focus-within so a keyboard gets the same state a pointer does — the
     teaser cards already had it, these did not. */
  .bpost:hover .bpost__frame img,
  .bpost:focus-within .bpost__frame img { transform: scale(1.09); filter: saturate(0.34) brightness(0.6) contrast(1.12); }
  .bpost:hover .bpost__frame::before,
  .bpost:focus-within .bpost__frame::before { opacity: 1; }
  .bpost__cat {
    position: absolute; top: 0.8rem; left: 0.8rem; z-index: 2;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--signal);
    background: oklch(0.105 0.02 250 / 0.66);
    border: 1px solid oklch(0.815 0.13 218 / 0.3);
    border-radius: var(--radius-sm);
    padding: 0.34rem 0.55rem;
    backdrop-filter: blur(6px);
  }
  .bpost__body {
    display: grid;
    align-content: start;
    gap: 0.55rem;
    padding: clamp(1.1rem, 1.9vw, 1.5rem);
  }
  .bpost__meta {
    font: 400 var(--step--1)/1 var(--mono);
    color: var(--faint);
  }
  .bpost__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.2rem, 2vw, 1.5rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: var(--ink);
    text-wrap: balance;
    transition: color 0.35s var(--e-quart);
  }
  .bpost:hover .bpost__title { color: var(--signal); }
  .bpost__text { font-size: var(--step--1); color: var(--mute); line-height: 1.6; }
  .bpost__more {
    display: inline-flex; align-items: center; gap: 0.5rem;
    margin-top: 0.35rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    transition: color 0.3s var(--e-quart);
  }
  .bpost__more svg { transition: transform 0.45s var(--e-expo); }
  .bpost:hover .bpost__more { color: var(--signal); }
  .bpost:hover .bpost__more svg { transform: translateX(5px); }

  /* --------------------------------------------------------------- pager */
  .pager {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.45rem;
    margin-top: clamp(2.5rem, 5vw, 4rem);
  }
  .pager__btn {
    min-width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    padding-inline: 0.7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font: 500 1rem/1 var(--mono);
    color: var(--mute);
    transition: color 0.3s, border-color 0.3s, background-color 0.3s;
  }
  .pager__btn:hover:not(:disabled) { color: var(--ink); border-color: var(--signal); background: oklch(0.815 0.13 218 / 0.08); }
  .pager__btn[aria-current='page'] {
    color: oklch(0.16 0.03 240);
    background: var(--signal);
    border-color: var(--signal);
  }
  .pager__btn:disabled { opacity: 0.3; cursor: not-allowed; }

  /* --------------------------------------------------- product news band */
  /* Deliberately not cards: these are release notes, and a changelog reads
     better as a column than as a gallery. */
  .pnewsband {
    background: linear-gradient(180deg, oklch(0.185 0.026 245 / 0.55), oklch(0.185 0.026 245 / 0.2));
    border-block: 1px solid var(--line);
    padding-block: clamp(3.5rem, 7vw, 6rem);
    scroll-margin-top: 6rem;
  }
  .pnewsband__head {
    display: grid;
    gap: clamp(1.2rem, 3vw, 3rem);
    margin-bottom: clamp(2rem, 4vw, 3rem);
  }
  @media (min-width: 58rem) {
    .pnewsband__head { grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr); align-items: end; }
  }
  .pnewsband__head h2 { margin-top: 0.6rem; }
  .pnewsband__note { color: var(--mute); max-width: 46ch; }

  .pnews__list { display: grid; }
  .pnews {
    display: grid;
    grid-template-columns: minmax(0, 170px) minmax(0, 1fr) auto;
    align-items: start;
    gap: clamp(1rem, 2.4vw, 2.4rem);
    padding-block: clamp(1.4rem, 2.6vw, 2rem);
    border-top: 1px solid var(--line);
    position: relative;
  }
  .pnews:last-child { border-bottom: 1px solid var(--line); }
  .pnews::after {
    content: '';
    position: absolute;
    inset: 0 -1rem;
    z-index: -1;
    border-radius: var(--radius);
    background: oklch(0.815 0.13 218 / 0.06);
    opacity: 0;
    transition: opacity 0.4s var(--e-quart);
  }
  .pnews:hover::after { opacity: 1; }
  .pnews__side { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.6rem; }
  .pnews__product {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: 1.25rem;
    line-height: 1;
    letter-spacing: 0.05em;
    color: var(--ink);
  }
  .pnews__ver {
    font: 500 var(--step--1)/1 var(--mono);
    color: var(--signal);
    border: 1px solid oklch(0.815 0.13 218 / 0.3);
    border-radius: var(--radius-sm);
    padding: 0.28rem 0.45rem;
  }
  .pnews__date {
    display: block;
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    margin-bottom: 0.5rem;
  }
  .pnews__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.15rem, 2vw, 1.5rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: var(--ink);
    margin-bottom: 0.5rem;
  }
  .pnews__text { font-size: var(--step--1); color: var(--mute); line-height: 1.6; max-width: 62ch; }
  .pnews__go {
    align-self: center;
    display: grid; place-items: center;
    width: 40px; height: 40px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    color: var(--faint);
    transition: color 0.3s, border-color 0.3s, transform 0.45s var(--e-expo);
  }
  .pnews:hover .pnews__go { color: var(--signal); border-color: var(--signal); transform: translateX(4px); }

  @media (max-width: 46rem) {
    .pnews { grid-template-columns: 1fr auto; }
    .pnews__side { grid-column: 1 / -1; }
  }
}

@layer motion {
  /* Cards stagger in on load and again on every filter change, so switching
     category reads as a change rather than a redraw.

     Scoped to `.is-staged`, which blog.js sets on the filterable grid as it
     starts. Two things depend on that: a `.bgrid` on any other page (the
     related posts under an article) keeps the ordinary [data-reveal] path, and
     if blog.js ever fails to load the cards stay visible instead of being
     held at opacity 0 by a release class that never arrives. */
  .js-motion .bgrid.is-staged .bpost { opacity: 0; transform: translate3d(0, 14px, 0); }
  .js-motion .bgrid.is-staged.is-in .bpost:not([hidden]) {
    opacity: 1;
    transform: none;
    transition: opacity 0.5s var(--e-quart), transform 0.6s var(--e-expo);
    transition-delay: calc(var(--i, 0) * 55ms);
  }
  @media (prefers-reduced-motion: reduce) {
    .js-motion .bgrid .bpost { opacity: 1 !important; transform: none !important; }
  }
}

/* =========================================================================
   Article
   ========================================================================= */
@layer components {
  .art__head {
    position: relative;
    isolation: isolate;
    overflow: clip;
    padding-block: clamp(8rem, 16vh, 12rem) clamp(2rem, 4vw, 3rem);
  }
  .art__glow {
    position: absolute;
    inset: -50% 20% auto -20%;
    height: 130%;
    z-index: -1;
    background: radial-gradient(50% 55% at 25% 32%, oklch(0.815 0.13 218 / 0.15), transparent 70%);
    pointer-events: none;
  }
  .art__cat {
    display: inline-block;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
    border: 1px solid oklch(0.815 0.13 218 / 0.32);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.6rem;
    margin-bottom: clamp(1.1rem, 2vw, 1.6rem);
  }
  .art__title {
    font-family: var(--display);
    font-weight: 600;
    text-transform: uppercase;
    font-size: var(--step-5);
    line-height: 0.96;
    letter-spacing: 0.022em;
    color: var(--ink);
    max-width: 18ch;
    text-wrap: balance;
  }
  .art__dek {
    margin-top: clamp(1.1rem, 2.2vw, 1.7rem);
    max-width: 54ch;
    font-size: var(--step-1);
    line-height: 1.55;
    color: var(--mute);
  }
  .art__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem 1.6rem;
    margin-top: clamp(1.6rem, 3vw, 2.4rem);
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
  }
  .art__meta span + span { position: relative; }
  .art__meta span + span::before {
    content: '·';
    position: absolute;
    left: -0.9rem;
  }

  .art__cover {
    width: min(100% - var(--edge) * 2, 1680px);
    margin: 0 auto clamp(2.5rem, 5vw, 4.5rem);
    border-radius: var(--radius);
    overflow: hidden;
    border: 1px solid var(--line);
    position: relative;
    isolation: isolate;
  }
  /* The frame is the 2:1 window; the image is taller than it, so the parallax
     shift has headroom and never exposes an edge. */
  .art__cover-frame {
    display: block;
    aspect-ratio: 2 / 1;
    overflow: hidden;
  }
  .art__cover img {
    width: 100%;
    height: 118%;
    object-fit: cover;
    filter: saturate(0.4) brightness(0.68) contrast(1.1);
    will-change: transform;
  }
  .art__cover figcaption {
    padding: 0.9rem clamp(1rem, 2vw, 1.5rem);
    font: 400 var(--step--1)/1.5 var(--mono);
    color: var(--faint);
    border-top: 1px solid var(--line);
    background: oklch(0.155 0.024 246 / 0.5);
  }

  /* The single-column default needs an explicit minmax(0,…) track: an auto
     column sizes to max-content, and .prose's 68ch measure then pushed the
     grid wider than the viewport on phones. */
  .art__layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(2rem, 4vw, 4rem);
  }
  @media (min-width: 64rem) {
    .art__layout { grid-template-columns: minmax(0, 230px) minmax(0, 1fr); }
    .art__aside { position: sticky; top: clamp(7rem, 15vh, 9.5rem); align-self: start; }
  }

  /* ------------------------------------------------------------------ toc */
  .toc__title {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
    margin-bottom: 1rem;
  }
  .toc__list { display: grid; gap: 0.15rem; counter-reset: toc; }
  .toc__list li { position: relative; }
  .toc__list a {
    display: block;
    padding: 0.5rem 0.7rem 0.5rem 1.1rem;
    border-left: 2px solid var(--line);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: var(--step--1);
    line-height: 1.4;
    color: var(--faint);
    text-decoration: none;
    transition: color 0.3s var(--e-quart), border-color 0.3s var(--e-quart),
                background-color 0.3s var(--e-quart);
  }
  .toc__list a:hover { color: var(--ink); border-color: var(--line-strong); }
  .toc__list li[data-active] a {
    color: var(--ink);
    border-color: var(--signal);
    background: oklch(0.815 0.13 218 / 0.07);
  }

  .share { margin-top: clamp(1.8rem, 3vw, 2.4rem); padding-top: 1.4rem; border-top: 1px solid var(--line); }
  .share__btn {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--mute);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 0.65rem 0.9rem;
    transition: color 0.3s, border-color 0.3s;
  }
  .share__btn:hover { color: var(--signal); border-color: var(--signal); }

  /* ---------------------------------------------------------------- prose */
  .prose { max-width: 68ch; }
  .prose > * + * { margin-top: 1.35rem; }
  .prose p { color: var(--mute); line-height: 1.75; }
  .prose strong { color: var(--ink); font-weight: 600; }
  .prose em { color: var(--ink); font-style: italic; }
  .prose__lead {
    font-size: var(--step-1);
    line-height: 1.6;
    color: var(--ink);
  }
  .prose h2 {
    margin-top: clamp(2.6rem, 5vw, 4rem);
    font-size: var(--step-3);
    scroll-margin-top: 7rem;
  }
  .prose h3 {
    margin-top: clamp(1.8rem, 3vw, 2.4rem);
    font-size: clamp(1.2rem, 2vw, 1.5rem);
    scroll-margin-top: 7rem;
  }
  .prose h2 + p, .prose h3 + p { margin-top: 0.9rem; }

  .prose__steps { counter-reset: step; display: grid; gap: 1rem; }
  .prose__steps li {
    position: relative;
    padding-left: 2.6rem;
    color: var(--mute);
    line-height: 1.7;
  }
  .prose__steps li::before {
    counter-increment: step;
    content: counter(step, decimal-leading-zero);
    position: absolute;
    left: 0; top: 0.1rem;
    font: 500 var(--step--1)/1 var(--mono);
    color: var(--signal);
  }

  .pull {
    margin-block: clamp(2rem, 4vw, 3rem);
    padding-left: clamp(1.2rem, 2.4vw, 2rem);
    border-left: 2px solid var(--signal);
  }
  .pull p {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.4rem, 2.8vw, 2rem);
    line-height: 1.06;
    letter-spacing: 0.024em;
    color: var(--ink);
  }

  .callout {
    margin-block: clamp(1.8rem, 3.5vw, 2.6rem);
    padding: clamp(1.2rem, 2.4vw, 1.8rem);
    border: 1px solid oklch(0.815 0.13 218 / 0.26);
    border-radius: var(--radius);
    background: oklch(0.815 0.13 218 / 0.055);
  }
  .callout__label {
    font: 500 var(--step--1)/1 var(--mono) !important;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal) !important;
    margin-bottom: 0.7rem;
  }
  .callout p:last-child { margin: 0; font-size: var(--step--1); }

  /* ----------------------------------------------------------------- code */
  .code {
    margin-block: clamp(1.8rem, 3.5vw, 2.6rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    background: oklch(0.12 0.022 248);
  }
  .code__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.6rem 0.7rem 0.6rem 1rem;
    border-bottom: 1px solid var(--line);
    background: oklch(0.155 0.024 246 / 0.6);
  }
  .code__lang {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--faint);
  }
  .code__copy {
    font: 500 0.72rem/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--mute);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.45rem 0.6rem;
    transition: color 0.3s, border-color 0.3s;
  }
  .code__copy:hover { color: var(--signal); border-color: var(--signal); }
  .code__copy[data-done] { color: var(--signal); border-color: var(--signal); }
  /* wide code scrolls inside its own box, never the page */
  .code pre { overflow-x: auto; padding: clamp(1rem, 2vw, 1.4rem); }
  .code code {
    font: 400 0.85rem/1.75 var(--mono);
    color: oklch(0.86 0.03 232);
    white-space: pre;
  }
  .tok-c { color: var(--faint); }
  .tok-k { color: oklch(0.80 0.13 300); }
  .tok-f { color: var(--signal); }
  .tok-n { color: var(--amber); }

  /* ----------------------------------------------------------------- tags */
  .tags { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: clamp(2.5rem, 5vw, 3.5rem); }
  .tag {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.45rem 0.7rem;
  }

  .author {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: clamp(1rem, 2vw, 1.6rem);
    margin-top: clamp(2rem, 4vw, 3rem);
    padding-top: clamp(1.6rem, 3vw, 2.2rem);
    border-top: 1px solid var(--line);
  }
  .author__mark {
    width: 46px; height: 46px;
    display: grid; place-items: center;
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    color: var(--mute);
  }
  .author__mark svg { width: 22px; height: 22px; }
  .author__name {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: 1.25rem;
    line-height: 1;
    letter-spacing: 0.04em;
    color: var(--ink);
    margin-bottom: 0.5rem;
  }
  .author__text { font-size: var(--step--1); color: var(--mute); line-height: 1.6; max-width: 52ch; }
  .author__link {
    display: inline-flex; align-items: center; gap: 0.5rem;
    margin-top: 0.8rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--signal);
    text-decoration: none;
  }
  .author__link svg { transition: transform 0.45s var(--e-expo); }
  .author__link:hover svg { transform: translateX(5px); }
}

/* =========================================================================
   Split headings (GSAP)
   Nothing here hides a heading. The split spans are laid out so GSAP can
   transform them; if the script never runs, the heading is untouched text.
   ========================================================================= */
@layer motion {
  .sp-c { display: inline-block; will-change: transform; }

  /* The mask has to clear the cap-height marks — Teko puts umlaut dots above
     the cap line, and a flush overflow box shears them off at rest. */
  .sp-m {
    display: inline-block;
    overflow: hidden;
    vertical-align: bottom;
    padding-top: 0.2em;
    margin-top: -0.2em;
    padding-bottom: 0.08em;
    margin-bottom: -0.08em;
  }
  .sp-w { display: inline-block; will-change: transform; }

  /* A word mask clips overflow, and a 44px text-shadow glow clipped to a word
     box becomes a visible rectangle. Char-split headings (the hero H1) have no
     mask, so they keep their glow. */
  .has-split:not(.hero__title) em { text-shadow: none; }

  /* A heading GSAP has taken over must not also be moved by the CSS reveal,
     or the two entrances fight each other. */
  .js-motion [data-reveal].has-split,
  .js-motion .hero__copy [data-lift].has-split {
    opacity: 1;
    transform: none;
  }

  @media (prefers-reduced-motion: reduce) {
    .sp-c, .sp-w { transform: none !important; }
    .sp-m { overflow: visible; }
  }
}

/* =========================================================================
   Page transitions
   The veil clears itself with a CSS animation, so no script is required to
   reveal the page. Without the `pt` class (set by a tiny inline script) it
   never appears at all, which is the no-JS path.
   ========================================================================= */
@layer motion {
  .pt-veil {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: var(--void);
    opacity: 0;
    pointer-events: none;       /* never intercepts a click, in any state */
  }
  .pt .pt-veil { animation: veil-out 0.5s var(--e-quart) forwards; }
  .pt.is-leaving .pt-veil {
    animation: none;
    opacity: 1;
    transition: opacity 0.3s var(--e-quart);
  }
  @keyframes veil-out { from { opacity: 1; } to { opacity: 0; } }

  @media (prefers-reduced-motion: reduce) {
    .pt .pt-veil, .pt.is-leaving .pt-veil { animation: none; opacity: 0; transition: none; }
  }
}

/* =========================================================================
   Timeline
   Three columns: year, spine, body. The spine is a static hairline that a
   brighter fill grows down as you scroll — the hairline is always there, so
   the column reads as a timeline even with no script at all.
   ========================================================================= */
@layer components {
  .tl { position: relative; display: grid; gap: 0; }

  .tl__spine {
    position: absolute;
    top: 0.6rem;
    bottom: 3rem;
    left: 11px;
    width: 1px;
    background: var(--line-strong);
  }
  .tl__fill {
    position: absolute;
    inset: 0;
    display: block;
    background: linear-gradient(var(--signal), oklch(0.815 0.13 218 / 0.35));
    transform: scaleY(var(--tl-p, 0));
    transform-origin: top;
  }
  @media (min-width: 56rem) {
    .tl__spine { left: calc(clamp(6rem, 11vw, 9rem) + 23px); }
  }

  .tl__item {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: 'mark year' 'body body';
    column-gap: 1rem;
    padding-bottom: clamp(2.6rem, 5vw, 4.5rem);
  }
  @media (min-width: 56rem) {
    .tl__item {
      grid-template-columns: clamp(6rem, 11vw, 9rem) 46px minmax(0, 1fr);
      grid-template-areas: 'year mark body';
      column-gap: 0;
    }
  }

  .tl__year {
    grid-area: year;
    align-self: start;
    font-family: var(--display);
    font-weight: 600;
    text-transform: uppercase;
    font-size: clamp(1.5rem, 3vw, 2.4rem);
    line-height: 0.9;
    letter-spacing: 0.03em;
    color: var(--faint);
    transition: color 0.6s var(--e-quart);
  }
  @media (min-width: 56rem) { .tl__year { text-align: right; padding-right: 1.4rem; } }
  .tl__year span { display: inline-block; }
  .tl__item[data-passed] .tl__year { color: var(--ink); }

  /* the dot sits on the spine, so its box has to be exactly spine-width */
  .tl__mark {
    grid-area: mark;
    position: relative;
    width: 23px;
    justify-self: start;
    align-self: start;
    height: 1.4em;
  }
  @media (min-width: 56rem) { .tl__mark { justify-self: center; width: 46px; } }
  .tl__mark::before {
    content: '';
    position: absolute;
    top: 0.42em;
    left: 7px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--void);
    border: 1px solid var(--line-strong);
    transition: background-color 0.5s var(--e-quart), border-color 0.5s var(--e-quart),
                box-shadow 0.5s var(--e-quart), transform 0.5s var(--e-expo);
  }
  @media (min-width: 56rem) { .tl__mark::before { left: 19px; } }
  .tl__item[data-passed] .tl__mark::before {
    background: var(--signal);
    border-color: var(--signal);
    box-shadow: 0 0 0 4px oklch(0.815 0.13 218 / 0.16), 0 0 18px var(--signal-glow);
    transform: scale(1.15);
  }
  .tl__mark--open::before { border-style: dashed; }

  .tl__body { grid-area: body; padding-top: 0.6rem; }
  @media (min-width: 56rem) { .tl__body { padding-top: 0; max-width: 62ch; } }

  .tl__when {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
    margin-bottom: 0.7rem;
  }
  .tl__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.35rem, 2.4vw, 1.9rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: var(--ink);
    margin-bottom: 0.7rem;
  }
  .tl__text { color: var(--mute); line-height: 1.7; }

  .tl__media {
    margin-top: clamp(1.2rem, 2.4vw, 1.8rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    aspect-ratio: 16 / 10;
    max-width: 34rem;
  }
  .tl__media img {
    width: 100%; height: 100%;
    object-fit: cover;
    filter: saturate(0.36) brightness(0.62) contrast(1.12);
    transform: scale(1.02);
    transition: transform 1.2s var(--e-expo), filter 0.8s var(--e-quart);
  }
  .tl__media:hover img { transform: scale(1.07); filter: saturate(0.7) brightness(0.76) contrast(1.05); }

  /* A product screenshot is mostly white UI; the photo treatment turns it to
     mud, so it gets a far lighter hand and sits on its own ground. */
  .tl__media--shot {
    background: oklch(0.19 0.026 245);
    aspect-ratio: auto;
  }
  .tl__media--shot img {
    filter: saturate(0.72) brightness(0.86) contrast(1.02);
    transform: none;
  }
  .tl__media--shot:hover img { filter: none; transform: none; }

  .tl__item--open .tl__body .btn { margin-top: 1.3rem; }
  .tl__item--open .tl__title { color: var(--signal); }
}

@layer motion {
  /* Entries arrive from the spine side; without JS they are simply in place. */
  .js-motion .tl__item .tl__body,
  .js-motion .tl__item .tl__year {
    opacity: 0;
    transform: translate3d(0, 18px, 0);
    transition: opacity 0.7s var(--e-quart), transform 0.9s var(--e-expo);
  }
  .js-motion .tl__item[data-reveal='shown'] .tl__body,
  .js-motion .tl__item[data-reveal='shown'] .tl__year {
    opacity: 1;
    transform: none;
  }
  .js-motion .tl__item[data-reveal='shown'] .tl__body { transition-delay: 90ms; }

  @media (prefers-reduced-motion: reduce) {
    .js-motion .tl__item .tl__body,
    .js-motion .tl__item .tl__year { opacity: 1 !important; transform: none !important; }
    .tl__fill { transform: scaleY(1); }
  }
}

/* =========================================================================
   Reasons
   A sticky index beside a scrolling list. The index is real navigation, not
   decoration — every tick jumps to its reason — so it stays in the DOM and in
   the a11y tree rather than being hidden from it.
   ========================================================================= */
@layer components {
  .reasons { display: grid; gap: clamp(2rem, 4vw, 4rem); }
  @media (min-width: 62rem) {
    .reasons { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); }
    .reasons__nav { position: sticky; top: clamp(7rem, 15vh, 10rem); align-self: start; }
  }
  /* below that the sticky column would eat the screen, so it steps aside */
  @media (max-width: 61.99rem) { .reasons__nav { display: none; } }

  .reasons__count {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    font-family: var(--display);
    font-weight: 600;
    line-height: 0.85;
    letter-spacing: 0.02em;
  }
  .reasons__count span {
    font-size: clamp(3.4rem, 6vw, 5rem);
    color: var(--signal);
    font-variant-numeric: tabular-nums;
  }
  .reasons__count i {
    font-style: normal;
    font-size: 1.4rem;
    color: var(--faint);
  }
  .reasons__now {
    margin-top: 0.9rem;
    font-size: var(--step--1);
    line-height: 1.5;
    color: var(--mute);
    min-height: 3em;          /* holds its box while the label swaps */
    max-width: 22ch;
  }

  .reasons__ticks {
    display: grid;
    gap: 0.35rem;
    margin-top: clamp(1.4rem, 2.6vw, 2rem);
    padding-top: 1.3rem;
    border-top: 1px solid var(--line);
  }
  .reasons__ticks button {
    display: block;
    width: 100%;
    padding-block: 7px;       /* invisible tap target around a 2px rule */
  }
  .reasons__ticks button::before {
    content: '';
    display: block;
    height: 2px;
    width: 26px;
    border-radius: 2px;
    background: var(--line-strong);
    transition: width 0.5s var(--e-expo), background-color 0.4s var(--e-quart);
  }
  .reasons__ticks button:hover::before { background: var(--mute); width: 38px; }
  .reasons__ticks li[data-active] button::before { background: var(--signal); width: 54px; }

  .reasons__list { display: grid; }
  .reason {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: clamp(1rem, 2.4vw, 2.2rem);
    align-items: start;
    padding-block: clamp(1.8rem, 3.4vw, 2.8rem);
    border-top: 1px solid var(--line);
    scroll-margin-top: 7rem;
  }
  .reason:first-child { border-top: 0; padding-top: 0; }
  .reason:last-child { border-bottom: 1px solid var(--line); }

  .reason__no {
    font-family: var(--display);
    font-weight: 600;
    font-size: clamp(1.6rem, 3vw, 2.3rem);
    line-height: 0.9;
    letter-spacing: 0.03em;
    color: var(--faint);
    font-variant-numeric: tabular-nums;
    transition: color 0.5s var(--e-quart);
  }
  .reason[data-active] .reason__no { color: var(--signal); }

  .reason__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.3rem, 2.4vw, 1.9rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: var(--ink);
    margin-bottom: 0.7rem;
  }
  .reason__text { color: var(--mute); line-height: 1.75; max-width: 62ch; }

  /* ------------------------------------------------- the honest counter */
  .against {
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.185 0.026 245 / 0.5), oklch(0.185 0.026 245 / 0.16));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  .against__inner { max-width: 62rem; }
  .against__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--amber);
    margin-bottom: 1rem;
  }
  .against__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: var(--step-3);
    line-height: 1;
    letter-spacing: 0.024em;
    color: var(--ink);
    margin-bottom: 1.4rem;
    max-width: 18ch;
  }
  .against__text { color: var(--mute); line-height: 1.75; max-width: 62ch; }
  .against__text + .against__text { margin-top: 1.1rem; }
}

/* =========================================================================
   Jobs
   ========================================================================= */
@layer components {
  .jobs { display: grid; border-top: 1px solid var(--line); }
  .job { border-bottom: 1px solid var(--line); }
  .job__head { margin: 0; font: inherit; text-transform: none; letter-spacing: 0; }

  .job__btn {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: 'title plus' 'chips plus';
    align-items: center;
    gap: 0.5rem clamp(1rem, 2vw, 2rem);
    width: 100%;
    padding: clamp(1.3rem, 2.4vw, 2rem) 0;
    text-align: left;
    color: var(--mute);
    transition: color 0.35s var(--e-quart);
  }
  @media (min-width: 52rem) {
    .job__btn {
      grid-template-columns: minmax(0, 1fr) auto auto;
      grid-template-areas: 'title chips plus';
    }
  }
  .job__btn:hover, .job[data-open='true'] .job__btn { color: var(--ink); }

  .job__title {
    grid-area: title;
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.35rem, 2.6vw, 2rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: currentColor;
  }
  .job__chips { grid-area: chips; display: flex; flex-wrap: wrap; gap: 0.4rem; }
  .job__chip {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--faint);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.6rem;
    white-space: nowrap;
  }
  .job[data-open='true'] .job__chip { color: var(--signal); border-color: oklch(0.815 0.13 218 / 0.32); }

  .job__plus {
    grid-area: plus;
    position: relative;
    width: 14px; height: 14px;
    align-self: center;
    flex: none;
  }
  .job__plus::before, .job__plus::after {
    content: '';
    position: absolute;
    inset: 50% 0 auto;
    height: 1px;
    background: currentColor;
    transition: transform 0.5s var(--e-expo), opacity 0.3s;
  }
  .job__plus::after { transform: rotate(90deg); }
  .job[data-open='true'] .job__plus::after { transform: rotate(0deg); opacity: 0; }

  /* Open by default; only JS collapses them. Written the other way round the
     job descriptions would be permanently unreachable without script — the
     same shape as the .practice__panel rule on the homepage. */
  .job__panel {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows 0.6s var(--e-expo);
  }
  .js-motion .job:not([data-open='true']) .job__panel { grid-template-rows: 0fr; }
  .job__panel > div { overflow: hidden; }
  /* the toggle is meaningless when everything is already open */
  html:not(.js-motion) .job__plus { display: none; }

  .job__pitch {
    font-size: var(--step-1);
    line-height: 1.55;
    color: var(--ink);
    max-width: 58ch;
    padding-bottom: clamp(1.2rem, 2.4vw, 1.8rem);
  }
  .job__cols {
    display: grid;
    gap: clamp(1.4rem, 3vw, 3rem);
    padding-bottom: clamp(1.4rem, 2.6vw, 2rem);
  }
  @media (min-width: 52rem) { .job__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .job__sub {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
    margin-bottom: 0.9rem;
  }
  .job__list { display: grid; gap: 0.6rem; }
  .job__list li {
    position: relative;
    padding-left: 1.4rem;
    color: var(--mute);
    line-height: 1.65;
    font-size: var(--step--1);
  }
  .job__list li::before {
    content: '';
    position: absolute;
    left: 0; top: 0.62em;
    width: 6px; height: 6px;
    background: var(--signal);
    transform: rotate(45deg);
  }
  .job__panel .btn { margin-bottom: clamp(1.4rem, 2.6vw, 2rem); }

  .jobs__open {
    display: grid;
    gap: 1.4rem;
    align-items: center;
    margin-top: clamp(2rem, 4vw, 3rem);
    padding: clamp(1.5rem, 3vw, 2.2rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.45);
  }
  @media (min-width: 52rem) { .jobs__open { grid-template-columns: minmax(0, 1fr) auto; } }
  .jobs__open-title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.2rem, 2vw, 1.5rem);
    letter-spacing: 0.026em;
    line-height: 1.02;
    color: var(--ink);
    margin-bottom: 0.6rem;
  }
  .jobs__open-text { font-size: var(--step--1); color: var(--mute); max-width: 52ch; }

  /* --------------------------------------------------------------- perks */
  .perksband {
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.185 0.026 245 / 0.5), oklch(0.185 0.026 245 / 0.16));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  /* Eight items: 1 / 2 / 4 divide evenly, three does not — so three is skipped. */
  .perks {
    display: grid;
    gap: 1px;
    grid-template-columns: minmax(0, 1fr);
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
  }
  @media (min-width: 44rem) { .perks { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (min-width: 78rem) { .perks { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  .perk {
    background: oklch(0.145 0.024 247);
    padding: clamp(1.3rem, 2.4vw, 1.9rem);
    transition: background-color 0.45s var(--e-quart);
  }
  .perk:hover { background: oklch(0.19 0.028 244); }
  .perk__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.1rem, 1.9vw, 1.4rem);
    line-height: 1.02;
    letter-spacing: 0.03em;
    color: var(--ink);
    margin-bottom: 0.6rem;
  }
  .perk__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }

  /* --------------------------------------------------------------- steps */
  .steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
    gap: clamp(1.6rem, 3vw, 2.6rem);
  }
  /* The border stays for layout and goes transparent; the rail itself is the
     pseudo-element, so it can be filled. Same construction as `.phase`. */
  .step {
    position: relative;
    padding-top: 1.4rem;
    border-top: 1px solid transparent;
  }
  .step__no {
    display: block;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.12em;
    color: var(--signal);
    margin-bottom: 1rem;
  }
  .step__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.2rem, 2vw, 1.5rem);
    letter-spacing: 0.03em;
    line-height: 1.02;
    color: var(--ink);
    margin-bottom: 0.7rem;
  }
  .step__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }
}

/* =========================================================================
   Product page
   Each product carries its own accent (--pa) so the three pages share one
   template without looking like the same page three times. It sits next to
   the house cyan rather than replacing it — the signal colour still marks
   milkycode, the accent marks the product.
   ========================================================================= */
@layer components {
  .product {
    --pa: oklch(0.74 0.148 268);
    --pa-dim: oklch(0.58 0.13 268);
    --pa-glow: oklch(0.74 0.148 268 / 0.3);
  }
  .product[data-product='x-url']      { --pa: oklch(0.78 0.13 190); --pa-dim: oklch(0.6 0.11 190); --pa-glow: oklch(0.78 0.13 190 / 0.3); }
  .product[data-product='dummy-email']{ --pa: oklch(0.8 0.15 68);   --pa-dim: oklch(0.63 0.13 66); --pa-glow: oklch(0.8 0.15 68 / 0.3); }

  .btn--product {
    --btn-line: color-mix(in oklab, var(--pa), transparent 45%);
    --btn-fg: var(--pa);
    --btn-fill: var(--pa);
    --btn-hover-fg: oklch(0.15 0.03 268);
  }

  /* ---------------------------------------------------------- hero */
  .phero--product { padding-block: clamp(8rem, 15vh, 11rem) clamp(2.5rem, 5vw, 4rem); }
  .phero--product .phero__glow {
    background:
      radial-gradient(52% 58% at 20% 28%, var(--pa-glow), transparent 68%),
      radial-gradient(40% 50% at 82% 10%, oklch(0.815 0.13 218 / 0.10), transparent 70%);
  }
  .prod__hero { display: grid; gap: clamp(2.5rem, 5vw, 4.5rem); align-items: center; }
  @media (min-width: 66rem) {
    .prod__hero { grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); }
  }

  .prod__badge {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--pa);
    margin-bottom: clamp(1rem, 2vw, 1.5rem);
  }
  .prod__mark {
    display: grid;
    place-items: center;
    width: 30px; height: 30px;
    border: 1px solid color-mix(in oklab, var(--pa), transparent 60%);
    border-radius: var(--radius-sm);
    background: color-mix(in oklab, var(--pa), transparent 92%);
    font-family: var(--display);
    font-size: 1.1rem;
    font-weight: 600;
  }
  .prod__name {
    font-family: var(--display);
    font-weight: 600;
    text-transform: uppercase;
    font-size: clamp(3.4rem, 8vw, 6.4rem);
    line-height: 0.88;
    letter-spacing: 0.02em;
    color: var(--ink);
    text-shadow: 0 0 60px var(--pa-glow);
  }
  .prod__claim {
    margin-top: clamp(1.1rem, 2.2vw, 1.7rem);
    max-width: 46ch;
    font-size: var(--step-1);
    line-height: 1.55;
    color: var(--mute);
  }
  .prod__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; margin-top: clamp(1.8rem, 3.4vw, 2.6rem); }
  .prod__meta {
    margin-top: 1.2rem;
    font: 400 var(--step--1)/1.5 var(--mono);
    color: var(--faint);
  }
  /* The mockup does not lie flat against the page: it sits on a plane, with
     the right edge hinged forward and the left one receding. --ry/--rx carry
     the pointer lean on top of that, so the script only ever writes two
     numbers and the resting angles stay here where they can be read. */
  .prod__stage { position: relative; }

  @media (min-width: 48rem) {
    .prod__stage { perspective: 1500px; perspective-origin: 62% 48%; }
    .prod__stage .ui {
      --ry: 0deg;
      --rx: 0deg;
      transform: rotateY(calc(-9deg + var(--ry))) rotateX(calc(3deg + var(--rx)));
      transform-origin: 85% 50%;
      will-change: transform;
      /* The light stays where it was, so the receding edge carries the shadow. */
      box-shadow:
        -36px 48px 104px -54px oklch(0.04 0.02 250 / 0.95),
        -12px 16px 38px -24px oklch(0.04 0.02 250 / 0.7);
    }
  }

  /* ------------------------------------------------------- ui mockups */
  /* Drawn in the browser rather than screenshotted: it stays sharp at any
     density, themes with the page, and never goes stale against the product. */
  .ui {
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: oklch(0.135 0.022 248);
    overflow: hidden;
    box-shadow: 0 30px 80px -40px oklch(0.05 0.02 250 / 0.9);
    color: var(--pa);
  }
  .ui__bar {
    display: flex; align-items: center; gap: 0.8rem;
    padding: 0.6rem 0.85rem;
    border-bottom: 1px solid var(--line);
    background: oklch(0.175 0.024 246);
  }
  .ui__dots { display: flex; gap: 5px; }
  .ui__dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); }
  .ui__url {
    font: 400 0.68rem/1 var(--mono);
    color: var(--faint);
    background: oklch(0.12 0.02 248);
    border-radius: 4px;
    padding: 0.35rem 0.6rem;
    flex: 1;
  }
  .ui__body { display: flex; min-height: 232px; }
  .ui__side {
    display: none;
    flex-direction: column;
    gap: 0.15rem;
    width: 122px;
    padding: 0.8rem 0.6rem;
    border-right: 1px solid var(--line);
  }
  @media (min-width: 30rem) { .ui__side { display: flex; } }
  .ui__nav {
    font: 400 0.7rem/1 var(--mono);
    color: var(--faint);
    padding: 0.5rem 0.55rem;
    border-radius: 6px;
  }
  .ui__nav.is-on { color: var(--pa); background: color-mix(in oklab, var(--pa), transparent 90%); }

  .ui__main { flex: 1; padding: 0.9rem; display: grid; gap: 0.8rem; align-content: start; }
  .ui__tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.5rem; }
  .ui__tile {
    display: grid; gap: 0.25rem;
    padding: 0.6rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: oklch(0.16 0.022 247);
  }
  .ui__k { font: 400 0.6rem/1 var(--mono); color: var(--faint); text-transform: uppercase; letter-spacing: 0.06em; }
  .ui__v { font-family: var(--display); font-weight: 600; font-size: 1.4rem; line-height: 1; color: var(--ink); }
  .ui__d { font: 500 0.62rem/1 var(--mono); }
  .ui__d--up { color: var(--pa); }
  .ui__d--dn { color: var(--amber); }

  .ui__chart { border: 1px solid var(--line); border-radius: 8px; padding: 0.5rem; background: oklch(0.16 0.022 247); }
  .ui__chart svg { width: 100%; height: 92px; display: block; }
  .ui__line { fill: none; stroke: var(--pa); stroke-width: 2; vector-effect: non-scaling-stroke; }

  .ui__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 48px 56px 78px;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.2rem;
    border-bottom: 1px solid var(--line);
    font: 400 0.7rem/1.2 var(--mono);
    color: var(--mute);
  }
  .ui__row--head { color: var(--faint); text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.6rem; }
  .ui__row:last-child { border-bottom: 0; }
  .ui__pos { font-family: var(--display); font-size: 1.05rem; font-weight: 600; color: var(--ink); }
  .spark { width: 100%; height: 22px; }
  .spark polyline { fill: none; stroke: var(--pa); stroke-width: 1.4; vector-effect: non-scaling-stroke; }

  .ui__bars { display: grid; gap: 0.55rem; }
  .ui__bar-row {
    display: grid;
    grid-template-columns: 92px minmax(0, 1fr) 30px;
    align-items: center;
    gap: 0.6rem;
    font: 400 0.68rem/1 var(--mono);
    color: var(--mute);
  }
  .ui__track { height: 8px; border-radius: 4px; background: oklch(0.2 0.02 247); overflow: hidden; }
  .ui__track i { display: block; height: 100%; width: var(--w); background: var(--pa); border-radius: 4px; }
  .ui__track i.is-alt { background: var(--line-strong); }
  .ui__val { font-family: var(--display); font-weight: 600; color: var(--ink); text-align: right; }
  .ui__note { font: 400 0.65rem/1 var(--mono); color: var(--faint); margin-top: 0.4rem; }

  /* ---------------------------------------------------------- proof */
  .proof {
    border-block: 1px solid var(--line);
    background: oklch(0.155 0.024 246 / 0.5);
    padding-block: clamp(1.6rem, 3vw, 2.4rem);
  }
  .proof__row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1.2rem clamp(1rem, 3vw, 3rem);
  }
  .proof__num {
    font-family: var(--display);
    font-weight: 600;
    font-size: clamp(1.5rem, 2.8vw, 2.1rem);
    line-height: 1;
    letter-spacing: 0.02em;
    /* Third time this one has bitten: `--pa` exists only on the product pages,
       so on the service pages the declaration was dropped and the number
       inherited body colour. See `.demo__label` and `.shots__tab`. */
    color: var(--pa, var(--signal));
  }
  .proof__lbl {
    margin-top: 0.35rem;
    font: 400 var(--step--1)/1.4 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
  }

  /* ------------------------------------------------------- features */
  .featband {
    border-bottom: 1px solid var(--line);
    background:
      radial-gradient(80% 100% at 15% 0%, color-mix(in oklab, var(--pa), transparent 92%), transparent 70%),
      linear-gradient(180deg, oklch(0.185 0.026 245 / 0.55), oklch(0.185 0.026 245 / 0.14));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  /* Six items, so the track count must divide six: 1 / 2 / 3, never 4.
     auto-fit happily reached four columns at ≥1500px and left two holes. */
  .feats {
    display: grid;
    gap: 1px;
    grid-template-columns: minmax(0, 1fr);
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
  }
  @media (min-width: 44rem) { .feats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (min-width: 68rem) { .feats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  .feat {
    background: oklch(0.145 0.024 247);
    padding: clamp(1.3rem, 2.4vw, 1.9rem);
    transition: background-color 0.45s var(--e-quart);
  }
  .feat:hover { background: color-mix(in oklab, var(--pa), oklch(0.145 0.024 247) 92%); }
  .feat__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.1rem, 1.9vw, 1.4rem);
    line-height: 1.02;
    letter-spacing: 0.03em;
    color: var(--ink);
    margin-bottom: 0.6rem;
  }
  .feat__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }

  /* ---------------------------------------------------------- shots */
  .shots__head {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    justify-content: space-between; gap: 1.4rem;
    margin-bottom: clamp(1.6rem, 3vw, 2.4rem);
  }
  .shots__tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; }
  .shots__tab {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: 1.05rem;
    line-height: 1;
    letter-spacing: 0.05em;
    color: var(--mute);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 0.6rem 0.95rem 0.5rem;
    transition: color 0.3s, border-color 0.3s, background-color 0.3s;
  }
  .shots__tab:hover { color: var(--ink); border-color: var(--line-strong); }
  .shots__tab[aria-selected='true'] {
    color: oklch(0.15 0.03 268);
    background: var(--pa, var(--signal));
    border-color: var(--pa, var(--signal));
  }
  .shots__pane[hidden] { display: none; }
  .shots__cap {
    margin-top: 1rem;
    font: 400 var(--step--1)/1.5 var(--mono);
    color: var(--faint);
  }

  /* ----------------------------------------------------------- demo */
  .demoband {
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.11 0.02 249), oklch(0.145 0.022 247));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  .demo__inner { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
  @media (min-width: 62rem) { .demo__inner { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); } }
  .demo__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* `--pa` exists only on the three product pages. Without the fallback the
       declaration is invalid everywhere else and the label quietly inherits
       body colour — the same trap `.shots__tab` fell into. */
    color: var(--pa, var(--signal));
    margin-bottom: 0.9rem;
  }
  .demo__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: var(--step-3);
    line-height: 1;
    letter-spacing: 0.024em;
    color: var(--ink);
    margin-bottom: 1rem;
    max-width: 16ch;
  }
  .demo__text { color: var(--mute); line-height: 1.7; max-width: 46ch; }
  .demo__list { display: grid; gap: 0.55rem; margin-top: 1.4rem; }
  .demo__list li {
    position: relative;
    padding-left: 1.5rem;
    font-size: var(--step--1);
    color: var(--mute);
  }
  .demo__list li::before {
    content: '';
    position: absolute; left: 0; top: 0.5em;
    width: 6px; height: 6px;
    background: var(--pa);
    transform: rotate(45deg);
  }

  /* A facade, not a fake player: it links out today, and swaps in a real
     <video> the moment a source is supplied. */
  .demo__player {
    position: relative;
    display: block;
    border-radius: var(--radius);
    overflow: hidden;
    text-decoration: none;
    isolation: isolate;
  }
  .demo__poster { display: block; }
  .demo__poster .ui { box-shadow: none; }
  .demo__player::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(to top, oklch(0.08 0.02 250 / 0.85), oklch(0.08 0.02 250 / 0.25));
    transition: opacity 0.5s var(--e-quart);
  }
  .demo__player:hover::after { opacity: 0.7; }
  .demo__play {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    place-items: center;
    pointer-events: none;
  }
  .demo__play svg {
    width: 68px; height: 68px;
    border-radius: 50%;
    color: oklch(0.15 0.03 268);
    background: var(--pa);
    box-shadow: 0 0 0 10px color-mix(in oklab, var(--pa), transparent 82%), 0 0 40px var(--pa-glow);
    transition: transform 0.5s var(--e-expo);
  }
  .demo__player:hover .demo__play svg { transform: scale(1.08); }
  .demo__cta {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    z-index: 2;
    padding: 1rem 1.2rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ink);
  }

  /* ------------------------------------------------------------ faq */
  .faq { border-top: 1px solid var(--line); }
  .faq__item { border-bottom: 1px solid var(--line); }
  .faq__head { margin: 0; font: inherit; text-transform: none; letter-spacing: 0; }
  .faq__btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    width: 100%;
    padding: clamp(1.1rem, 2vw, 1.6rem) 0;
    text-align: left;
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.1rem, 2vw, 1.45rem);
    line-height: 1.05;
    letter-spacing: 0.026em;
    color: var(--mute);
    transition: color 0.35s var(--e-quart);
  }
  .faq__btn:hover, .faq__item[data-open='true'] .faq__btn { color: var(--ink); }
  .faq__plus { position: relative; width: 14px; height: 14px; flex: none; }
  .faq__plus::before, .faq__plus::after {
    content: ''; position: absolute; inset: 50% 0 auto; height: 1px;
    background: currentColor; transition: transform 0.5s var(--e-expo), opacity 0.3s;
  }
  .faq__plus::after { transform: rotate(90deg); }
  .faq__item[data-open='true'] .faq__plus::after { transform: rotate(0deg); opacity: 0; }
  /* open by default; only JS collapses, so the answers are never script-gated */
  .faq__panel { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.6s var(--e-expo); }
  .js-motion .faq__item:not([data-open='true']) .faq__panel { grid-template-rows: 0fr; }
  .faq__panel > div { overflow: hidden; }
  .faq__panel p { max-width: 68ch; padding-bottom: clamp(1.1rem, 2vw, 1.6rem); color: var(--mute); line-height: 1.7; }
  html:not(.js-motion) .faq__plus { display: none; }

  /* ------------------------------------------------------ product cta */
  .prodcta {
    border-top: 1px solid var(--line);
    background:
      radial-gradient(65% 90% at 50% 115%, var(--pa-glow), transparent 68%),
      linear-gradient(180deg, oklch(0.12 0.02 249), oklch(0.16 0.024 247));
    padding-block: clamp(4rem, 8vw, 7rem);
    text-align: center;
  }
  .prodcta__inner { display: grid; justify-items: center; gap: 1.1rem; }
  .prodcta__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--pa);
  }
  .prodcta__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: var(--step-4);
    line-height: 1;
    letter-spacing: 0.022em;
    color: var(--ink);
    max-width: 20ch;
    text-wrap: balance;
  }
  .prodcta__text { color: var(--mute); max-width: 48ch; line-height: 1.7; }
  .prodcta__actions { display: flex; flex-wrap: wrap; gap: 0.85rem; justify-content: center; margin-top: 0.6rem; }
  .prodcta__foot {
    margin-top: clamp(1.6rem, 3vw, 2.4rem);
    padding-top: 1.4rem;
    border-top: 1px solid var(--line);
    width: min(100%, 44rem);
    font: 400 var(--step--1)/1.6 var(--mono);
    color: var(--faint);
  }
  .prodcta__foot a { color: var(--mute); text-decoration: none; border-bottom: 1px solid var(--line-strong); }
  .prodcta__foot a:hover { color: var(--pa); border-color: var(--pa); }
}

/* =========================================================================
   Service page
   ========================================================================= */
@layer components {
  /* ------------------------------------------------- problem / answer */
  /* Two columns that mean opposite things, so they are tinted differently:
     the left reads as the state you are in, the right as the way out. */
  .cxband {
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.175 0.026 246 / 0.6), oklch(0.175 0.026 246 / 0.16));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  .cx {
    display: grid;
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
  }
  @media (min-width: 56rem) { .cx { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  .cx__col {
    position: relative;
    isolation: isolate;
    --mx: 50%;
    --my: 50%;
    padding: clamp(1.4rem, 2.8vw, 2.2rem);
  }
  /* Each column carries a wash in its own colour. The right one always had
     one; the left was flat, which read as the unfinished half of a pair. */
  .cx__col--pain {
    background:
      linear-gradient(180deg, oklch(0.805 0.155 68 / 0.05), transparent 60%),
      oklch(0.145 0.024 247);
  }
  .cx__col--fix {
    background:
      linear-gradient(180deg, oklch(0.815 0.13 218 / 0.05), transparent 60%),
      oklch(0.155 0.024 246);
  }
  /* The same light the blog cards use, in each column's colour. `inset: 0` and
     not -1px as on `.post`: these two share a 1px divider, and a light that
     bleeds past it would glow into the other argument. */
  .cx__col::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.55s var(--e-quart);
  }
  .cx__col--pain::before {
    background: radial-gradient(320px circle at var(--mx) var(--my),
                                oklch(0.805 0.155 68 / 0.15), transparent 70%);
  }
  .cx__col--fix::before {
    background: radial-gradient(320px circle at var(--mx) var(--my),
                                oklch(0.815 0.13 218 / 0.16), transparent 70%);
  }
  .cx__col:hover::before, .cx__col:focus-within::before { opacity: 1; }
  .cx__label, .cx__list { position: relative; z-index: 1; }
  .cx__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: clamp(1.2rem, 2.4vw, 1.8rem);
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--line);
  }
  .cx__col--pain .cx__label { color: var(--amber); }
  .cx__col--fix .cx__label { color: var(--signal); }
  .cx__list { display: grid; gap: clamp(1.2rem, 2.2vw, 1.7rem); }
  .cx__list li { position: relative; padding-left: 1.6rem; }
  .cx__list li::before {
    content: '';
    position: absolute;
    left: 0; top: 0.5em;
    width: 7px; height: 7px;
    transform: rotate(45deg);
  }
  .cx__col--pain li::before { background: var(--amber); opacity: 0.65; }
  .cx__col--fix li::before { background: var(--signal); }
  .cx__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.05rem, 1.8vw, 1.3rem);
    line-height: 1.05;
    letter-spacing: 0.03em;
    color: var(--ink);
    margin-bottom: 0.45rem;
  }
  .cx__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }

  /* ------------------------------------------------------------ phases */
  .phaseband {
    border-bottom: 1px solid var(--line);
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  /* Four items: 1 / 2 / 4 tracks, never three — same rule as the other
     hairline grids, see "Hairline grids" in DESIGN.md. */
  .phases { display: grid; gap: clamp(1.6rem, 3vw, 2.4rem); grid-template-columns: minmax(0, 1fr); }
  @media (min-width: 44rem) { .phases { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (min-width: 78rem) { .phases { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

  .phase { position: relative; padding-top: 2.2rem; }
  /* The last segment used to be cut back to the node, which left the final
     phase without a line at all. It gets a full one now and fades out instead,
     so the track still ends deliberately rather than being chopped. */
  .phases > :last-child::before {
    -webkit-mask: linear-gradient(90deg, #000 52%, transparent 100%);
    mask: linear-gradient(90deg, #000 52%, transparent 100%);
  }
  .phase::after {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 13px; height: 13px;
    border-radius: 50%;
    background: var(--void);
    /* The node takes its colour from the same fill value as its rail, so it
       lights as the run reaches it rather than on a timer of its own. */
    border: 1px solid color-mix(in oklab, var(--line-strong), var(--signal) calc(var(--fill, 1) * 100%));
    box-shadow: 0 0 0 4px oklch(0.815 0.13 218 / calc(var(--fill, 1) * 0.12));
  }

  /* ------------------------------------------------------ the step rails */
  /* Three components on four page types draw the same line above a numbered
     step: the phases on the service pages, the steps on the contact page and
     the values on "über uns". One rail, one fill, one scroll handler.

     `--fill` is set per item by the script. Its fallback is 1, so with no JS —
     and in every browser that never runs the handler — the rails are simply
     drawn, which is the resting state the design wants anyway. */
  .phase::before,
  .step::before,
  .value::before {
    content: '';
    position: absolute;
    left: 0; right: 0;
    height: 1px;
    background-color: var(--line-strong);
    background-image: linear-gradient(90deg, var(--signal), var(--signal-dim));
    background-repeat: no-repeat;
    background-size: calc(var(--fill, 1) * 100%) 100%;
  }
  .phase::before { top: 6px; }
  .step::before, .value::before { top: -1px; }
  .phase__no {
    display: block;
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.12em;
    color: var(--signal);
    margin-bottom: 0.8rem;
  }
  .phase__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.2rem, 2vw, 1.5rem);
    line-height: 1.02;
    letter-spacing: 0.03em;
    color: var(--ink);
    margin-bottom: 0.3rem;
  }
  .phase__when {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    margin-bottom: 0.7rem;
  }
  .phase__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }


  /* ------------------------------------------------------------ models */
  .models { display: grid; gap: clamp(1rem, 2vw, 1.6rem); grid-template-columns: minmax(0, 1fr); }
  @media (min-width: 62rem) { .models { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  .model {
    position: relative;
    display: grid;
    align-content: start;
    gap: 0.55rem;
    padding: clamp(1.4rem, 2.6vw, 2rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.5);
    transition: border-color 0.4s var(--e-quart), background-color 0.4s var(--e-quart), transform 0.5s var(--e-expo);
  }
  .model:hover { border-color: var(--line-strong); transform: translateY(-3px); }
  /* one recommendation, marked rather than merely styled differently */
  .model--best {
    border-color: oklch(0.815 0.13 218 / 0.38);
    background:
      linear-gradient(180deg, oklch(0.815 0.13 218 / 0.07), transparent 55%),
      oklch(0.17 0.026 245 / 0.6);
  }
  .model--best:hover { border-color: var(--signal); }
  .model__flag {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
    margin-bottom: 0.3rem;
  }
  .model__title {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.35rem, 2.4vw, 1.8rem);
    line-height: 1;
    letter-spacing: 0.03em;
    color: var(--ink);
  }
  .model__sub {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
  }
  .model__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; margin-top: 0.4rem; }
  .model__list { display: grid; gap: 0.5rem; margin-top: 0.8rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }
  .model__list li {
    position: relative;
    padding-left: 1.3rem;
    font: 400 var(--step--1)/1.55 var(--mono);
    color: var(--mute);
  }
  .model__list li::before {
    content: '';
    position: absolute; left: 0; top: 0.55em;
    width: 6px; height: 6px;
    background: var(--signal);
    transform: rotate(45deg);
  }

  /* -------------------------------------------------- reference teaser */
  .refteaser {
    position: relative;
    overflow: clip;
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.115 0.02 249), oklch(0.15 0.022 247));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  .refteaser__inner { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
  @media (min-width: 62rem) { .refteaser__inner { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); } }
  /* The plane runs in under the copy, so the copy needs to be above it —
     the figure comes later in source order and would otherwise paint over —
     and it needs a ground of its own, exactly as `.phero--refs` gives one. */
  .refteaser__inner > :first-child { position: relative; z-index: 1; }
  .refteaser .demo__title,
  .refteaser .demo__text { text-shadow: 0 2px 18px oklch(0.06 0.02 250 / 0.85); }
  .refteaser .demo__title { margin-bottom: 1rem; }
  .refteaser .btn { margin-top: 1.6rem; }
  /* The reel from the references hero — same plane, same tilt, same mask
     geometry, so it reads as the same object rather than a second version of
     it. Only its placement differs: no frame, it bleeds off the right of the
     band, and its left fade runs in over the copy.

     The hero's column counts, its responsive column drops and its background
     grade all apply unchanged. The one thing that cannot carry over is the
     narrow-width zoom: the hero scales the plane up to 1.42 because there it
     fills a tall full-bleed hero, and in this much flatter cell that lands on a
     blurred close-up of a single screenshot. */
  /* The cell carries no plane of its own — it only holds the band open, since
     the plane is absolutely positioned and has no height. */
  .refteaser__media { min-height: clamp(25rem, 44vw, 41rem); }
  .refteaser__media .reel {
    /* `--reel-gap` lives on `.phero--refs`. Without it here the grid gap and
       the card margins resolve to nothing — and the -50% loop travel depends
       on that margin, not on the gap. */
    --reel-gap: clamp(0.8rem, 1.4vw, 1.15rem);
    z-index: auto;              /* -2 would put it behind the band's gradient */
    /* Right: off the edge of the band. Left: far enough under the copy that
       only the transparent tail of the mask arrives there. No vertical
       overhang — the mask's 58% radius against its 86% transparent stop
       reaches zero exactly at the top and bottom edges, which is what keeps
       the columns' diagonal clips from showing. */
    /* Positioned against the band, not against the grid cell. Anchored to the
       cell, the bleed is a percentage of a column that sits far inside a wide
       viewport, and at 1920 the plane stopped 95px short of the edge. Against
       the band, `right` is the band's own edge at every width. */
    inset: 0 -8% 0 32%;
    opacity: 0.5;
    /* The hero's mask is centred at 66%, which puts its outer falloff inside
       the viewport here and left the right edge of the band draining away. The
       centre moves right and the x-radius grows so the plane is still at full
       strength where the band clips it — flush right, fading only to the left.

       The y-radius is not free: 56% against the 90% transparent stop puts the
       vertical fade at zero exactly on the box edges (0.56 x 0.9 = 0.504), which
       is what keeps the columns' diagonal clips from showing. It is the same
       arithmetic as the hero's 58/86, redone for this stop. */
    -webkit-mask: radial-gradient(92% 56% at 82% 50%, #000 6%, oklch(0 0 0 / 0.34) 48%, transparent 90%);
    mask: radial-gradient(92% 56% at 82% 50%, #000 6%, oklch(0 0 0 / 0.34) 48%, transparent 90%);
  }
  /* Below 62rem the band is a single column, so there is no copy to run under
     and no right-hand half to bleed into: the plane centres in its own cell and
     comes back down to a scale that shows a plane rather than one card. */
  @media (max-width: 61.99rem) {
    /* Single column here, so the band is no longer the right frame of
       reference — the plane would cover the copy. Back onto the cell. */
    .refteaser__media { position: relative; }
    .refteaser__media .reel { inset: 0 -12%; }
    .refteaser__media .reel__plane {
      transform: rotate(-13deg) skewX(8deg) skewY(8deg) scale(1.1);
    }
  }}

/* =========================================================================
   References
   Cards with real gaps rather than a hairline grid, so a ragged last row is
   simply a shorter row — five projects never divide evenly into 2/3 columns,
   and the hairline treatment would show that as a hole.
   ========================================================================= */
@layer components {
  .refs {
    display: grid;
    gap: clamp(1.2rem, 2.4vw, 2rem);
    grid-template-columns: minmax(0, 1fr);
  }
  /* The project shots are 2.93:1 banners. Three columns shrank them to a
     strip; two keep them legible at any desktop width. */
  @media (min-width: 52rem) { .refs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

  .ref {
    position: relative;          /* the stretched link resolves against this */
    display: grid;
    grid-template-rows: auto 1fr;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.5);
    overflow: hidden;
    transition: border-color 0.5s var(--e-quart), background-color 0.5s var(--e-quart),
                transform 0.6s var(--e-expo);
  }
  .ref:hover {
    border-color: oklch(0.815 0.13 218 / 0.34);
    background: oklch(0.19 0.028 244 / 0.6);
    transform: translateY(-4px);
  }
  .ref[hidden] { display: none; }

  .ref__frame {
    position: relative;
    aspect-ratio: 1465 / 500;   /* the source images' native ratio — no crop */
    overflow: hidden;
    isolation: isolate;
    background: var(--void-deep);
  }
  /* Same reading as the rail and the blog covers: the project shows itself,
     and the house tint arrives on hover. */
  .ref__frame img {
    width: 100%; height: 100%;
    object-fit: cover;
    /* The same resting grade the homepage rail uses, so five photographs shot
       by five different people still sit on one page as one set. */
    filter: saturate(0.8) brightness(0.82) contrast(1.04);
    transform: scale(1.03);
    transition: transform 1.2s var(--e-expo), filter 0.8s var(--e-quart);
  }
  /* This had no transition either — it only ever sat at full strength. */
  .ref__frame::before {
    content: ''; position: absolute; inset: 0; z-index: 1;
    background: var(--duotone); mix-blend-mode: color;
    opacity: 0;
    transition: opacity 0.8s var(--e-quart);
  }
  .ref:hover .ref__frame img,
  .ref:has(.ref__more:focus-visible) .ref__frame img {
    transform: scale(1.05);
    filter: saturate(0.55) brightness(0.74) contrast(1.06);
  }
  .ref:hover .ref__frame::before,
  .ref:has(.ref__more:focus-visible) .ref__frame::before { opacity: 1; }
  /* A product screenshot is mostly light UI and takes the duotone badly, so
     this variant keeps the overlay off in both states and only grades. */
  .ref__frame--shot { background: oklch(0.19 0.026 245); }
  .ref__frame--shot img { filter: none; transform: none; }
  .ref__frame--shot::before { display: none; }
  .ref:hover .ref__frame--shot img,
  .ref:has(.ref__more:focus-visible) .ref__frame--shot img {
    filter: saturate(0.7) brightness(0.85) contrast(1.02);
    transform: scale(1.03);
  }

  .ref__tags {
    position: absolute;
    top: 0.8rem; left: 0.8rem; right: 0.8rem;
    z-index: 2;
    display: flex; flex-wrap: wrap; gap: 0.35rem;
  }
  .ref__tag {
    font: 500 0.68rem/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--signal);
    background: oklch(0.105 0.02 250 / 0.7);
    border: 1px solid oklch(0.815 0.13 218 / 0.3);
    border-radius: var(--radius-sm);
    padding: 0.32rem 0.5rem;
    backdrop-filter: blur(6px);
  }

  .ref__body {
    display: grid;
    align-content: start;
    gap: 0.7rem;
    padding: clamp(1.1rem, 2vw, 1.6rem);
  }
  .ref__name {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.25rem, 2.1vw, 1.6rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: var(--ink);
    text-wrap: balance;
    transition: color 0.35s var(--e-quart);
  }
  .ref:hover .ref__name { color: var(--signal); }
  .ref__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; }
  .ref__tech {
    display: flex; flex-wrap: wrap; gap: 0.3rem;
    margin-top: 0.3rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--line);
  }
  /* The whole card is the target. One real anchor, stretched over the card
     with a pseudo-element, rather than wrapping everything in an <a> or
     bolting a click handler onto the article: the accessibility tree still
     sees exactly one link per card, and it still works with JS off.
     The trade is that the card's text is no longer selectable — acceptable
     for a navigation tile, and the reason this is not done on prose. */
  .ref__more::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 3;
  }
  /* The card carries the focus ring, since the link itself is a small strip
     at the bottom and the thing being activated is the whole tile. */
  .ref:has(.ref__more:focus-visible) {
    outline: 2px solid var(--signal);
    outline-offset: 3px;
    border-color: oklch(0.815 0.13 218 / 0.34);
    background: oklch(0.19 0.028 244 / 0.6);
  }
  .ref:has(.ref__more:focus-visible) .ref__name { color: var(--signal); }

  .ref__tech li {
    font: 400 0.66rem/1 var(--mono);
    color: var(--faint);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.32rem 0.5rem;
  }

  /* --------------------------------------------------------------- trust */
  .trustband {
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.175 0.026 246 / 0.6), oklch(0.175 0.026 246 / 0.16));
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  .trust__inner { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
  @media (min-width: 62rem) { .trust__inner { grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr); } }
  .trustband .demo__title { margin-bottom: 1rem; max-width: 18ch; }
  .trustband .btn { margin-top: 1.6rem; }
  .trustband .demo__text + .demo__text { margin-top: 1.1rem; }
  .trust__aside { display: grid; gap: clamp(1.4rem, 3vw, 2rem); }
  /* who the clients are frames the figures, so it sits directly above them */
  .trust__who {
    font-size: var(--step--1);
    line-height: 1.7;
    color: var(--mute);
    padding-left: 1.1rem;
    border-left: 2px solid var(--signal);
  }
  .trust__facts { margin-top: 0; padding-top: 0; border-top: 0; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@layer motion {
  /* Same staging contract as the blog grid: the hidden state is scoped to a
     class JS sets, so no script means no hiding. */
  .js-motion .refs.is-staged .ref { opacity: 0; transform: translate3d(0, 14px, 0); }
  .js-motion .refs.is-staged.is-in .ref:not([hidden]) {
    opacity: 1;
    transform: none;
    transition: opacity 0.5s var(--e-quart), transform 0.6s var(--e-expo);
    transition-delay: calc(var(--i, 0) * 55ms);
  }
  @media (prefers-reduced-motion: reduce) {
    .js-motion .refs .ref { opacity: 1 !important; transform: none !important; }
  }
}

/* =========================================================================
   Contact
   ========================================================================= */
@layer components {
  /* The scroll stop is shorter than the one on "über uns": the quickways are
     the page's actual job and must stay within reach. */
  .phero--contact {
    padding-block: clamp(8rem, 15vh, 11rem) clamp(2rem, 4vw, 3rem);
    min-height: 140svh;
  }
  .phero--contact .shell { position: relative; }
  .phero--contact .phero__glow { opacity: 0.3; }

  /* The still the camp scene composes to — cold relay upper right, fire below
     it. It is what shows with WebGL refused, with the module failed, and with
     no JS at all, so the frame has to read from the gradient alone. */
  .phero--contact .phero__canvas {
    background:
      radial-gradient(38% 32% at 71% 30%, oklch(0.62 0.13 220 / 0.34), transparent 68%),
      radial-gradient(20% 16% at 61% 67%, oklch(0.74 0.15 62 / 0.32), transparent 72%),
      linear-gradient(#04101b 0%, #061a2a 52%, #030a11 100%);
  }
  /* Heavier at the foot than the "über uns" veil: over there free copy runs
     across the scene, here it is four cards, and a card needs a quiet ground. */
  .phero--contact .phero__veil {
    background:
      linear-gradient(100deg,
        oklch(0.105 0.02 250 / 0.90) 0%,
        oklch(0.105 0.02 250 / 0.72) 32%,
        oklch(0.105 0.02 250 / 0.26) 62%,
        transparent 82%),
      linear-gradient(to bottom,
        oklch(0.105 0.02 250 / 0.55) 0%,
        transparent 20%,
        transparent 52%,
        oklch(0.115 0.022 249 / 0.72) 82%,
        oklch(0.135 0.024 248) 100%);
  }
  /* The tiles sit over a live scene, not over a flat band. */
  .phero--contact .qway { background: oklch(0.155 0.024 246 / 0.84); }
  .phero--contact .qway:hover { background: oklch(0.19 0.028 244 / 0.9); }

  @media (max-width: 61.99rem) {
    .phero--contact { min-height: 118svh; }
    /* On a phone the copy runs the full width, so there is no clear column for
       the scene to live in: it has to sit under the text rather than beside
       it. The band behind the copy is nearly opaque and the sky is left open. */
    .phero--contact .phero__veil {
      background:
        linear-gradient(to bottom,
          oklch(0.105 0.02 250 / 0.52) 0%,
          oklch(0.105 0.02 250 / 0.84) 18%,
          oklch(0.105 0.02 250 / 0.86) 70%,
          oklch(0.115 0.022 249 / 0.78) 88%,
          oklch(0.135 0.024 248) 100%);
    }
  }

  /* three ways to reach us, ranked by how fast they actually are */
  /* Four tiles: 1 / 2 / 4 tracks, never three — see "Hairline grids". */
  .quickways {
    display: grid;
    gap: clamp(0.8rem, 1.6vw, 1.2rem);
    grid-template-columns: minmax(0, 1fr);
    margin-top: clamp(2.4rem, 5vw, 3.6rem);
  }
  @media (min-width: 40rem) { .quickways { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (min-width: 74rem) { .quickways { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  .qway {
    display: grid;
    gap: 0.35rem;
    padding: clamp(1.1rem, 2vw, 1.5rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.5);
    text-decoration: none;
    transition: border-color 0.4s var(--e-quart), background-color 0.4s var(--e-quart),
                transform 0.5s var(--e-expo);
  }
  .qway:hover {
    border-color: oklch(0.815 0.13 218 / 0.36);
    background: oklch(0.19 0.028 244 / 0.6);
    transform: translateY(-3px);
  }
  .qway__k {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
  }
  .qway__i {
    width: 20px;
    height: 20px;
    flex: none;
    transition: transform 0.5s var(--e-expo);
  }
  .qway:hover .qway__i { transform: translateY(-2px) scale(1.06); }
  .qway__v {
    font-family: var(--display);
    text-transform: uppercase;
    font-weight: 600;
    font-size: clamp(1.15rem, 2vw, 1.5rem);
    line-height: 1.02;
    letter-spacing: 0.026em;
    color: var(--ink);
  }
  .qway__n { font-size: var(--step--1); color: var(--faint); }

  /* ----------------------------------------------------------- layout */
  .ct { display: grid; gap: clamp(2rem, 4vw, 3.5rem); }
  @media (min-width: 68rem) {
    .ct { grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.85fr); align-items: start; }
    .ct__side { position: sticky; top: clamp(7rem, 15vh, 9.5rem); }
  }

  /* ------------------------------------------------------------- form */
  .form { display: grid; gap: clamp(1.1rem, 2vw, 1.5rem); }
  .form__row { display: grid; gap: clamp(1.1rem, 2vw, 1.5rem); }
  @media (min-width: 40rem) { .form__row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

  .ff { display: grid; gap: 0.5rem; }
  .ff label {
    font: 500 var(--step--1)/1.3 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--mute);
  }
  .ff abbr { color: var(--signal); text-decoration: none; border: 0; }

  .ff input,
  .ff select,
  .ff textarea {
    width: 100%;
    padding: 0.9rem 1rem;
    font-family: var(--body);
    font-size: var(--step-0);
    line-height: 1.5;
    color: var(--ink);
    background: oklch(0.135 0.022 248);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    transition: border-color 0.3s var(--e-quart), background-color 0.3s var(--e-quart),
                box-shadow 0.3s var(--e-quart);
  }
  .ff textarea { resize: vertical; min-height: 8rem; }
  .ff input::placeholder,
  .ff textarea::placeholder { color: var(--faint); }
  .ff input:hover, .ff select:hover, .ff textarea:hover { border-color: var(--mute); }
  .ff input:focus-visible,
  .ff select:focus-visible,
  .ff textarea:focus-visible {
    outline: none;
    border-color: var(--signal);
    box-shadow: 0 0 0 3px oklch(0.815 0.13 218 / 0.18);
  }
  /* the red is only ever applied after a real validation run, never on
     :invalid alone — an empty required field is not an error until submit */
  .ff input[aria-invalid='true'],
  .ff textarea[aria-invalid='true'] {
    border-color: var(--amber);
    box-shadow: 0 0 0 3px oklch(0.805 0.155 68 / 0.14);
  }
  .ff__err {
    font: 400 var(--step--1)/1.4 var(--mono);
    color: var(--amber);
  }
  .ff__err[hidden] { display: none; }

  .ff--check {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
    gap: 0.7rem;
  }
  .ff--check input {
    width: 20px; height: 20px;
    margin-top: 0.15rem;
    padding: 0;
    accent-color: var(--signal);
    border-radius: var(--radius-sm);
  }
  .ff--check label {
    font-family: var(--body);
    text-transform: none;
    letter-spacing: 0;
    font-size: var(--step--1);
    line-height: 1.6;
  }
  .ff--check a { color: var(--signal); }
  .ff--check .ff__err { grid-column: 1 / -1; }

  .form__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 1.4rem;
    margin-top: 0.4rem;
  }
  .form__hint { font: 400 var(--step--1)/1.4 var(--mono); color: var(--faint); }
  .form__note {
    padding: clamp(1rem, 2vw, 1.3rem);
    border: 1px solid oklch(0.815 0.13 218 / 0.3);
    border-radius: var(--radius);
    background: oklch(0.815 0.13 218 / 0.06);
    font-size: var(--step--1);
    line-height: 1.6;
    color: var(--mute);
  }
  .form__note[hidden] { display: none; }
  .form__note a { color: var(--signal); }

  /* ------------------------------------------------------------ cards */
  .ct__side { display: grid; gap: clamp(1rem, 2vw, 1.4rem); }
  .ctcard {
    padding: clamp(1.2rem, 2.2vw, 1.7rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.5);
  }
  .ctcard__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
    margin-bottom: 1rem;
  }
  .ctcard__addr {
    font-style: normal;
    color: var(--ink);
    line-height: 1.7;
    font-size: var(--step-0);
  }
  .ctcard__muted { color: var(--faint); }
  .ctcard__list { display: grid; gap: 0.7rem; margin-top: 1.2rem; padding-top: 1.2rem; border-top: 1px solid var(--line); }
  .ctcard__list dt {
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    margin-bottom: 0.25rem;
  }
  .ctcard__list dd { color: var(--mute); }
  .ctcard__list a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line-strong); }
  .ctcard__list a:hover { color: var(--signal); border-color: var(--signal); }

  .ctcard__text { font-size: var(--step--1); color: var(--mute); line-height: 1.65; margin-top: 1rem; }
  .ctcard__coords {
    margin-top: 0.8rem;
    font: 400 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--faint);
  }

  /* A drawn schematic, not a map embed: no third-party request, no consent
     banner, and it themes with the page. The address does the real work. */
  .minimap {
    position: relative;
    display: block;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: oklch(0.125 0.02 248);
    text-decoration: none;
    transition: border-color 0.4s var(--e-quart);
  }
  .minimap:hover { border-color: oklch(0.815 0.13 218 / 0.4); }
  /* a schematic has no affordance of its own, so it says what it does */
  .minimap__cue {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 0.55rem 0.7rem;
    font: 500 0.68rem/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink);
    background: linear-gradient(to top, oklch(0.09 0.02 250 / 0.92), transparent);
    transition: color 0.3s var(--e-quart);
  }
  .minimap:hover .minimap__cue { color: var(--signal); }
  .minimap svg { display: block; width: 100%; height: 128px; }
  .minimap__road { fill: none; stroke: var(--line-strong); stroke-width: 5; stroke-linecap: round; }
  .minimap__road--thin { stroke-width: 2.5; }
  .minimap__pin { fill: var(--signal); }
  .minimap__ring { fill: none; stroke: var(--signal); stroke-width: 1; opacity: 0.5; }

  .ctcard__go {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.9rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--signal);
    text-decoration: none;
  }
  .ctcard__go svg { transition: transform 0.45s var(--e-expo); }
  .ctcard__go:hover svg { transform: translateX(5px); }

  .ctcard__social { display: flex; flex-wrap: wrap; gap: 0.45rem; }
  .ctcard__social a {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--mute);
    text-decoration: none;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.7rem;
    transition: color 0.3s, border-color 0.3s;
  }
  .ctcard__social a:hover { color: var(--signal); border-color: var(--signal); }
  .soc__i { width: 16px; height: 16px; flex: none; }
}

@layer motion {
  @media (prefers-reduced-motion: no-preference) {
    .browser.is-scrollable .browser__hint svg { animation: browser-nudge 1.8s var(--e-quart) infinite; }
  }
  @keyframes browser-nudge {
    0%, 60%, 100% { transform: translateY(0); }
    30%           { transform: translateY(3px); }
  }
}

@layer motion {
  @media (prefers-reduced-motion: no-preference) {
    .minimap__ring { transform-origin: 120px 76px; animation: pin-pulse 3s var(--e-quart) infinite; }
  }
  @keyframes pin-pulse {
    0%   { transform: scale(0.6); opacity: 0.65; }
    70%  { transform: scale(1.5); opacity: 0; }
    100% { transform: scale(1.5); opacity: 0; }
  }
}

/* -------------------------------------------------------------- 404 page */
@layer components {

  /* The scene owns the whole viewport; the copy is a separate layer over it.
     Nothing here is revealed by script — with the canvas blank the overlay
     still reads against the painted fallback sky. */
  .void {
    position: relative;
    min-height: 100svh;
    display: grid;
    grid-template-rows: 1fr auto;
    isolation: isolate;
    overflow: clip;
  }
  .void__canvas {
    position: absolute;
    inset: 0;
    z-index: var(--z-canvas);
    width: 100%;
    height: 100%;
    /* The still the scene is composed from. It is what the page shows when
       WebGL is refused, when the module fails, and when there is no JS at
       all — so it lives here rather than in a script branch. */
    background:
      radial-gradient(64% 40% at 50% 2%, oklch(0.66 0.115 214 / 0.5), transparent 70%),
      radial-gradient(30% 20% at 50% 27%, oklch(0.60 0.205 34 / 0.34), transparent 72%),
      radial-gradient(120% 32% at 50% 58%, oklch(0.52 0.075 213 / 0.55), transparent 76%),
      linear-gradient(#01080e 0%, #05283a 38%, #1d7089 57%, #04141d 78%, #010609 100%);
  }
  /* Legibility: the copy sits low, so the scrim is heaviest at the foot and
     just dense enough at the top to keep the menu off the skyline. */
  .void__scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
      linear-gradient(to top,
        oklch(0.105 0.020 250 / 0.62) 0%,
        oklch(0.105 0.020 250 / 0.42) 20%,
        oklch(0.105 0.020 250 / 0.18) 42%,
        transparent 60%),
      linear-gradient(to bottom,
        oklch(0.105 0.020 250 / 0.66) 0%,
        transparent 22%);
  }

  .void__inner {
    position: relative;
    z-index: 2;
    grid-row: 2;
    text-align: center;
    padding-block: 0 clamp(3rem, 11vh, 7.5rem);
    display: grid;
    justify-items: center;
    gap: clamp(0.9rem, 2vh, 1.5rem);
  }

  .void__code {
    margin: 0;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.22em;
    color: var(--amber);
    text-shadow: 0 0 22px oklch(0.805 0.155 68 / 0.45);
  }
  .void__code span { color: var(--mute); letter-spacing: 0.18em; margin-right: 0.55em; }

  .void__title {
    margin: 0;
    /* The copy sits over live city lights, so it carries its own ground
       instead of asking the scrim to blot the scene out. */
    text-shadow: 0 2px 34px oklch(0.105 0.02 250 / 0.9), 0 0 12px oklch(0.105 0.02 250 / 0.7);
    /* A ch cap is measured on Teko's narrow zero, so "GEFUNDEN." runs past a
       13ch box at tablet widths and the line sits off-centre. min-width wins
       over max-width in the cascade, so the box can never be narrower than its
       longest word. */
    max-width: 16ch;
    min-width: min-content;
    /* Its own ramp, not --step-5: "NICHT GEFUNDEN." is a single unbreakable
       word pair, so the floor has to clear a 390px viewport, not a 1480px one. */
    font-size: clamp(2.4rem, 1.35rem + 4.6vw, 6rem);
    line-height: 0.9;
    text-transform: uppercase;
    text-wrap: balance;
  }
  .void__lead {
    margin: 0;
    text-shadow: 0 1px 22px oklch(0.105 0.02 250 / 0.92);
    max-width: 44ch;
    font-size: var(--step-1);
    line-height: 1.5;
    color: var(--mute);
    text-wrap: balance;
  }

  .void__act { margin-top: clamp(0.4rem, 1.6vh, 1.1rem); }
  /* The one action on the page, so it gets a filled-in weight the nav
     version never needs. */
  .void__act .btn { padding: 1rem 2rem; font-size: var(--step-0); }

  .void__alt {
    margin: 0;
    text-shadow: 0 1px 16px oklch(0.105 0.02 250 / 0.9);
    font: 400 var(--step--1)/1.7 var(--mono);
    letter-spacing: 0.04em;
    color: oklch(0.800 0.020 232 / 0.72);
  }
  .void__alt a {
    color: var(--signal);
    text-decoration-color: oklch(0.815 0.130 218 / 0.35);
    text-underline-offset: 4px;
    transition: color 0.3s var(--e-quart);
  }
  .void__alt a:hover { color: var(--ink); }

  /* Keyed on aspect, not width: the scene widens its lens below 0.9, which
     puts far more lit city behind the copy — so the ground under it has to
     come back up on tall tablets too, not just on phones. */
  @media (max-aspect-ratio: 9 / 10) {
    .void__scrim {
      background:
        linear-gradient(to top,
          oklch(0.105 0.020 250 / 0.86) 0%,
          oklch(0.105 0.020 250 / 0.66) 26%,
          oklch(0.105 0.020 250 / 0.22) 48%,
          transparent 64%),
        linear-gradient(to bottom,
          oklch(0.105 0.020 250 / 0.7) 0%,
          transparent 20%);
    }
  }

  @media (max-width: 620px) {
    .void__title { max-width: 13ch; }
    .void__lead { font-size: var(--step-0); max-width: 34ch; }
    .void__inner { gap: 0.8rem; padding-bottom: clamp(2rem, 6vh, 4rem); }
    .void__act .btn { padding: 0.9rem 1.6rem; }
    .void__alt { line-height: 1.9; }
  }
  /* Short landscape phones: the scene needs the height more than the copy does. */
  @media (max-height: 560px) {
    .void__inner { gap: 0.55rem; padding-bottom: 1.6rem; }
    .void__title { font-size: clamp(2rem, 1.2rem + 3vw, 3rem); }
  }
}

@layer motion {
  /* The construct pulses in the scene; the error code answers it. Purely
     decorative, so it is the first thing reduced motion drops. */
  @media (prefers-reduced-motion: no-preference) {
    .void__code { animation: void-blink 3.4s var(--e-quart) infinite; }
  }
  @keyframes void-blink {
    0%, 62%, 100% { opacity: 1; }
    70%           { opacity: 0.38; }
    76%           { opacity: 1; }
    82%           { opacity: 0.55; }
  }
}

/* --------------------------------------------------- the wall of voices */
@layer components {

  /* A full-bleed band, same construction as .clients above it: the colour
     runs edge to edge and only the contents sit on the shell column. */
  .wall {
    background:
      radial-gradient(90% 60% at 50% 0%, oklch(0.815 0.13 218 / 0.06), transparent 64%),
      linear-gradient(180deg, oklch(0.185 0.026 245 / 0.62), oklch(0.185 0.026 245 / 0.20));
    border-block: 1px solid var(--line);
    padding-block: clamp(3.5rem, 7vw, 6rem);
  }
  /* Heading left, the review invitation right — dropping under it as soon as
     the two no longer fit side by side. */
  .wall__head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: clamp(1.2rem, 3vw, 2.4rem);
    margin-bottom: clamp(2rem, 4vw, 3.2rem);
  }
  .wall__head .btn--google { margin-top: 0; }
  .wall__h {
    margin: 0;
    font-size: var(--step-3);
    line-height: 1;
    text-transform: uppercase;
  }

  /* Desktop: the window generates no box at all, so the grid below is laid
     out by .wall__inner exactly as it was before this wrapper existed. It only
     becomes a real element on mobile, where it is the clipping window and the
     grid becomes the running track. */
  .wall__win { display: contents; }

  .wall__cols {
    --wall-gap: clamp(0.85rem, 1.4vw, 1.15rem);
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--wall-gap);
    height: clamp(28rem, 58vh, 40rem);
  }

  /* The window. The mask is what makes cards arrive and leave rather than
     get chopped off at a hard edge. */
  /* Masked, and the declaration order is the whole trick.
     Firefox supports both `mask` and the `-webkit-mask` alias. With the
     -webkit form written *last* it wins the cascade there, and on this
     construction it does not paint — the cards meet the column edge dead
     flat while the scroll keeps running. Prefixed first, standard last, and
     Gecko lands on the standard property. Same order and same shorthand the
     reference implementation uses.

     Shorthand on purpose: `mask` resets mask-mode, -size, -repeat and the
     rest to their initial values in one go, so there is nothing left over
     from a longhand to resolve differently between engines.

     A mask and not two painted overlays: the cards have their own background
     and hairline, so an overlay only darkens them — they stay legible as
     ghost rectangles instead of dissolving. Only a mask makes them actually
     transparent. */
  .wcol {
    position: relative;
    overflow: hidden;
    -webkit-mask: linear-gradient(#0000 3%, #000 27%, #000 84%, #0000 100%);
    mask: linear-gradient(#0000 3%, #000 27%, #000 84%, #0000 100%);
  }

  /* No `will-change: transform` here on purpose. It promotes the track to
     its own compositor layer, which is one more reason for an ancestor mask
     to go missing. A linear translate needs no hint. */
  .wcol__track {
    display: flex;
    flex-direction: column;
    animation: wall-run var(--dur, 50s) linear infinite;
    animation-delay: var(--lag, 0s);
  }
  .wcol--down .wcol__track { animation-direction: reverse; }

  /* Hovering a column stops that column — the whole point of the effect is
     that you can actually read the one you reached for. focus-within keeps
     it working for a keyboard. */
  .wcol:hover .wcol__track,
  .wcol:focus-within .wcol__track { animation-play-state: paused; }

  @keyframes wall-run {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(0, -50%, 0); }
  }

  .wcard {
    margin: 0 0 var(--wall-gap);      /* margin, not gap — see the loop note */
    padding: clamp(1.1rem, 1.9vw, 1.5rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: #1e2933;
    transition: border-color 0.35s var(--e-quart), background-color 0.35s var(--e-quart);
  }
  .wcard:hover {
    border-color: var(--signal);
    background: #24313d;
  }
  .wcard blockquote {
    margin: 0 0 1.1rem;
    font-size: var(--step--1);
    line-height: 1.65;
    color: var(--mute);
    text-wrap: pretty;
  }
  .wcard__by {
    display: flex;
    align-items: center;         /* centred against the three-line block */
    gap: 0.8rem;
    font-style: normal;
  }
  .wcard__id { display: grid; gap: 0.28rem; min-width: 0; }
  .wcard__stars { display: inline-flex; gap: 2px; color: var(--amber); }
  /* A monogram, not a stock portrait: no third-party request, and no face
     standing in for a person who has not said this. */
  .wcard__mark {
    flex: none;
    display: grid;
    place-items: center;
    /* 50px, the size Google serves its review avatars at — so a real
       profile picture can drop straight in without the circle changing. */
    width: 50px; height: 50px;
    border-radius: 50%;
    border: 1px solid oklch(0.815 0.13 218 / 0.35);
    background: oklch(0.815 0.13 218 / 0.10);
    font: 500 0.9rem/1 var(--mono);
    letter-spacing: 0.04em;
    color: var(--signal);
  }
  .wcard__by cite {
    display: block;
    font-style: normal;
    font-family: var(--display);
    font-size: 1.15rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink);
  }
  /* Was `cite + span`, which now matches the stars. Named, so inserting
     anything between name and role cannot silently retarget it. */
  .wcard__role {
    display: block;
    font: 400 0.72rem/1.4 var(--mono);
    letter-spacing: 0.03em;
    /* --mute, not --faint: --faint falls short of the 4.5:1 this line needs at
       11.5px on the card ground. The name above it carries the hierarchy. */
    color: var(--mute);
  }

  @media (max-width: 64rem) {
    .wall__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); height: clamp(26rem, 60vh, 36rem); }
  }
  /* One column below the tablet break. The tracks stack rather than one of
     them being dropped — no quote disappears just because the screen is
     narrow. */
  /* One column below the tablet break — and one *strip*, not three stacked
     windows each running on its own clock, which read as three unrelated
     boxes. The three columns are flattened away (display:contents generates
     no box, so their masks and heights go with them) and the six sets become
     flex items of the grid, reordered into A B C A B C. That is exactly two
     copies of the same twelve cards, which is what lets the same -50% travel
     loop seamlessly on one long track. */
  @media (max-width: 44rem) {
    .wall__win {
      display: block;
      height: clamp(24rem, 68vh, 34rem);
      overflow: hidden;
      /* prefixed first, standard last — see the note on .wcol */
      -webkit-mask: linear-gradient(#0000 3%, #000 27%, #000 84%, #0000 100%);
      mask: linear-gradient(#0000 3%, #000 27%, #000 84%, #0000 100%);
    }
    .wall__cols {
      display: flex;
      flex-direction: column;
      grid-template-columns: none;
      gap: 0;                        /* the cards carry their own margin */
      height: auto;
      animation: wall-run 78s linear infinite;
    }
    .wall__win:hover .wall__cols,
    .wall__win:focus-within .wall__cols { animation-play-state: paused; }

    .wcol, .wcol__track { display: contents; }
    /* Originals first in column order, then the duplicates in the same order.
       Any other interleaving breaks the halves and the loop jumps. */
    .wcol:nth-child(1) .wcol__set:nth-child(1) { order: 1; }
    .wcol:nth-child(2) .wcol__set:nth-child(1) { order: 2; }
    .wcol:nth-child(3) .wcol__set:nth-child(1) { order: 3; }
    .wcol:nth-child(1) .wcol__set:nth-child(2) { order: 4; }
    .wcol:nth-child(2) .wcol__set:nth-child(2) { order: 5; }
    .wcol:nth-child(3) .wcol__set:nth-child(2) { order: 6; }

    .wall__h { font-size: var(--step-2); }
  }
}

@layer motion {
  /* With the animation off the duplicate set is dead weight and the fixed
     column height would clip the rest, so the wall becomes a plain grid. */
  @media (prefers-reduced-motion: reduce) {
    .wcol__track { animation: none; }
    .wcol__set[aria-hidden='true'] { display: none; }
    .wall__cols { height: auto; }
    /* nothing is clipped, so there is nothing to fade into */
    .wcol { height: auto; overflow: visible; -webkit-mask: none; mask: none; }
    /* the mobile strip is the same construction one level up */
    .wall__cols { animation: none; }
    .wall__win { height: auto; overflow: visible; -webkit-mask: none; mask: none; }
  }
}

/* ------------------------------------------------------------- stack grid */
@layer components {

  .stackgrid {
    position: relative;
    /* the icons travel outward past the field, so the section clips them */
    overflow: clip;
    padding-block: clamp(4rem, 8vw, 7.5rem);
  }

  /* One geometry, declared once and used by both layers, so an icon placed in
     column 4 lands in the cell drawn at column 4 and nowhere else. */
  .stackgrid__field {
    --gcols: 12;
    --grows: 6;
    --cell: clamp(40px, 5.2vw, 78px);
    --cgap: clamp(5px, 0.7vw, 12px);
    /* The push is deliberately modest. The reference translates a flat 50px
       and does the real work with scale — a large radial slide reads as things
       flying about, not as a diagram resolving. */
    --spread: clamp(8px, 1.7vw, 22px);
    position: relative;
    /* the copy is the only in-flow child, so this centres it in the field —
       without it the block sat at the top and covered four rows of cells */
    display: grid;
    place-items: center;
    min-height: calc(var(--grows) * var(--cell) + (var(--grows) - 1) * var(--cgap));
  }
  .sgrid,
  .sicons {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(var(--gcols), var(--cell));
    grid-template-rows: repeat(var(--grows), var(--cell));
    gap: var(--cgap);
    justify-content: center;
    align-content: center;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* the schematic: where an icon belongs, whether or not one is there */
  .sgrid i {
    border: 1px dashed oklch(0.97 0.01 230 / 0.12);
    border-radius: var(--radius);
  }

  /* The layer only exists to position; the tiles take their own hits. */
  .sicons { pointer-events: none; }

  .sicon {
    /* the outward push is proportional to the distance from the middle of the
       field, so the whole set converges at one rate rather than sliding */
    --dx: calc(var(--col) - (var(--gcols) + 1) / 2);
    --dy: calc(var(--row) - (var(--grows) + 1) / 2);
    position: relative;
    grid-column: var(--col);
    grid-row: var(--row);
    pointer-events: auto;
    /* Scale is what sells the convergence: the tile starts oversized and
       settles onto its cell at exactly 1. It was inverted at first — shrinking
       on the way in, which reads as retreating rather than arriving. */
    transform:
      translate(calc(var(--dx) * var(--spread) * var(--away, 0)),
                calc(var(--dy) * var(--spread) * var(--away, 0)))
      scale(calc(1 + var(--away, 0) * var(--pop, 0.4)));
    /* Stands in for the reference's overdamped spring: the scroll handler
       writes --away every frame, and this lags it just enough to smooth. */
    transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  }
  /* a little variation, so they do not arrive as one block */
  .sicon:nth-child(3n)   { --pop: 0.55; }
  .sicon:nth-child(3n+1) { --pop: 0.3; }
  .sicon:nth-child(4n)   { --pop: 0.46; }

  .sicon__tile {
    display: grid;
    place-items: center;
    width: 100%;
    aspect-ratio: 1;
    border-radius: var(--radius);
    border: 1px solid var(--line-strong);
    background: oklch(0.185 0.026 245 / 0.92);
    color: var(--brand);
    transition: transform 0.4s var(--e-expo), border-color 0.4s var(--e-quart),
                box-shadow 0.4s var(--e-quart);
  }
  /* `fill` is only a fallback here: the monochrome glyphs carry no fill of
     their own and inherit the brand colour, while Docker and Tux ship their
     own per-path fills and gradients and simply override it. */
  .sicon__tile svg {
    width: 56%;
    height: 56%;
    fill: currentColor;
  }
  .sicon:hover .sicon__tile,
  .sicon:focus-within .sicon__tile {
    transform: translateY(-4px);
    border-color: color-mix(in oklab, var(--brand) 55%, transparent);
    box-shadow: 0 12px 34px oklch(0.05 0.02 250 / 0.55),
                0 0 0 1px color-mix(in oklab, var(--brand) 22%, transparent);
  }

  /* The name is always in the accessibility tree — only its visibility is
     tied to hover, so a screen reader still reads all thirteen. */
  .sicon__tip {
    position: absolute;
    top: calc(100% + 0.5rem);
    left: 50%;
    z-index: 3;
    white-space: nowrap;
    font: 500 0.7rem/1 var(--mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink);
    background: oklch(0.105 0.02 250 / 0.94);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.55rem;
    opacity: 0;
    transform: translate(-50%, -4px);
    transition: opacity 0.28s var(--e-quart), transform 0.28s var(--e-expo);
    pointer-events: none;
  }
  .sicon:hover .sicon__tip,
  .sicon:focus-within .sicon__tip {
    opacity: 1;
    transform: translate(-50%, 0);
  }

  /* Sits over the field, with a wash that dissolves the cells behind it
     rather than punching a hole in the grid. */
  .stackgrid__copy {
    position: relative;
    z-index: 2;
    display: grid;
    justify-items: center;
    gap: clamp(0.7rem, 1.4vw, 1.1rem);
    max-width: min(44ch, 100%);
    margin-inline: auto;
    padding: clamp(1.4rem, 3vw, 2.4rem) clamp(1rem, 2.4vw, 2rem);
    text-align: center;
    background: radial-gradient(72% 62% at 50% 50%,
      var(--void) 0%, oklch(0.135 0.024 248 / 0.94) 46%,
      oklch(0.135 0.024 248 / 0.68) 68%, transparent 100%);
  }
  .stackgrid__copy .stackgrid__eyebrow {
    margin: 0;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;      /* same as .marker__id */
    color: var(--signal);
  }
  .stackgrid__copy h2 {
    margin: 0;
    font-size: var(--step-3);
    line-height: 1;
    text-transform: uppercase;
  }
  .stackgrid__copy p {
    margin: 0;
    font-size: var(--step--1);
    line-height: 1.7;
    color: var(--mute);
  }

  @media (max-width: 64rem) {
    .stackgrid__field { --gcols: 8; --grows: 9; --cell: clamp(46px, 8.4vw, 72px); }
    .sicon { grid-column: var(--col-t); grid-row: var(--row-t);
             --dx: calc(var(--col-t) - (var(--gcols) + 1) / 2);
             --dy: calc(var(--row-t) - (var(--grows) + 1) / 2); }
  }

  /* Below the tablet break the field cannot hold thirteen cells and a heading
     at a readable size, so the diagram is dropped and the icons become an
     ordinary labelled row. Nothing is hidden. */
  @media (max-width: 44rem) {
    /* Copy first, then the icons: with no diagram to read, the heading has to
       lead rather than turn up underneath thirteen tiles. */
    .stackgrid__field {
      display: flex;
      flex-direction: column;
      min-height: 0;
    }
    .stackgrid__copy { order: -1; }
    .stackgrid__copy h2 { font-size: var(--step-2); }
    .sgrid { display: none; }
    .sicons {
      position: static;
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: clamp(0.9rem, 3vw, 1.4rem);
      margin-top: clamp(2rem, 6vw, 3rem);
    }
    .sicon { width: clamp(56px, 17vw, 74px); transform: none; transition: none; }
    .sicon__tip {
      position: static;
      display: block;
      opacity: 1;
      transform: none;
      background: none;
      border: 0;
      padding: 0.45rem 0 0;
      text-align: center;
      white-space: normal;
      color: var(--faint);
      font-size: 0.62rem;
      letter-spacing: 0.04em;
    }
    .stackgrid__copy { background: none; padding-inline: 0; }
  }
}

@layer motion {
  @media (prefers-reduced-motion: reduce) {
    /* the icons stay where the diagram says they belong */
    .sicon { transform: none !important; }
  }
}

/* ------------------------------------------------------- the big next link */
@layer components {
  .nextref {
    border-block: 1px solid var(--line);
    background: linear-gradient(180deg, oklch(0.115 0.02 249), oklch(0.15 0.022 247));
  }
  /* The whole band is the target, so the row is the anchor rather than a
     button sitting inside one. */
  .nextref__link {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: clamp(1.2rem, 4vw, 3rem);
    padding-block: clamp(2.8rem, 6vw, 4.5rem);
    text-decoration: none;
    color: var(--ink);
  }
  .nextref__label {
    display: block;
    margin: 0 0 0.7rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--signal);
  }
  .nextref__title {
    display: block;
    font-family: var(--display);
    font-size: var(--step-4);
    font-weight: 600;
    line-height: 0.86;
    text-transform: uppercase;
    text-wrap: balance;
  }
  .nextref__arrow {
    flex: none;
    display: grid;
    place-items: center;
    width: clamp(56px, 7vw, 82px);
    aspect-ratio: 1;
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    color: var(--ink);
    transition: background 0.5s var(--e-expo), border-color 0.4s var(--e-quart),
                color 0.4s var(--e-quart), transform 0.6s var(--e-expo);
  }
  .nextref__arrow svg { transition: transform 0.6s var(--e-expo); }
  .nextref__link:hover .nextref__arrow,
  .nextref__link:focus-visible .nextref__arrow {
    background: var(--signal);
    border-color: var(--signal);
    color: oklch(0.13 0.02 245);
    transform: scale(1.06);
  }
  .nextref__link:hover .nextref__arrow svg { transform: translateX(5px); }
  .nextref__link:hover .nextref__title { color: var(--signal); }
  .nextref__title { transition: color 0.4s var(--e-quart); }

/* --------------------------------------------------- reference card link */
  /* Only laprinta has a case study so far; the link appears on the cards that
     have one rather than being faked on all five. */
  .ref__more {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    margin-top: clamp(1rem, 2vw, 1.4rem);
    padding-block: 0.3rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--signal);
    text-decoration: none;
    transition: color 0.3s var(--e-quart);
  }
  .ref__more svg { transition: transform 0.45s var(--e-expo); }
  .ref__more:hover { color: var(--ink); }
  .ref__more:hover svg { transform: translateX(4px); }
}

/* --------------------------------------------------------- before / after */
@layer components {
  /* No-script default: two figures, one above the other, both fully readable.
     `.is-ba` is added by the script and only then do they overlap. */
  .ba {
    display: grid;
    gap: clamp(1rem, 2vw, 1.6rem);
    margin-top: clamp(2rem, 4vw, 3rem);
  }
  .ba__pane { margin: 0; position: relative; }
  .ba__pane img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--line);
    border-radius: var(--radius);
  }
  .ba__pane figcaption {
    position: absolute;
    top: 0.9rem;
    inset-inline-start: 0.9rem;
    padding: 0.35rem 0.7rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink);
    background: oklch(0.105 0.02 250 / 0.82);
    border-radius: 999px;
    backdrop-filter: blur(4px);
  }
  .ba:not(.is-ba) .ba__range { display: none; }

  /* --- taken over by script ------------------------------------------- */
  .ba.is-ba {
    position: relative;
    display: block;
    border-radius: var(--radius);
    overflow: hidden;
    isolation: isolate;
    touch-action: pan-y;
  }
  .ba.is-ba .ba__pane { position: absolute; inset: 0; }
  .ba.is-ba .ba__pane--before { position: relative; }
  .ba.is-ba .ba__pane img { border: 0; border-radius: 0; height: 100%; object-fit: cover; }
  /* The "after" is clipped to the handle, so dragging wipes it over the top. */
  .ba.is-ba .ba__pane--after { clip-path: inset(0 0 0 var(--ba, 50%)); }
  .ba.is-ba .ba__pane--after figcaption { inset-inline: auto 0.9rem; }
  .ba.is-ba::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0;
    left: var(--ba, 50%);
    width: 2px;
    margin-left: -1px;
    background: var(--signal);
    box-shadow: 0 0 18px var(--signal-glow);
    pointer-events: none;
  }
  /* The range sits over the whole image: its thumb is the handle, its track
     is invisible, and it keeps every native affordance. */
  .ba__range {
    position: absolute;
    inset: 0;
    z-index: 2;
    width: 100%;
    margin: 0;
    background: none;
    appearance: none;
    -webkit-appearance: none;
    cursor: ew-resize;
  }
  .ba__range::-webkit-slider-thumb {
    appearance: none;
    -webkit-appearance: none;
    width: 46px; height: 46px;
    border-radius: 50%;
    border: 2px solid var(--signal);
    background: oklch(0.105 0.02 250 / 0.72);
    box-shadow: 0 0 22px var(--signal-glow);
    cursor: ew-resize;
  }
  .ba__range::-moz-range-thumb {
    width: 46px; height: 46px;
    border-radius: 50%;
    border: 2px solid var(--signal);
    background: oklch(0.105 0.02 250 / 0.72);
    box-shadow: 0 0 22px var(--signal-glow);
    cursor: ew-resize;
  }
  .ba__range:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

  /* the figures row without a logo should not leave a hole where it was */
  .cbrand__row--nologo { grid-template-columns: minmax(0, 1fr); }
  .shots__note {
    margin: 0 0 clamp(1.4rem, 2.6vw, 2rem);
    max-width: 62ch;
    font-size: var(--step--1);
    line-height: 1.7;
    color: var(--faint);
  }
}

/* ------------------------------------------------------- orbit of voices */
@layer components {

  .orbit {
    display: grid;
    gap: clamp(2.5rem, 6vw, 5rem);
    align-items: center;
    margin-top: clamp(1rem, 3vw, 2rem);
    /* A rotated element contributes its *circumscribed* box to scrollable
       overflow, so the spinning ring — a square — widened the page by up to
       √2 of its own size even though every monogram stays inside the circle.
       Clipped on the x axis only; the stage is centred in a wider column, so
       nothing visible is cut. overflow-y stays visible for the tile's shadow. */
    overflow-x: clip;
    overflow-y: visible;
  }
  @media (min-width: 62rem) {
    .orbit { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr); }
  }

  /* The overall score, shown beside the section label on both pages. */
  .rating {
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    margin: 0;
    flex-wrap: wrap;
  }
  .rating__num {
    font-family: var(--display);
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.01em;
    color: var(--ink);
  }
  .rating__stars { display: inline-flex; gap: 2px; color: var(--amber); transform: translateY(1px); }
  .rating__src {
    font: 400 0.7rem/1 var(--mono);
    letter-spacing: 0.04em;
    color: var(--faint);
  }

  /* The label and the score sit on one line with a rule between them, the way
     the marker does elsewhere. */
  .orbit__top {
    display: grid;
    grid-template-columns: auto minmax(1.5rem, 1fr) auto;
    align-items: center;
    gap: clamp(0.8rem, 2vw, 1.6rem);
    margin-bottom: clamp(1rem, 2vw, 1.4rem);
  }
  .orbit__top::after {
    content: '';
    grid-column: 2;
    grid-row: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--line-strong), transparent);
  }
  .orbit__top .rating { grid-column: 3; }

  .orbit__eyebrow {
    margin: 0;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--signal);
  }
  .orbit__intro h2 { margin: 0; }
  .orbit__lead {
    margin: clamp(1.2rem, 2.4vw, 1.8rem) 0 0;
    max-width: 34ch;
    font-size: var(--step-0);
    line-height: 1.7;
    color: var(--mute);
  }
  /* The one place on the site with a foreign brand mark in a control, so the
     button keeps the house shape and lets the G carry its own four colours. */
  .btn--google {
    margin-top: clamp(1.6rem, 3vw, 2.2rem);
    --btn-line: var(--line-strong);
    --btn-fill: var(--ink);
  }
  .btn--google .btn__g { flex: none; }

  /* ---------------------------------------------------------- the stage */
  .orbit__stage {
    --stage: min(33rem, 74vw);
    --dot: clamp(2.6rem, 5vw, 3.6rem);
    /* Derived, not guessed: this puts a monogram's outer edge exactly on the
       stage edge, so the ring can never push the page wider than its own box. */
    --r: calc((var(--stage) - var(--dot)) / 2);
    position: relative;
    width: var(--stage);
    height: var(--stage);
    margin-inline: auto;
    isolation: isolate;
  }

  .orbit__rings { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
  .orbit__track { fill: none; stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
  /* Lit segments on the inner tracks: a dash pattern long enough to read as an
     arc, offset so the two rings never line up, each drifting at its own rate. */
  .orbit__arc {
    fill: none;
    stroke: var(--signal);
    stroke-width: 2;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
    transform-origin: 50% 50%;
    opacity: 0.85;
  }
  .orbit__arc--a { stroke-dasharray: 74 340; stroke-dashoffset: 0; }
  .orbit__arc--b { stroke-dasharray: 38 226; stroke-dashoffset: 60; opacity: 0.55; }

  .orbit__ring { position: absolute; inset: 0; }
  /* Each seat is a point on the circle; the button inside carries the matching
     counter-rotation, so the initials never stand on their heads. */
  .orbit__seat {
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--dot);
    height: var(--dot);
    margin: calc(var(--dot) / -2);
    transform: rotate(calc(var(--i) * (360deg / var(--n)))) translateY(calc(var(--r) * -1));
  }
  .orbit__dot {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--line-strong);
    background: oklch(0.155 0.024 246);
    font: 500 0.72rem/1 var(--mono);
    letter-spacing: 0.04em;
    color: var(--mute);
    cursor: pointer;
    transform: rotate(calc(var(--i) * (-360deg / var(--n))));
    transition: color 0.4s var(--e-quart), border-color 0.4s var(--e-quart),
                background-color 0.4s var(--e-quart), box-shadow 0.4s var(--e-quart),
                scale 0.5s var(--e-expo);
  }
  /* Scoped away from the active dot rather than relying on source order, so
     hovering the selected one cannot shrink it from 1.22 to 1.12. */
  .orbit__dot:not([aria-pressed='true']):hover,
  .orbit__dot:not([aria-pressed='true']):focus-visible {
    color: var(--ink);
    border-color: var(--signal);
    scale: 1.12;
  }
  .orbit__dot:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }
  .orbit__dot[aria-pressed='true'] {
    color: oklch(0.15 0.03 250);
    background: var(--signal);
    border-color: var(--signal);
    scale: 1.22;
    box-shadow: 0 0 0 6px oklch(0.815 0.13 218 / 0.14), 0 0 26px var(--signal-glow);
  }

  /* ---------------------------------------------------------- the core */
  /* Centred on the tracks in both layouts: the stage is square on desktop and
     the ring keeps the full stage width at the top of the stacked layout, so
     half the stage height is the ring's centre either way. Declared before the
     tile in source order, which is what puts the tile over it on desktop. */
  .orbit__core {
    position: absolute;
    top: calc(var(--stage) / 2);
    left: 50%;
    transform: translate(-50%, -50%);
    display: grid;
    justify-items: center;
    gap: 0.35rem;
    margin: 0;
    text-align: center;
    pointer-events: none;
  }
  .orbit__core-num {
    font-family: var(--display);
    font-size: clamp(2rem, 7vw, 2.6rem);
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.01em;
    color: var(--ink);
  }
  .orbit__core-stars { display: inline-flex; gap: 3px; color: var(--amber); }

  /* --------------------------------------------------------- the tile */
  .orbit__card {
    position: absolute;
    top: 50%; left: 50%;
    /* Sized off the dot orbit: the tile's corner has to stay inside the
       innermost edge of a monogram (241px orbit less a 29px radius = 212 at
       this stage size), or the two collide on the diagonal. */
    width: min(56%, 18rem);
    transform: translate(-50%, -50%);
  }
  /* One height for every review, whatever its length: the tile is a fixed box,
     the footer sits on the bottom row and the text takes what is left and
     scrolls. Letting the box size to its content made it jump on every swap. */
  .orbit__review {
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    height: clamp(15rem, 26vw, 16.5rem);
    margin: 0;
    padding: clamp(1.1rem, 2.2vw, 1.5rem);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: linear-gradient(180deg,
      oklch(0.192 0.026 245 / 0.95) 0%,
      oklch(0.150 0.024 246 / 0.95) 52%,
      oklch(0.118 0.021 248 / 0.96) 100%);
    backdrop-filter: blur(8px);
    box-shadow: 0 24px 60px oklch(0.05 0.02 250 / 0.55);
  }
  .orbit__review[hidden] { display: none; }
  .orbit__text {
    margin: 0 0 1rem;
    min-height: 0;                 /* a grid row must be told it may shrink */
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
    font-size: var(--step--1);
    line-height: 1.65;
    color: var(--ink);
    text-wrap: pretty;
  }
  /* Only when there is actually more below — an edge that fades on a review
     that ends inside the box would be a lie. */
  .orbit__review.is-over .orbit__text {
    mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
  }
  /* Characters that have not been typed yet are already in place and simply
     not painted, which is what keeps the tile from changing height. */
  .orbit__text .c { opacity: 0; }
  .orbit__text .c.is-in { opacity: 1; }

  /* Name and stars share the first line, the role gets the full width below —
     "Leitung Digital · Aurea Klinikgruppe" does not fit beside a five-star row
     and was being cut. It may wrap; the tile has a fixed height and the text
     above simply yields the space. */
  .orbit__review figcaption {
    align-self: end;
    display: grid;
    /* One column, stacked: name, rating, role. The monogram is gone from the
       tile — it is already circling in the ring, and dropping it gives the
       name and the company line the full width. */
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0.3rem;
    font-style: normal;
    padding-top: 0.9rem;
    border-top: 1px solid var(--line);
  }
  .orbit__stars { justify-self: start; }
  .orbit__role {
    font: 400 0.68rem/1.35 var(--mono);
    letter-spacing: 0.02em;
    color: var(--faint);
  }
  .orbit__review cite {
    font-style: normal;
    font-family: var(--display);
    font-size: 1.1rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .orbit__stars { display: inline-flex; gap: 2px; flex: none; color: var(--amber); }

  /* The ring has to be wide enough that the tile's corner clears a monogram;
     at 40vw it did not at the low end of this range. */
  @media (min-width: 75rem) {
    .orbit__stage { --stage: clamp(26rem, 44vw, 34rem); }
  }

  /* The tile only sits inside the ring once the ring is big enough to hold a
     readable review — which is the two-column layout and nothing narrower.
     Below that the stage stacks: ring above, review below. Forcing it into
     the centre at 900px put the tile's corner 16px past the monograms. */
  @media (max-width: 74.99rem) {
    .orbit__stage {
      --stage: min(21rem, 74vw);
      height: auto;
      display: grid;
      gap: clamp(1.6rem, 5vw, 2.4rem);
      justify-items: center;
    }
    .orbit__rings { position: absolute; top: 0; width: var(--stage); height: var(--stage); }
    .orbit__ring { position: relative; inset: auto; width: var(--stage); height: var(--stage); }
    .orbit__card {
      position: relative;
      top: auto; left: auto;
      width: min(100%, 30rem);
      transform: none;
    }
  }
}

@layer motion {
  /* The reel runs only when motion is welcome. With it off the columns simply
     stand still — the plane is a still life of the work, which is a perfectly
     good hero, and the duplicate sets are harmless because each column clips. */
  @media (prefers-reduced-motion: no-preference) {
    .reel__run { animation: reel-run var(--dur, 48s) linear infinite; }
    .reel__col--up .reel__run { animation-direction: reverse; }
  }
  @keyframes reel-run {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(0, -50%, 0); }
  }
}

@layer motion {
  /* One animation, applied twice: forwards on the ring, reversed on every
     monogram inside it. Same duration and no delay, so they stay in lockstep
     and the initials never tilt. The arcs drift on their own clocks. */
  @media (prefers-reduced-motion: no-preference) {
    .orbit__ring { animation: orbit-spin 96s linear infinite; }
    .orbit__dot { animation: orbit-spin 96s linear infinite reverse; }
    .orbit__arc--a { animation: orbit-spin 34s linear infinite; }
    .orbit__arc--b { animation: orbit-spin 52s linear infinite reverse; }
  }
  @keyframes orbit-spin {
    from { rotate: 0deg; }
    to   { rotate: 360deg; }
  }
}

/* ------------------------------------------------ the "über uns" hero */
@layer components {

  /* A short scroll stop: the section is taller than the viewport, the canvas
     pins inside it, and the copy scrolls over a scene that turns slightly.
     No hijacking — the page keeps its own scrollbar and its own speed. */
  .phero--mind {
    min-height: 168svh;
    padding-bottom: clamp(6rem, 12vh, 10rem);
  }
  .phero__stage {
    position: absolute;
    inset: 0;
    z-index: -2;
    pointer-events: none;
  }
  .phero__canvas {
    position: sticky;
    top: 0;
    display: block;
    width: 100%;
    height: 100svh;
    /* The still the scene composes to. It is what shows when WebGL is refused,
       when the module fails, and when there is no JS at all. */
    background:
      radial-gradient(46% 40% at 72% 32%, oklch(0.52 0.16 292 / 0.42), transparent 68%),
      radial-gradient(30% 22% at 70% 72%, oklch(0.60 0.17 292 / 0.28), transparent 72%),
      linear-gradient(#04040c 0%, #0a0916 58%, #08080f 100%);
  }
  /* The copy runs over the scene, so the scene gets a ground under the copy. */
  .phero__veil {
    position: absolute;
    inset: 0;
    background:
      linear-gradient(100deg,
        oklch(0.105 0.02 250 / 0.90) 0%,
        oklch(0.105 0.02 250 / 0.74) 30%,
        oklch(0.105 0.02 250 / 0.30) 58%,
        transparent 80%),
      linear-gradient(to bottom,
        oklch(0.105 0.02 250 / 0.55) 0%,
        transparent 22%,
        transparent 68%,
        oklch(0.135 0.024 248) 100%);
  }
  .phero--mind .phero__glow { opacity: 0.35; }

  @media (max-width: 61.99rem) {
    /* Less headroom on a phone, and the scene needs a heavier ground because
       the copy runs the full width rather than sitting in a column. */
    .phero--mind { min-height: 132svh; }
    .phero__veil {
      background:
        linear-gradient(to bottom,
          oklch(0.105 0.02 250 / 0.72) 0%,
          oklch(0.105 0.02 250 / 0.5) 30%,
          oklch(0.105 0.02 250 / 0.62) 62%,
          oklch(0.135 0.024 248) 100%);
    }
  }
}

/* ----------------------------------------------- the "Geschichte" hero */
@layer components {

  /* Same construction as the "über uns" hero — a short scroll stop with the
     canvas pinned inside it — but a shorter one. The timeline is what the page
     is for, and it should not be three swipes away. */
  .phero--desk {
    min-height: 146svh;
    padding-bottom: clamp(5rem, 10vh, 8rem);
  }
  /* The still the scene composes to: the cold room, the desk's own pool of
     light. It is what shows when WebGL is refused, when the module fails, and
     when there is no JS at all. */
  .phero--desk .phero__canvas {
    background:
      radial-gradient(42% 34% at 64% 54%, oklch(0.42 0.09 232 / 0.40), transparent 70%),
      radial-gradient(26% 20% at 62% 40%, oklch(0.72 0.13 218 / 0.20), transparent 74%),
      linear-gradient(#04080f 0%, #071420 62%, #050c14 100%);
  }
  .phero--desk .phero__glow { opacity: 0.3; }

  @media (max-width: 61.99rem) {
    .phero--desk { min-height: 124svh; }
    /* The shared phone veil is tuned for a scene that fills the frame. This one
       is a single object low in it, so the ground lifts where the desk stands
       and stays heavy where the copy does. */
    .phero--desk .phero__veil {
      background:
        linear-gradient(to bottom,
          oklch(0.105 0.02 250 / 0.80) 0%,
          oklch(0.105 0.02 250 / 0.62) 28%,
          oklch(0.105 0.02 250 / 0.46) 62%,
          oklch(0.105 0.02 250 / 0.54) 84%,
          oklch(0.135 0.024 248) 100%);
    }
  }
}

/* -------------------------------------------------- the "Karriere" hero */
@layer components {

  /* A shorter stop than the "Geschichte" hero: the open roles are what the
     page is for, and the scene has one beat, not four. */
  .phero--night {
    min-height: 132svh;
    padding-bottom: clamp(5rem, 10vh, 8rem);
  }
  /* The still the scene composes to: a warm street under a hazy night sky.
     It is what shows when WebGL is refused, when the module fails, and when
     there is no JS at all. */
  .phero--night .phero__canvas {
    background:
      radial-gradient(30% 22% at 58% 72%, oklch(0.72 0.12 68 / 0.28), transparent 72%),
      radial-gradient(48% 26% at 66% 88%, oklch(0.55 0.08 232 / 0.30), transparent 76%),
      linear-gradient(#04080f 0%, #061019 64%, #0b1a25 100%);
  }
  .phero--night .phero__glow { opacity: 0.26; }

  @media (max-width: 61.99rem) {
    .phero--night { min-height: 118svh; }
    /* The block sits low in the frame on a phone, so the ground lifts there
       and stays heavy where the copy runs. */
    .phero--night .phero__veil {
      background:
        linear-gradient(to bottom,
          oklch(0.105 0.02 250 / 0.82) 0%,
          oklch(0.105 0.02 250 / 0.64) 30%,
          oklch(0.105 0.02 250 / 0.46) 64%,
          oklch(0.105 0.02 250 / 0.54) 86%,
          oklch(0.135 0.024 248) 100%);
    }
  }
}

/* ------------------------------------------------ the "Zehn Gründe" hero */
@layer components {

  /* The shortest stop of the three page scenes: the ten reasons are the page,
     and the arch has said what it has to say in one loop. */
  .phero--arch {
    min-height: 128svh;
    padding-bottom: clamp(5rem, 10vh, 8rem);
  }
  /* The still the scene composes to: the lit opening under a dark structure.
     It is what shows when WebGL is refused, when the module fails, and when
     there is no JS at all. */
  .phero--arch .phero__canvas {
    background:
      radial-gradient(24% 26% at 62% 70%, oklch(0.66 0.13 68 / 0.26), transparent 72%),
      radial-gradient(40% 30% at 60% 86%, oklch(0.52 0.07 232 / 0.26), transparent 76%),
      linear-gradient(#04080f 0%, #061019 60%, #08131c 100%);
  }
  .phero--arch .phero__glow { opacity: 0.28; }

  @media (max-width: 61.99rem) {
    .phero--arch { min-height: 116svh; }
    .phero--arch .phero__veil {
      background:
        linear-gradient(to bottom,
          oklch(0.105 0.02 250 / 0.82) 0%,
          oklch(0.105 0.02 250 / 0.64) 30%,
          oklch(0.105 0.02 250 / 0.46) 64%,
          oklch(0.105 0.02 250 / 0.54) 86%,
          oklch(0.135 0.024 248) 100%);
    }
  }
}

/* ------------------------------------------------------- case study page */
@layer components {

  /* ------------------------------------------------------------ the hero */
  /* The title image is the ground. It sits behind everything at full bleed;
     a scrim leans in from the west so the copy always has a dark field under
     it, whatever the photograph is doing there. */
  .chero {
    position: relative;
    isolation: isolate;
    display: grid;
    align-items: end;
    min-height: clamp(30rem, 74svh, 46rem);
    padding-block: clamp(9rem, 17vh, 13rem) clamp(3rem, 6vw, 5rem);
    overflow: clip;
    border-bottom: 1px solid var(--line);
  }
  .chero__media {
    position: absolute;
    inset: 0;
    z-index: -2;
  }
  .chero__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 62% 50%;   /* the shop sits right of centre in the frame */
  }
  /* Two layers doing different jobs: the west wedge carries the copy, the
     foot fade hands the section over to the page ground. */
  .chero__scrim {
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
      linear-gradient(96deg,
        oklch(0.105 0.02 250 / 0.96) 0%,
        oklch(0.105 0.02 250 / 0.90) 26%,
        oklch(0.105 0.02 250 / 0.62) 50%,
        oklch(0.105 0.02 250 / 0.28) 72%,
        oklch(0.105 0.02 250 / 0.14) 100%),
      linear-gradient(to top,
        oklch(0.135 0.024 248) 0%,
        oklch(0.135 0.024 248 / 0.55) 12%,
        transparent 34%),
      linear-gradient(to bottom,
        oklch(0.105 0.02 250 / 0.72) 0%,
        transparent 26%);
  }
  .chero__inner { position: relative; }
  .chero__eyebrow {
    margin: clamp(1.6rem, 3vw, 2.4rem) 0 0;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--signal);
  }
  .chero__title {
    margin: 0.8rem 0 0;
    /* No max-width: the `<br>` in the markup sets the breaks. A ch-based cap
       is measured on Teko's very narrow zero, so 15ch is far less than 15
       capitals — it split VERSORGUNGSWERK across two lines. */
    font-size: var(--step-5);
    line-height: 0.88;
    text-transform: uppercase;
    text-wrap: nowrap;
    hyphens: none;
  }
  /* …but a viewport narrower than the word still has to cope. */
  @media (max-width: 34rem) {
    .chero__title { text-wrap: wrap; overflow-wrap: anywhere; }
  }
  .chero__lead {
    margin: clamp(1rem, 2vw, 1.5rem) 0 0;
    font-family: var(--display);
    font-size: var(--step-2);
    font-weight: 400;
    line-height: 1.05;
    letter-spacing: 0.01em;
    color: var(--signal);
  }
  .chero__text {
    margin: 0.9rem 0 0;
    max-width: 46ch;
    font-size: var(--step-0);
    line-height: 1.6;
    color: var(--mute);
  }
  .chero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem;
    margin-top: clamp(1.8rem, 3.5vw, 2.6rem);
  }

  /* ------------------------------------------------------ client + figures */
  .cbrand {
    background: linear-gradient(180deg, oklch(0.185 0.026 245 / 0.5), oklch(0.185 0.026 245 / 0.16));
    border-bottom: 1px solid var(--line);
    padding-block: clamp(2.2rem, 4.5vw, 3.4rem);
  }
  .cbrand__row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: clamp(1.6rem, 4vw, 3.5rem);
  }
  /* Dark-on-transparent artwork: a light plate rather than an inversion, so
     the brand keeps its own two colours. */
  .cbrand__mark {
    display: grid;
    place-items: center;
    padding: clamp(0.9rem, 1.8vw, 1.3rem) clamp(1.2rem, 2.4vw, 1.8rem);
    background: oklch(0.97 0.004 230);
    border-radius: var(--radius);
  }
  .cbrand__mark img { display: block; width: clamp(140px, 17vw, 210px); height: auto; }
  .cbrand__facts {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(1rem, 2.5vw, 2.4rem);
    margin: 0;
  }

  /* ----------------------------------------------------------- the details */
  .cdetails {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4.5rem);
    margin-top: clamp(2rem, 4vw, 3rem);
  }
  .cmeta { margin: 0; display: grid; }
  .cmeta__row {
    display: grid;
    grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
    gap: 1rem;
    padding-block: clamp(0.8rem, 1.6vw, 1.1rem);
    border-top: 1px solid var(--line);
  }
  .cmeta__row:last-child { border-bottom: 1px solid var(--line); }
  .cmeta dt {
    font: 400 var(--step--1)/1.5 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--faint);
  }
  .cmeta dd {
    margin: 0;
    font-size: var(--step-0);
    line-height: 1.5;
    color: var(--ink);
  }
  .cmeta dd a { color: var(--signal); text-underline-offset: 4px; }
  .cmeta dd a:hover { color: var(--ink); }
  .cmeta__sub {
    display: block;
    margin-top: 0.2rem;
    font-size: var(--step--1);
    color: var(--faint);
  }

  .ctech__label {
    margin: 0 0 clamp(1rem, 2vw, 1.4rem);
    font: 400 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--faint);
  }
  .ctech__list { display: flex; flex-wrap: wrap; gap: 0.6rem; }
  /* --tc is the brand's own hex, carried on the item so the mark, the ring
     and the hover wash all move together. */
  /* A link, not a chip: these point at technology pages. Everything about the
     resting and hover look is unchanged — only the element it hangs on. */
  .ctech__item {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    text-decoration: none;
    padding: 0.55rem 0.9rem 0.55rem 0.55rem;
    font: 400 var(--step--1)/1 var(--mono);
    color: var(--mute);
    border: 1px solid var(--line);
    border-radius: 999px;
    background: oklch(0.135 0.024 248 / 0.6);
    transition: border-color 0.35s var(--e-quart), color 0.35s var(--e-quart),
                background 0.35s var(--e-quart), transform 0.35s var(--e-expo);
  }
  .ctech__item:hover {
    color: var(--ink);
    border-color: color-mix(in oklab, var(--tc) 55%, transparent);
    background: color-mix(in oklab, var(--tc) 12%, oklch(0.135 0.024 248));
    transform: translateY(-2px);
  }
  .ctech__item:focus-visible {
    outline: 2px solid var(--signal);
    outline-offset: 3px;
    color: var(--ink);
  }
  .ctech__mark {
    display: grid;
    place-items: center;
    width: 26px; height: 26px;
    border-radius: 50%;
    background: color-mix(in oklab, var(--tc) 16%, transparent);
    color: var(--tc);
  }
  .ctech__mark svg { width: 15px; height: 15px; fill: currentColor; }

  /* ----------------------------------------------------------- the story */
  .cstory {
    list-style: none;
    display: grid;
    gap: clamp(2.2rem, 5vw, 3.8rem);
    margin: clamp(2rem, 4vw, 3rem) 0 0;
    padding: 0;
    counter-reset: none;
  }
  .cstory__item {
    display: grid;
    grid-template-columns: minmax(0, 5rem) minmax(0, 1fr);
    gap: clamp(1rem, 3vw, 2.5rem);
    padding-top: clamp(1.4rem, 3vw, 2rem);
    border-top: 1px solid var(--line);
  }
  .cstory__num {
    font: 500 var(--step--1)/1 var(--mono);
    letter-spacing: 0.06em;
    color: var(--signal);
  }
  .cstory__body { max-width: 62ch; }
  .cstory__body h2 {
    margin: 0 0 clamp(0.8rem, 1.6vw, 1.1rem);
    font-size: var(--step-2);
    line-height: 1;
    text-transform: uppercase;
  }
  .cstory__body p {
    margin: 0;
    font-size: var(--step-0);
    line-height: 1.7;
    color: var(--mute);
  }
  .cstory__body p + p { margin-top: 1rem; }

  /* ---------------------------------------------------- the browser frame */
  .browser {
    margin: 0;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: oklch(0.145 0.024 247);
    overflow: hidden;
  }
  .browser__bar {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.75rem 1.1rem;
    border-bottom: 1px solid var(--line);
    background: oklch(0.185 0.026 245 / 0.7);
  }
  .browser__dots { display: flex; gap: 6px; flex: none; }
  .browser__dots i {
    width: 10px; height: 10px;
    border-radius: 50%;
    background: var(--line-strong);
  }
  /* The description sits in the title bar where the URL used to be: it is
     what someone actually needs while looking at the shot, and a made-up
     address was decoration pretending to be information. */
  .browser__cap {
    flex: 1;
    min-width: 0;
    font: 400 var(--step--1)/1.5 var(--sans);
    color: var(--mute);
  }
  /* The window. Full-page captures run to 2560px tall, so this shows a
     viewport-shaped slice and the capture scrolls inside it. */
  .browser__view {
    height: clamp(22rem, 58svh, 34rem);
    overflow-y: auto;
    overscroll-behavior: contain;      /* the page must not lurch at the end */
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
  }
  .browser__view:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
  .browser__view img { display: block; width: 100%; height: auto; }

  /* --------------------------------------------------- the scroll affordance */
  /* Both of these are switched on by script, and only once it has measured
     that the capture actually overflows its window — an invitation to scroll
     something that does not scroll is worse than no invitation. */
  .browser__hint {
    position: absolute;
    left: 50%;
    bottom: clamp(0.7rem, 1.6vw, 1.1rem);
    z-index: 3;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.45rem 0.85rem 0.45rem 0.7rem;
    font: 500 0.7rem/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ink);
    background: oklch(0.105 0.02 250 / 0.82);
    border: 1px solid oklch(0.815 0.13 218 / 0.34);
    border-radius: 999px;
    backdrop-filter: blur(6px);
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transform: translate(-50%, 6px);
    transition: opacity 0.45s var(--e-quart), transform 0.45s var(--e-expo), visibility 0.45s;
  }
  .browser__hint svg { color: var(--signal); }
  /* A fade at the foot of the window says "there is more" before anyone reads
     the pill. */
  .browser__fade {
    position: absolute;
    left: 0; right: 0;
    bottom: 0;
    height: clamp(3rem, 7vw, 4.5rem);
    z-index: 2;
    pointer-events: none;
    opacity: 0;
    background: linear-gradient(to top, oklch(0.105 0.02 250 / 0.9), transparent);
    transition: opacity 0.45s var(--e-quart);
  }
  .browser { position: relative; }
  .browser.is-scrollable .browser__hint {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
  }
  .browser.is-scrollable .browser__fade { opacity: 1; }
  /* Once you have scrolled you know; the affordance gets out of the way. */
  .browser.is-scrolled .browser__hint { opacity: 0; visibility: hidden; transform: translate(-50%, 6px); }
  .browser.is-scrolled .browser__fade { opacity: 0; }
  /* Landscape captures get a window that hugs them instead of a fixed height,
     so a short capture leaves no band of empty frame under it.
     Deliberately NOT `align-content: center`: on a scroll container, centring
     overflowing content pushes its top above the scroll origin, where it can
     never be reached. With `height: auto` there is no spare space to centre
     into anyway — the property could only ever do harm here. */
  .browser:not(.browser--tall) .browser__view {
    height: auto;
    max-height: clamp(22rem, 58svh, 34rem);
  }

  /* ------------------------------------------------------- the services */
  /* The global 0.75 is tuned for one- and two-line Teko. Umlauts on a third
     line land in the previous line's descenders, so this one gets air. */
  .csrv__h {
    margin: clamp(1.6rem, 3vw, 2.4rem) 0;
    line-height: 0.86;
  }
  .csrv {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
  }
  .csrv li {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: clamp(1rem, 2.2vw, 1.4rem) clamp(1.1rem, 2.4vw, 1.6rem);
    background: oklch(0.145 0.024 247);
    font-size: var(--step-0);
    color: var(--mute);
    transition: background 0.35s var(--e-quart), color 0.35s var(--e-quart);
  }
  .csrv li:hover { background: oklch(0.175 0.026 246); color: var(--ink); }
  .csrv__tick { flex: none; color: var(--signal); }

  @media (max-width: 61.99rem) {
    .cdetails { grid-template-columns: minmax(0, 1fr); }
    .cbrand__row { grid-template-columns: minmax(0, 1fr); justify-items: start; }
    .cstory__item { grid-template-columns: minmax(0, 1fr); gap: 0.5rem; }
    .csrv { grid-template-columns: minmax(0, 1fr); }
  }
  @media (max-width: 44rem) {
    .chero { min-height: clamp(26rem, 66svh, 34rem); }
    .chero__title { font-size: var(--step-4); }
    .cbrand__facts { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
    .cmeta__row { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
  }
}

/* =========================================================================
   Service page signatures
   One distinctive block per service page, so the four read as siblings rather
   than as one template filled in four times. Each is built from the tokens
   above and carries no imagery of its own.
   ========================================================================= */
@layer components {

  /* ----------------------------------------------- the bus (Plattform) */
  /* Three stages with the connector living in the grid gap. Drawing it there
     rather than as one line behind the cards is what makes it survive the
     stack: the same pseudo-element becomes a vertical link on a phone without
     any second set of coordinates. */
  .pipeband {
    padding-block: clamp(4rem, 8vw, 7rem);
    border-block: 1px solid var(--line);
    background:
      radial-gradient(60% 100% at 50% 0%, oklch(0.815 0.13 218 / 0.06), transparent 70%),
      var(--void-deep);
  }
  .pipe {
    --pipe-gap: clamp(1.6rem, 3.4vw, 3.2rem);
    display: grid;
    gap: var(--pipe-gap);
    list-style: none;
    margin: 0;
    padding: 0;
  }
  @media (min-width: 62rem) { .pipe { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

  .pipe__stage {
    position: relative;
    padding: clamp(1.3rem, 2.4vw, 1.9rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: linear-gradient(180deg, oklch(0.175 0.026 246 / 0.75), oklch(0.145 0.024 248 / 0.75));
  }
  .pipe__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--signal);
    margin-bottom: 1rem;
  }
  .pipe__chips { display: flex; flex-wrap: wrap; gap: 0.45rem; list-style: none; margin: 0 0 1.1rem; padding: 0; }
  .pipe__chips li {
    padding: 0.4rem 0.7rem 0.32rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: oklch(0.135 0.024 248 / 0.7);
    font: 400 var(--step--1)/1 var(--mono);
    color: var(--mute);
  }
  .pipe__text { font-size: var(--step-0); color: var(--faint); margin: 0; }

  /* The connector. It sits in the gap, so its length is the gap and nothing
     has to be measured. */
  .pipe__stage:not(:last-child)::after {
    content: '';
    position: absolute;
    background: linear-gradient(90deg, var(--signal-dim), oklch(0.815 0.13 218 / 0.15));
    pointer-events: none;
  }
  @media (min-width: 62rem) {
    .pipe__stage:not(:last-child)::after {
      top: 2.1rem;
      left: 100%;
      width: var(--pipe-gap);
      height: 1px;
    }
  }
  @media (max-width: 61.99rem) {
    .pipe__stage:not(:last-child)::after {
      left: 2.1rem;
      top: 100%;
      height: var(--pipe-gap);
      width: 1px;
      background: linear-gradient(180deg, var(--signal-dim), oklch(0.815 0.13 218 / 0.15));
    }
  }
  .pipe__note {
    margin-top: clamp(1.6rem, 3vw, 2.4rem);
    max-width: 62ch;
    font-size: var(--step-0);
    color: var(--faint);
  }

  /* --------------------------------------- the design system (Marken) */
  .dsysband { padding-block: clamp(4rem, 8vw, 7rem); }
  .dsys__intro { max-width: 68ch; font-size: var(--step-0); color: var(--mute); margin-bottom: clamp(2rem, 4vw, 3rem); }

  /* Two skins, one set of tokens. Everything inside reads these and nothing
     else, which is the argument the block is making. */
  .dsys {
    --sk-bg:      oklch(0.16 0.025 247);
    --sk-panel:   oklch(0.20 0.026 245);
    --sk-line:    oklch(0.97 0.01 230 / 0.12);
    --sk-ink:     oklch(0.97 0.008 230);
    --sk-muted:   oklch(0.72 0.02 232);
    --sk-accent:  oklch(0.815 0.13 218);
    --sk-onaccent: oklch(0.16 0.03 240);
    --sk-radius:  14px;
    --sk-display: var(--display);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--sk-bg);
    transition: background 0.5s var(--e-quart);
  }
  .dsys[data-skin='b'] {
    --sk-bg:      oklch(0.965 0.006 95);
    --sk-panel:   oklch(1 0 0);
    --sk-line:    oklch(0.2 0.02 95 / 0.14);
    --sk-ink:     oklch(0.24 0.02 95);
    --sk-muted:   oklch(0.48 0.02 95);
    --sk-accent:  oklch(0.62 0.17 38);
    --sk-onaccent: oklch(0.99 0 0);
    --sk-radius:  4px;
    --sk-display: var(--sans);
  }
  .dsys__bar {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    padding: clamp(0.9rem, 1.8vw, 1.2rem) clamp(1.1rem, 2.2vw, 1.6rem);
    border-bottom: 1px solid var(--sk-line);
    transition: border-color 0.5s var(--e-quart);
  }
  .dsys__eyebrow {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--sk-muted);
    margin: 0;
    transition: color 0.5s var(--e-quart);
  }
  .dsys__toggle { display: flex; gap: 0.35rem; }
  .dsys__toggle button {
    padding: 0.5rem 0.9rem 0.42rem;
    border: 1px solid var(--sk-line);
    border-radius: var(--sk-radius);
    background: transparent;
    font: 600 var(--step--1)/1 var(--display);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--sk-muted);
    cursor: pointer;
    transition: background 0.35s var(--e-quart), color 0.35s var(--e-quart),
                border-color 0.35s var(--e-quart), border-radius 0.5s var(--e-quart);
  }
  .dsys__toggle button[aria-pressed='true'] {
    background: var(--sk-accent);
    border-color: var(--sk-accent);
    color: var(--sk-onaccent);
  }
  .dsys__grid { display: grid; gap: 1px; background: var(--sk-line); transition: background 0.5s var(--e-quart); }
  @media (min-width: 62rem) { .dsys__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  .dsys__panel {
    padding: clamp(1.2rem, 2.4vw, 1.8rem);
    background: var(--sk-panel);
    transition: background 0.5s var(--e-quart);
  }
  .dsys__ptitle {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--sk-muted);
    margin: 0 0 1.1rem;
    transition: color 0.5s var(--e-quart);
  }
  .dsys__ptitle--sep { margin-top: 1.8rem; }
  .dsys__type, .dsys__colors, .dsys__space { list-style: none; margin: 0; padding: 0; }
  .dsys__type li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 0.5rem;
    border-bottom: 1px solid var(--sk-line);
    transition: border-color 0.5s var(--e-quart);
  }
  .dsys__type li:last-child { border-bottom: 0; }
  .dsys__step {
    font-family: var(--sk-display);
    font-weight: 600;
    line-height: 1.1;
    color: var(--sk-ink);
    transition: color 0.5s var(--e-quart), font-family 0.5s step-end;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .dsys__tok {
    flex: none;
    font: 400 0.72rem/1 var(--mono);
    color: var(--sk-muted);
    transition: color 0.5s var(--e-quart);
  }
  .dsys__colors li, .dsys__space li {
    display: flex; align-items: center; gap: 0.75rem; padding-block: 0.42rem;
  }
  .dsys__chip {
    width: 30px; height: 30px; flex: none;
    border-radius: calc(var(--sk-radius) * 0.6);
    border: 1px solid var(--sk-line);
    transition: background 0.5s var(--e-quart), border-radius 0.5s var(--e-quart);
  }
  .dsys__space i {
    display: block; height: 10px; width: var(--w); flex: none;
    background: var(--sk-accent);
    border-radius: calc(var(--sk-radius) * 0.4);
    transition: background 0.5s var(--e-quart);
  }
  .dsys__demo {
    padding: clamp(1rem, 2vw, 1.4rem);
    border: 1px solid var(--sk-line);
    border-radius: var(--sk-radius);
    transition: border-color 0.5s var(--e-quart), border-radius 0.5s var(--e-quart);
  }
  .dsys__demoEyebrow {
    font: 500 0.72rem/1 var(--mono);
    text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--sk-accent); margin: 0 0 0.6rem;
    transition: color 0.5s var(--e-quart);
  }
  .dsys__demoTitle {
    font-family: var(--sk-display); font-weight: 600;
    font-size: 1.5rem; line-height: 1.1; color: var(--sk-ink); margin: 0 0 0.5rem;
    transition: color 0.5s var(--e-quart);
  }
  .dsys__demoText { font-size: var(--step-0); color: var(--sk-muted); margin: 0 0 1.1rem; transition: color 0.5s var(--e-quart); }
  .dsys__demoRow { display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .dsys__btn {
    display: inline-block;
    padding: 0.6rem 1rem 0.5rem;
    border: 1px solid var(--sk-accent);
    border-radius: var(--sk-radius);
    background: var(--sk-accent);
    color: var(--sk-onaccent);
    font: 600 var(--step--1)/1 var(--sk-display);
    text-transform: uppercase; letter-spacing: 0.05em;
    transition: background 0.5s var(--e-quart), color 0.5s var(--e-quart),
                border-color 0.5s var(--e-quart), border-radius 0.5s var(--e-quart);
  }
  .dsys__btn--ghost { background: transparent; color: var(--sk-accent); }
  .dsys__specimen {
    font-size: var(--step-0);
    line-height: 1.6;
    max-width: 34ch;
    color: var(--sk-muted);
    margin: 0;
    transition: color 0.5s var(--e-quart);
  }
  .dsys__field {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    margin-top: 1rem; padding: 0.7rem 0.9rem;
    border: 1px solid var(--sk-line);
    border-radius: var(--sk-radius);
    transition: border-color 0.5s var(--e-quart), border-radius 0.5s var(--e-quart);
  }
  .dsys__flabel { font: 400 var(--step--1)/1 var(--mono); color: var(--sk-muted); transition: color 0.5s var(--e-quart); }
  .dsys__finput {
    display: inline-flex; align-items: baseline; gap: 0.4rem;
    font-family: var(--sk-display); font-weight: 600; font-size: 1.15rem;
    color: var(--sk-ink); transition: color 0.5s var(--e-quart);
  }
  .dsys__finput i { font: 400 0.72rem/1 var(--mono); font-style: normal; color: var(--sk-muted); }
  .dsys__tags { display: flex; flex-wrap: wrap; gap: 0.4rem; list-style: none; margin: 1rem 0 0; padding: 0; }
  .dsys__tags li {
    padding: 0.35rem 0.6rem 0.28rem;
    border: 1px solid var(--sk-line);
    border-radius: calc(var(--sk-radius) * 0.7);
    font: 400 0.72rem/1 var(--mono);
    color: var(--sk-muted);
    transition: border-color 0.5s var(--e-quart), color 0.5s var(--e-quart), border-radius 0.5s var(--e-quart);
  }

  /* ------------------------------------------- the order route (Commerce) */
  .oflowband {
    padding-block: clamp(4rem, 8vw, 7rem);
    border-block: 1px solid var(--line);
    background: var(--void-deep);
  }
  .oflow { position: relative; }
  .oflow__list {
    display: grid;
    gap: clamp(1.4rem, 2.6vw, 2rem);
    list-style: none; margin: 0; padding: 0;
  }
  @media (min-width: 62rem) { .oflow__list { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
  @media (min-width: 40rem) and (max-width: 61.99rem) { .oflow__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

  /* The rail the marker runs along. Behind the steps on wide screens, down
     their left on narrow ones — same element, two orientations. */
  .oflow__rail {
    position: absolute;
    background: var(--line);
    overflow: hidden;
  }
  .oflow__rail i {
    display: block;
    background: linear-gradient(90deg, var(--signal-dim), var(--signal));
    transform-origin: left center;
  }
  @media (min-width: 62rem) {
    .oflow__rail { left: 0; right: 0; top: 6px; height: 2px; }
    .oflow__rail i { height: 100%; width: 100%; transform: scaleX(var(--p, 0)); transition: transform 0.5s var(--e-quart); }
  }
  @media (max-width: 61.99rem) {
    .oflow__rail { left: 6px; top: 0; bottom: 0; width: 2px; }
    .oflow__rail i {
      width: 100%; height: 100%; transform-origin: center top;
      transform: scaleY(var(--p, 0));
      background: linear-gradient(180deg, var(--signal-dim), var(--signal));
      transition: transform 0.5s var(--e-quart);
    }
    .oflow__list { padding-left: 2.2rem; }
  }
  /* Column heights differ with the copy, so the meta line is pushed to the
     bottom rather than following the text — six timings on one line read as a
     measurement, six ragged ones read as an accident. */
  .ostep { position: relative; padding-top: 1.9rem; display: flex; flex-direction: column; }
  @media (max-width: 61.99rem) { .ostep { padding-top: 0; } }
  .ostep__dot {
    position: absolute; top: 0; left: 0;
    width: 14px; height: 14px;
    border-radius: 50%;
    border: 2px solid var(--line-strong);
    background: var(--void-deep);
    transition: border-color 0.4s var(--e-quart), background 0.4s var(--e-quart),
                box-shadow 0.4s var(--e-quart);
  }
  @media (max-width: 61.99rem) { .ostep__dot { left: -2.2rem; top: 0.35rem; } }
  .ostep.is-on .ostep__dot {
    border-color: var(--signal);
    background: var(--signal);
    box-shadow: 0 0 0 5px oklch(0.815 0.13 218 / 0.16);
  }
  .ostep__sys {
    font: 500 0.72rem/1 var(--mono);
    text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--faint); margin: 0 0 0.5rem;
    transition: color 0.4s var(--e-quart);
  }
  .ostep.is-on .ostep__sys { color: var(--signal); }
  .ostep__t {
    font-family: var(--display); font-weight: 600; text-transform: uppercase;
    font-size: var(--step-1); line-height: 1; letter-spacing: 0.02em;
    color: var(--mute); margin: 0 0 0.5rem;
    transition: color 0.4s var(--e-quart);
  }
  .ostep.is-on .ostep__t { color: var(--ink); }
  .ostep__x { font-size: var(--step--1); line-height: 1.55; color: var(--faint); margin: 0 0 0.6rem; flex: 1 0 auto; }
  .ostep__meta {
    font: 400 0.72rem/1 var(--mono); color: var(--faint); margin: 0;
    padding-top: 0.5rem; border-top: 1px solid var(--line);
  }

  /* ---------------------------------------------- the incident (Betrieb) */
  .incband {
    padding-block: clamp(4rem, 8vw, 7rem);
    border-block: 1px solid var(--line);
    background:
      radial-gradient(50% 80% at 82% 10%, oklch(0.805 0.155 68 / 0.05), transparent 70%),
      var(--void-deep);
  }
  .inc { display: grid; gap: clamp(1.6rem, 3.2vw, 2.6rem); align-items: start; }
  @media (min-width: 62rem) { .inc { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }

  .inc__side { display: grid; gap: 1rem; align-content: start; }
  @media (min-width: 62rem) { .inc__side { position: sticky; top: 6.5rem; } }
  .inc__board {
    padding: clamp(1.2rem, 2.4vw, 1.8rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: linear-gradient(180deg, oklch(0.175 0.026 246), oklch(0.145 0.024 248));
  }
  .inc__head {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    margin-bottom: 1.2rem;
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase; letter-spacing: 0.08em;
  }
  .inc__live { display: inline-flex; align-items: center; gap: 0.55rem; color: var(--ink); }
  .inc__live i {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--signal);
    box-shadow: 0 0 0 0 var(--signal-glow);
  }
  .inc__range { color: var(--faint); }
  .inc__bars { display: flex; gap: 2px; align-items: flex-end; height: 54px; }
  .inc__bars i {
    flex: 1 1 auto;
    min-width: 2px;
    height: 100%;
    border-radius: 1px;
    background: oklch(0.640 0.115 224 / 0.55);
  }
  .inc__bars i.is-warn { background: var(--amber-dim); }
  .inc__bars i.is-bad  { background: oklch(0.62 0.2 26); }
  .inc__stats {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem; margin: 1.4rem 0 0; padding-top: 1.2rem;
    border-top: 1px solid var(--line);
  }
  .inc__stats dt {
    font-family: var(--display); font-weight: 600; font-size: var(--step-1);
    line-height: 1; color: var(--ink);
  }
  .inc__stats dd { margin: 0.35rem 0 0; font: 400 var(--step--1)/1.3 var(--mono); color: var(--faint); }

  /* What the board is actually watching — the point being that it is the
     business transaction and not the machine. */
  .inc__watch {
    list-style: none; margin: 0; padding: clamp(1.1rem, 2.2vw, 1.5rem);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.155 0.024 246 / 0.5);
  }
  .inc__watch > p {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--faint); margin: 0 0 0.9rem;
  }
  .inc__watch li {
    display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
    padding-block: 0.5rem;
    border-top: 1px solid var(--line);
    font-size: var(--step--1);
    color: var(--mute);
  }
  .inc__watch li span { flex: none; font: 400 0.72rem/1 var(--mono); color: var(--signal); }

  .ilog { list-style: none; margin: 0; padding: 0; }
  .ilog__item {
    display: grid;
    grid-template-columns: 4.5rem minmax(0, 1fr);
    gap: 1rem;
    padding-block: clamp(0.9rem, 1.8vw, 1.2rem);
    border-bottom: 1px solid var(--line);
  }
  .ilog__item:first-child { padding-top: 0; }
  .ilog__item:last-child { border-bottom: 0; padding-bottom: 0; }
  .ilog__time {
    font: 500 var(--step-0)/1.2 var(--mono);
    color: var(--signal);
    font-variant-numeric: tabular-nums;
  }
  .ilog__t {
    font-family: var(--display); font-weight: 600; text-transform: uppercase;
    font-size: var(--step-1); line-height: 1; letter-spacing: 0.02em;
    color: var(--ink); margin: 0 0 0.4rem;
  }
  .ilog__x { font-size: var(--step-0); color: var(--faint); margin: 0; }

  @media (max-width: 40rem) {
    .ilog__item { grid-template-columns: minmax(0, 1fr); gap: 0.3rem; }
    .inc__stats { grid-template-columns: minmax(0, 1fr); gap: 0.8rem; }
    /* 90 bars at 2px plus 2px of gap need 358px before any padding, which a
       phone does not have. The floor comes down; the two days that matter keep
       a floor of their own so they stay findable. */
    .inc__bars { gap: 1px; height: 44px; }
    .inc__bars i { min-width: 1px; }
    .inc__bars i.is-bad, .inc__bars i.is-warn { min-width: 3px; }
  }
}

@layer motion {
  @media (prefers-reduced-motion: no-preference) {
    /* The rails are driven by scroll position, not by a timer, so there is no
       delay to stagger here — the order comes from where each item sits. The
       short transition only smooths the coarse steps of a wheel; anything
       longer and the fill lags behind the page. */
    .phase::before,
    .step::before,
    .value::before {
      transition: background-size 0.18s linear;
    }
    .phase::after {
      transition: border-color 0.25s linear, box-shadow 0.25s linear;
    }

    /* A packet crossing the connector. The gap is the whole track, so the
       travel is 0 to 100% of a pseudo-element and needs no measurement. */
    .pipe__stage:not(:last-child)::before {
      content: '';
      position: absolute;
      width: 5px; height: 5px;
      border-radius: 50%;
      background: var(--signal);
      box-shadow: 0 0 8px var(--signal);
      pointer-events: none;
      animation: pipe-run 3.4s var(--e-quart) infinite;
    }
    .pipe__stage:nth-child(2):not(:last-child)::before { animation-delay: 1.7s; }
    @media (min-width: 62rem) {
      .pipe__stage:not(:last-child)::before { top: calc(2.1rem - 2px); left: 100%; }
      @keyframes pipe-run {
        0%   { transform: translateX(0); opacity: 0; }
        12%  { opacity: 1; }
        70%  { opacity: 1; }
        80%, 100% { transform: translateX(var(--pipe-gap)); opacity: 0; }
      }
    }
    @media (max-width: 61.99rem) {
      .pipe__stage:not(:last-child)::before { left: calc(2.1rem - 2px); top: 100%; }
      @keyframes pipe-run {
        0%   { transform: translateY(0); opacity: 0; }
        12%  { opacity: 1; }
        70%  { opacity: 1; }
        80%, 100% { transform: translateY(var(--pipe-gap)); opacity: 0; }
      }
    }

    /* The status dot breathes; the day it broke keeps drawing the eye. */
    .inc__live i { animation: inc-live 2.6s ease-out infinite; }
    @keyframes inc-live {
      0%   { box-shadow: 0 0 0 0 var(--signal-glow); }
      70%  { box-shadow: 0 0 0 7px oklch(0.815 0.13 218 / 0); }
      100% { box-shadow: 0 0 0 0 oklch(0.815 0.13 218 / 0); }
    }
    .inc__bars i.is-bad { animation: inc-bad 3s ease-in-out infinite; }
    @keyframes inc-bad {
      0%, 100% { background: oklch(0.62 0.2 26); }
      50%      { background: oklch(0.72 0.22 26); }
    }
  }
}

/* ---------------------------------------------------- catalogue arithmetic */
/* E-Commerce: the two numbers the page already claims, multiplied out. The
   product is the argument — a price at this scale cannot be stored, only
   computed, and that is what decides the shop. */
@layer components {
  .calc {
    display: grid;
    gap: clamp(0.6rem, 1.6vw, 1.4rem);
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: linear-gradient(180deg, oklch(0.185 0.026 245 / 0.55), oklch(0.185 0.026 245 / 0.12));
    padding: clamp(1.6rem, 4vw, 3rem) clamp(1.2rem, 3vw, 2.5rem);
  }
  @media (min-width: 48rem) {
    .calc { grid-template-columns: 1fr auto 1fr auto 1fr; align-items: center; }
  }
  .calc__part { display: grid; gap: 0.35rem; }
  .calc__n {
    font-family: var(--display);
    font-size: var(--step-3);
    font-weight: 600;
    line-height: 0.85;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
  }
  .calc__part--sum .calc__n { color: var(--signal); }
  .calc__l {
    font: 500 var(--step--1)/1.3 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
  }
  .calc__op {
    font-family: var(--display);
    font-size: var(--step-2);
    line-height: 1;
    color: var(--signal-dim);
  }
  .calc__note {
    max-width: 62ch;
    margin-top: clamp(1.4rem, 3vw, 2.2rem);
    margin-bottom: clamp(2rem, 4vw, 3rem);
    color: var(--mute);
  }
}

/* ------------------------------------------------------ system landscape */
/* Plattform-Architektur: three lanes, sources on top, targets below, and the
   layer that did not exist before in the middle. Connectors are drawn with
   pseudo-elements so there is no SVG to keep in sync with the labels. */
@layer components {
  .arch { display: grid; gap: 0; }
  .arch__lane {
    position: relative;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: oklch(0.145 0.024 247);
    padding: clamp(1rem, 2.4vw, 1.5rem);
  }
  .arch__lane + .arch__lane { margin-top: clamp(2rem, 4vw, 3.2rem); }
  /* the vertical run between two lanes */
  .arch__lane + .arch__lane::before {
    content: '';
    position: absolute;
    left: 50%;
    bottom: 100%;
    width: 1px;
    height: clamp(2rem, 4vw, 3.2rem);
    background: linear-gradient(180deg, transparent, var(--signal-dim));
  }
  .arch__lane--core {
    border-color: oklch(0.815 0.13 218 / 0.34);
    background:
      radial-gradient(90% 140% at 50% 0%, oklch(0.815 0.13 218 / 0.10), transparent 70%),
      oklch(0.16 0.026 246);
  }
  .arch__label {
    font: 500 var(--step--1)/1 var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--faint);
    margin-bottom: clamp(0.8rem, 2vw, 1.1rem);
  }
  .arch__lane--core .arch__label { color: var(--signal); }
  .arch__row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 1px;
    background: var(--line);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    overflow: hidden;
  }
  .arch__box {
    background: oklch(0.185 0.026 245);
    padding: 0.85rem 0.9rem;
    font: 500 var(--step--1)/1.3 var(--mono);
    letter-spacing: 0.04em;
    color: var(--mute);
    text-align: center;
  }
  .arch__lane--core .arch__box { background: oklch(0.205 0.03 244); color: var(--ink); }
  .arch__note {
    max-width: 62ch;
    margin-top: clamp(1.4rem, 3vw, 2.2rem);
    color: var(--mute);
  }
}

/* --------------------------------------------------- related-service links */
/* The reset strips link colour everywhere, so a link sitting inside a feat
   card would read as plain heading text. Put the affordance back deliberately. */
@layer components {
  .feat__title a {
    color: inherit;
    text-decoration: none;
    transition: color 0.3s var(--e-quart);
  }
  .feat__title a::after {
    content: '→';
    display: inline-block;
    margin-left: 0.35em;
    color: var(--signal);
    transition: transform 0.4s var(--e-expo);
  }
  .feat__title a:hover,
  .feat__title a:focus-visible { color: var(--signal); }
  .feat__title a:hover::after { transform: translateX(4px); }
}

/* --------------------------------------------------------- topic intro */
/* The SEO block between the hero strip and "wann ihr uns braucht": display
   heading on the left, the core points beside it, the prose underneath.
   Deliberately without a band background — the proof strip above and the
   cxband below both carry one, and a third would stack three tinted slabs. */
@layer components {
  .topicband { padding-block: clamp(3.5rem, 7vw, 6rem); }
  .topic { display: grid; gap: clamp(1.8rem, 3.5vw, 2.8rem); }

  .topic__head { display: grid; gap: clamp(1.4rem, 3vw, 3rem); align-items: start; }
  @media (min-width: 62rem) {
    .topic__head { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); }
  }
  .topic__title {
    font-family: var(--display);
    font-size: var(--step-4);
    font-weight: 600;
    line-height: 0.86;
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--ink);
    /* German compounds get long. The markup carries a soft hyphen at the
       seam; these two make sure nothing can reach into the column beside it
       even for a word that has none. */
    -webkit-hyphens: auto;
    hyphens: auto;
    overflow-wrap: break-word;
  }

  .topic__points { display: grid; gap: 0.6rem 2.5rem; align-content: start; }
  @media (min-width: 40rem) {
    .topic__points { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  .topic__point {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0.7rem;
    align-items: baseline;
    font-size: var(--step-0);
    font-weight: 400;
    color: var(--ink);
  }
  .topic__point::before {
    content: '+';
    font-family: var(--mono);
    font-size: var(--step--1);
    color: var(--signal);
  }

  /* prose on the left, the low-poly form in the space beside it */
  .topic__main { display: grid; gap: clamp(2rem, 4vw, 3.5rem); align-items: center; }
  @media (min-width: 62rem) {
    .topic__main { grid-template-columns: minmax(0, 1fr) minmax(0, 0.52fr); }
  }
  .topic__col { display: grid; gap: clamp(1.5rem, 3vw, 2.2rem); align-content: start; }

  .topic__body { display: grid; gap: 1.05rem; max-width: 64ch; }
  .topic__body p { color: var(--mute); }
  .topic__body strong { font-weight: 500; color: var(--ink); }

  .topic__actions { display: flex; flex-wrap: wrap; gap: 0.8rem; }

  .topic__art { margin: 0; }
  .topic__art img { width: 100%; max-width: 20rem; height: auto; margin-inline: auto; }
  @media (min-width: 62rem) { .topic__art img { max-width: none; } }
}
