/* ==========================================================================
   High Level Detailing — Den Haag
   Mobile-first. Base styles target 375; breakpoints scale up.
   ========================================================================== */

/* --- FONTS ---------------------------------------------------------------- */
/* Manrope for the body and the headings (pass 47, the owner's pick), from
   Google Fonts in index.html. The self-hosted candidates and the test panel
   went with the choice. */


/* --- TOKENS --------------------------------------------------------------- */

:root {
  /* Colour */
  --ground:            #0A0A0A;
  --surface:           #171717;
  --surface-2:         #202020;
  --hairline:          #292929;
  --text:              #FFFFFF;
  --text-soft:         #F5F5F5;
  --text-dim:          #A3A3A3;   /* #979797 (the reference's) until pass 48 */
  --text-resting:      rgba(255, 255, 255, 0.2);
  --logo-grey:         #979797;

  /* --accent is the flat marker AND the button colour (pass 24: flat — the
   gradient and its glisten are gone; blue for one round, then #FF9F1C).
   --accent-ink is the type on top of it: black, as on the cards' fills —
   white on this orange is 2:1. */
  /* Pass 26: a saturated amber that pops on black and sits with the hero
     footage's street-light reflections; #FF9F1C read as faded. */
  /* Pass 29: a deeper amber — copper — used more sparingly (owner: "more
     expensive, controlled"). #FFB300 stays as the "amber" preset. */
  /* Pass 32: the gold of the owner's mock's button, sampled (#CD913F);
     copper is the "koper" preset. */
  /* Pass 43: #CD913F nudged more saturated and a hair yellower (owner: the
     faded old gold, but popping a little more). */
  --accent:       #D88924;   /* burnt amber since pass 59 (owner); champagne #C9A45C in 58 */
  --accent-hover: #E59A3A;
  /* The amber a step back — the hero's label, the stars, the corner's first
     line — so they support the H1 and the button (pass 59). */
  --accent-soft:  #C27D2C;
  /* Darker bronze for hairlines and edges (pass 59). */
  --bronze:       #9A642D;
  --accent-ink:   #0A0A0A;
  /* WhatsApp's own green — the pill's disc and its hover fill only (pass 52). */
  --wa-green:     #25D366;
  /* The eyebrow bullet inverts with the ground: accent on dark, ink on light.
   Indirected through its own token so .section--light can flip it. */
  --bullet:       var(--accent);
  --bullet-glow:  var(--accent-glow);
  --accent-line:  rgba(216, 137, 36, 0.7);    /* thin borders (0.55 until pass 33: muddy on the active row) */
  --accent-glow:  rgba(216, 137, 36, 0.35);   /* eyebrow bullet only */
  /* The resting edge of a black button: the line mixed toward grey (pass 42). */
  --accent-line-muted: color-mix(in oklab, var(--accent-line) 55%, rgba(150, 150, 150, 0.4));
  /* The two ends of the package ramp. Both fully saturated — a cream reads as
   washed out the moment it fills a whole card. */
  --ramp-hi: #FFBE12;
  /* Bright orange, not the old brick #F04E06. The dearest package should be the
   most appetising card on the page. */
  --ramp-lo: #FF5A00;

  /* THE ROOM'S GRADE IS IN THE JPEG, NOT IN CSS. If the photograph is ever
   replaced with an ungraded one, grade the FILE — see HANDOFF, "the workshop —
   the grade", for the two rules worth keeping. */

  /* Spacing — 5px base scale */
  --s5: 5px;  --s10: 10px; --s15: 15px; --s20: 20px; --s25: 25px;
  --s30: 30px; --s40: 40px; --s50: 50px; --s100: 100px;


  /* Layout */
  --container:   1600px;
  --pad-x:       var(--s20);
  /* Rail to content. 1px rule + --s15, the same clearance the manifesto's
     grid gives the statement. */
  --rail-gap:    calc(var(--s15) + 1px);
  --pad-top:     var(--s50);
  --pad-bottom:  var(--s100);
  --header-h:    68px;

  /* Type. Michroma is a wide square-set display face — roughly 1.8× the set
     width of Canela at the same size — so the heading scale, the tracking and
     the manifesto measure are all pulled in to compensate. */
  /* Pass 26: the reference's own face for both, weight 300 on headings
     (design-language.md §2). The ladder below is still written for
     Michroma, which set ~1.8× wide, so a normal grotesk runs at 1.2. */
  --font-body: 'Manrope', system-ui, sans-serif;
  /* Michroma for the headings (pass 56, owner): wide, one weight. Manrope
     read too clean (pass 55). */
  --font-head: 'Michroma', 'Manrope', system-ui, sans-serif;
  /* The card name's face. Michroma for one pass (27 — a spec plate); back
     on the body face in pass 28: on one line with the price, "Exterieur
     detailing" in Michroma clipped at 375, and the caps read as a flyer
     beside a sentence-case price list. */
  --font-card: var(--font-body);
  --head-weight: 400;   /* Michroma's one weight */
  /* Multiplies every heading size; the ladder is Michroma's, so a normal
     grotesk runs at 1.2 — 1.32 since pass 27b ("bigger"). */
  --head-scale: 1.1;    /* Michroma; a normal grotesk runs at 1.32 */

  /* THE REFERENCE'S STEPS, FLUID BETWEEN THEM (pass 43, owner: ours did not
     shrink enough): it sets the H1 27 / 34 / 42px at 390 / 1000 / 1200+,
     the manifesto 28 / 42 / 55. One line from 375 to 1200 passes through
     all three. The manifesto token is divided by 1.32 (Geist's scale). */
  --fs-h1:        clamp(33px, 27.6px + 1.2vw, 42px);   /* 27px at 375 until pass 49 (review: too small on a phone) */
  /* The slogan (pass 58): 28px at 375 ("INDRUK MAAKT." on one line down to
     360), 58px at 1920 — under the concept's 64px, calmer. */
  --fs-hero-name: clamp(26px, 20px + 2.1vw, 58px);
  --fs-section:   22px;
  --fs-manifesto: clamp(20.5px, 11.9px + 2.48vw, 41.7px);
  --fs-subhead:   15px;
  --fs-body:      15px;
  --fs-micro:     14px;
  /* The ledger's price is the thing you scan for, so it is set as display
     type rather than as body copy. Michroma, tabular, roughly twice the
     package name. */
  --fs-price:     27px;
  --fs-caption:   12px;
  --fs-legal:     12px;
  /* The lead review. Between the subhead and a section heading — big enough to
     read as the section's statement, small enough to stay body copy. */
  --fs-quote:     19px;

  --lh-heading:   1.30;
  --lh-manifesto: 1.1;    /* the reference's (1.26 until pass 43) */
  --lh-body:      1.20;
  --track-heading: 0;   /* no custom tracking on any heading face (owner, pass 39; -0.03em on Geist and per-preset values in 30–38) */
  --measure-manifesto: 16.5em;   /* the reference's 900px at 55px (19–21ch until pass 43) */
  /* Air under the statement before the man who made it. The reference leaves
     roughly a third of the paragraph's own height here; 70px did not. */
  --gap-attribution: 84px;
  --gap-certs: var(--s40);

  /* Shape */
  --radius-sm: 2px;   /* 4px until pass 59 (owner: tighter); surfaces: photos, panels, cards */
  --radius-control: 4px;   /* things you press or type in: buttons, the switch, fields, arrows (pass 76, owner) */
  --radius-md: 4px;

  /* Flat-colouring a monochrome PNG/SVG. Deliberately not mask-image: Chrome
   blocks CSS masks on file:// and the demo has to open straight off disk. */
  --ink-dim:    brightness(0) invert(0.592);  /* #979797 */
  --ink-text:   brightness(0) invert(1);      /* #FFFFFF */
  --ink-light:  brightness(0) invert(0.85);   /* #D9D9D9 — the cert marks (pass 49) */
  --ink-ground: brightness(0) invert(0.039);  /* #0A0A0A */
  /* Greys are one invert(); a hue needs the full chain. Solved numerically
     against #FFB300 (scratch: inkfilter.py, a random search over the six
     filters) — verified exact. Each accent preset carries its own,
     otherwise the icons would stay on the default while everything around
     them changed. */
  --ink-accent: brightness(0) invert(0.205) sepia(0.831) saturate(5.545)
                hue-rotate(313.8deg) brightness(1.404) contrast(1.655);
  /* The type and icons ON the accent. A literal, not var(--ink-ground): the
     light sections flip that to white and the button would follow. */
  --ink-accent-ink: brightness(0) invert(0.039);

  /* Motion — the same sampled spring as the CustomEase in content.js, for
     anything CSS can drive on its own. */
  --ease-spring: linear(0.008, 0.156, 0.431, 0.655, 0.802, 0.873,
                        0.931, 0.963, 0.978, 0.988, 0.994, 1);
  --dur-hover: 0.4s;
}

@media (min-width: 768px) {
  :root {
    --pad-x: var(--s30); --pad-top: 120px; --pad-bottom: 120px;
    --fs-section: 32px; --fs-subhead: 17px;
    --fs-caption: 13px; --fs-price: 34px; --fs-quote: 22px;
    --gap-attribution: 90px; --gap-certs: var(--s50);
    --header-h: 80px;
  }
}

@media (min-width: 1200px) {
  :root {
    --pad-x: var(--s40); --pad-top: 150px; --pad-bottom: 150px;
    --fs-section: 40px; --fs-subhead: 18px;
    --fs-price: 40px; --fs-quote: 25px;
    --gap-attribution: 100px;   /* the reference's (132 until pass 43) */
    --header-h: 88px;
  }
}


/* --- BASE ----------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-body);
  overflow-x: hidden;
  overflow-x: clip;   /* pass 69: `hidden` made body a scroll box and broke every position: sticky */
}

/* blockquote carries a 40px inline margin and a 1em block margin from the UA
   sheet. The pull-quote beside the prices zeroes it locally; the reviews are
   the second block to need it, so it belongs in the reset. */
h1, h2, p, dl, dd, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

h1, h2, .manifesto__text {
  font-family: var(--font-head);
  font-weight: var(--head-weight);
}
h3 { font-weight: 500; }   /* the browser's 700 would be faked — no such cut is loaded */

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Every section aligns to one container, so the gap from the viewport edge to
   the H1 on the left is the same gap on the right — site-wide. */
.container,
.header__inner,
.hero__inner,
.manifesto__inner,
.footer {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

/* .container is used ONLY by the five railed sections below the workshop, and
   the rail runs at --pad-x inside it — so their content has to clear the line
   the way the manifesto's does (its grid is 1px + --s15). Without this the
   type sits on top of the hairline. .railseg is absolute against the container
   BOX, not its content, so its own left: var(--pad-x) is unaffected. */
.container { padding-left: calc(var(--pad-x) + var(--rail-gap)); }

/* 1px hairline that draws itself out from the left. §4 */
.rule {
  display: block; height: 1px; width: 100%;
  background: var(--hairline);
  transform-origin: 0% 100%;
}

.word { display: inline-block; }

/* Pre-animation state. Scoped to .motion-ready so the page still reads if the
   GSAP CDN is unreachable. */
.motion-ready .word,
.motion-ready [data-reveal],
.motion-ready .cert {
  opacity: 0.001; filter: blur(2px); transform: translateY(10px);
}
.motion-ready .manifesto__text .word {
  opacity: 1; filter: none; transform: none; color: var(--text-resting);
}
.motion-ready .rule { transform: scaleX(0); }
/* The packages domino in as whole cards, so the card is the pre-state, not its
   parts. Everything inside it rides along. */
.motion-ready .prices__foot {
  opacity: 0.001; filter: blur(2px); transform: translateY(14px);
}
/* The panel and its photo arrive on opacity alone (pass 72): a blurred
   photo snapped sharp when the filter dropped — the owner's "pop". */
.motion-ready #prices-panel { opacity: 0.001; transform: translateY(14px); }
.motion-ready .prices__panel-media { opacity: 0.001; }
/* The gallery. The photographs are targeted as a group rather than through
   [data-reveal] because there are fifteen of them and they arrive on their own
   trigger, below the section's heading. */
.motion-ready .werk__lane,
.motion-ready .werk__ig,
.motion-ready #review-list .review,
.motion-ready #faq-list .faq__item,
.motion-ready .step {
  opacity: 0.001; filter: blur(2px); transform: translateY(10px);
}
/* The row hairlines draw themselves out from the left — §4's third primitive,
   and until this pass the footer was the only place using it. */
.motion-ready .faq__item .faq__rule { transform: scaleX(0); }

/* The cards arrive on opacity and movement alone, with no blur — this block
   has to be readable the moment it lands. PUT `backdrop-filter` BACK ON A CARD
   AND TWO BUGS COME BACK WITH IT; see HANDOFF, "the packages". */
.motion-ready .tier {
  opacity: 0.001; transform: translateY(14px);
}
/* No pre-state needed — .vrule__fill's own clip-path starts it empty. */
.motion-ready .header__logo,
.motion-ready .header__actions,
.motion-ready .hero__corner,
.motion-ready .hero__card,
.motion-ready .hero__kicker,
.motion-ready .hero__lede,
.motion-ready .hero__actions { opacity: 0.001; }
/* Below 768 the corner is `display: contents`, so its children carry the pre-state. */
@media (max-width: 767px) {
  .motion-ready .hero__corner-title,
  .motion-ready .hero__corner-link { opacity: 0.001; }
}


/* --- BUTTONS -------------------------------------------------------------- */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s10);
  /* 10px (pass 36): 11 read heavy, 9 read thin and clipped a label. */
  padding: 10px var(--s15);
  border-radius: var(--radius-control);
  font-size: var(--fs-micro);
  line-height: var(--lh-body);
  white-space: nowrap;
  overflow: hidden;
  isolation: isolate;
}

.btn__icon { width: 17px; height: 17px; flex: none; }

.btn--lg { padding: 13px var(--s25); font-size: var(--fs-subhead); }
.btn--lg .btn__icon { width: 19px; height: 19px; }

/* The colour does all the work: flat, no border, no highlight, no shadow.
   A gradient and a glisten were here and were taken out (pass 24). */
.btn--primary {
  color: var(--accent-ink);
  font-weight: 500;
  background: var(--accent);
  transition: background-color var(--dur-hover) var(--ease-spring);
}
.btn--primary .btn__icon { filter: var(--ink-accent-ink); }
.btn--primary:hover,
.btn--primary:focus-visible { background: var(--accent-hover); }

/* The label rolls up on hover: the reference's clipped two-copy text roll.
   The second copy is ::after reading data-text, which stampLabels() sets from
   the text — a label changed at runtime must be re-stamped. */
.btn__label {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}
.btn__label > span,
.btn__label::after {
  display: block;
  transition: transform 0.5s var(--ease-spring);
}
.btn__label::after {
  content: attr(data-text);
  position: absolute;
  left: 0; top: 100%;
  white-space: nowrap;
}
/* Hover only where there is a real pointer — on a touch screen :hover sticks
   after the tap and the label would stay rolled. */
@media (hover: hover) {
  .btn:hover .btn__label > span,
  .btn:focus-visible .btn__label > span,
  .btn:hover .btn__label::after,
  .btn:focus-visible .btn__label::after { transform: translateY(-100%); }
}

/* BLACK, A MUTED GOLD EDGE, THE GOLD RISING ON HOVER (pass 43, owner): the
   reference's black buttons, with the accent filling up from the bottom
   edge on the spring while the label rolls. The fill is ::before under the
   label (.btn isolates and clips). */
.btn--ink {
  color: var(--text);
  background: var(--ground);
  box-shadow: inset 0 0 0 1px var(--accent-line-muted);
  transition: color var(--dur-hover) var(--ease-spring),
              box-shadow var(--dur-hover) var(--ease-spring);
}
.btn--ink::before {
  content: '';
  position: absolute; inset: 0;
  z-index: -1;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform 0.55s var(--ease-spring);
}
.btn--ink .btn__icon { filter: var(--ink-text); transition: filter var(--dur-hover) var(--ease-spring); }
@media (hover: hover) {
  .btn--ink:hover { color: var(--accent-ink); box-shadow: inset 0 0 0 1px var(--accent); }
  .btn--ink:hover::before { transform: scaleY(1); }
  .btn--ink:hover .btn__icon { filter: var(--ink-accent-ink); }
}
.btn--ink:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* THE ROLL ON A TEXT LINK (pass 44): the buttons' label flip, and gold. */
.roll { transition: color var(--dur-hover) var(--ease-spring); }
.roll:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (hover: hover) {
  .roll:hover { color: var(--accent); }
  .roll:hover .btn__label > span,
  .roll:hover .btn__label::after { transform: translateY(-100%); }
}

/* Dark face, thin accent edge. The header phone and the floating pair.
   Solid (pass 27): the translucent face read as a glitch over the video. */
.btn--gold-line {
  color: var(--text);
  background: var(--ground);
  box-shadow: inset 0 0 0 1px var(--accent-line);
  transition: background-color var(--dur-hover) var(--ease-spring),
              box-shadow var(--dur-hover) var(--ease-spring);
}
.btn--gold-line .btn__icon { filter: var(--ink-text); }
.btn--gold-line:hover {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.btn--gold-line:hover .btn__icon { filter: var(--ink-accent-ink); }

.btn--ghost {
  color: var(--text);
  background: rgba(10, 10, 10, 0.55);
  box-shadow: inset 0 0 0 1px var(--hairline);
  backdrop-filter: blur(8px);
  transition: background-color var(--dur-hover) var(--ease-spring);
}
.btn--ghost .btn__icon { filter: var(--ink-text); }
.btn--ghost:hover { background: rgba(10, 10, 10, 0.85); }

/* Icon-only square button — header phone, floating call/WhatsApp. Same
   --radius-sm as every other button on the page; circles read as a second
   shape language. */
.btn--icon { width: 42px; height: 42px; padding: 0; border-radius: var(--radius-control); }
.btn--icon .btn__icon { width: 17px; height: 17px; }


/* --- PRELOADER ------------------------------------------------------------ */

.preloader {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--ground);
}

.preloader[hidden] { display: none; }

.preloader__inner { display: grid; justify-items: center; gap: var(--s20); }

.preloader__logo {
  height: 74px;
  width: auto;
  mix-blend-mode: difference;
}

.preloader__welcome {
  font-size: var(--fs-micro);
  line-height: var(--lh-body);
  color: var(--text-soft);
  text-align: center;
}

.preloader__town {
  font-size: var(--fs-legal);
  line-height: var(--lh-body);
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: center;
  margin-top: calc(var(--s15) * -1);
}

@media (min-width: 768px) { .preloader__logo { height: 100px; } }


/* --- HEADER --------------------------------------------------------------- */

.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
}

/* THE HEADER IS TRANSPARENT, SO WHATEVER SCROLLS UNDER IT COLLIDES WITH IT.
   The scrim belongs to the header rather than to each section: it is off over
   the hero (which has its own) and on everywhere below, and it inverts with
   the ground. Do not give a new section a top scrim for this. */
.header::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  z-index: -1;
  height: calc(var(--header-h) + var(--s25));
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(180deg,
    rgba(10, 10, 10, 0.60) 0%,
    rgba(10, 10, 10, 0.28) 42%,
    rgba(10, 10, 10, 0) 100%);
  transition: opacity 0.4s var(--ease-spring);
}
:root[data-header="scrim"] .header::before { opacity: 1; }
/* NONE ON THE LIGHT GROUND (pass 24, owner's call — it read as a dark band
   along the top of the white sections). The logo and the hamburger invert
   to ink there instead, which is all the legibility they need. */
:root[data-ground="light"] .header::before { opacity: 0; }

.header__inner {
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s15);
}

/* THE BLEND DOES NOT ACTUALLY BLEND, and never did — .header is its own
   stacking context, so the logo differences against nothing and comes out
   plain white. Right on every dark section, invisible on the white one;
   [data-ground] below is what actually handles it. */
.header__logo { mix-blend-mode: difference; flex: none; }
/* THE LOGO'S TOP ON THE CALL BUTTON'S TOP (pass 41, owner). Both are
   centred in the header, so the logo sat (its height − the button's) / 2
   higher. A margin on the LINK, not a transform (the intro owns its
   transform) and not on the img (the preloader's logo flies to the link's
   box): in a centred flex row a top margin M moves it down M/2. */
/* 0 since pass 49: the phone's buttons are 44px, the logo's own height. */
@media (min-width: 768px) { .header__logo { margin-top: 17px; } }
/* A step up (38 → 44, 46 → 54) in pass 23. */
.header__logo img {
  height: 44px; width: auto;
  transition: filter 0.3s var(--ease-spring), opacity 0.4s var(--ease-spring),
              visibility 0s linear 0s;
}
/* THE LOGO LEAVES ONCE CONTENT REACHES IT (pass 49, review: it sat over
   buttons and headings below the hero). [data-logo] is watchGround's; the
   img, because the intro owns the link's opacity. Back over the open menu. */
:root[data-logo="off"]:not([data-menu="open"]) .header__logo { pointer-events: none; }
:root[data-logo="off"]:not([data-menu="open"]) .header__logo img {
  opacity: 0; visibility: hidden;
  transition: filter 0.3s var(--ease-spring), opacity 0.4s var(--ease-spring),
              visibility 0s linear 0.4s;
}

.header__actions { display: flex; align-items: center; gap: 8px; }

/* THE REFERENCE'S TOP-RIGHT PAIR (pass 43, owner): a gold square with the
   phone icon and a black "Offerte aanvragen" WhatsApp button, at its sizes —
   44px tall with a 15px label on a phone (30 until pass 49: under the
   44px tap target), 36 with 16px from 768 (the
   reference measured: 159 × 36, 8 / 15px padding, radius 4). Then the
   menu. The number left the button (35–42 showed it from 768). */
.header__call { width: 44px; height: 44px; }
.header__call .btn__icon { width: 17px; height: 17px; }
.header__offerte {
  height: 44px;
  padding: 0 var(--s15);
  gap: 8px;
  font-size: 15px;
  line-height: 1.2;
}
.header__offerte .btn__icon { width: 15px; height: 15px; }
/* Not on a phone (pass 51, owner: the top-right was crowded; the pill is
   the WhatsApp route there, and the menu has it too). */
@media (max-width: 767px) { .header__offerte { display: none; } }
/* From 1024 the phone square shows the number (pass 53: a computer cannot
   call it). */
.header__call-num { display: none; }
.header__call { transition: opacity 0.45s var(--ease-spring),
                            transform 0.45s var(--ease-spring),
                            background-color var(--dur-hover) var(--ease-spring); }
.header__call[data-tucked="1"] {
  opacity: 0;
  transform: translateY(-8px) scale(0.9);
  pointer-events: none;
}

/* THE FIXED HEADER OVER A LIGHT SECTION. [data-ground] is written by
   watchGround() in content.js — it says which ground is under the header right
   now. The logo drops its useless blend and inverts to ink; so does the
   hamburger. Without it both are white on white. */
:root[data-ground="light"] .header__logo { mix-blend-mode: normal; }
:root[data-ground="light"] .header__logo img { filter: var(--ink-ground); }
:root[data-ground="light"] .hamburger span {
  background: var(--ground);
  mix-blend-mode: normal;
}
/* Same 42px box as the phone button beside it. It was 34px, which left the
   two controls visibly different heights and their edges close enough to
   mis-tap. The box is invisible; only the three lines show. */
.hamburger {
  width: 44px; height: 44px;
  display: flex; flex-direction: column;
  justify-content: center; align-items: flex-end;
  gap: 6px;
}
/* White, and flipped to ink by [data-ground="light"] above. A difference
   blend here would do nothing — see the note on the logo. */
.hamburger span {
  display: block; height: 1px; width: 24px;
  background: var(--text);
  transition: opacity var(--dur-hover) var(--ease-spring),
              background-color 0.3s var(--ease-spring);
}
.hamburger:hover span { opacity: 0.7; }
/* Open: the outer lines cross, the middle one goes. */
.hamburger span { transition: opacity var(--dur-hover) var(--ease-spring),
                              background-color 0.3s var(--ease-spring),
                              transform 0.46s var(--ease-spring); }
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* Over the open menu the header is always on black, whatever section is
   underneath — these outrank the [data-ground="light"] rules above. */
:root[data-menu="open"] .header::before { opacity: 0; }
:root[data-menu="open"] .header__logo img { filter: none; }
:root[data-menu="open"] .hamburger span { background: var(--text); }

/* --- THE MENU -------------------------------------------------------------
   Four anchors as a numbered list in the heading face, hairlines between, the
   two contact routes underneath — over the page's own black, under the header
   (z 45 < 50) so the logo and the cross stay where they are. Opens on
   [data-menu] with the workhorse (opacity, blur, a rise), one row behind the
   other. */
.menu {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: var(--ground);
  opacity: 0;
  transition: opacity 0.4s var(--ease-spring);
  overflow: auto;
}
.menu[hidden] { display: none; }
:root[data-menu="open"] .menu { opacity: 1; }

.menu__inner {
  min-height: 100%;
  padding: calc(var(--header-h) + var(--s30)) var(--pad-x) var(--s40);
  display: grid;
  align-content: center;
  gap: var(--s40);
}

.menu__list { border-top: 1px solid var(--hairline); }
.menu__item {
  border-bottom: 1px solid var(--hairline);
  opacity: 0.001;
  transform: translateY(10px);
  filter: blur(2px);
  transition: opacity 0.7s var(--ease-spring),
              transform 0.7s var(--ease-spring),
              filter 0.7s var(--ease-spring);
  transition-delay: calc(0.08s + var(--i) * 0.06s);
}
:root[data-menu="open"] .menu__item { opacity: 1; transform: none; filter: blur(0px); }

.menu__link {
  display: flex; align-items: baseline; gap: var(--s20);
  padding: var(--s20) 0;
  font-family: var(--font-head);
  font-size: calc(var(--fs-section) * var(--head-scale));
  line-height: 1.15;
  letter-spacing: var(--track-heading);
  text-transform: uppercase;
  color: var(--text);
  transition: color var(--dur-hover) var(--ease-spring),
              padding-left var(--dur-hover) var(--ease-spring);
}
.menu__index {
  flex: none;
  min-width: 2.2em;
  font-size: var(--fs-caption);
  color: var(--text-dim);
}
@media (hover: hover) { .menu__link:hover { padding-left: var(--s10); } }
.menu__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

.menu__contact {
  display: flex; flex-wrap: wrap; gap: var(--s10);
  opacity: 0.001;
  transform: translateY(10px);
  transition: opacity 0.7s var(--ease-spring) 0.34s,
              transform 0.7s var(--ease-spring) 0.34s;
}
:root[data-menu="open"] .menu__contact { opacity: 1; transform: none; }
.menu__btn { padding-block: 13px; }

@media (min-width: 768px) {
  .menu__inner { max-width: 720px; margin-inline: auto; padding-inline: var(--pad-x); }
  .menu__link { font-size: 34px; padding: var(--s25) 0; }
}

@media (min-width: 768px) {
  .header__logo img { height: 54px; }
  .header__actions { gap: 10px; }
  .header__call { width: 36px; height: 36px; }
  .header__call .btn__icon { width: 17px; height: 17px; }
  .header__offerte { height: 36px; padding: 0 var(--s15); font-size: 16px; }
  .header__offerte .btn__icon { width: 17px; height: 17px; }
  .header__call[data-tucked="1"] { opacity: 1; transform: none; pointer-events: auto; }
}
@media (min-width: 1024px) {
  .header__call { width: auto; gap: 8px; padding: 0 var(--s15); font-size: 16px; font-weight: 500; }
  .header__call-num { display: inline; color: var(--accent-ink); white-space: nowrap; }
}
/* Pass 58 (owner's concept): from 768 "Offerte aanvragen" is the one header button. */
@media (min-width: 768px) { .header__call { display: none; } }
/* Quieter than the hero's button (pass 59): dark, an amber edge, white text;
   no amber fill on hover either — that is the hero button's. Solid since
   pass 64 (owner: see-through read as unfinished). */
.header__offerte {
  background: var(--ground);
  box-shadow: inset 0 0 0 1px rgba(216, 137, 36, 0.45);
}
.header__offerte::before { display: none; }
@media (hover: hover) {
  .header__offerte:hover { color: var(--text); }
  .header__offerte:hover .btn__icon { filter: var(--ink-text); }
}

/* THE EDGE LIGHT (pass 75–77, owner, after Draven): on hover the border
   brightens AND THICKENS where the pointer is, not all round — a radial
   light at (--mx, --my), set by edgeLight() in content.js, masked to a
   ring whose width grows 1 → 2px. No fill. Gradient masks only: file://
   blocks url() masks. */
.hero__card:not(.is-compact) .hero__card-cta::before { display: none; }
/* The light sits exactly at the pointer, NO TRANSITION on its position
   (pass 79): a transition restarted on every pointermove and the light
   only moved in jumps. */
.header__offerte::after,
.hero__card:not(.is-compact) .hero__card-cta::after {
  content: '';
  position: absolute; inset: 0;
  z-index: 1;
  /* The ring is a transparent BORDER cut out with a padding-box mask: the
     padding box keeps the rounded inner corner, so the ring lies exactly on
     the button's own 2px edge. A content-box cut (pass 75–79) had square
     inner corners that stuck out as two dots. */
  border: 1px solid transparent;
  border-radius: inherit;
  pointer-events: none;
  /* Horizontal only (pass 80, owner): as tall as the button, ~140px wide,
     centred on the pointer's x. */
  background: radial-gradient(70px 100% at var(--mx, 50%) 50%, #F7B755 0%, #D88924 45%, rgba(216, 137, 36, 0) 100%) border-box;
  -webkit-mask: linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0) border-box;
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) padding-box exclude, linear-gradient(#000 0 0) border-box;
  opacity: 0;
  transition: opacity 0.35s var(--ease-spring), border-width 0.45s var(--ease-spring);
}
@media (hover: hover) {
  .header__offerte:hover::after,
  .hero__card:not(.is-compact) .hero__card-cta:hover::after { opacity: 1; border-width: 2px; }
  /* 2px ALL ROUND on hover (pass 79, owner); only the brightness follows
     the pointer. The card's button keeps its dark ground. */
  .header__offerte:hover,
  .hero__card:not(.is-compact) .hero__card-cta:hover { color: var(--text); box-shadow: inset 0 0 0 2px rgba(216, 137, 36, 0.5); }
}


/* --- HERO ----------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  isolation: isolate;
}

/* The frame opens; the video itself never scales. Bottom edge is already
   fully revealed at rest — only width and top expand. */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  will-change: clip-path;
}

.hero__media video { width: 100%; height: 100%; object-fit: cover; }

/* LEGIBILITY IS A DARKENING OF THE PICTURE ON THE TEXT'S SIDE, NOT A SHADOW
   ON THE TEXT (owner's call, pass 23 — the footage flares white where the
   copy sits, and a text shadow reads cheap). On a phone the copy spans the
   width, so the band behind it is darkened; from 768 it is the left half,
   fading to the picture on the right. The header's top and the corners' base
   keep their own darkening. */
.hero__scrim {
  position: absolute; inset: 0; z-index: -1;
  background:
    /* A neutral black pool behind the copy (pass 64: pass 59's was brown —
       owner), which also takes the edge off the brightest highlights there. */
    /* Pass 93 (owner): deeper and taller on a phone, from the label through
       the button (0.62 / 40% and a 0.42 band before). */
    radial-gradient(ellipse 120% 46% at 30% 52%, rgba(8, 8, 8, 0.80) 0%, rgba(8, 8, 8, 0) 100%),
    linear-gradient(180deg,
    rgba(10,10,10,0.55) 0%, rgba(10,10,10,0.32) 18%,
    rgba(10,10,10,0.55) 40%, rgba(10,10,10,0.55) 64%,
    rgba(10,10,10,0.78) 100%);
}
@media (min-width: 768px) {
  .hero__scrim {
    background:
      /* Neutral black, darker on the copy's side (pass 64, owner). */
      radial-gradient(ellipse 46% 52% at 22% 50%, rgba(8, 8, 8, 0.5) 0%, rgba(8, 8, 8, 0) 100%),
      linear-gradient(90deg,
        rgba(8,8,8,0.90) 0%, rgba(8,8,8,0.72) 26%,
        rgba(8,8,8,0.28) 48%, rgba(8,8,8,0.00) 64%),
      linear-gradient(180deg,
        rgba(10,10,10,0.30) 0%, rgba(10,10,10,0.00) 14%,
        rgba(10,10,10,0.00) 72%, rgba(10,10,10,0.55) 100%);
  }
}

/* The copy sits a step above centre (the bottom padding is the step), with
   the two corner elements below it. */
.hero__inner {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  padding-top: var(--header-h);
  padding-bottom: 16vh;
  display: grid;
  align-content: center;
}

.eyebrow {
  display: flex; align-items: center; gap: var(--s10);
  font-size: var(--fs-micro);
  font-weight: 300;
  line-height: var(--lh-body);
  letter-spacing: 0.01em;
}

.eyebrow__bullet {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--bullet); flex: none;
  box-shadow: 0 0 10px 1px var(--bullet-glow);
}

.hero__title {
  margin-top: 0;
  /* PASS 35–36 — THE REFERENCE'S SHAPE. Two authored lines of nearly equal
     length at a size that carries the screen (30 → 44px), leading 1.08 —
     the heading token's 1.30 across the full width was what made the old
     H1 read as a caption over the footage. THE BREAK IS CONTENT'S, NOT THE
     BOX'S: a `max-width` in ch wrapped line one in pass 35 and left the em
     dash starting line two. The cap here is a backstop for a narrow phone
     only; the 359px guard drops the size before it is reached. Divided by
     1.32 so the other faces keep their width compensation. */
  max-width: min(100%, 24ch);
  font-size: var(--fs-hero-name);
  line-height: 1;
  letter-spacing: var(--track-heading);
}

.hero__line { display: block; }
/* THE CONCEPT (pass 58). The label — the H1's first line — in tracked gold
   capitals with a hairline after it; the slogan's second line gold; the
   lede in the text face; one filled button; the score. */
.hero__kicker {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;         /* close to the headline (pass 73) */
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.18em;   /* 0.26em from 768; one line at 360 */
  text-transform: uppercase;
  color: var(--accent-soft);
}
.hero__line { text-transform: uppercase; }
/* A step smaller on a phone: "LAK DIE WEER" ran to the right edge (pass 73). */
/* Phone (pass 91, owner): as big as "INDRUK MAAKT." fits the width, ~28px at
   375 (25 before), so the H1 leads the lede and the button. */
@media (max-width: 767px) { .hero__title { font-size: clamp(24px, 8.4vw - 3.5px, 31px); } }
.hero__line--gold { color: var(--accent); }
.hero__lede {
  margin-top: var(--s25);
  max-width: 30ch;            /* narrower on a phone (pass 73) */
  font-size: 16px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.86);
}
/* PASS 89 (owner): "Gratis offerte" leads, an outlined "Bekijk pakketten"
   beside it from 768. */
.hero__actions {
  margin-top: var(--s30);
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--s15);
}
.hero__cta,
.hero__cta2 {
  height: 56px;
  padding: 0 30px;
  gap: 16px;
  /* 14px / 600 / 0.14em (pass 59): 13 / 500 / 0.2em read thin on the amber. */
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.hero__cta2 { background: transparent; }
/* Phone (pass 93, owner): the button full width, 52px (fitted to its label
   in 91–92); no packages link (the corner's "Diensten" is that). The block
   sits a little lower than on a desktop. */
@media (max-width: 767px) {
  .hero__cta { align-self: stretch; height: 52px; padding: 0 24px; }
  .hero__cta2 { display: none; }
  /* Pass 97: the lede joins the H1 (~24px of visible gap; 18px margin, pass 98), and the button
     and the score share one gap (~30px visible). Margins are smaller than
     the visible gaps: the H1's and the lede's line boxes carry space. */
  .hero__lede { margin-top: 18px; color: rgba(255, 255, 255, 0.82); }
  .hero__kicker { margin-bottom: 14px; }
  .hero__actions { margin-top: 24px; }
  .hero__score { margin-top: 28px; }
  .hero__inner { padding-bottom: 9vh; }

  /* PASS 96, THE H1 LEADS (owner): Michroma cannot grow, so everything round
     it steps down. The label a softer gold, the lede 15px at 72%, the button
     48px with a 13px label, the score 13px. */
  .hero__kicker { color: #B07A3E; }
  .hero__lede { font-size: 15px; color: rgba(255, 255, 255, 0.72); }
  .hero__cta { height: 48px; font-size: 13px; letter-spacing: 0.12em; }
  .hero__body .eyebrow.hero__score { font-size: 13px; }
  .hero__score .hero__score-stars .stars__star { width: 11px; height: 11px; }
}
/* The header steps down on a phone too (pass 96): the logo 38px (its top on
   the button's: a 6px bottom margin lifts it 3px), the call square dark with
   an amber edge instead of filled, so the H1's gold is the first gold. */
@media (max-width: 767px) {
  .header__logo { margin-bottom: 6px; }
  .header__logo img { height: 38px; }
  .header__call { background: rgba(10, 10, 10, 0.55); box-shadow: inset 0 0 0 1px var(--accent-line); }
  .header__call .btn__icon { filter: var(--ink-text); }   /* white: --ink-accent renders red on this icon */
}
.hero__cta-arrow { font-size: 16px; letter-spacing: 0; transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) { .hero__cta:hover .hero__cta-arrow { transform: translateX(4px); } }
.hero__score { margin-top: var(--s30); }
@media (min-width: 768px) {
  .hero__kicker { gap: 14px; margin-bottom: 20px; font-size: 13px; letter-spacing: 0.26em; }
  .hero__lede { margin-top: var(--s25); max-width: 34em; font-size: 18px; }
  .hero__lede-line { display: block; }
  .hero__actions { flex-direction: row; align-items: center; }
  .hero__score { margin-top: var(--s25); }
}

/* THE GOOGLE SCORE IS THE EYEBROW (pass 43, owner: "subtle but legible"),
   the reference's "2,600+ Successful Repairs": its size, 14 / 16px, soft
   white, the gold bullet. */
.hero__body .eyebrow { font-size: 14px; font-weight: 400; color: var(--text-soft); }
/* G  ★★★★★  4,9 · 84 reviews (pass 44, owner). The real multicolour G. */
.hero__score { gap: 8px; justify-self: start; transition: color var(--dur-hover) var(--ease-spring); }
@media (hover: hover) { .hero__score:hover { color: var(--text); } }
.hero__score:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.hero__score-g { width: 14px; height: 14px; flex: none; }
.hero__score-stars { display: inline-flex; gap: 2px; }
.hero__score-stars .stars__star { width: 12px; height: 12px; fill: var(--accent-soft); }
@media (min-width: 768px) {
  .hero__score-g { width: 16px; height: 16px; }
  .hero__score-stars .stars__star { width: 13px; height: 13px; }
}
@media (min-width: 768px) { .hero__body .eyebrow { font-size: 16px; } }

/* THE BOTTOM-LEFT CORNER IS THE H2, AS A BLOCK (pass 43, owner: "more
   blocky, like the reference"), with "Bekijk pakketten →" under it — an
   underlined link in GOLD at the H2's size (pass 51, owner; the one gold
   text on the page). ON A PHONE the corner is `display: contents`: the
   H2 joins the H1's block, and the link — "Diensten", white — sits alone
   bottom-left on the pill's bottom line (pass 53, owner). */
.hero__corner {
  position: absolute;
  left: var(--pad-x);
  bottom: var(--s30);
  z-index: 2;
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.3;
  color: var(--text);
}
.hero__corner-title { margin: 0; font: inherit; color: var(--text); }
.hero__corner-line { display: block; }
.hero__corner-link {
  display: inline-flex; align-items: center; gap: var(--s10);
  justify-self: start;
  margin-top: var(--s15);
  padding-bottom: 4px;
  font-size: inherit;   /* the H2's: 16 / 18px */
  line-height: 1.2;
  color: var(--accent);
  box-shadow: inset 0 -1px 0 0 var(--accent-line);
}
.hero__corner-arrow { transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) {
  .hero__corner-link:hover { color: var(--accent-hover); box-shadow: inset 0 -1px 0 0 var(--accent-hover); }
  .hero__corner-link:hover .hero__corner-arrow { transform: translateX(4px); }
}
@media (max-width: 767px) {
  .hero__corner { display: contents; }
  /* Layout A: the H2 reads as the H1 line's second sentence; the score
     (in .hero__body, which steps aside) comes after it. */
  .hero__body { display: contents; }
  .hero__score { order: 1; }
  .hero__corner-title { display: none; }   /* pass 58: the lede says it on a phone */
  /* "Diensten", white, alone in the bottom-left with its underline on the
     pill's bottom edge (pass 52–53, owner); gold from 768. */
  .hero__corner-link {
    position: absolute;
    left: var(--pad-x);
    bottom: var(--s30);
    z-index: 2;
    margin: 0;
    font-size: 16px;
    color: var(--text);
    box-shadow: inset 0 -1px 0 0 rgba(255, 255, 255, 0.5);
  }
}
@media (min-width: 768px) {
  /* Its bottom on the card's (40px up). */
  .hero__corner { bottom: var(--s40); max-width: 300px; font-size: 18px; }
  /* Pass 58 (owner's concept): "Specialist in / lakcorrectie, coating en
     interieur" in tracked capitals on a hairline left of the column. */
  .hero__corner {
    left: max(calc(var(--pad-x) - 26px), 10px);
    max-width: none;
    padding: 2px 0 2px 25px;
    box-shadow: inset 1px 0 0 var(--bronze);
    font-size: 12px;
    font-weight: 500;
    line-height: 1.7;
    letter-spacing: 0.22em;
    text-transform: uppercase;
  }
  .hero__corner-line:first-child { color: var(--accent-soft); }
  .hero__corner-link { display: none; }
}

/* PHONE HERO, PASS 102–104 — overrides the phone rules above. The copy is
   one group, centred in the space between the header (top padding) and the
   bottom row of Diensten + the pill (bottom padding), as on the desktop.
   The H1 in two lines at the desktop's proportions. Rhythm: 16 (label → H1),
   22 (H1 → lede), 28 (lede → button), 20 (button → score). */
@media (max-width: 767px) {
  .hero__inner {
    align-content: center;
    padding-top: var(--header-h);
    padding-bottom: 112px;      /* the bottom row: the pill (48) at 30 up, plus air */
  }
  .hero__kicker { margin-bottom: 16px; color: var(--accent-soft); }
  /* "INDRUK MAAKT." is ~11.5em: 0.084 × the column fills ~96% of it
     (30px at 398), so "LAK DIE WEER" fits one line too. */
  .hero__title {
    max-width: none;
    font-size: min(calc((100vw - 2 * var(--pad-x)) * 0.084), 32px);
    line-height: 1.14;
  }
  .hero__lede { margin-top: 22px; max-width: 30ch; font-size: 15px; line-height: 1.5; color: rgba(255, 255, 255, 0.78); }
  .hero__actions { margin-top: 28px; }
  .hero__cta { height: 52px; gap: 12px; font-size: 13px; letter-spacing: 0.12em; }
  .hero__score { margin-top: 20px; }

  /* "Diensten" a quiet text link: it points on, it does not compete. */
  .hero__corner-link { font-size: 14px; color: rgba(255, 255, 255, 0.72); box-shadow: inset 0 -1px 0 0 rgba(255, 255, 255, 0.28); }

  /* Header: the call is a bare icon, the menu's weight; logo and icons share a centre line. */
  .header__logo { margin-bottom: 0; }
  .header__logo img { height: 40px; }
  .header__call { background: transparent; box-shadow: none; }
  .header__call .btn__icon { width: 20px; height: 20px; }

  /* The pill a step smaller, so the button stays the strongest shape. */
  .hero-wrap .is-compact.hero__card { height: 48px; padding: 0 4px; border-radius: 24px; }
  .hero-wrap .is-compact .hero__card-avatar { width: 38px; height: 38px; }
  .hero-wrap .is-compact .hero__card-name, .hero-wrap .hero__card-fill-name { font-size: 14px; }
  .hero-wrap .is-compact .hero__card-role, .hero-wrap .hero__card-fill-role { font-size: 12px; }
  .hero-wrap .is-compact .hero__card-cta { width: 40px; height: 40px; }
  .hero-wrap .is-compact .hero__card-cta::after { border-radius: 24px; }
  .hero-wrap .is-compact .hero__card-cta .btn__icon,
  .hero-wrap .is-compact .hero__card-cta:hover .btn__icon { width: 30px; height: 30px; }
  .hero-wrap .is-compact .hero__card-fill { padding: 0 4px; }
  .hero-wrap .is-compact .hero__card-fill::before { width: 38px; }
}

.stars__star { width: 13px; height: 13px; fill: var(--accent); }
.rating__score { font-weight: 500; }
.rating__count { color: var(--text-dim); transition: color var(--dur-hover) var(--ease-spring); }
.rating:hover .rating__count { color: var(--text); }



.avatar {
  border-radius: 50%;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--hairline);
  overflow: hidden; flex: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 26px; height: 26px; }
.avatar--md { width: 46px; height: 46px; }
.avatar--lg { width: 76px; height: 76px; border-radius: var(--radius-sm); }


/* --- Hero contact card ---------------------------------------------------
   Portrait tile left spanning both rows, name and role beside it, button under
   the copy. It lives in .hero-wrap, beside .hero rather than inside it, so
   that from 1024 it can be fixed above every later section (.hero isolates
   and clips; a fixed child of it sat under the z-indexed sections). */
.hero-wrap { position: relative; }
.hero__card {
  position: absolute;
  right: var(--pad-x);
  bottom: var(--s25);
  z-index: 3;
  max-width: calc(100% - var(--pad-x) * 2);
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--s15);
  row-gap: var(--s10);
  align-items: start;
  padding: var(--s15);
  /* Rounder and a little translucent since pass 24 (owner's call: on the
     white sections a solid black chip was too hard). It was opaque in pass
     23 because glass flickered against the video — so the blur is mild and
     the black is 86%, not the old 55%. */
  background: rgba(10, 10, 10, 0.86);
  backdrop-filter: blur(14px);
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.11);
}
.hero__card[hidden] { display: none; }

/* The tile STRETCHES so its top and bottom land on the name and the button.
   Do not give it a fixed height. */
.hero__card-avatar {
  grid-row: span 2;
  align-self: stretch;
  width: 62px; height: auto;
  border-radius: var(--radius-sm);
}

.hero__card-name {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: var(--lh-body);
}
.hero__card-role {
  margin-top: 2px;
  font-size: var(--fs-micro);
  line-height: var(--lh-body);
  color: var(--text-dim);
}
/* The card's button is `.btn--ink` (pass 43): black, the gold rising on
   hover. The gold disc on a phone's pill is set below. */
.hero__card-cta { grid-column: 2; }
.hero__card-cta .btn__icon { width: 17px; height: 17px; }

@media (min-width: 768px) {
  .hero__card { bottom: var(--s40); }   /* on the corner block's line */
}

/* FIXED ON EVERY WIDTH (pass 34; from 1024 since pass 23): the page's CTA in
   the bottom-right corner for the whole page. floatiesSequence stands it
   down over the workshop, the reviews' curtain and the offerte with GSAP
   (the intro tweens the same two properties, so no CSS transition here).
   Below 1024 it is a slim chip — the portrait, the name, a square WhatsApp
   button — the floating pair it replaces there was the same action twice. */
/* THE PILL: `.is-compact`, set by JS — always below 1024, and on a desktop
   once the hero is scrolled past (pass 46, owner: the full card over the
   later sections competed with their content). His face (greyscale),
   "Tuncay / Eigenaar", the WhatsApp mark white in a solid green disc at the
   right end (pass 52, the review). 52px tall (pass 48, owner: 42 was hard
   to see). The whole pill is one link (the CTA's ::after spans the card).

   THE GREEN GROWS FROM THE DISC (pass 52): `.hero__card-fill` is a green
   layer clipped by `circle()` from the disc's centre — 0 at rest, the
   whole pill on hover / keyboard focus, back on leave; a short fill on
   press on a phone. It carries a DARK copy of the two lines (white on
   WhatsApp green is ~2:1), so the colour changes exactly at the curved
   edge. Layers: the real text (0) < the fill (1) < the face and the mark
   (2) < the link's hit area (3). CSS only; the card's opacity and
   transform stay cardApply()'s. */
.hero__card.is-compact {
    position: fixed;
    z-index: 60;
    bottom: var(--s30);   /* s20 until pass 42 */
    display: flex;
    align-items: center;
    column-gap: var(--s10);
    height: 52px;
    padding: 0 5px;
    overflow: hidden;
    background-color: #141414;
    backdrop-filter: none;
    border-radius: 26px;
    box-shadow: inset 0 0 0 1px var(--hairline), 0 8px 22px rgba(0, 0, 0, 0.45);
    /* Back to charcoal AT ONCE as the circle starts to shrink. */
    transform-origin: 100% 50%;
    transition: background-color 0s, box-shadow 0s, scale 0.45s var(--ease-spring);
}
  /* align-self: center — the desktop tile's `stretch` left it on the
     pill's top edge (a fixed height does not stretch; pass 44). */
  .is-compact .hero__card-avatar {
    position: relative; z-index: 2;
    width: 42px; height: 42px;
    flex: none;
    align-self: center;
    border-radius: 50%;
    filter: grayscale(1);
  }
  .is-compact .hero__card-body { position: relative; z-index: 0; }
  .is-compact .hero__card-name { font-size: 15px; font-weight: 400; line-height: 1.15; }
  .is-compact .hero__card-role { margin-top: 1px; font-size: 13px; line-height: 1.15; }
  /* position: static so its ::after spans the card. .btn--ink's gold
     ::before is off: static, it filled the whole pill (pass 48). */
  .is-compact .hero__card-cta {
    position: static;
    flex: none;
    width: 42px; height: 42px;
    margin: 0 0 0 auto; padding: 0;
    border-radius: 50%;
    background: var(--wa-green);
    box-shadow: none;
    /* .btn's isolation made the CTA a stacking context: its ::after (z 3)
       then sat under the face (z 2) and a tap on the face missed (pass 52). */
    isolation: auto;
  }
  .is-compact .hero__card-cta::before { display: none; }
  .is-compact .hero__card-cta::after { content: ''; position: absolute; inset: 0; z-index: 3; border-radius: 26px; }
  .is-compact .hero__card-cta .btn__label { display: none; }
  /* The mark fills 70% of its box (Font Awesome's 640 grid): a 34px box is a 24px glyph. White always. */
  .is-compact .hero__card-cta .btn__icon,
  .is-compact .hero__card-cta:hover .btn__icon {
    position: relative; z-index: 2;
    display: block; width: 34px; height: 34px;
    filter: var(--ink-text);
  }

.hero__card-fill { display: none; }
.is-compact .hero__card-fill {
  position: absolute; inset: 0; z-index: 1;
  display: flex; align-items: center;
  column-gap: var(--s10);
  padding: 0 5px;
  background: var(--wa-green);
  color: var(--accent-ink);
  pointer-events: none;
  /* The disc's centre: the 5px padding + half its 42px. */
  clip-path: circle(0 at calc(100% - 26px) 50%);
  /* Out: 0.5s. In: 0.9s (below) — slower than the hover moves, so it
     reads as a fill, not a flash (pass 53, owner: "more premium"). */
  transition: clip-path 0.5s var(--ease-spring);
}
.is-compact .hero__card-fill::before { content: ''; width: 42px; flex: none; }   /* the face's place */
.hero__card-fill-name, .hero__card-fill-role { display: block; line-height: 1.15; white-space: nowrap; }
.hero__card-fill-name { font-size: 15px; }
.hero__card-fill-role { margin-top: 1px; font-size: 13px; }
/* The pill's own ground and edge turn green once the circle has covered
   it (0.45s in): the clip's anti-aliased edge at the rounded ends let the
   charcoal through as a dark fringe (pass 53, owner, on the white ground). */
@media (hover: hover) {
  .hero__card.is-compact:hover .hero__card-fill {
    clip-path: circle(260px at calc(100% - 26px) 50%);
    transition-duration: 0.9s;
  }
  /* THE GROW (pass 85, owner; a 4px lift in 84): `scale`, not `transform`
     — GSAP's cardApply owns the card's transform. !important because GSAP
     also writes an inline `scale: none` on the card. From its right edge,
     so it stays in the corner. */
  .hero__card.is-compact:hover {
    scale: 1.06 !important;
    background-color: var(--wa-green);
    box-shadow: inset 0 0 0 1px var(--wa-green), 0 14px 28px rgba(0, 0, 0, 0.5);
    transition: background-color 0s linear 0.45s, box-shadow 0s linear 0.45s, scale 0.45s var(--ease-spring);
  }
}
.hero__card.is-compact:focus-within .hero__card-fill {
  clip-path: circle(260px at calc(100% - 26px) 50%);
  transition-duration: 0.9s;
}
.hero__card.is-compact:active .hero__card-fill {
  clip-path: circle(260px at calc(100% - 26px) 50%);
  transition-duration: 0.25s;
}
@media (prefers-reduced-motion: reduce) {
  :root:not(.motion-forced) .hero__card-fill { transition: none; }
}
@media (min-width: 1024px) {
  .hero__card {
    position: fixed;
    z-index: 60;
    /* Its right edge on the header's, not the window's (pass 35): the
       header is the 1600px container, so past that width `--pad-x` alone
       left the card 160px further out at 1920. */
    /* `100%`, NOT `100vw`: a fixed element's percentage is the viewport
       without the scrollbar, which is the width the header's container is
       centred in. With 100vw the card sat a scrollbar past the header. */
    right: max(var(--pad-x), calc((100% - var(--container)) / 2 + var(--pad-x)));
  }
  .hero__card:not(.is-compact) {
    /* THE REFERENCE'S CARD (pass 43, owner — measured: 283 × 140, 10px
       padding, radius 3, #0A0A0A, a 90 × 120 portrait, 16 / 14px type, the
       159 × 36 button at the bottom). Opaque, no blur. */
    min-width: 0;               /* 283px until pass 59: the label without "Tuncay" narrows it */
    grid-template-columns: 96px auto;
    grid-template-rows: 1fr auto;
    column-gap: 14px; row-gap: 0;
    padding: 14px;              /* 10 until pass 75 (owner: make it noticed) */
    border-radius: 2px;
    background: var(--ground);
    backdrop-filter: none;
    box-shadow: inset 0 0 0 1px rgba(154, 100, 45, 0.22);   /* bronze, faint (pass 59) */
  }
  .hero__card:not(.is-compact) .hero__card-avatar { width: 96px; height: 128px; border-radius: 2px; }
  .hero__card:not(.is-compact) .hero__card-name   { font-size: 16px; font-weight: 400; }
  .hero__card:not(.is-compact) .hero__card-role   { margin-top: 4px; font-size: 14px; }
  .hero__card:not(.is-compact) .hero__card-cta {
    align-self: end; justify-self: start;
    height: 40px; padding: 0 var(--s20);
    font-size: 16px; line-height: 1.2;
    box-shadow: inset 0 0 0 1px var(--accent-line);
  }
  .hero__card:not(.is-compact) .hero__card-cta .btn__icon { display: none; }   /* the reference's has none */
}


/* --- MANIFESTO + CERTIFICATIONS ------------------------------------------- */

/* Bottom padding is deliberately much smaller than --pad-bottom. The outro
   dissolves this block as the prices section arrives, so the two have to be
   close enough that the manifesto is still on screen while it dissolves. */
.manifesto { padding-block: var(--pad-top) 0; }


.manifesto__inner {
  display: grid;
  grid-template-columns: 1px 1fr;
  column-gap: var(--s15);
}

.vrule { position: relative; background: var(--hairline); }
/* Clipped rather than scaled: the band needs a moving TOP as well as a moving
   bottom, so the white can travel down the rail instead of only growing. JS
   writes the inset string, the same way the hero frame does. */
.vrule__fill {
  position: absolute; inset: 0;
  background: var(--text);
  clip-path: inset(0 0 100% 0);
}

.manifesto__content { padding-block: var(--s30) var(--s40); }

.manifesto__text {
  max-width: var(--measure-manifesto);
  /* A paragraph reads better in the text face (pass 56): Michroma set it heavy and loud. */
  font-family: var(--font-body);
  font-stretch: normal;
  font-weight: 300;
  font-size: calc(var(--fs-manifesto) * 1.32);
  line-height: var(--lh-manifesto);
  letter-spacing: normal;
  color: var(--text-resting);
}

/* Credentials, straight under the statement (the signature went in pass
   83) — no hairlines, no separate section. */
.certs {
  margin-top: var(--gap-attribution);   /* the signature's place since pass 83 */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s25);
  max-width: 620px;
}

.cert__logo {
  --cert-h: 28px;   /* 24 until pass 49 (review: TAC almost invisible) */
  --cert-box: 36px;
  display: flex; align-items: center;
  height: var(--cert-box); width: 100%;
}

.cert__logo img {
  height: calc(var(--cert-h) * var(--cert-scale, 1));
  width: auto; max-width: 100%;
  object-fit: contain; object-position: left center;
  filter: var(--ink-light);
}

.cert__caption {
  margin-top: var(--s10);
  font-size: var(--fs-micro);   /* --fs-caption (12) until pass 49 */
  line-height: var(--lh-body);
  color: var(--text-dim);
  text-wrap: pretty;
}

@media (min-width: 768px) {
  .manifesto__inner { column-gap: var(--s25); }
  .certs      { gap: var(--s50); max-width: 560px; }
  .cert__logo { --cert-h: 34px; --cert-box: 44px; }
}

/* The editorial photo (pass 60). A plate, narrower than the type so the words
   still lead. Below 1024 it sits ABOVE the statement, right-aligned: below it
   the statement would leave the screen before it dissolves (pass 61). */
.manifesto__figure {
  width: 64%; max-width: 340px;
  margin: 0 0 var(--s40) auto;
  aspect-ratio: 938 / 1173;
  overflow: hidden;
  border-radius: var(--radius-sm);
}
.manifesto__photo {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
}
.motion-ready .manifesto__photo { clip-path: inset(100% 0 0 0); }

@media (min-width: 1024px) {
  .manifesto__content {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(240px, 25vw, 460px);
    grid-template-rows: auto auto 1fr;
    column-gap: clamp(48px, 6vw, 120px);
  }
  .manifesto__text, .certs { grid-column: 1; }
  .manifesto__figure {
    grid-column: 2; grid-row: 1 / span 2; align-self: start;
    width: auto; max-width: none; margin: 0;
  }
}


/* --- PRICES / THE WORKSHOP ------------------------------------------------
   A sticky photograph that opens out of black, dark cards carried over it, and
   the manifesto's rail continued down the left.

   THIS SECTION IS THE MOST FRAGILE PART OF THE BUILD AND IT IS DOCUMENTED IN
   FULL IN reference/workshop.md. Read that before changing anything here. */

/* Plain dark ground since pass 66 (owner): the sticky workshop photo, its
   reveal from black and the stage that held it are gone. */
.prices {
  position: relative;
  z-index: 1;
  background: var(--ground);
  padding-bottom: var(--pad-bottom);
  isolation: isolate;
  --text-dim: #B5B5B5;   /* #A3A3A3 site-wide; lighter here, where people read prices (pass 69) */
}

/* Same grid as .manifesto__inner, so the rail lands on exactly the same x and
   the line reads as one continuous stroke across the section boundary. */
.prices__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1px 1fr;
  column-gap: var(--s15);
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad-x);
}

/* No background of its own. The manifesto's rail has a grey track because the
   statement needs somewhere to fill INTO; this one is the line continuing to
   grow past the end of that track, so anything pre-drawn here would give the
   ending away. */
.prices__rail { background: none; visibility: hidden; }   /* no line since pass 67; the cell keeps the grid */
/* Starts with the head, not the section (pass 34): the content's 88/112px
   of top padding is on the CONTENT, and the rail is a grid item beside it,
   so it needs the same offset. This breaks the join with the manifesto's
   rail on purpose — each section's line is its content's height now. */
.prices__rail { margin-top: 88px; }
@media (min-width: 768px) { .prices__rail { margin-top: 112px; } }

/* On the CONTENT rather than the grid — the rail is a grid item, and padding
   here would push it down and break its join with the manifesto's rail. */
/* 88px clears the 68px header. */
.prices__content { padding-block: 88px var(--s5); }

.prices__eyebrow { color: var(--text-soft); }

/* THE SWITCH: a two-option segmented control (pass 70, owner; the iOS
   switch of passes 44–69 read as on/off, not "pick one"). Thin hairline
   frame, 2px corners; the chosen option sits on a thumb with an amber edge
   that slides on the spring — an edge, not a fill. Full width on a phone,
   as wide as its words from 768. */
.seg {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: 100%;
  max-width: 420px;
  margin-top: var(--s20);
  padding: 3px;
  border-radius: var(--radius-control);
  background: #0F0F0F;
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.seg__thumb {
  position: absolute;
  top: 3px; bottom: 3px; left: 3px;
  width: calc(50% - 3px);
  border-radius: 2px;
  background: var(--accent);   /* filled (pass 72, owner lifted the hero-only fill rule) */
  transition: transform 0.55s var(--ease-spring);
}
.seg[data-on="1"] .seg__thumb { transform: translateX(100%); }
.seg__opt {
  position: relative;
  z-index: 1;
  min-height: 44px;
  padding: 0 var(--s20);
  font-size: 15px;
  color: var(--text-dim);
  white-space: nowrap;
  transition: color var(--dur-hover) var(--ease-spring);
}
.seg__opt.is-on { color: #0A0A0A; font-weight: 500; }
@media (hover: hover) { .seg__opt:not(.is-on):hover { color: var(--text-soft); } }
.seg__opt:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (min-width: 768px) {
  .seg { display: inline-grid; width: auto; }
  .seg__opt { padding: 0 var(--s30); font-size: var(--fs-body); }
}
/* A step below the rows from 1024 (pass 86, owner: it was nearly a row's
   size): navigation, not content. */
@media (min-width: 1024px) {
  .seg__opt { min-height: 38px; padding: 0 var(--s25); font-size: 14px; }
}

.section-title { margin-top: var(--s10); }
.tabs__tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.section-title {
  margin-top: var(--s15);
  max-width: 18ch;
  font-size: var(--fs-section);
  line-height: var(--lh-heading);
  letter-spacing: var(--track-heading);
}
/* Scaled by the heading face on every width since pass 38 (from 768
   before, when the workshop's heading was one of these; it is
   .prices__title now). 22 → 32 → 40 × the face's scale: ~31 / 44 / 56px
   on Geist, the reference's 55px standalone heading (45 before). */
.section-title { font-size: calc(var(--fs-section) * var(--head-scale)); }

/* --- THE PACKAGES ---------------------------------------------------------
   PASS 71 — THE OWNER'S MOCKUP, IN OUR SHARP STYLE. Every row is a box of
   its own (hairline edge, 2px corners, 10px apart): index | name, tagline,
   duration with a clock | from-price | chevron. The active row (from 1024)
   or the open row (below) carries the amber edge and a warmer ground. The
   panel (right from 1024, sticky) and the opened row (below 1024) share
   one order: label, statement, price in amber, who it is for, thin amber
   checks, fact tiles, the ask, the note. Line icons are inline SVG
   (uiIcon in content.js). Both lists share every rule. */

.prices__subline {
  margin-top: var(--s10);
  max-width: 44ch;
  font-size: var(--fs-body);
  line-height: 1.45;
  color: var(--text-dim);
  text-wrap: pretty;
}

.tiers { display: grid; margin-top: var(--s25); }
.tiers[hidden] { display: none; }

/* THE BOX. On the li, so the opened row sits inside the same edge. */
/* No box since pass 72 (owner: less boxy): a faint fill and a hairline
   between rows; only the active or open row has an edge. */
.tier {
  position: relative;
  border: 1px solid transparent;
  border-radius: 2px;
  background-color: #101010;
  transition: border-color var(--dur-hover) var(--ease-spring),
              background-color var(--dur-hover) var(--ease-spring),
              box-shadow var(--dur-hover) var(--ease-spring);
}

.tier + .tier { border-top-color: var(--hairline); }
.tier.is-open + .tier { border-top-color: transparent; }
@media (min-width: 1024px) { .tier.is-active + .tier { border-top-color: transparent; } }

/* THE ROW. Phone: the duration under the words, the price and chevron
   centred on the right. */
.tier__card {
  --ink:       #F4F4F4;
  --ink-soft:  #BDBDBD;
  --ink-faint: #9C9C9C;
  position: relative;
  display: grid;
  /* The words take the full width; the duration and the price share the
     bottom line ("€1.100" beside the words squeezed them to 5 lines). */
  grid-template-columns: 38px minmax(0, 1fr) auto 16px;
  grid-template-areas:
    "index main main  chev"
    "index dur  price chev";
  column-gap: 12px;
  row-gap: 12px;
  align-items: start;
  padding: 16px 12px 16px 14px;
}

.tier__index {
  grid-area: index;
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  font-size: 20px;
  line-height: 1;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  transition: color var(--dur-hover) var(--ease-spring);
}

.tier__main { grid-area: main; min-width: 0; }
.tier__name {
  margin: 0;                  /* h3 is not in the global reset */
  display: grid;
  justify-items: start;
  gap: 6px;
  font-family: var(--font-head);
  font-size: 15px;
  font-weight: var(--head-weight);
  line-height: 1.25;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink);
}
/* The control, stretched over the whole box by its ::after — it stays
   inside the <h3> (a <button> may only hold phrasing content). */
.tier__toggle {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  min-width: 0;
  max-width: 100%;
}
.tier__toggle::after { content: ''; position: absolute; inset: 0; z-index: 2; }
.tier__toggle:focus-visible::after { outline: 2px solid var(--accent); outline-offset: -2px; }

/* An outline, never a fill. */
.tier__flag {
  padding: 4px 7px 3px;
  box-shadow: inset 0 0 0 1px var(--accent-line);
  color: var(--accent);
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The short name where the row is narrow (below 768, 1024–1199). */
.tier__name-full { display: none; }
@media (min-width: 768px) and (max-width: 1023px), (min-width: 1200px) {
  .tier__name-full { display: inline; }
  .tier__name-short { display: none; }
}

.tier__tagline {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-dim);
  text-wrap: pretty;
}

/* UNCONFIRMED durations — see the note above `packages` in content.js. */
.tier__dur {
  grid-area: dur;
  align-self: end;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  line-height: 1;
  color: var(--ink-soft);
  white-space: nowrap;
}
.tier__dur-icon { width: 17px; height: 17px; flex: none; color: var(--ink-faint); }

.tier__price {
  grid-area: price;
  align-self: end;
  display: grid;
  justify-items: end;
  gap: 4px;
  text-align: right;
}
.tier__from { font-size: 13px; line-height: 1; color: var(--text-dim); }
.tier__amount {
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  font-size: 22px;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* Michroma has no euro glyph: the sign is on the body face. */
.tier__cur, .price__cur {
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 0.72em;
  vertical-align: 0.14em;
  margin-right: 0.06em;
}
.tier__unit { font-size: 13px; color: var(--text-dim); white-space: nowrap; }

.tier__chev {
  grid-area: chev;
  align-self: center;
  display: flex;
  color: var(--ink-faint);
  transition: color var(--dur-hover) var(--ease-spring), transform 0.45s var(--ease-spring);
}
.tier__chev-icon { width: 16px; height: 16px; }

/* --- the active / open box ---------------------------------------------
   Active means something only beside the panel (from 1024); below it the
   same look marks the open row. Hover styles stay in (hover: hover). */
@media (min-width: 1024px) {
  /* No right edge: its top and bottom lines run into the photo (pass 72). */
  .tier.is-active {
    border-color: var(--accent-line);
    border-right-color: transparent;
    background-color: #12100C;
    background-image: linear-gradient(105deg, rgba(216, 137, 36, 0) 55%, rgba(216, 137, 36, 0.09) 100%);
  }
  .tier.is-active .tier__index,
  .tier.is-active .tier__chev { color: var(--accent); }
  .tier.is-active .tier__chev { transform: translateX(3px); }
}
/* BELOW 1024 THE CHEVRON IS A SMALL AMBER + (pass 87; pass 86's boxed +
   read heavy). Same place and size as the arrow; the vertical bar folds
   away when the row is open, leaving −. The svg is hidden. */
@media (max-width: 1023px) {
  .tier__chev { position: relative; width: 16px; height: 16px; }
  .tier__chev-icon { display: none; }
  .tier__chev::before,
  .tier__chev::after {
    content: '';
    position: absolute; left: 50%; top: 50%;
    width: 14px; height: 1.5px;
    margin: -0.75px 0 0 -7px;
    border-radius: 1px;
    background: var(--accent);
    transition: transform 0.45s var(--ease-spring);
  }
  .tier__chev::after { transform: rotate(90deg); }
  .tier.is-open .tier__chev::after { transform: rotate(90deg) scaleX(0); }
}
@media (max-width: 1023px) {
  .tier.is-open { border-color: var(--accent-line); }   /* the edge only, no warm ground (pass 72) */
  .tier.is-open .tier__index { color: var(--accent); }
  .prices__panel { display: none; }
}
@media (hover: hover) {
  .tier:not(.is-active):not(.is-open):hover { background-color: #141414; }
  .tier:not(.is-active):not(.is-open):hover .tier__chev { color: var(--ink); }
}

/* --- the row, opened (below 1024) --------------------------------------
   0fr → 1fr on the spring; closed it is `visibility: hidden` so its link
   is not tabbable. One open at a time, all closed at first. */
.tier__more {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows 0.55s var(--ease-spring), visibility 0s linear 0.55s;
}
.tier.is-open .tier__more {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 0.55s var(--ease-spring), visibility 0s linear 0s;
}
.tier__more-inner {
  min-height: 0;
  overflow: hidden;
  padding: 0 14px;            /* no bottom padding: a closed 0fr row would still hold it */
}
@media (min-width: 1024px) { .tier__more { display: none; } }

/* The photograph edge to edge inside the box. */
.tier__photo {
  position: relative;
  margin: 0 -14px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--ground);
}
.tier__photo img {
  width: 100%; height: 100%;
  object-fit: cover;
  transform: scaleX(var(--flip, 1));
  filter: saturate(0.85) brightness(0.94) contrast(1.06);
}
.tier__photo::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(16, 16, 16, 0) 55%, rgba(16, 16, 16, 0.85) 100%);
}

/* --- the shared detail parts (opened row and panel) --------------------- */
.tier__title,
.prices__panel-title {
  margin: 0;                  /* the panel's is an h3 */
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  line-height: 1.15;
  letter-spacing: var(--track-heading);
  color: var(--text);
  overflow-wrap: break-word;
  text-wrap: balance;
}
.tier__title { margin-top: var(--s20); font-size: 28px; }

.tier__priceline,
.prices__panel-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 8px;
  margin-top: 10px;
  line-height: 1;
}
.price__from { font-size: 17px; color: var(--text-soft); }
.price__amt {
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  font-size: 32px;
  color: var(--accent);       /* the owner's call, pass 71: the price is the panel's accent */
  white-space: nowrap;
}
.price__unit { font-size: 15px; color: var(--text-dim); }
.price__unit:empty { display: none; }

.tier__desc,
.prices__panel-desc {
  margin-top: var(--s15);
  max-width: 44ch;
  font-size: 16px;
  line-height: 1.55;
  color: var(--text-soft);
  text-wrap: pretty;
}

/* Thin amber checks (pass 71: the circles read heavy). A data: SVG
   background — file:// blocks url() masks, not backgrounds. */
.tier__includes,
.prices__panel-list {
  margin: var(--s20) 0 0;
  display: grid;
  gap: 10px;
}
.tier__includes li,
.prices__panel-list li {
  position: relative;
  padding-left: 28px;
  font-size: 16px;
  line-height: 1.4;
  color: var(--text);
}
.tier__includes li::before,
.prices__panel-list li::before {
  content: '';
  position: absolute; left: 0; top: 0.12em;
  width: 18px; height: 18px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M4.5 12.5l5 5L19.5 7' fill='none' stroke='%23D88924' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* THE FACTS (pass 75: the boxed tiles read odd, a lone one most of all).
   No boxes: a hairline above, the facts side by side as wide as their
   words, a hairline between them; the before/after's tracked labels. */
.facts {
  display: flex;
  flex-wrap: wrap;
  margin-top: var(--s20);
  padding-top: var(--s15);
  border-top: 1px solid var(--hairline);
}
.facts:empty { display: none; }
.fact {
  display: grid;
  grid-template-columns: 20px auto;
  grid-template-areas: "icon label" "icon value";
  column-gap: 12px;
  row-gap: 3px;
  align-items: center;
  padding-right: var(--s25);
}
.fact + .fact { padding-left: var(--s25); border-left: 1px solid var(--hairline); }
.fact__icon { grid-area: icon; width: 20px; height: 20px; color: var(--accent); }
.fact__label {
  grid-area: label;
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.fact__value { grid-area: value; font-size: 15px; line-height: 1.25; color: var(--text); white-space: nowrap; }
/* A narrow panel (1024–1199, short screens): one fact under the other,
   no divider — side by side the second wrapped and kept its line (pass 83). */
@container (max-width: 330px) {
  .prices__panel-facts { flex-direction: column; gap: var(--s10); }
  .prices__panel-facts .fact + .fact { padding-left: 0; border-left: 0; }
}
/* A phone: tighter, so two facts stay on one line (they wrapped, and the
   second kept its divider). */
@media (max-width: 767px) {
  .fact { column-gap: 10px; padding-right: var(--s15); }
  .fact + .fact { padding-left: var(--s15); }
  .fact__label { letter-spacing: 0.1em; }
}

/* The ask: full width, sentence case, the WhatsApp mark. */
.tier__ask,
.prices__panel-cta {
  width: 100%;
  justify-content: center;
  margin-top: var(--s20);
  min-height: 50px;
  font-size: 15px;
  letter-spacing: 0;
  text-transform: none;
}
.tier__ask { margin-bottom: 18px; }

/* --- the panel -----------------------------------------------------------
   One photograph, the parts above, over a gradient. Two image layers, one
   `is-on`, cross-fading; the body dips out and back (`is-swapping`) around
   a change. Hidden below 1024 (the row opens in place). */
.prices__panel {
  position: relative;
  overflow: hidden;
  background-color: var(--ground);
}
.prices__panel-media {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background-color: var(--ground);
}
/* THE PHOTO TREATMENT (pass 33/36): the subject sharp on its side, the
   frame darkened a touch; the gradient alone separates the words. The
   focal point (--pos-s / --pos-l) and the mirroring (--flip) are the
   item's, set inline by renderPrices. Nothing is blurred live. */
.prices__panel-shot {
  position: absolute; inset: 0;
  opacity: 0;
  /* The mirror is static, on the shot; the settle is on the images. */
  transform: scaleX(var(--flip, 1));
  transition: opacity 0.7s var(--ease-spring);
}
.prices__panel-shot.is-on { opacity: 1; }
.prices__panel-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--pos-s, 50% 50%);
  filter: saturate(0.85) brightness(0.94) contrast(1.06);
  transform: scale(1.04);
  transition: transform 1.1s var(--ease-spring);
}
.is-on > .prices__panel-img { transform: none; }
.prices__panel-media::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10, 10, 10, 0) 40%, rgba(10, 10, 10, 0.78) 78%, var(--ground) 100%);
}

.prices__panel-body {
  position: relative;
  padding: var(--s20) var(--s20) var(--s30);
  /* A size container (pass 68): the title and price scale to the column. */
  container-type: inline-size;
}
.prices__panel-body > * {
  transition: opacity 0.42s var(--ease-spring),
              transform 0.62s var(--ease-spring),
              filter 0.42s var(--ease-spring);
}
.prices__panel-body > :nth-child(2) { transition-delay: 0.04s; }
.prices__panel-body > :nth-child(3) { transition-delay: 0.08s; }
.prices__panel-body > :nth-child(4) { transition-delay: 0.12s; }
.prices__panel-body > :nth-child(5) { transition-delay: 0.16s; }
.prices__panel-body > :nth-child(6) { transition-delay: 0.20s; }
.prices__panel.is-swapping .prices__panel-body > * {
  opacity: 0;
  transform: translateY(8px);
  filter: blur(2px);
  transition-duration: 0.22s;
  transition-delay: 0s;
}

/* The label: the mockup's amber code line. */
.prices__panel-code {
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}
.prices__panel-title { margin-top: var(--s15); font-size: clamp(26px, 10cqi, 46px); }
.prices__panel-price { margin-top: var(--s15); }
.prices__panel-price .price__from { font-size: 18px; }
.prices__panel-price .price__amt { font-size: clamp(32px, 12cqi, 52px); }
.prices__panel-cta .btn__icon { order: 0; }
.prices__panel-note {
  margin-top: var(--s10);
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-dim);
}

/* --- the foot ------------------------------------------------------------
   The price note under the rows, then the question and three ways in, each
   with an icon box (the mockup's). Under the list on a phone, full width
   under both columns from 1024. */
.prices__foot {
  position: relative;
  margin-top: var(--s15);
  display: grid;
  justify-items: start;
}
.prices__note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-dim);
}
.prices__note-icon { width: 18px; height: 18px; flex: none; margin-top: 1px; color: var(--text-dim); }
/* ONE QUIET PROMPT (pass 88, owner): a hairline and room above, the
   question in grey, an amber "Help me kiezen". The three suggestions
   unfold under it on a click (0fr → 1fr, as the rows) — small amber icons,
   no lines between them. */
.prices__help-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px var(--s15);
  width: 100%;
  margin-top: var(--s40);
  padding-top: var(--s25);
  border-top: 1px solid var(--hairline);
}
.prices__help { font-size: 15px; line-height: 1.4; color: var(--text-dim); }
.prices__help-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding-bottom: 2px;
  font-size: 15px;
  line-height: 1.4;
  color: var(--accent-soft);
  box-shadow: inset 0 -1px 0 var(--accent-line);
  transition: color var(--dur-hover) var(--ease-spring), box-shadow var(--dur-hover) var(--ease-spring);
}
.prices__help-chev { width: 14px; height: 14px; transform: rotate(90deg); transition: transform 0.45s var(--ease-spring); }
.prices__help-toggle[aria-expanded="true"] .prices__help-chev { transform: rotate(-90deg); }
@media (hover: hover) {
  .prices__help-toggle:hover { color: var(--accent); box-shadow: inset 0 -1px 0 var(--accent); }
}
.prices__help-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.guide-wrap {
  display: grid;
  grid-template-rows: 0fr;
  width: 100%;
  visibility: hidden;
  transition: grid-template-rows 0.55s var(--ease-spring), visibility 0s linear 0.55s;
}
.guide-wrap.is-open {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 0.55s var(--ease-spring), visibility 0s linear 0s;
}
.guide-wrap__inner { min-height: 0; overflow: hidden; }
.guide { display: grid; width: 100%; padding-top: var(--s15); }
.guide__opt {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) 14px;
  grid-template-areas: "icon title chev" "icon text chev";
  column-gap: var(--s10);
  row-gap: 2px;
  align-items: center;
  width: 100%;
  padding: 8px 0;
  text-align: left;
}
.guide__icon { grid-area: icon; align-self: start; display: grid; place-items: center; width: 18px; height: 18px; margin-top: 1px; color: var(--accent); }
.guide__svg { width: 16px; height: 16px; }
.guide__title { grid-area: title; font-size: 14px; line-height: 1.35; color: var(--text-soft); transition: color var(--dur-hover) var(--ease-spring); }
.guide__text { grid-area: text; font-size: 13px; line-height: 1.4; color: var(--text-dim); }
.guide__chev {
  grid-area: chev;
  width: 14px; height: 14px;
  color: var(--accent);
  transition: transform var(--dur-hover) var(--ease-spring);
}
@media (hover: hover) {
  .guide__opt:hover .guide__title { color: var(--accent-soft); }
  .guide__opt:hover .guide__chev { transform: translateX(3px); }
}
.guide__opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- below 1024: a card per package (pass 92, owner) -------------------
   Each row its own box: a lighter charcoal, a soft edge, 10px apart, 2px
   corners. Below 768 the inside is stacked: number and + on top, the name
   at full width (18px: "SHOWROOMKLAAR" does not fit beside the number),
   the tagline from the same left edge, then duration | price. */
@media (max-width: 1023px) {
  /* The hidden rail's cell is closed below 1024 (pass 95): 20px each side,
     the before/after's gutter (36 | 20 before). */
  .prices__inner { grid-template-columns: 0 minmax(0, 1fr); column-gap: 0; }
  .tiers { gap: 10px; }
  .tier,
  .tier + .tier,
  .tier.is-open + .tier { border-color: #242424; background-color: #121212; }
  .tier.is-open { border-color: var(--accent-line); }
}
@media (hover: hover) and (max-width: 1023px) {
  .tier:not(.is-open):hover { background-color: #161616; }
}
@media (max-width: 767px) {
  .prices__title { font-size: 26px; }
  .prices__subline { margin-top: 8px; }
  .seg { margin-top: 22px; }
  .seg__opt { min-height: 42px; }          /* 48 with the switch's padding */
  .seg__opt:not(.is-on) { color: #D0D0D0; }

  /* PASS 94 (owner's mockup): the photo on the left, the full card height;
     number and + on top, the name, the tagline, then duration | price over a
     hairline. The name is sized to the column, the card's largest text:
     SHOWROOMKLAAR and INTERIEURREINIGING are single words and must fit it
     (~15px at 375; column = 100vw - 144px, ~15.3px of column per px). */
  .tier__card {
    grid-template-columns: 72px minmax(0, 1fr) auto;
    grid-template-areas:
      "thumb index chev"
      "thumb main  main"
      "thumb dur   dur";
    column-gap: 10px;
    row-gap: 0;
    padding: 8px 12px 8px 8px;
  }
  .tier__thumb {
    grid-area: thumb;
    align-self: stretch;
    display: block;
    position: relative;
    min-height: 132px;
    border-radius: 2px;
    overflow: hidden;
    background: var(--ground);
  }
  .tier__thumb img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    transform: scaleX(var(--flip, 1));
    filter: saturate(0.85) brightness(0.94) contrast(1.06);
  }
  .tier__index {
    margin-top: 8px;
    font-size: 12px;
    letter-spacing: 0.06em;
    color: #A39882;                         /* muted champagne */
    align-self: center;
  }
  .tier.is-open .tier__index { color: var(--accent); }
  .tier__chev { margin-top: 8px; align-self: center; justify-self: end; }
  .tier__main { margin-top: 8px; }
  .tier__name { font-size: clamp(13px, 6.5vw - 9.5px, 17px); gap: 6px; color: #FFFFFF; }
  .tier__flag { font-size: 9px; padding: 3px 6px 2px; }
  .tier__tagline { margin-top: 6px; font-size: 13px; line-height: 1.45; }
  /* Duration and price share one cell over one hairline. */
  .tier__dur, .tier__price { margin: 12px 0 6px; padding-top: 10px; }
  .tier__dur {
    align-self: stretch;
    align-items: flex-end;
    border-top: 1px solid #262626;
  }
  .tier__price { grid-area: auto; grid-row: 3; grid-column: 2 / 4; justify-self: end; }
  .tier__from { font-size: 12px; }
  .tier__amount { font-size: 17px; }   /* under the name (pass 95) */
  /* OPEN, THE ROW SHEDS WHAT THE OPENED PART REPEATS (pass 97, owner): the
     thumbnail (the big photo is it), the duration and the price. */
  .tier.is-open .tier__card {
    grid-template-columns: 0 minmax(0, 1fr) auto;
    grid-template-areas: "thumb index chev" "thumb main main";
    column-gap: 0;
    padding: 14px 14px 16px;
  }
  .tier.is-open .tier__thumb,
  .tier.is-open .tier__dur,
  .tier.is-open .tier__price { display: none; }
  .tier.is-open .tier__index, .tier.is-open .tier__chev { margin-top: 0; }
  .tier__more-inner { padding: 0 14px; }
  .tier__photo { margin: 0 -14px; }
}
.tier__thumb { display: none; }   /* phone only */
@media (max-width: 767px) { .tier__thumb { display: block; } }

/* --- 768 and up --------------------------------------------------------- */
@media (min-width: 768px) {
  .prices__inner { column-gap: var(--s25); }
  .prices__content { padding-block: 112px var(--s5); }
  .prices__subline { font-size: var(--fs-subhead); }
  .tiers { margin-top: var(--s30); }

  /* The duration takes its own column, the mockup's desktop row. */
  .tier__card {
    grid-template-columns: 50px minmax(0, 1fr) 128px 116px 18px;
    grid-template-areas: "index main dur price chev";
    column-gap: var(--s20);
    align-items: center;
    padding: 18px var(--s20);
  }
  .tier__dur, .tier__price { align-self: center; }
  .tier__index { font-size: 24px; }
  .tier__name { display: flex; align-items: center; flex-wrap: wrap; gap: 4px var(--s15); font-size: 17px; }
  .tier__price { justify-items: start; text-align: left; }
  .tier__amount { font-size: 26px; }
  .tier__chev-icon { width: 18px; height: 18px; }

  .tier__photo { aspect-ratio: 21 / 9; }
  .tier__more-inner { padding: 0 var(--s20); }
  .tier__ask { margin-bottom: var(--s25); }
  .tier__photo { margin: 0 calc(-1 * var(--s20)); }
  .tier__includes { grid-template-columns: 1fr 1fr; column-gap: var(--s30); }
  .facts { max-width: 480px; }
  .tier__ask { width: auto; padding-inline: var(--s40); }

}

/* --- THE SPLIT (1024 and up) ---------------------------------------------
   The list left, the panel right beside the head and the list, the foot
   full width under both (the mockup's). Placed by grid AREA, not DOM
   order: the markup stays in the order a phone reads it. */
@media (min-width: 1024px) {
  .prices__content {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(340px, 40%);
    column-gap: 0;              /* the active row meets the photo (pass 72) */
    grid-template-areas:
      "head  panel"
      "tiers panel"
      "foot  panel";
    /* THE HELPER UNDER THE ROWS, BESIDE THE PHOTO (pass 87, owner: full
       width under both it looked detached). The photo runs past the last
       row to the helper's end (the empty 72/100/120px row of 83–86 is gone). */
    align-content: start;
  }
  .prices__foot { grid-area: foot; padding: 0 var(--s30) var(--s15) 0; }   /* s40 until pass 88 */
  .prices__head { grid-area: head; padding-right: var(--s30); }
  .prices__title { font-size: 32px; }   /* one line beside the panel */
  .tiers        { grid-area: tiers; }

  /* STICKY, AS TALL AS THE LIST BUT NEVER TALLER THAN THE SCREEN (pass 69,
     71). It stretches over the head and the list; max-height caps it at
     the screen so its words stay in view while it rides along. Works only
     because body is overflow-x: clip — `hidden` breaks sticky. */
  .prices__panel {
    grid-area: panel;
    position: sticky; top: 0;
    /* As tall as the head and the list, capped at the screen (passes 69–74,
       restored in 82). Its min-height is set by sizePanel() in content.js to
       fit the LONGEST item, so hovering a long one (Perfect Finish) cannot
       grow the section and move the page. */
    align-self: stretch;
    max-height: 100vh; max-height: 100svh;
    display: grid;
    border: 1px solid var(--hairline);
    border-left: 0;
    border-radius: 2px;
  }

  /* 1024–1199: the column is narrow, so the duration goes back under the
     words. */
  .tier__card {
    grid-template-columns: 44px minmax(0, 1fr) auto 18px;
    grid-template-areas:
      "index main main  chev"
      "index dur  price chev";
    align-items: start;
    row-gap: 12px;
  }
  .tier__price { justify-items: end; text-align: right; align-self: end; }
  .tier__dur { align-self: end; }

  /* The photograph fills the panel; the words sit over its quieter side. */
  .prices__panel-media { position: absolute; inset: 0; aspect-ratio: auto; }
  .prices__panel-img { object-position: var(--pos-l, 50% 50%); }
  /* THE GRADIENT DOES ALL OF IT (pass 36), keyed to where the words start
     (--tx, the body's padding-left, pass 37). */
  .prices__panel { --tx: 20%; }
  .prices__panel-media::after {
    background:
      /* A RAMP (pass 72, owner): clear at the list, darkening evenly to
         near black at the right edge — no flat block. */
      /* Darker and reaching further left since pass 75 (owner: the words
         over the photo were hard to read). */
      linear-gradient(90deg, rgba(10, 10, 10, 0.08) 0%,
                             rgba(10, 10, 10, 0.34) 8%,
                             rgba(10, 10, 10, 0.6) 18%,
                             rgba(10, 10, 10, 0.78) 30%,
                             rgba(10, 10, 10, 0.9) 45%,
                             rgba(10, 10, 10, 0.96) 62%,
                             rgba(10, 10, 10, 0.99) 80%,
                             #0A0A0A 100%),
      linear-gradient(180deg, rgba(10, 10, 10, 0) 55%, rgba(10, 10, 10, 0.55) 85%, rgba(10, 10, 10, 0.8) 100%);
  }
  /* Centred; the top padding clears the header once the panel sticks. */
  /* Centred, a step high (pass 88, owner): the bottom padding outweighs
     the top's header clearance. */
  .prices__panel-body {
    align-self: center;
    width: 100%;
    padding: 72px var(--s30) 120px var(--tx);
  }

  /* The suggestions one line each beside the photo: icon, title, packages. */
  .guide__opt {
    grid-template-columns: 18px auto minmax(0, 1fr) 14px;
    grid-template-areas: "icon title text chev";
    column-gap: var(--s15);
  }
  .guide__icon { align-self: center; margin-top: 0; }
}

@media (min-width: 1200px) {
  .prices__content { grid-template-columns: minmax(0, 1fr) minmax(420px, 42%); }
  /* The other sections' heading size, one line beside the panel (pass 86). */
  .prices__title { font-size: clamp(40px, 3.4vw, 56px); }
  /* ~20% taller rows (pass 86, owner: the section read small for what it
     sells); 18px padding until then. */
  .tier__card {
    grid-template-columns: 56px minmax(0, 1fr) 118px 120px 18px;
    grid-template-areas: "index main dur price chev";
    align-items: center;
    padding: 24px var(--s25);
  }
  .tier__name { font-size: 19px; }
  .tier__tagline { margin-top: 8px; }
  .tier__index { font-size: 28px; }
  .tier__price { justify-items: start; text-align: left; }
  .tier__dur, .tier__price { align-self: center; }
  .tier__amount { font-size: 31px; }
  .prices__panel { --tx: 30%; }
  .prices__panel-body { padding-right: var(--s40); }
}

/* The panel carries the price note where it shows it (1024+, taller than
   820px; pass 84): the copy under the list floated below the panel's extra
   row, a repeat. On short screens the panel drops its note and this stays. */
@media (min-width: 1024px) and (min-height: 821px) {
  .prices__note { display: none; }
}

/* SHORT SCREENS (pass 69): a 1366×768 laptop shows ~657px. The words go
   further left, the gaps tighten and the note steps out, so the whole
   panel fits the sticky screen. */
@media (min-width: 1024px) and (max-height: 820px) {
  .prices__panel { --tx: 16%; }
  .prices__panel-body { padding-top: 74px; padding-bottom: var(--s20); }
  .prices__panel-title { margin-top: var(--s10); font-size: clamp(22px, 8cqi, 32px); }
  .prices__panel-price { margin-top: var(--s10); }
  .prices__panel-price .price__amt { font-size: clamp(28px, 9cqi, 38px); }
  .prices__panel-desc { margin-top: var(--s10); line-height: 1.45; }
  .prices__panel-list { margin-top: var(--s15); gap: 6px; }
  .prices__panel-facts { margin-top: var(--s15); }
  .prices__panel-facts { padding-top: var(--s10); }
  .prices__panel-cta { margin-top: var(--s15); min-height: 46px; }
  .prices__panel-note { display: none; }
}

/* --- WERK / THE GALLERY ---------------------------------------------------
   Dark, by the owner's decision. */
.werk {
  position: relative;
  z-index: 1;
  background: var(--ground);
  /* A shorter seam under it than the standard (pass 27, "awkward gap"): the
     feed's bottom edge fades into the ground, so the black after the small
     Instagram link read as twice its measure. 50 / 80 against 100 / 150. */
  padding-block: var(--pad-top) var(--s50);
}
@media (min-width: 1024px) {
  .werk { padding-bottom: 80px; }
  /* THE SPLIT (pass 38): the heading left, the intro on the centre line,
     level with the heading's last line. */
  .werk__head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: end;
  }
  .werk__head > .eyebrow, .werk__head > .section-title { grid-column: 1; }
  .werk__head > .eyebrow { grid-row: 1; }
  .werk__head > .section-title { grid-row: 2; }
  /* `.werk__head >` for weight: the base .werk__intro rule comes later in the file. */
  .werk__head > .werk__intro { grid-column: 2; grid-row: 1 / span 2; align-self: end; margin-top: 0; font-size: var(--fs-subhead); }
}

.werk__intro {
  margin-top: var(--s15);
  max-width: 42ch;
  font-size: var(--fs-micro);
  line-height: 1.45;
  color: var(--text-dim);
  text-wrap: pretty;
}

/* A FEED, NOT A CONTACT SHEET (pass 24, owner's call): three columns at
   every width, like an Instagram grid, each one drifting slowly — up, down,
   up — through the whole set, and pausing under the pointer. Each column
   holds its five photographs twice, and the keyframe moves it exactly half
   its height, so the loop has no seam; the gap is a margin on the tile, not
   a grid gap, so half the height IS one set. The window shows three rows on
   a phone and two from 768 (square tiles, so 1:1 and 3:2). A CSS keyframe,
   not JS: it is the page's one continuous motion and the preview pane
   stalls requestAnimationFrame. */
.werk__feed {
  --werk-gap: var(--s5);
  position: relative;
  margin-top: var(--s30);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--werk-gap);
  aspect-ratio: 1;
  overflow: hidden;
}
/* The top and bottom edges fade into the ground rather than cut. Two
   gradients, not mask-image — Chrome blocks masks on file://. */
.werk__feed::before,
.werk__feed::after {
  content: '';
  position: absolute; left: 0; right: 0;
  height: 7%;
  z-index: 2;
  pointer-events: none;
}
.werk__feed::before { top: 0;    background: linear-gradient(180deg, var(--ground), transparent); }
.werk__feed::after  { bottom: 0; background: linear-gradient(0deg,   var(--ground), transparent); }
/* The lane is the entrance's element (GSAP), the column inside it the
   drift's (this animation) — a CSS animation beats an inline transform, so
   the two must not share one element. */
.werk__lane { min-width: 0; }
.werk__col {
  list-style: none;
  margin: 0; padding: 0;
  will-change: transform;
  animation: werk-drift var(--werk-dur, 75s) linear infinite;
}
/* The middle column runs the other way. */
.werk__lane:nth-child(2) .werk__col { animation-direction: reverse; }
@keyframes werk-drift {
  to { transform: translateY(-50%); }
}
@media (hover: hover) {
  .werk__lane:hover .werk__col { animation-play-state: paused; }
}
/* Paused off screen (pass 51); werkSequence sets .is-onscreen. Only with
   GSAP — without it nothing would ever un-pause. */
.motion-ready .werk:not(.is-onscreen) .werk__col { animation-play-state: paused; }

.shot {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1;
  margin-bottom: var(--werk-gap);
  border-radius: 6px;
  background: var(--surface);
}
/* The whole tile is the button that opens the lightbox. */
.shot__btn {
  display: block;
  width: 100%; height: 100%;
  cursor: zoom-in;
}
.shot__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* THE GRADE, and the one place on this site where a filter on a photograph is
   right: fifteen frames from several phones and light sources have to read as
   one set. Saturation goes DOWN, never up — pushing it amplifies the
   colour-cast mismatch. Static values on small tiles, so nothing re-rasters. */
.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.92) contrast(1.06);
  transition: transform 0.7s var(--ease-spring),
              filter var(--dur-hover) var(--ease-spring);
}
@media (hover: hover) {
  .shot:hover img {
    transform: scale(1.045);
    filter: saturate(1) contrast(1.06);
  }
}

/* The way out: their Instagram, with its own mark. It took the place of the
   "toon alle 15" button. */
.werk__ig {
  margin-top: var(--s25);
  display: inline-flex;
  align-items: center;
  gap: var(--s10);
  padding: 11px var(--s15) 11px 11px;
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--hairline);
  font-size: var(--fs-micro);
  line-height: var(--lh-body);
  color: var(--text-soft);
  transition: color var(--dur-hover) var(--ease-spring),
              box-shadow var(--dur-hover) var(--ease-spring);
}
.werk__ig-mark { width: 22px; height: 22px; flex: none; }
.werk__ig .linkline__arrow { transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) {
  .werk__ig:hover { color: var(--text); box-shadow: inset 0 0 0 1px var(--accent-line); }
  .werk__ig:hover .linkline__arrow { transform: translateX(4px); }
}
.werk__ig:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* THE LIGHTBOX. The photograph whole, on black, with a clear way out in
   the top-right and the previous/next pair on the sides. Opened by any tile,
   closed by the ×, the backdrop or Escape. */
.lightbox {
  position: fixed; inset: 0;
  z-index: 90;
  display: grid; place-items: center;
  padding: 64px var(--pad-x);
  background: rgba(10, 10, 10, 0.94);
  opacity: 0;
  transition: opacity 0.35s var(--ease-spring);
}
.lightbox[hidden] { display: none; }
.lightbox.is-on { opacity: 1; }
.lightbox__img {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  border-radius: 6px;
  transform: scale(0.96);
  transition: transform 0.5s var(--ease-spring), opacity 0.3s var(--ease-spring);
}
.lightbox.is-on .lightbox__img { transform: none; }
.lightbox__close,
.lightbox__nav {
  position: absolute;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  color: var(--text);
  background: rgba(10, 10, 10, 0.6);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);
  transition: background-color var(--dur-hover) var(--ease-spring),
              color var(--dur-hover) var(--ease-spring);
}
.lightbox__close { top: var(--s15); right: var(--s15); }
.lightbox__close svg, .lightbox__nav svg { width: 18px; height: 18px; }
.lightbox__nav { top: 50%; transform: translateY(-50%); }
.lightbox__nav--prev { left: var(--s10); }
.lightbox__nav--next { right: var(--s10); }
@media (hover: hover) {
  .lightbox__close:hover, .lightbox__nav:hover { background: var(--text); color: #0A0A0A; }
}
.lightbox__close:focus-visible, .lightbox__nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lightbox__count {
  position: absolute; left: 50%; bottom: var(--s20);
  transform: translateX(-50%);
  font-size: var(--fs-caption);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* SHARED — the gallery's button and every FAQ row use this mark. Two bars
   rather than a typed "+": a glyph sits on the body face's own baseline and
   never optically centres. --plus-inset is what lets the same element run
   edge-to-edge at 16px and sit inside a 22px ring. */
.plus {
  position: relative;
  width: 16px;
  height: 16px;
  flex: none;
}
.plus::before,
.plus::after {
  content: '';
  position: absolute;
  inset: 50% var(--plus-inset, 0) auto var(--plus-inset, 0);
  height: 1px;
  background: var(--accent);
  transition: background-color var(--dur-hover) var(--ease-spring);
}
.plus::after { transform: rotate(90deg); }

@media (min-width: 768px) {
  .werk__feed {
    --werk-gap: var(--s10);
    --werk-dur: 110s;
    margin-top: var(--s40);
    aspect-ratio: 3 / 2;
  }
  .werk__ig { margin-top: var(--s30); }
  .lightbox { padding: 72px 80px; }
  .lightbox__nav--prev { left: var(--s20); }
  .lightbox__nav--next { right: var(--s20); }
}


/* --- THE RAIL, CONTINUED --------------------------------------------------
   The site's connective tissue, continued below the workshop — it used to stop
   at the end of the ledger and everything after read as a different website.

   Absolutely positioned inside .container at left: var(--pad-x), which lands
   on exactly the same x as the ledger's .vrule at every width and needs no
   grid change in five sections. .container carries position: relative FOR
   THIS — do not remove it. */
.container { position: relative; }

/* ONE SEGMENT PER SECTION, THE HEIGHT OF ITS CONTENT (pass 34, owner: a
   hairline "should start where the section's main content starts, and end
   where the section's content ends"). Passes 24–33 stretched each segment
   by its section's padding so the segments met as one unbroken line; now
   they stop and start with the content, drawn one at a time by the pen.
   The colour still inverts per section with the section's tokens. */
.railseg {
  position: absolute;
  left: var(--pad-x);
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--hairline);
  pointer-events: none;
}
.railseg__fill {
  position: absolute;
  inset: 0;
  background: var(--text);
  /* JS writes the inset; this is only the resting state for the no-GSAP and
     reduced-motion paths, where the rail is simply drawn in full. */
  clip-path: inset(0 0 100% 0);
}
/* The white sections take a quieter fill — full-strength ink down the side of
   a white page is a much louder line than white is on black. */
.section--light .railseg__fill { background: var(--text-dim); }


/* --- A TEXT LINK WITH AN ARROW ------------------------------------------
   Shared by the reviews' Google link and the
   FAQ's fallback. */
.linkline {
  display: inline-flex;
  align-items: center;
  gap: var(--s10);
  font-size: var(--fs-micro);
  line-height: var(--lh-body);
  color: var(--text);
  box-shadow: inset 0 -1px 0 0 var(--hairline);
  padding-bottom: 3px;
  transition: box-shadow var(--dur-hover) var(--ease-spring),
              color var(--dur-hover) var(--ease-spring);
}
.linkline__arrow { transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) {
  .linkline:hover { box-shadow: inset 0 -1px 0 0 var(--text); }
  .linkline:hover .linkline__arrow { transform: translateX(4px); }
}
.linkline:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }


/* --- HOE HET WERKT --------------------------------------------------------
   A TIMELINE (pass 24b — hairline rows "got ignored", and icon chips before
   them read as an afterthought). Three nodes on a vertical track: the
   site's own rail, drawn down by the scroll (offerteSequence scrubs
   `.steps__fill` through railBand), and each node lights in the accent as
   the line reaches it. Beside the form from 1024, under it on a phone. */
.steps { margin-top: var(--s40); }

.steps__heading {
  margin: 0;                     /* h3 is not in the global reset */
  font-family: var(--font-head);
  font-size: 10px;
  font-weight: var(--head-weight);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* The form's label box, so the heading centres on "Naam" beside it from 1024 (pass 27). */
  line-height: calc(var(--fs-caption) * var(--lh-body));
  color: var(--text-dim);
}

.steps__list {
  --node: 44px;
  position: relative;
  margin-top: var(--s25);
  display: grid;
  gap: var(--s30);
}
/* The track and its fill, down the middle of the nodes. */
.steps__track,
.steps__fill {
  position: absolute;
  left: calc(var(--node) / 2);
  top: calc(var(--node) / 2);
  bottom: calc(var(--node) / 2);
  width: 1px;
}
.steps__track { background: var(--hairline); }
.steps__fill  { background: var(--accent); clip-path: inset(0 0 100% 0); }

.step {
  position: relative;
  display: grid;
  grid-template-columns: var(--node) minmax(0, 1fr);
  column-gap: var(--s20);
  align-items: start;
}
.step__node {
  position: relative;
  z-index: 1;
  width: var(--node); height: var(--node);
  display: grid; place-items: center;
  border-radius: var(--radius-sm);
  background: var(--ground);
  box-shadow: inset 0 0 0 1px var(--hairline);
  color: var(--text-dim);
  transition: box-shadow 0.5s var(--ease-spring), color 0.5s var(--ease-spring);
}
.step__node svg { width: 20px; height: 20px; display: block; }
.step.is-lit .step__node { box-shadow: inset 0 0 0 1px var(--accent); color: var(--accent); }

.step__index {
  display: block;
  font-family: var(--font-head);
  font-size: 10px;
  letter-spacing: 0.16em;
  line-height: 1;
  color: var(--text-dim);
}
.step__name {
  margin: 6px 0 0;
  font-family: var(--font-body);
  font-size: var(--fs-subhead);
  font-weight: 500;
  line-height: 1.25;
  color: var(--text);
}
.step__desc {
  margin-top: 6px;
  max-width: 34ch;
  font-size: var(--fs-micro);
  line-height: 1.45;
  color: var(--text-dim);
  text-wrap: pretty;
}


/* --- REVIEWS, AND THE LIGHT GROUND TOKENS --------------------------------
   THE LIGHT GROUND'S TOKENS. .section--light re-declares the whole colour set
   on the section element rather than restyling each component, so the entire
   subtree flips with one block and a new component needs no light-mode rule.

   Two values are NOT the dark set inverted: --text-dim is #6B6B6B (#979797 on
   white is about 2.6:1) and the eyebrow bullet goes to ink. See HANDOFF, "the
   light ground". */
.section--light {
  --ground:       #FFFFFF;
  --surface:      #F5F5F5;
  --surface-2:    #EDEDED;
  --hairline:     rgba(10, 10, 10, 0.09);
  --text:         #0A0A0A;
  --text-soft:    #212121;
  --text-dim:     #6B6B6B;
  --bullet:       var(--text);
  --bullet-glow:  transparent;
  /* Monochrome marks flip with the ground — see the note on --ink-dim. */
  --ink-dim:      brightness(0) invert(0.42);
  --ink-text:     brightness(0);
  --ink-ground:   brightness(0) invert(1);
  background: var(--ground);
  color: var(--text);
}

/* TWO LIGHT SECTIONS IN A ROW ARE ONE WHITE FIELD, NOT TWO BLOCKS — when
   they are in a row. Since pass 45 the case files sit between the reviews
   and the FAQ, so this does not match; it is kept for a reorder. */
.reviews + .faq.section--light { padding-top: 0; }
.reviews + .faq.section--light .railseg { top: 0; }

.reviews {
  position: relative;
  z-index: 1;
  padding-block: var(--pad-top) var(--pad-bottom);
}

/* The score under the heading; the link under the quotes (pass 39). */
.reviews__rating { margin-top: var(--s25); }
.reviews__foot {
  margin-top: var(--s30);
  display: flex; flex-wrap: wrap;
  align-items: center;
  column-gap: var(--s30); row-gap: var(--s15);
}
.reviews__foot .reviews__link { margin-top: 0; }

/* THE LEAD IS THE ONE CARD, THE OTHER THREE ARE ROWS (pass 38, owner: the
   reviews are the business's biggest strength and have to be READ). Three
   351px cards side by side were read as a strip, not as words; full-width
   rows at subhead size read like the reference's workflow table. */
.reviews__list {
  margin-top: var(--s30);
  display: grid;
}

.review { padding-block: var(--s25); border-top: 1px solid var(--hairline); }
.review--lead { padding: var(--s25); border-top: 0; border-radius: var(--radius-md); margin-bottom: var(--s15); }

/* Weight 400, not 300. Three short quotes at 300 on a white ground read as
   grey filler beside the inverted lead — the lead keeps 300 because it is
   white on black and 19px. */
.review__quote {
  font-size: var(--fs-subhead);
  font-weight: 400;
  line-height: 1.5;
  color: var(--text-soft);
  text-wrap: pretty;
  max-width: 58ch;
}

/* The lead answers the biggest objection on the page — handing your keys to
   someone you have never met — so it is set larger and given the full width.
   It is a hierarchy decision, not a layout one. */
.review--lead {
  /* The one inverted card, because it answers the biggest objection on the
   page. A LITERAL #0A0A0A (the page's black; #0F0F0F until 37c), NOT
   var(--ground): inside .section--light that token is #FFFFFF, and the
   card rendered white on white and vanished. */
  background: #0A0A0A;
  color: var(--text-soft);
}
.review--lead .review__quote {
  font-size: var(--fs-quote);
  font-weight: 300;
  line-height: 1.42;
  color: #FFFFFF;
  max-width: 34ch;
}
.review--lead .review__meta { color: rgba(255, 255, 255, 0.62); }
.review--lead .review__name { color: #FFFFFF; }
.review--lead .review__job { color: rgba(255, 255, 255, 0.62); }
.review--lead .review__mark {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
  color: #FFFFFF;
}

.review__meta {
  margin-top: var(--s20);
  display: flex;
  align-items: center;
  gap: var(--s15);
  font-size: var(--fs-caption);
  line-height: var(--lh-body);
  color: var(--text-dim);
}

/* Initials, not a face — see the note in renderReviews. The one circular
   object in the section, which is what makes it read as a person. */
.review__mark {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--hairline);
  background: var(--ground);
  /* The BODY face, not Michroma. Michroma is a geometric display cut and a
     lone capital in it reads as a notdef box rather than as initials — "D"
     came out as a rectangle with a bar in it. */
  font-family: var(--font-body);
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--text-soft);
}

.review__mark img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.review__mark--photo { box-shadow: none; background: none; }
.review__who { display: grid; gap: 2px; min-width: 0; }
/* The G and five stars at the meta's right end (pass 51, the review: they
   read as real Google reviews). */
.review__source { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; flex: none; }
.review__g { width: 14px; height: 14px; }
.review__stars { display: inline-flex; gap: 2px; }
.review__stars .stars__star { width: 12px; height: 12px; }
.review__name { color: var(--text); }
/* Tracked-out caps for the job, which is how both references set the line
   under a name. It is the only tracked type on the page besides the logo, and
   at 10px against a 12px name it reads as a caption rather than a second
   name. */
.review__job {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.reviews__link {
  margin-top: var(--s25);
  display: inline-flex;
  align-items: center;
  gap: var(--s10);
  font-size: var(--fs-micro);
  line-height: var(--lh-body);
  color: var(--text);
  box-shadow: inset 0 -1px 0 0 var(--hairline);
  padding-bottom: 3px;
  transition: box-shadow var(--dur-hover) var(--ease-spring);
}
.reviews__arrow { transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) {
  .reviews__link:hover { box-shadow: inset 0 -1px 0 0 var(--text); }
  .reviews__link:hover .reviews__arrow { transform: translateX(4px); }
}

@media (min-width: 768px) {
  .reviews__list { margin-top: var(--s40); }
  .review { padding-block: var(--s30); }
  .review--lead { padding: var(--s40); }
}

/* THE SPLIT (pass 38, the reference's composition): the heading alone in the
   left half, everything else starting on the page's centre line. */
@media (min-width: 1024px) {
  .reviews .container {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }
  .reviews__head { grid-column: 1; grid-row: 1 / span 2; }
  .reviews__list { grid-column: 2; margin-top: 0; }
  .reviews__foot { grid-column: 2; }

  /* THE SCORE IS THE LEFT COLUMN'S MONUMENT, AND IT TRAVELS (pass 39). The
     section is ~1.4 screens tall since its quotes became rows, and a
     heading alone above a screen of white read as a hole — the reference
     gets away with an empty left half because its sections are one screen
     tall. The head sticks under the header while the quotes pass. */
  .reviews__head { position: sticky; top: calc(var(--header-h) + var(--s30)); }
  .reviews__rating {
    margin-top: var(--s40);
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    align-items: center;
    column-gap: var(--s15);
    row-gap: var(--s5);
  }
  .reviews__rating .rating__score {
    grid-area: 1 / 1 / 3 / 2;
    font-family: var(--font-head);
    font-weight: var(--head-weight);
    font-size: calc(56px * var(--head-scale));
    line-height: 0.86;
  }
  .reviews__rating .rating__mark  { grid-area: 1 / 2 / 2 / 3; width: 20px; height: 20px; align-self: end; }
  .reviews__rating .rating__stars { grid-area: 1 / 3 / 2 / 4; align-self: end; justify-self: start; }
  .reviews__rating .rating__count { grid-area: 2 / 2 / 3 / 4; align-self: start; }
}


/* --- VOOR & NA (pass 62–65) ---------------------------------------------------
   Visual proof after the manifesto, from the owner's concept (pass 65): one
   tall before/after that opens outward on scroll, then its file — the case
   number, the car, three facts with icons, the gold link — on the photo's
   dark left side from 1024, under the photo below that. Arrows bottom-right.
   No visible head, no rail, no cards. */
.cases {
  position: relative;
  z-index: 1;
  background: var(--ground);
  padding-block: var(--pad-top) var(--pad-bottom);
}
/* THE FRAME SITS IN THE PAGE GUTTER (pass 74, owner: a white line showed
   on its left edge at full width). Not max-width centred: centred in the
   container the frame sat at x.5 at 1920 and showed a light seam. The
   gutter (--pad-x) is whole pixels, so both edges are too. */
.cases > .container { max-width: none; padding-inline: var(--pad-x); }

.case { position: relative; }

/* THE FRAME owns the expand (clip-path); the photographs inside own the
   stretch (--zoom). The compare keeps its own box for the drag maths. */
.case__frame { position: relative; }
.motion-ready .case__frame { clip-path: inset(20% 20% round 2px); }
/* The page's text edges, for the file and the arrows over a full-width frame. */
/* Inside the frame: how far the page's text edge is from the frame's edge
   (100% is the frame; the gutter is already outside it). */
.cases { --edge: max(0px, (100% + 2 * var(--pad-x) - var(--container)) / 2); }

/* THE COMPARE. The before is clipped to the left of the handle (JS writes
   the inset). The handle is a white hairline with a black knob on the gold
   edge — the site's black button. */
.compare {
  --zoom: 1;
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #141414;
  cursor: ew-resize;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.compare__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: 58% 50%;
  transform: scale(var(--zoom));
  pointer-events: none;
  -webkit-user-drag: none;
}
.compare__before { position: absolute; inset: 0; clip-path: inset(0 50% 0 0); }
/* PLACEHOLDER: no real "before" yet — the after-photo dulled, dusted and
   flattened. Off per case once it has a `before` (CONTENT.cases). */
.compare__before[data-sim] .compare__img {
  filter: saturate(0.45) brightness(0.72) contrast(0.8) sepia(0.22) blur(0.6px);
}
.compare__before[data-sim]::after {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(120% 90% at 40% 45%, rgba(150, 140, 120, 0.16), rgba(60, 55, 50, 0.3));
}
/* Minimal labels in the top corners: tracked capitals, no box. */
.compare__tag {
  position: absolute; top: 14px;
  z-index: 2;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.7);
  pointer-events: none;
}
.compare__tag--before { left: 14px; }
.compare__tag--after { right: 14px; }
.compare__handle {
  position: absolute; top: 0; bottom: 0; left: 50%;
  z-index: 3;
  width: 52px; margin-left: -26px;
  display: flex; align-items: center; justify-content: center;
  outline: none;
}
.compare__handle::before {
  content: '';
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1px; margin-left: -0.5px;
  background: rgba(255, 255, 255, 0.9);
}
.compare__knob {
  position: relative;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ground);
  box-shadow: inset 0 0 0 1px var(--accent-line), 0 6px 18px rgba(0, 0, 0, 0.45);
  transition: transform 0.4s var(--ease-spring), background-color var(--dur-hover) var(--ease-spring);
}
.compare__knob svg {
  width: 20px; height: 20px;
  fill: none; stroke: var(--text); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.compare.is-dragging .compare__knob { transform: scale(0.92); }
@media (hover: hover) { .compare:hover .compare__knob { background: #141414; } }
.compare__handle:focus-visible .compare__knob { outline: 2px solid var(--accent); outline-offset: 3px; }

/* The dark side the file sits on — from 1024 only. */
.case__shade { display: none; }

/* THE FILE. Below 1024: a panel under the photo. */
.case__file {
  padding: var(--s30) 0 0;
  display: grid;
  gap: var(--s30);
}
.case__count {
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.case__word { white-space: nowrap; }
.case__car {
  margin: var(--s10) 0 0;
  max-width: 14ch;
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  font-size: 24px;
  line-height: 1.15;
  letter-spacing: var(--track-heading);
}
/* Three facts, each an icon, a small label and the value (owner's sketch). */
.case__facts {
  margin: var(--s25) 0 0;
  display: grid;
  gap: var(--s15);
}
.case__fact {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  column-gap: var(--s15);
  align-items: center;
}
.case__icon {
  grid-row: 1 / span 2;
  width: 24px; height: 24px;
  fill: none; stroke: var(--text-soft); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.case__fact dt {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* The price a step up (pass 68): the last fact is "Vanaf". */
.case__fact:last-child dd { font-size: 22px; letter-spacing: 0.01em; }
.case__fact dd {
  margin: 2px 0 0;
  font-size: var(--fs-subhead);
  line-height: 1.3;
  color: var(--text);
}
/* The small gold link (owner, pass 62). */
.case__link {
  justify-self: start;
  display: inline-flex; align-items: center; gap: 10px;
  padding-bottom: 4px;
  font-size: var(--fs-subhead);
  color: var(--accent-soft);
  box-shadow: inset 0 -1px 0 0 var(--accent-line);
}
.case__arrow { transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) {
  .case__link:hover { box-shadow: inset 0 -1px 0 0 var(--accent); }
  .case__link:hover .case__arrow { transform: translateX(4px); }
}

/* THE ARROWS with the counter between. Below 1024 they share the link's line. */
.case__nav {
  position: absolute; right: 0; bottom: -10px;
  display: flex; align-items: center; gap: var(--s10);
}
/* Solid and clear on any part of the photo (pass 68): the page's black, a
   bright edge, white line arrows; the amber edge of the slider's knob on
   hover. */
.case__step {
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border: 0; border-radius: var(--radius-control);
  background: rgba(8, 8, 8, 0.94);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.78), 0 6px 18px rgba(0, 0, 0, 0.45);
  color: var(--text);
  cursor: pointer;
  transition: box-shadow var(--dur-hover) var(--ease-spring), color var(--dur-hover) var(--ease-spring);
}
.case__step svg {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
@media (hover: hover) {
  .case__step:hover { box-shadow: inset 0 0 0 1px var(--accent), 0 6px 18px rgba(0, 0, 0, 0.45); color: var(--accent-soft); }
}
.case__step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Pre-state for the file's entrance (casesSequence). */
.motion-ready [data-case-reveal] { opacity: 0.001; filter: blur(2px); transform: translateY(10px); }


@media (min-width: 768px) {
  .compare { aspect-ratio: 16 / 9; }
  .compare__tag { top: 20px; }
  .compare__tag--before { left: 20px; }
  .compare__tag--after { right: 20px; }
  .compare__knob { width: 52px; height: 52px; }
  .case__car { font-size: 30px; }
}

/* From 1024: the photo fills the screen and the file sits on its dark left. */
@media (min-width: 1024px) {
  .compare {
    aspect-ratio: auto;
    /* The full screen height (pass 68, owner); the labels clear the header. */
    height: max(560px, 100vh);
    height: max(560px, 100svh);
  }
  .compare__tag { top: calc(var(--header-h) + var(--s20)); }
  .case__shade {
    display: block;
    position: absolute; inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
      /* A RAMP, NOT A WASH (pass 68, owner, after the concept): near black at
         the very left, falling steeply through the words' column, gone by
         60% — plus a soft pool behind the words themselves, so the end of a
         long title still reads. */
      radial-gradient(ellipse 34% 46% at 18% 50%, rgba(8, 8, 8, 0.5) 0%, rgba(8, 8, 8, 0) 100%),
      linear-gradient(90deg,
        rgba(8, 8, 8, 0.97) 0%, rgba(8, 8, 8, 0.94) 8%,
        rgba(8, 8, 8, 0.86) 16%, rgba(8, 8, 8, 0.72) 23%,
        rgba(8, 8, 8, 0.54) 30%, rgba(8, 8, 8, 0.36) 37%,
        rgba(8, 8, 8, 0.20) 44%, rgba(8, 8, 8, 0.08) 51%,
        rgba(8, 8, 8, 0) 60%),
      linear-gradient(0deg, rgba(8, 8, 8, 0.45) 0%, rgba(8, 8, 8, 0) 18%);
  }
  .motion-ready .case__shade { opacity: 0; }
  /* Over the handle's layer, but only the link takes the pointer: dragging
     across the words still moves the compare. */
  .case__file {
    position: absolute;
    z-index: 4;
    /* The manifesto's text edge, or clear of the left arrow (20 + 48 + 24) where that is further in. */
    top: 0; bottom: 0; left: max(calc(var(--edge) + 1px + var(--s25)), 92px);
    width: min(28%, 400px);
    padding: 0;
    align-content: center;
    gap: var(--s50);   /* the link set apart from the facts (pass 68) */
    pointer-events: none;
  }
  .case__link { pointer-events: auto; }
  .case__car { font-size: clamp(28px, 2.3vw, 42px); line-height: 1.1; }
  .case__facts { margin-top: var(--s30); gap: var(--s20); }
  .case__fact dd { font-size: 18px; }
  /* ONE ARROW EACH SIDE, AT MID-HEIGHT (pass 105–106, desktop only): the
     nav is a layer the photo's size with the arrows at its ends; only the
     buttons take the pointer, so a drag still works. The file keeps clear
     of the left arrow (see .case__file's left). */
  .case__nav {
    top: 0; left: 0; right: 0; bottom: auto;
    z-index: 4;
    height: max(560px, 100svh);
    justify-content: space-between;
    padding: 0 20px;
    pointer-events: none;
  }
  .case__nav .case__step { pointer-events: auto; }
}

/* --- FAQ ------------------------------------------------------------------
   The second light section. It needs no colour of its own. */
.faq {
  position: relative;
  z-index: 1;
  padding-block: var(--pad-top) var(--pad-bottom);
}

.faq__list {
  margin-top: var(--s30);
  /* No line over the list (pass 34, owner); the rows draw their own. */
  border-bottom: 1px solid var(--hairline);
  max-width: 760px;
}

/* The hairline sits ABOVE each row as an element rather than below it as a
   border, so it can draw itself from the left. The list's own border-bottom
   closes the last row. */
.faq__item { position: relative; }
.faq__rule {
  display: block;
  height: 1px;
  background: var(--hairline);
  transform-origin: 0% 100%;
}
.faq__item:first-child .faq__rule { display: none; }

.faq__ask {
  margin-top: var(--s40);
  display: grid;
  gap: var(--s10);
}
.faq__ask-label {
  font-size: var(--fs-subhead);
  line-height: 1.3;
  color: var(--text);
}
/* The box and the send side by side; stacked on a phone. */
.faq__ask-row { display: grid; gap: var(--s10); }
.faq__ask-send { padding-block: 13px; padding-inline: var(--s20); }
.faq__ask-send .btn__icon { width: 18px; height: 18px; }
@media (min-width: 768px) {
  .faq__ask-row { grid-template-columns: minmax(0, 1fr) auto; }
}

.faq__q { margin: 0; font: inherit; font-weight: 400; }

/* The whole row is the control, so the target is the full width at any size —
   a 22px chip is not a tap target on a phone. */
.faq__toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s20);
  padding-block: var(--s20);
  text-align: left;
  color: var(--text);
  transition: color var(--dur-hover) var(--ease-spring);
}
.faq__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.faq__q-text {
  font-size: 17px;   /* pass 38: the reference sets its questions a size up from the body (15 before) */
  font-weight: 400;
  line-height: 1.3;
  text-wrap: pretty;
}

/* The height is a 0fr → 1fr GRID ROW, not a max-height — the only way to
   animate to "however tall the content happens to be". */
.faq__a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.46s var(--ease-spring);
}
.faq__a-in { overflow: hidden; }
.faq__a p {
  padding-bottom: var(--s20);
  padding-right: var(--s40);
  font-size: var(--fs-micro);
  line-height: 1.5;
  color: var(--text-dim);
  text-wrap: pretty;
  max-width: 60ch;
}
.faq__item.is-open .faq__a { grid-template-rows: 1fr; }

/* THE OPEN ROW INVERTS — §4's fill-wipe primitive used as a state instead of a
   hover, unrolling downward with the answer. The fill is a LITERAL #0A0A0A:
   inside .section--light --ground is #FFFFFF, which is how the inverted review
   card once rendered white on white and vanished. */
.faq__item::before {
  content: '';
  position: absolute;
  inset: 0 calc(var(--s10) * -1);
  background: #0A0A0A;
  border-radius: var(--radius-sm);
  transform: scaleY(0);
  transform-origin: 0 0;
  transition: transform 0.34s var(--ease-spring);
}
.faq__item.is-open::before { transform: scaleY(1); }
/* The fill is the only positioned child, so everything else needs a layer. */
.faq__toggle, .faq__a { position: relative; z-index: 1; }

/* Tokens re-declared for the subtree, the same way .section--light does it, so
   a new child inside a row needs no rule of its own. */
.faq__item.is-open {
  --text:      #FFFFFF;
  --text-soft: #F5F5F5;
  --text-dim:  #A3A3A3;
  --hairline:  #292929;
}
/* Explicit, because the DELAYS are the whole point: the ink flips only once
   the black has swept past it, and on the way back the black waits for the ink.
   Delay comes from the destination state, so the closed rule owns the close. */
.faq__item .faq__toggle,
.faq__item .faq__a p { transition: color 0.16s linear 0.2s; }
.faq__item.is-open .faq__toggle { color: #FFFFFF; transition-delay: 0.12s; }
.faq__item.is-open .faq__a p { color: #979797; transition-delay: 0.12s; }

/* A hairline ring, not the reference's filled accent disc: eight orange discs
   down a white column is far more accent than this site spends anywhere. */
.faq__mark {
  --plus-inset: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--hairline);
  transition: transform 0.46s var(--ease-spring),
              box-shadow var(--dur-hover) var(--ease-spring);
}
/* + becomes ×. The reference rotates its chip 90° between two 45° states;
   rotating the mark itself by 45° is the same gesture with one transform. */
.faq__item.is-open .faq__mark {
  transform: rotate(45deg);
  box-shadow: inset 0 0 0 1px var(--accent-line);
}

@media (hover: hover) {
  .faq__toggle:hover .faq__mark { box-shadow: inset 0 0 0 1px var(--accent-line); }
}

@media (min-width: 768px) {
  .faq__list { margin-top: var(--s40); }
  .faq__toggle { padding-block: var(--s25); }
}

@media (min-width: 1024px) {
  /* THE SPLIT (pass 38): heading left, the list and the ask right, from the
   page's centre line — the reviews' axis and the reference's. */
  .faq .container {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
  }
  .faq__head { grid-column: 1; grid-row: 1 / span 2; }
  .faq__list, .faq__ask { grid-column: 2; }
  .faq__list { margin-top: 0; max-width: none; }
  /* The reference's 22px question (15–18 before pass 38). */
  .faq__q-text { font-size: 22px; }
  /* IT TRAVELS WITH THE ROWS. Eight rows are taller than the heading, so the
     column under it was ~600px of empty white — the same hole the reviews had,
     and having both sections solve it the same way is what read as
     copy-pasted. The reviews' score is static; this moves. */
  .faq__head {
    position: sticky;
    top: calc(var(--header-h) + var(--s30));
  }
  .faq__toggle { padding-block: var(--s30); }
}


/* --- OFFERTE --------------------------------------------------------------
   Black again, and the last thing before the footer. THE FORM IS THE ASK
   (pass 24, owner's call): five fields, then "Stuur via WhatsApp" (primary)
   and "Stuur via e-mail" (secondary), both carrying the answers. The photo
   request is a note under the buttons — it matters for lakcorrectie and
   coating, not for the rest. */
.offerte {
  position: relative;
  z-index: 1;
  background: var(--ground);
  padding-block: var(--pad-top) var(--pad-bottom);
}

.offerte__lead {
  margin-bottom: var(--s30);
  max-width: 34ch;
  font-size: var(--fs-subhead);
  font-weight: 300;
  line-height: 1.45;
  color: var(--text-soft);
  text-wrap: pretty;
}

.offerte__call {
  margin-top: var(--s20);
  font-size: var(--fs-caption);
  line-height: var(--lh-body);
  color: var(--text-dim);
}
.offerte__phone {
  color: var(--text-soft);
  box-shadow: inset 0 -1px 0 0 var(--hairline);
  padding-bottom: 2px;
  transition: box-shadow var(--dur-hover) var(--ease-spring),
              color var(--dur-hover) var(--ease-spring);
}
.offerte__phone:hover { color: var(--text); box-shadow: inset 0 -1px 0 0 var(--accent-line); }

.offerte__aside { margin-top: var(--s30); }

.offerte__form {
  display: grid;
  gap: var(--s15);
}

.field { display: grid; gap: 6px; margin: 0; }

.field__label {
  font-size: var(--fs-caption);
  line-height: var(--lh-body);
  color: var(--text-dim);
}

/* Square, like every other surface on this page. --surface rather than
   transparent: an outlined-only input on a black ground reads as decoration
   until you tap it, and this form is already the quiet half of the section. */
.field__input {
  width: 100%;
  padding: 11px var(--s15);
  font: inherit;
  font-size: 16px;   /* under 16px iOS zooms into the field (14 until pass 49) */
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  transition: border-color var(--dur-hover) var(--ease-spring);
}
.field__input:hover { border-color: var(--text-dim); }
.field__input:focus {
  outline: none;
  border-color: var(--accent);
}
.field__input:focus-visible { outline: 1px solid var(--accent-line); outline-offset: 1px; }

/* The native arrow is drawn in the OS's own colour and is invisible on a dark
   field in most browsers, so it is replaced with a chevron of our own. */
.field__select {
  appearance: none;
  padding-right: var(--s30);
  background-image: linear-gradient(45deg, transparent 50%, var(--text-dim) 50%),
                    linear-gradient(135deg, var(--text-dim) 50%, transparent 50%);
  background-position: calc(100% - 17px) calc(50% + 1px), calc(100% - 12px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
/* Chrome paints the open list with the OS palette unless the option itself
   carries a colour. */
.field__select option { color: var(--text); background: var(--surface); }

/* The two ways to send. WhatsApp is the filled one; e-mail the outlined. */
.offerte__send {
  margin-top: var(--s5);
  display: flex; flex-wrap: wrap;
  gap: var(--s10);
}
.offerte__send .btn {
  padding-block: 13px;
  padding-inline: var(--s20);
  font-size: var(--fs-micro);
}
/* Two full-width rows on a phone — side by side they wrapped unevenly. */
@media (max-width: 767px) {
  .offerte__send .btn { flex: 1 1 100%; }
}
.offerte__send .btn__icon { width: 18px; height: 18px; }
/* Below 1024: WhatsApp and "Verstuur per e-mail"; from 1024 one "Verstuur
   aanvraag" with the QR beside the form (pass 53). */
.offerte__send-desk { display: none !important; }
@media (min-width: 1024px) {
  .offerte__wa, .offerte__mail { display: none !important; }
  .offerte__send-desk { display: inline-flex !important; }
}
.offerte__qr { display: none; }
@media (min-width: 1024px) {
  .offerte__qr { margin-top: var(--s40); display: flex; align-items: center; gap: var(--s20); }
  .offerte__qr .qr { width: 180px; flex: none; }   /* 120 until pass 74 (owner) */
}
.offerte__qr-title { font-size: var(--fs-body); color: var(--text); }
.offerte__qr-text { margin-top: 4px; font-size: var(--fs-micro); color: var(--text-dim); }

/* --- THE CONTACT FORM'S PARTS (pass 53) ---------------------------------- */
.field__textarea { resize: vertical; min-height: 104px; line-height: 1.45; }
.check {
  display: grid; grid-template-columns: 18px 1fr; gap: var(--s10);
  align-items: start;
  font-size: var(--fs-micro); line-height: 1.4; color: var(--text-dim);
  cursor: pointer;
}
.check__box { width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--accent); }
.contact-status { font-size: var(--fs-micro); line-height: 1.45; color: var(--text-soft); }
.contact-status:empty { display: none; }
.contact-status { margin-top: var(--s15); }
.contact-status[data-tone="done"] { font-size: var(--fs-subhead); color: var(--text); }
.contact-status[data-tone="warn"],
.contact-status[data-tone="error"] { padding-left: var(--s10); box-shadow: inset 2px 0 0 var(--accent-line); }
/* A QR is dark on light or a phone will not read it: black modules on a white tile. */
.qr { padding: 10px; background: #FFFFFF; border-radius: var(--radius-sm); line-height: 0; }
.qr svg { width: 100%; height: auto; display: block; }

.offerte__formnote {
  font-size: var(--fs-caption);
  line-height: 1.45;
  color: var(--text-dim);
  text-wrap: pretty;
}

@media (min-width: 768px) {
  .offerte__form { grid-template-columns: 1fr 1fr; column-gap: var(--s20); }
  /* .field--wide is written by the renderer, not matched with :nth-child or
   :has — the fields come from CONTENT, and an unsupported :has() invalidates
   its whole selector list, which would take the buttons with it. */
  .field--wide,
  .offerte__send,
  .offerte__formnote { grid-column: 1 / -1; }
}

/* THE SPLIT (pass 38, the reference's "Start your build"): the heading
   and, under it, the timeline in the left half; the lead and the form from
   the page's centre line. Pass 24–37 had the heading across the top and
   fixed 640 + 400 columns from the left. The phone keeps heading → lead →
   form → steps. */
@media (min-width: 1024px) {
  .offerte__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas:
      "head  aside"
      "steps aside";
    row-gap: 80px;
    align-items: start;
  }
  .offerte__head  { grid-area: head; }
  /* Dropped by the eyebrow's line and the title's top margin, so the
     lead's first line sits level with the heading (pass 40 — it sat at the
     eyebrow's height and read as mismatched). */
  .offerte__aside { grid-area: aside; margin-top: calc(var(--fs-micro) * var(--lh-body) + var(--s15)); max-width: 680px; }
  .offerte__steps { grid-area: steps; margin-top: 0; max-width: 420px; }
  .offerte__form { grid-template-columns: 1fr 1fr; }
}


/* --- FOOTER --------------------------------------------------------------- */

.footer {
  position: relative;
  z-index: 1;
  background: var(--ground);
  padding-bottom: var(--s25);
}
.footer__top { padding-top: var(--s15); }
.footer__logo img { height: 40px; width: auto; }

.footer__address { margin-top: var(--s25); font-size: var(--fs-body); line-height: var(--lh-body); }

.footer__hours {
  margin-top: var(--s10);
  display: grid; grid-template-columns: auto auto;
  justify-content: start;
  column-gap: var(--s15); row-gap: var(--s5);
  font-size: var(--fs-micro); line-height: var(--lh-body);
  color: var(--text-dim);
}
.footer__hours dd { margin: 0; color: var(--text); }

/* The route link where the Google embed was (pass 49): the embed set
   cookies before consent, which needs a banner. */
.footer__map { margin-top: var(--s30); }
.footer__route { gap: var(--s10); height: 44px; padding: 0 var(--s20); }

.footer__cols {
  margin-top: var(--s50);
  display: grid; grid-template-columns: 2fr 1fr; gap: var(--s20);
}
.footer__heading { font-size: var(--fs-micro); line-height: var(--lh-body); color: var(--text-dim); }
.footer__list { margin-top: var(--s25); display: grid; gap: var(--s10); }
.footer__link {
  font-size: var(--fs-micro); line-height: var(--lh-body);
  transition: color var(--dur-hover) var(--ease-spring);
}
.footer__link:hover { color: var(--accent-hover); }

.footer__socials { margin-top: var(--s25); display: flex; gap: var(--s20); }
.footer__social {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px;
}
.footer__social img {
  width: 100%; height: 100%; object-fit: contain;
  filter: var(--ink-dim);
  transition: filter var(--dur-hover) var(--ease-spring);
}
.footer__social:hover img { filter: var(--ink-text); }
/* Font Awesome's mark fills 70% of its box; the others fill theirs. */
.footer__social img[src$="whatsapp.svg"] { transform: scale(1.43); }

.footer__map + .rule { margin-top: var(--s40); }

.footer__legal {
  padding-top: var(--s20);
  display: flex; justify-content: space-between; gap: var(--s15);
  font-size: var(--fs-legal); font-weight: 300;
  line-height: var(--lh-heading); color: var(--text-dim);
}

@media (min-width: 768px) {
  .footer { padding-bottom: var(--s40); }
  .footer__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
    gap: var(--s20);
  }
  .footer__top { padding-top: var(--s25); }
  .footer__address { margin-top: var(--s20); }
  .footer__cols {
    margin-top: var(--s25);
    grid-template-columns: 1fr 1fr;
  }
}


/* --- FLOATING CALL / WHATSAPP --------------------------------------------
   Icons only, bottom-right, and only once the hero is behind you — otherwise
   they crowd the contact card sitting in that same corner. */

.floaties {
  position: fixed;
  right: var(--pad-x);
  bottom: var(--s25);
  z-index: 60;
  display: grid;
  gap: var(--s10);
  opacity: 0;
  transform: translateY(14px) scale(0.9);
  pointer-events: none;
  transition: opacity 0.5s var(--ease-spring),
              transform 0.5s var(--ease-spring);
}

.floaties[data-shown="1"] {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/* From 1024 the contact card is fixed to this corner for the whole page
   (pass 23), so the pair would be the same two actions twice. Below that
   the header has only the phone, and the card scrolls away with the hero. */
/* Retired on every width (pass 34): the contact card is fixed to this corner
   on a phone too. The markup and floatiesSequence's toggling stay. */
.floaties { display: none; }

.floaties .btn--icon { width: 48px; height: 48px; }
/* Near-solid, not the 0.72 it was. [data-ground] is measured at the HEADER's
   edge and these sit in the opposite corner, so at the dark/white seam the two
   disagree and a 72% black chip over white goes muddy brown. */
.floaties .btn--icon .btn__icon { width: 19px; height: 19px; }
/* The outlined button reads as the quieter of the pair, so its mark carries
   the gold and a little more size to hold its own next to the filled one. */
.floaties #float-wa .btn__icon { width: 23px; height: 23px; filter: var(--ink-accent); }
.floaties #float-wa:hover .btn__icon { filter: var(--ink-ground); }


/* --- THE CONTACT WINDOW (pass 89, the owner's mockup) ---------------------
   From 1024, every contact button opens it (wireContact). One column: his
   face and "Neem contact op", the form, OF, the WhatsApp QR. It grows out of
   the button that opened it: #contact-morph is a full-screen panel in the
   box's colour whose clip-path GSAP opens from the button's rectangle to the
   box's (initContactWindow). The page behind does not scroll. */
.contact {
  --contact-bg: #121212;
  position: fixed; inset: 0;
  z-index: 100;   /* over the header (50) and the card (60) */
  display: grid; place-items: center;
  padding: var(--s20);
}
.contact[hidden] { display: none; }
.contact__backdrop {
  position: absolute; inset: 0;
  background: rgba(5, 5, 5, 0.86);   /* 0.72 until pass 90: more contrast for the window */
  opacity: 0;
  transition: opacity 0.45s var(--ease-spring);
}
.contact.is-open .contact__backdrop { opacity: 1; }
.contact__morph {
  position: fixed; inset: 0;
  background: var(--contact-bg);
  opacity: 0;
  pointer-events: none;
}
:root.has-contact { overflow: hidden; }
/* The card is the window while it is open (the morph grew out of it). */
:root.contact-from-card .hero__card { visibility: hidden !important; }

.contact__box {
  position: relative;
  width: min(680px, 100%);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  padding: var(--s25) var(--s30) var(--s15);
  background: var(--contact-bg);
  border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--hairline);
  opacity: 0;
}
.contact__close {
  position: absolute; top: var(--s10); right: var(--s10);
  width: 44px; height: 44px;
  display: grid; place-items: center;
  color: var(--text-dim);
  transition: color var(--dur-hover) var(--ease-spring);
}
.contact__close:hover { color: var(--text); }
.contact__close svg { width: 22px; height: 22px; }

.contact__head {
  display: grid; grid-template-columns: auto 1fr;
  align-items: center; gap: var(--s20);
  padding-right: var(--s40);   /* clear of the × */
}
.contact__avatar { width: 56px; height: 56px; border-radius: var(--radius-sm); }
.contact__title {
  margin: 0;
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  font-size: 22px;
  line-height: var(--lh-heading);
}
.contact__sub { margin-top: 4px; font-size: var(--fs-body); color: var(--text-dim); }

.contact__form {
  margin-top: var(--s25);
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 12px var(--s20);
  align-items: start;
}
.contact__form .field--wide,
.contact__send,
.contact__reply { grid-column: 1 / -1; }
.contact__form .field__label { font-size: var(--fs-micro); color: var(--text-soft); }
.field__opt { color: var(--text-dim); }
/* Fields a step lighter than the window (pass 90, owner: readability). */
.contact__form .field__input { padding: 10px var(--s15); background: #1C1C1C; border-color: #303030; }
.contact__form .field__input:hover { border-color: #5A5A5A; }
.contact__form .field__input:focus { border-color: var(--accent); }
.contact__form .field__input::placeholder { color: #7C7C7C; }
.contact__form .field__textarea { min-height: 64px; }
.contact__tel-note {
  display: grid; grid-template-columns: auto 1fr; gap: 8px;
  align-items: start;
  margin-top: 2px;
  font-size: var(--fs-caption); line-height: 1.45;
  color: var(--text-dim);
}
/* The WhatsApp mark white in a green disc, as on the pill. */
.contact__tel-mark {
  display: grid; place-items: center;
  width: 18px; height: 18px; margin-top: 1px;
  border-radius: 50%;
  background: var(--wa-green);
}
.contact__tel-mark img { width: 12px; height: 12px; filter: var(--ink-text); }
/* The counter sits on the label's line, right. */
.contact__form .field { position: relative; }
.contact__count { position: absolute; top: 1px; right: 0; font-size: var(--fs-caption); color: var(--text-dim); }

/* THE TOPIC CHIPS: one at a time, none chosen at first (owner). */
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  height: 38px; padding: 0 12px;
  font: inherit; font-size: 13px;
  color: var(--text-soft);
  background: #1C1C1C;
  border-radius: 999px;   /* pills, the owner's mockup (pass 90) */
  box-shadow: inset 0 0 0 1px #303030;
  transition: color var(--dur-hover) var(--ease-spring),
              box-shadow var(--dur-hover) var(--ease-spring);
}
.chip__icon { width: 16px; height: 16px; flex: none; }
.chip:hover { box-shadow: inset 0 0 0 1px #5A5A5A; }
.chip[aria-pressed="true"] {
  color: var(--accent);
  background: rgba(216, 137, 36, 0.08);
  box-shadow: inset 0 0 0 1px var(--accent-line);
}
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.contact__send {
  height: 50px; gap: var(--s15);
  font-size: var(--fs-body); font-weight: 600;
}
.contact__send-arrow { font-size: 16px; transition: transform var(--dur-hover) var(--ease-spring); }
@media (hover: hover) { .contact__send:hover .contact__send-arrow { transform: translateX(4px); } }
.contact__reply,
.contact__note {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-size: var(--fs-caption); line-height: 1.45;
  color: var(--text-dim);
}
.contact__reply { margin-top: -4px; }
.contact__reply-icon, .contact__note-icon { width: 15px; height: 15px; flex: none; }

/* OF: the word on a hairline across. */
.contact__or {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--s15);
  margin: var(--s10) 0;
  font-size: var(--fs-caption); letter-spacing: 0.12em;
  color: var(--text-dim);
}
.contact__or::before, .contact__or::after { content: ''; height: 1px; background: var(--hairline); }

.contact__wa {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: var(--s20);
  padding: 12px var(--s15);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.contact__qr { position: relative; width: 96px; padding: 6px; }
.contact__qr-logo {
  position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--wa-green);
  box-shadow: 0 0 0 3px #FFFFFF;
}
.contact__qr-logo img { width: 18px; height: 18px; filter: var(--ink-text); }
.contact__wa-text { font-size: var(--fs-micro); line-height: 1.45; color: var(--text-dim); }
.contact__wa-title { margin-bottom: 4px; font-size: var(--fs-body); font-weight: 600; color: var(--text); }
.contact__web { display: grid; justify-items: center; }
.contact__web-btn { height: 44px; padding: 0 var(--s20); box-shadow: inset 0 0 0 1px var(--accent-line); }
.contact__note { margin-top: 12px; }
.contact__note a { color: var(--text-soft); box-shadow: inset 0 -1px 0 0 var(--hairline); }
.contact__note a:hover { box-shadow: inset 0 -1px 0 0 var(--accent); }

@media (prefers-reduced-motion: reduce) {
  :root:not(.motion-forced) .contact__backdrop { transition: none; }
}

/* --- PRIVACY STATEMENT (privacy.html, pass 51) ---------------------------- */
.legal {
  max-width: 68ch;
  margin: 0 auto;
  padding: var(--s50) var(--pad-x) var(--s100);
}
.legal__back { font-size: var(--fs-micro); color: var(--text-dim); }
.legal__back:hover { color: var(--text); }
.legal__title {
  margin: var(--s40) 0 0;
  font-size: calc(var(--fs-section) * var(--head-scale));
  font-weight: var(--head-weight);
  line-height: var(--lh-heading);
}
.legal__meta { margin-top: var(--s10); font-size: var(--fs-micro); color: var(--text-dim); }
.legal h2 { margin: var(--s40) 0 0; font-size: var(--fs-subhead); font-weight: 500; }
.legal p { margin-top: var(--s10); font-size: var(--fs-body); line-height: 1.6; color: var(--text-soft); }
.legal a:not(.legal__back) { box-shadow: inset 0 -1px 0 0 var(--hairline); }

/* --- REDUCED MOTION ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  :root:not(.motion-forced) .word,
  :root:not(.motion-forced) [data-reveal],
  :root:not(.motion-forced) .cert,
  :root:not(.motion-forced) .header__logo,
  :root:not(.motion-forced) .header__actions,
  :root:not(.motion-forced) .hero__corner,
  :root:not(.motion-forced) .hero__corner-title,
  :root:not(.motion-forced) .hero__corner-link,
  :root:not(.motion-forced) .hero__card {
    opacity: 1; filter: none; transform: none;
  }
  :root:not(.motion-forced) .rule { transform: none; }
  :root:not(.motion-forced) .werk__col { animation: none; }
  :root:not(.motion-forced) .werk__lane,
  :root:not(.motion-forced) .werk__ig,
  :root:not(.motion-forced) #review-list .review,
  :root:not(.motion-forced) #faq-list .faq__item,
  :root:not(.motion-forced) .step,
  :root:not(.motion-forced) .faq__item .faq__rule {
    opacity: 1; filter: none; transform: none;
  }
  :root:not(.motion-forced) .tier,
  :root:not(.motion-forced) #prices-note,
  :root:not(.motion-forced) #prices-panel,
  :root:not(.motion-forced) .prices__panel-media,
  :root:not(.motion-forced) .prices__inner { opacity: 1; filter: none; transform: none; }
  /* The fill wipe lands instantly rather than
     travelling — the state still reads, the motion does not. */
  :root:not(.motion-forced) .faq__item::before { transition: none; }
  :root:not(.motion-forced) .vrule__fill { clip-path: inset(0 0 0 0); }
  :root:not(.motion-forced) .manifesto__text .word { color: var(--text); }
  :root:not(.motion-forced) * { transition-duration: 0.01ms !important; }
}
