/* ============================================================
   PRESTIGE CONSULTING GROUP — Core stylesheet
   "Op maat gemaakt. Uitzonderlijk uitgevoerd."

   Layer 1 of 4:  pcg (tokens + primitives)
                  components (nav, cards, footer …)
                  motion (transitions, kinetics)
                  pricing / configurator (page-specific)
   ============================================================ */

/* ---------- Fonts are requested from the <head>, NOT from here ----------
   There was an @import for Playfair Display + Carlito at this spot. It is gone
   on purpose, and it must not come back as a "safety net".

   An @import is the slowest way to load a font: the browser cannot even see it
   until this stylesheet has downloaded and started parsing, so it adds a whole
   serial round trip in front of the font. Measured on localhost before the fix:
     HTML -> css/pcg.css (1031ms) -> fonts.googleapis.com (1047ms)
          -> fonts.gstatic.com woff2 (1207ms, landing ~1429ms)
     first-contentful-paint: 1460ms
   i.e. no text could paint until a three-hop third-party chain finished. The
   request now lives in tools/sync-shell.js's head(), alongside preconnects to
   both Google hosts, so it starts with the stylesheets rather than behind them.

   Everything in this sheet still degrades to the system fallback rather than
   assuming the webfont arrived — keep it that way. --ff-display and --ff-body
   below both carry a real fallback stack for exactly that reason. */

/* ---------- Tokens ---------- */
:root{
  /* Donkere huid — dark skin */
  --onyx:#0B0B0C;
  --onyx-deep:#070708;
  --houtskool:#141416;
  --houtskool-2:#1A1A1C;
  --goud:#C9A14A;
  --champagne:#E6CF8F;
  --creme-d:#F4ECD8;
  --steen:#918A79;

  /* Lichte huid — light skin */
  --papier:#F8F3E9;
  --creme:#EFE6D3;
  /* SURFACE / RULE FILLS ONLY — 2.69:1 on --papier. Never text. */
  --antiekgoud:#B8902F;
  /* The light-skin gold INK. --goud and --antiekgoud are surface-only;
     both fail AA as text on papier, which is why every .skin-creme rule
     routes --gold-text through here instead (5.51:1 on --papier,
     4.91:1 on --creme). */
  --brons:#7A5E1C;
  --inkt:#1A1610;
  /* SURFACE / RULE FILLS ONLY — 1.64:1 on --papier. Never text. */
  --zand:#CDBF9F;

  /* ---------- The alloy — ONE metal for the whole site ----------
     These six values are verbatim the six addColorStop values in the hero
     seal's radial ramp (js/pcg-hero-canvas.js, the g.addColorStop block:
     #FBF2DC / #F0DEA9 / #E6CF8F / #C9A14A / #96742C / #5A461A). Publishing
     them as tokens is the whole point: the canvas, every button fill and
     every section seam are then provably the same metal instead of three
     near-misses. Ordered dark -> light, the way a light ramp reads.
     These are PIGMENT. --gold-glow / --cast-gold / --halo-gold below stay
     on their rgba literals because those are LIGHT, not metal. */
  --m-0:#5A461A;
  --m-1:#96742C;
  --m-2:#C9A14A;
  --m-3:#E6CF8F;
  --m-4:#F0DEA9;
  --m-5:#FBF2DC;

  /* --gold-grad is a SURFACE gradient: button fills, rules, the progress
     bar, the small diamond bullets. It is never allowed on text — see
     .gold-foil below for why.

     Rebuilt from the alloy above, and this was an ACCESSIBILITY fix rather
     than a taste change. The old ramp ran #9c7a2f -> #8a6a28 at its two
     ends and carried a hard-coded #15110a ink, which MEASURES 3.87:1 on
     #8a6a28 — so the two corners of every filled button, chip, tag and
     language toggle on all 11 pages failed AA. Both end stops are now
     --m-1 #96742C and the ink is --ink-on-gold, which measures 4.53:1
     there and rises across every other stop (8.13:1 on --m-2 #C9A14A,
     12.80:1 on --m-3 #E6CF8F). The highlight also moved 50% -> 42%, so
     the specular peak now sits left of centre under the same light as
     --sheen and --wash, and the metal gained range rather than losing it. */
  --gold-grad:linear-gradient(120deg,var(--m-1) 0%,var(--m-2) 24%,var(--m-3) 42%,var(--m-2) 70%,var(--m-1) 100%);
  /* The ink that sits ON gold. Every filled gold control routes through
     this instead of the old hard-coded #15110a: 4.53:1 on the gradient's
     worst point (--m-1), i.e. AA at every size, and higher everywhere
     else. Do not lighten it — the darkest stop of the ramp is what the
     ratio is measured against, not the middle. */
  --ink-on-gold:var(--onyx);
  --gold-glow:0 0 0 1px rgba(201,161,74,.28), 0 24px 70px -30px rgba(201,161,74,.5);

  /* Gold ink for anything that renders outside a skin — the footer, the
     nav, the intro curtain, the instrument console. Every .skin-*
     overrides it, so this is only ever the dark-ground default. */
  --gold-text:var(--champagne);

  /* ---------- Material ----------
     One light source: high, a little to the left, warm. --sheen is the
     light landing on a surface; --wash is its spill; --lift-* is the
     shadow it casts (a tight contact term plus a wide ambient one, so
     lift reads as light rather than as a default drop-shadow). Cream
     skins re-declare all of these with a warmer, softer cast. */
  --sheen:linear-gradient(180deg,rgba(255,255,255,.055) 0,rgba(255,255,255,0) 38%);
  --sheen-lit:linear-gradient(180deg,rgba(255,255,255,.09) 0,rgba(255,255,255,0) 46%);
  --wash:radial-gradient(118% 80% at 16% -8%,rgba(201,161,74,.13),transparent 64%);
  /* One elevation scale, three steps: 1 = attached to an edge (the
     scrolled bar), 2 = raised out of its own grid (a hovered card),
     3 = floating free of the page (menus, the dock, a lifted image). */
  --lift-1:0 1px 2px rgba(0,0,0,.32), 0 10px 22px -14px rgba(0,0,0,.7);
  --lift-2:0 2px 4px rgba(0,0,0,.32), 0 22px 48px -26px rgba(0,0,0,.66);
  --lift-3:0 3px 8px rgba(0,0,0,.42), 0 48px 104px -40px rgba(0,0,0,.88);
  /* The milled pocket. --lift-* is a thing raised OFF the page; --lip-tl is
     a cell cut INTO it: the top-left inner wall falls into shadow and the
     bottom-right one catches the light, under the same high-left source.
     It is the second half of a treatment the cells already started (they
     all carry --sheen). Two 1px inset hairlines, decoration only.
     Whoever consumes this must keep it in BOTH the base and the hover
     value (box-shadow:var(--lip-tl),var(--lift-2)) or the existing
     transition will animate a shadow in from nothing on hover. */
  --lip-tl:inset 1px 1px 0 rgba(0,0,0,.34), inset -1px -1px 0 rgba(255,255,255,.05);
  --cast-gold:0 14px 34px -16px rgba(201,161,74,.42);
  --halo-gold:0 0 30px rgba(201,161,74,.16);

  /* ---------- The raking hairline ----------
     The site builds most of its structure out of 1px fills of --rule, and
     a flat fill at the same value along its whole length is exactly what a
     PRINTED line looks like. --rake is the same line MACHINED: one 105deg
     gradient whose specular peak sits at 52%, so a hairline catches the
     light once along its length, from the same direction --sheen and the
     hero canvas are lit from. Mean alpha is .17 against --rule's flat .22,
     so pages get quieter, not louder.

     ############  --rake IS A GRADIENT, NOT A COLOUR  ############
     NEVER write `border:1px solid var(--rake)` — a gradient is not a valid
     <color> there and the border silently disappears. --rule and --rule-soft
     stay exactly as they are for that reason: they feed `border:1px solid`
     in ~40 places. --rake / --rake-soft are for BACKGROUND fills only
     (grid gaps, .hairline, 1px pseudo-element rules). The two pairs of
     names are close enough that this will eventually be got wrong, so the
     rule is written here rather than assumed.
     Also: a gradient across a 90px button border is a smear, not a rake —
     only full-width or long runs earn it. */
  --rake:linear-gradient(105deg,rgba(201,161,74,.07) 0%,rgba(201,161,74,.20) 30%,rgba(201,161,74,.34) 52%,rgba(201,161,74,.18) 74%,rgba(201,161,74,.06) 100%);
  --rake-soft:linear-gradient(105deg,rgba(201,161,74,.035) 0%,rgba(201,161,74,.10) 30%,rgba(201,161,74,.17) 52%,rgba(201,161,74,.09) 74%,rgba(201,161,74,.03) 100%);

  /* Graduations, for the .scale primitive (css/instrument-live.css) and the
     process rail. Declared here so there is one tick vocabulary, not three.
     --tick-major is a graphic that CONVEYS a value, so it clears 1.4.11's
     3:1: --m-1 #96742C measures 4.53:1 on --onyx and 4.23:1 on --houtskool
     (and 3.93:1 on --papier, so it survives the cream re-declare too).
     --tick-minor is deliberately BELOW 3:1 — 1.71:1 flattened over onyx —
     and is therefore never the sole carrier of anything; it is texture
     behind the majors, the way a caliper is graduated.
     --needle is the current-value marker: --m-3 #E6CF8F, 12.80:1 on onyx.
     All three are re-declared on the cream skins below, where --m-3 would
     fall to 1.39:1 on --papier. */
  --tick-major:var(--m-1);
  --tick-minor:rgba(201,161,74,.30);
  --needle:var(--m-3);

  /* ---------- The seam ----------
     Every chapter break on this site is currently two flat colours meeting
     on one pixel row. --seam-plate is that row rebuilt as banknote
     engraving: a 168 x 14 tile of the hero seal's own interference figure,
     unrolled from polar to linear. Two harmonic families, 7 cycles and 12
     cycles — N1 and N2 from js/pcg-hero-canvas.js — each drawn twice at a
     half-wavelength phase offset so the four strands braid, plus the two
     straight rules at y=.6 and y=13.4 that are the envelope's dying ends.
     7 and 12 are coprime, so the interference never settles inside the
     tile. Every segment is a `t` (reflected quadratic), so the path is
     hand-checkable and no generator is involved: 14 half-waves x 12px and
     24 half-waves x 7px both span exactly 168px, which is what makes the
     tile repeat without a visible join.
     It is a MASK, so the metal underneath (--seam-ink) is one gradient
     across the whole viewport while the line-work tiles — the seam is lit
     once along its length, not once per tile. */
  --seam-h:14px;
  --seam-ink:linear-gradient(120deg,var(--m-2) 0%,var(--m-4) 16%,var(--m-2) 46%,var(--m-1) 74%,var(--m-0) 100%);
  --seam-plate:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='168' height='14' viewBox='0 0 168 14'%3E%3Cg fill='none' stroke='%23fff' stroke-linecap='butt'%3E%3Cpath stroke-width='1' stroke-opacity='.45' d='M0 .6H168'/%3E%3Cpath stroke-width='1' stroke-opacity='.45' d='M0 13.4H168'/%3E%3Cpath stroke-width='1' stroke-opacity='.6' d='M0 7q6 -4.4 12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0'/%3E%3Cpath stroke-width='1' stroke-opacity='.46' d='M0 7q6 4.4 12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0t12 0'/%3E%3Cpath stroke-width='.9' stroke-opacity='.38' d='M0 7q3.5 -2.4 7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0'/%3E%3Cpath stroke-width='.9' stroke-opacity='.3' d='M0 7q3.5 2.4 7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0t7 0'/%3E%3C/g%3E%3C/svg%3E");

  /* Paper on a plate. A sheet laid on the engraved plate has a lit top cut
     edge and its own underside shading along the bottom. Both are DECOR at
     1px / a soft ramp, never a text pair, so there is no ratio to clear —
     but the cast is deliberately the same warm-open black the --lift-*
     tokens already use on paper, because a neutral or cool cast on cream
     reads as dirt on the sheet rather than as depth.
     --edge-lip is a literal rather than var(--rule) on purpose: --rule is
     only ever declared on a .skin-*, so `--edge-lip:var(--rule)` at :root
     would resolve to the guaranteed-invalid value HERE and stay invalid on
     every descendant, including the skins that do define --rule.
     --edge-shade is only ever CONSUMED on the cream skins (see "Paper on a
     plate" below), because a shadow cast onto --onyx is arithmetically
     invisible — .62 black over #0B0B0C lands on ~#040404. The :root value
     below is therefore a fallback for a cream-less context, not the value
     the design depends on. */
  --edge-lip:rgba(201,161,74,.22);
  --edge-shade:rgba(0,0,0,.62);

  /* ---------- The pairing: Bodoni Moda + Switzer ----------
     Both are SELF-HOSTED from assets/fonts/ and declared in css/fonts.css,
     which the shell head loads first and preloads two faces from. Nothing
     type-related touches a third party any more, which is both the largest
     first-paint win on the site and the tidier GDPR answer for a Belgian
     audience. tools/fetch-fonts.mjs re-fetches them; don't hand-edit
     css/fonts.css.

     WHY THESE TWO, since the file has now carried three answers.
     The pairing was prototyped against Noe Display + GT America, which is
     what pierstone.com runs, and it did look better than what it replaced.
     Both are commercial retail faces licensed per domain, and no licence
     covers prestigeconsulting.be, so they cannot ship here — see
     assets/fonts/LICENSES.md, and do not reintroduce them.
     These are the free, self-hostable faces that get closest:
       Bodoni Moda  a true didone, standing in for Noe Display. High stroke
                    contrast and sharp, unbracketed serifs, which is what
                    makes it sit with the engraved guilloche and the struck
                    monogram where Playfair read softer and rounder.
                    VARIABLE on weight 400-900 and optical size 6-96, in one
                    file per subset, so every display weight the design sets
                    costs a single request — unlike the six static cuts the
                    previous grotesque needed.
       Switzer      the grotesque, standing in for GT America, which it is
                    genuinely close to in proportion and mood. Replaces
                    Carlito, a free Calibri metric-clone that was the
                    weakest thing in the stack.

     A didone's hairlines get thin and fragile below roughly 20px, so
     --ff-display is for display sizes ONLY. Small tracked labels belong to
     --ff-body. When adding a rule, ask which size it renders at, not which
     token looks more important. The scrolling service strip that used to sit
     under the hero was the case that proved it: set in the display face at
     12-15px, a didone's hairlines simply broke. That strip is now gone
     entirely, but the rule it established stands.

     2026-08-11 — BODONI MODA IS OUT. ONE FAMILY, SWITZER, EVERYWHERE.

     It was dropped for a legibility fault the owner spotted on the live page,
     not for taste. Bodoni is a Didone, so extreme stroke contrast is the design
     rather than a defect — but two things followed from it that this site could
     not carry:

       - THE CAPITAL U. Bodoni Moda's U is a thick left stem beside a near
         hairline right stem, so at display sizes it reads as "LI" or as a
         broken glyph. Dutch makes that unavoidable rather than occasional: the
         formal "U" is a one-letter word, and a lone U in a heading was
         illegible. All-caps ("UITGAVE") was just as bad.
       - THE WATERMARK NUMERALS. At a watermark alpha the hairlines vanish
         before the stems, so "04" rendered as "0 1" — a WRONG number, not a
         faint one. Raising opacity did not fix it; measured side by side, at
         .45 the 4 was still ambiguous while the 0 had gone dark enough to fight
         the heading.

     Switzer has near-uniform stroke weight, so both failure modes disappear.
     This also restores the "one family" decision taken on 2026-08-03.

     The two tokens stay TWO tokens on purpose, even though they now resolve to
     the same family. They encode intent at ~60 call sites, and collapsing them
     would erase the only record of which rules are display rules — and would
     make reintroducing a display face a 60-site edit instead of a one-line one.
     Do not "simplify" them away.

     The old rule that --ff-display is for display sizes ONLY no longer has a
     hairline reason behind it, but keep honouring it: it is still the only
     marker of which rules are display rules. */
  --ff-display:'Switzer',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --ff-body:'Switzer',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;

  /* Type scale — fluid, minor-third-ish, optically tuned */
  --fs-micro:clamp(10px,.72vw,11.5px);
  --fs-eyebrow:clamp(10px,.78vw,12px);
  --fs-fine:clamp(12.5px,.9vw,13.5px);
  --fs-body:clamp(15px,1.05vw,16.5px);
  --fs-lead:clamp(17px,1.5vw,21px);
  --fs-h3:clamp(23px,2.4vw,30px);
  --fs-h2:clamp(31px,4.2vw,52px);
  /* 86px -> 72px at the top end, and 6.4vw -> 5.4vw in the middle.
     Two reasons, and the first is not taste:
     Bodoni Moda is a didone, so it carries far more visual weight per point
     than the grotesque this scale was tuned for — 86px of Bodoni shouts where
     86px of Switzer spoke. The scale had been calibrated against the wrong
     kind of face.
     Second, a 6.4vw headline grew faster than the hero column it lives in,
     which is what drove it into the seal (see the hero measure note in
     components.css). 5.4vw keeps the fold composed instead of crowded.
     The h1 -> display ratio is preserved: 96/72 = 1.333. */
  --fs-h1:clamp(38px,5.4vw,72px);
  /* 44px, not 50px, at the min end: h1 -> display is 44/38 = 1.158 there,
     tightening deliberately on small screens where two big steps in a row
     read as a mistake rather than a hierarchy. */
  --fs-display:clamp(44px,7vw,96px);
  /* Apparatus — captions, plate legends, figure legends. Print makes
     apparatus SMALLER, not paler; this site had been doing the opposite. */
  --fs-caption:clamp(11.5px,.86vw,13px);
  /* Ledger figures (prices, durations) and instrument read-outs. One size
     each, so a figure is recognisable as a figure across the whole site. */
  --fs-figure:clamp(30px,3.4vw,46px);
  --fs-readout:clamp(44px,5.4vw,66px);

  /* Tracking — THREE steps, not ten. The sheets had drifted to ten
     different micro-trackings on one idiom (.16 / .18 / .2 / .22 / .24 /
     .26 / .28 / .34em, plus four different negative values on figures).
     Everything now routes through these:
       --ls-btn      uppercase controls  (buttons, the skip link)
       --ls-label    uppercase apparatus (labels, crumbs, caps, tabs)
       --ls-eyebrow  the widest step, section eyebrows only
     --ls-figure is the one NEGATIVE step: a figure is a number, not a
     display heading, so it takes a fraction of the display tracking. */
  --ls-btn:.16em;
  --ls-label:.22em;
  --ls-eyebrow:.34em;
  --ls-figure:-.010em;

  /* Measures. --maxw-prose is running text; these two are the short forms
     that must NOT inherit a 68ch line. */
  --measure-note:34ch;
  --measure-caption:30ch;

  /* Spacing scale */
  --sp-1:.5rem;  --sp-2:.85rem; --sp-3:1.25rem; --sp-4:1.8rem;
  --sp-5:2.6rem; --sp-6:3.6rem; --sp-7:5rem;    --sp-8:7rem;
  /* Four section rhythms, not one. --sp-seam joins two sections that
     share a skin (they are one movement); --sp-section is the normal
     chapter break; --sp-section-loose is reserved for the closing
     address, where the page should visibly slow down. */
  --sp-seam:clamp(40px,4.6vw,78px);
  /* 100px at the floor, not 86px: a 14px engraved seam now sits in the
     bottom of every chapter gap that meets a cream section, and at 86px
     that was 16% of the gap spent on the joint. */
  --sp-section:clamp(100px,12vw,180px);
  --sp-section-tight:clamp(64px,8vw,120px);
  --sp-section-loose:clamp(112px,15vw,232px);
  /* …and the opposite case: a section that ENDS on a full-bleed rule (a
     price list, a card lattice, an accordion) has already drawn its own
     bottom edge. A full --sp-section under a rule is air nothing earned.
     Applied by the :has() rule under "Layout". */
  --sp-section-close:clamp(48px,5.4vw,88px);

  /* Motion */
  --ease:cubic-bezier(.22,1,.36,1);
  --ease-soft:cubic-bezier(.4,0,.2,1);
  --ease-curtain:cubic-bezier(.76,0,.24,1);
  --dur-1:.28s; --dur-2:.45s; --dur-3:.6s; --dur-4:.9s; --dur-5:1.2s;

  --maxw:1280px;
  --maxw-prose:68ch;
  --gutter:clamp(22px,5vw,72px);
  --nav-h:82px;
}

*{margin:0;padding:0;box-sizing:border-box}
/* font-synthesis-style, NOT font-synthesis:none — the latter would also
   disable synthetic BOLD, which is the wanted fallback if one of the six
   woff2 files fails to arrive (and they do: Fontshare 500s on the
   six-weight request often enough that the fallback path is real, not
   theoretical). None of the loaded faces is an italic, so a stray
   font-style:italic anywhere would shear an upright into a fake oblique;
   this forbids the browser from faking the slant at all. */
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-synthesis-style:none;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + 20px)}
body{
  font-family:var(--ff-body);
  /* 400, written honestly. This said 300 for the life of the sheet, but
     300 was never in the Fontshare request, so it always snapped to 400. */
  font-weight:400;
  font-size:var(--fs-body);
  background:var(--onyx);
  color:var(--creme-d);
  overflow-x:hidden;
  line-height:1.62;
}
/* max-width without height:auto is the classic distortion pair: the moment
   author CSS constrains an <img>'s width, the height="" attribute hint keeps
   standing and the image stretches. Every <img> on this site carries real
   width/height attributes (they earn the aspect-ratio hint that prevents
   layout shift), so this rule is what lets them scale instead of skew. */
img{display:block;max-width:100%;height:auto}
a{color:inherit;text-decoration:none}
::selection{background:rgba(201,161,74,.28);color:#fff}

/* Focus: invisible for mouse users, unmistakable for keyboard users.
   A struck collar rather than a plain ring: the 2px outline is the ring
   that carries 2.4.11 on its own (--m-3 #E6CF8F is 12.80:1 on --onyx,
   --brons #7A5E1C is 5.51:1 on --papier and 4.91:1 on --creme, all well
   past the 3:1 a focus indicator needs), and the 1px box-shadow inside it
   is a fillet — pure ornament, so it is dropped entirely in forced
   colours where the UA owns the indicator.
   The nine components that override this with a negative outline-offset
   keep their offset and inherit the fillet for free. Any component that
   declares its own box-shadow on :focus-visible (.btn does) outranks the
   fillet at (0,2,0) and keeps its own — intended, the outline still lands. */
:focus{outline:none}
:focus-visible{
  outline:2px solid var(--m-3);
  outline-offset:3px;
  box-shadow:0 0 0 1px rgba(230,207,143,.42);
  border-radius:2px;
}
.skin-creme :focus-visible,.skin-creme-deep :focus-visible{
  outline-color:var(--brons);
  box-shadow:0 0 0 1px rgba(122,94,28,.5);
}
@media (forced-colors:active){
  :focus-visible,.skin-creme :focus-visible,.skin-creme-deep :focus-visible{box-shadow:none}
}

/* Skip link */
.skip{
  position:absolute;left:var(--gutter);top:-100px;z-index:200;
  background:var(--gold-grad);color:var(--ink-on-gold);
  font-size:12px;letter-spacing:var(--ls-btn);text-transform:uppercase;font-weight:600; /* --ls-btn: uppercase control */
  padding:14px 22px;transition:top var(--dur-2) var(--ease);
}
.skip:focus{top:14px}

/* scrollbar */
body::-webkit-scrollbar{width:10px}
body::-webkit-scrollbar-track{background:#000}
body::-webkit-scrollbar-thumb{background:#2a2622}
body::-webkit-scrollbar-thumb:hover{background:var(--brons)}

/* ---------- Skins ---------- */
.skin-onyx{background:var(--onyx);color:var(--creme-d);--ink:var(--creme-d);--muted:var(--steen);--rule:rgba(201,161,74,.22);--rule-soft:rgba(201,161,74,.11);--gold-text:var(--champagne);--surface:rgba(255,255,255,.024)}
.skin-char{background:var(--houtskool);color:var(--creme-d);--ink:var(--creme-d);--muted:var(--steen);--rule:rgba(201,161,74,.22);--rule-soft:rgba(201,161,74,.11);--gold-text:var(--champagne);--surface:rgba(255,255,255,.03)}
/* --muted is the deck / caption / body-secondary ink in every cream
   section on every page, so it has to clear 4.5:1 as BODY text, not as
   large text — measured failures ran from 11px to 22.7px. The old
   #8a7f68 measured 3.57:1 on --papier #F8F3E9 and 3.18:1 on --creme
   #EFE6D3. #665c4b measures 5.94:1 and 5.29:1, so it clears AA on both
   grounds with headroom for a stacked opacity, and it is still a warm
   neutral so the paper cast survives. The dark skins are untouched:
   --steen measures 5.73:1 on --onyx and 5.36:1 on --houtskool. */
.skin-creme{background:var(--papier);color:var(--inkt);--ink:var(--inkt);--muted:#665c4b;--rule:rgba(122,94,28,.26);--rule-soft:rgba(122,94,28,.13);--gold-text:var(--brons);--surface:rgba(26,22,16,.028)}
.skin-creme-deep{background:var(--creme);color:var(--inkt);--ink:var(--inkt);--muted:#665c4b;--rule:rgba(122,94,28,.26);--rule-soft:rgba(122,94,28,.13);--gold-text:var(--brons);--surface:rgba(26,22,16,.035)}

/* Paper takes light differently from onyx: the highlight has to be
   brighter to register at all, and the shadow has to be warm and open
   or it reads as dirt on the sheet rather than as depth. */
.skin-creme,.skin-creme-deep{
  --sheen:linear-gradient(180deg,rgba(255,255,255,.92) 0,rgba(255,255,255,0) 44%);
  --sheen-lit:linear-gradient(180deg,#fff 0,rgba(255,255,255,0) 54%);
  --wash:radial-gradient(118% 80% at 16% -8%,rgba(122,94,28,.075),transparent 64%);
  --lift-1:0 1px 2px rgba(58,44,16,.10), 0 8px 18px -12px rgba(58,44,16,.22);
  --lift-2:0 2px 4px rgba(58,44,16,.11), 0 22px 48px -26px rgba(58,44,16,.30);
  --lift-3:0 3px 8px rgba(58,44,16,.12), 0 48px 100px -44px rgba(58,44,16,.34);
  --cast-gold:0 14px 34px -16px rgba(122,94,28,.30);
  --halo-gold:none;
  /* The pocket inverts on paper: the lit wall is bright white, not a 5%
     white, and the shadowed wall is the warm open cast, not black. */
  --lip-tl:inset 1px 1px 0 rgba(58,44,16,.13), inset -1px -1px 0 rgba(255,255,255,.9);
  /* The rake in bronze. Same 105deg / 52% geometry — same light — but
     paper needs more of it to register at all. STILL A GRADIENT: never
     `border:1px solid var(--rake)`. */
  --rake:linear-gradient(105deg,rgba(122,94,28,.09) 0%,rgba(122,94,28,.24) 30%,rgba(122,94,28,.40) 52%,rgba(122,94,28,.22) 74%,rgba(122,94,28,.08) 100%);
  --rake-soft:linear-gradient(105deg,rgba(122,94,28,.045) 0%,rgba(122,94,28,.12) 30%,rgba(122,94,28,.20) 52%,rgba(122,94,28,.11) 74%,rgba(122,94,28,.04) 100%);
  /* Graduations on paper. --m-3 would fall to 1.39:1 here, so both the
     major ticks and the needle route to --brons: 5.51:1 on --papier,
     4.91:1 on --creme — past 3:1 with real headroom. --tick-minor stays
     deliberately under 3:1 (1.54:1 flattened over --papier). */
  --tick-major:var(--brons);
  --tick-minor:rgba(122,94,28,.30);
  --needle:var(--brons);
  /* The sheet's own edges. The lip is the lit cut edge along the top; the
     shade is the underside darkening along the bottom, and it is the warm
     open cast the --lift-* values above already use so it cannot read as
     dirt on the paper. */
  --edge-lip:rgba(255,255,255,.92);
  --edge-shade:rgba(58,44,16,.34);
  /* Apparatus ink: SMALLER, not paler. 8.85:1 on --papier and 7.89:1 on
     --creme, i.e. AAA at body size — a caption is quiet because it is
     small and ruled, never because it is faded. */
  --ink-cap:#4A4335;
}
/* …and on the dark grounds. 8.95:1 on --onyx, 8.37:1 on --houtskool. */
.skin-onyx,.skin-char{--ink-cap:#B8AE99}

/* ---------- The seam, applied ----------
   --seam-plate / --seam-ink / --edge-* were declared above but consumed
   nowhere; this is what puts them on the page. Every chapter break was two
   flat colours meeting on a single pixel row, nine times on the homepage
   alone. Now each break carries a 14px band of the hero seal's own
   interference figure, so the ornament in the hero turns out to be what the
   whole site is cut from.

   BOTH edges of a sheet are cut by the SAME element — the cream section —
   so the engraving always lands on the dark plate OUTSIDE the paper:
   ::before is the sheet's top edge, pulled up by its own height onto the dark
   ground above; ::after is its bottom edge, pushed down onto the dark ground
   below. Engraving is cut into metal, it is not printed on paper, and that
   single asymmetry is what makes the join read as light rather than as a
   border.

   Emitting the lower seam from the cream side is a deliberate correction to
   the first pass, which put it on the FOLLOWING dark element's ::before.
   That was a live defect, verified by render: on policies.html the element
   after the cream body is `a.next-page.skin-onyx`, whose own ::before IS its
   hover glow (components.css:527). The seam selector is (0,2,0) against that
   rule's (0,1,0), so it silently replaced the glow and that page lost its
   hover state. Nothing anywhere in the eleven sheets styles a skinned
   section's ::after (audited), so this pair is collision-free — and the
   lower seam now also survives `.cta-band{overflow:hidden}`, because it is
   painted by the cream section, which does not clip.

   No markup, no new stylesheet, no manifest entry, no JS: it cannot be wiped
   by tools/sync-shell.js, which is the failure mode this repo actually has.

   The mask is the whole trick. --seam-ink is ONE gradient across the full
   viewport width while the line-work tiles over it, so the seam is lit once
   along its length rather than once per 168px tile.

   Decor only: aria-hidden by construction (pseudo-elements are not exposed),
   pointer-events:none, and nothing here is a text pair, so there is no
   contrast ratio to clear. */
:is(.skin-onyx,.skin-char,.skin-creme,.skin-creme-deep){position:relative}

:is(.skin-onyx,.skin-char) + :is(.skin-creme,.skin-creme-deep)::before,
:is(.skin-creme,.skin-creme-deep):has(+ :is(.skin-onyx,.skin-char))::after{
  content:'';position:absolute;left:0;right:0;height:var(--seam-h);
  /* z-index 2, not 4. These sections are position:relative with
     z-index:auto, so they create NO stacking context (deliberately — a
     context per band is what breaks position:sticky descendants) and every
     seam therefore competes in the ROOT stacking context with the site's
     own sticky furniture: .pi-meter is z-index:3 (instrument-live.css:468)
     and .subnav is 60 (pricing.css:7). At 4 the seam painted over the
     pinned price meter as the boundary crossed it. 2 still clears the
     decorative z-index:0 layers (.page-hero__bg, .cta-band__glow) and the
     1 the sheet edge uses below — and a POSITIVE z-index is mandatory on
     ::after, because the next section is a later positioned sibling whose
     background would otherwise paint straight over the lower seam. */
  z-index:2;pointer-events:none;
  background:var(--seam-ink);
  /* Single mask layer, no compositing: this is the floor every engine that
     supports mask-image at all reaches, and it is the state that was
     rendered and approved. The two-layer edge fade is added separately
     below, as an enhancement, for the reason documented there. */
  -webkit-mask-image:var(--seam-plate);
          mask-image:var(--seam-plate);
  -webkit-mask-repeat:repeat-x;
          mask-repeat:repeat-x;
  -webkit-mask-size:168px var(--seam-h);
          mask-size:168px var(--seam-h);
  opacity:.85;
}
/* Cream after dark: lift the band up onto the dark section above. */
:is(.skin-onyx,.skin-char) + :is(.skin-creme,.skin-creme-deep)::before{
  top:calc(var(--seam-h) * -1);
}
/* Cream before dark: push the band down onto the dark section below. */
:is(.skin-creme,.skin-creme-deep):has(+ :is(.skin-onyx,.skin-char))::after{
  bottom:calc(var(--seam-h) * -1);
}

/* The envelope's dying ends. Two mask layers INTERSECTED — the engraved
   plate AND a horizontal fade — so the seam is not chopped square where it
   meets the viewport edge, the same way the seal's guilloche envelope dies
   out rather than stopping.

   mask-composite is the single fragile property in this whole signature, and
   this is written as a PROGRESSIVE ENHANCEMENT rather than as the plan's
   `@supports not (…){display:none}` on purpose. With two mask layers and no
   compositing support the default `add` UNIONS them, and the union of an
   engraved plate and an opaque fade is a SOLID GOLD BAND — precisely the
   failure the plan wanted to avoid. Declaring the single-layer mask first
   and only adding the second layer inside @supports inverts the risk: an
   engine that does not understand mask-composite never sees the second
   layer at all and keeps the verified single-layer seam. Safari cannot be
   tested from here; under this structure Safari's worst case is the flat
   correct seam, never a strip and never a slab. The -webkit- pair is listed
   first so the standard properties (guaranteed present by the @supports
   gate) win where both are honoured. */
@supports (mask-composite:intersect){
  :is(.skin-onyx,.skin-char) + :is(.skin-creme,.skin-creme-deep)::before,
  :is(.skin-creme,.skin-creme-deep):has(+ :is(.skin-onyx,.skin-char))::after{
    -webkit-mask-image:var(--seam-plate),linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
    -webkit-mask-repeat:repeat-x,no-repeat;
    -webkit-mask-size:168px var(--seam-h),100% 100%;
    -webkit-mask-composite:source-in;
            mask-image:var(--seam-plate),linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
            mask-repeat:repeat-x,no-repeat;
            mask-size:168px var(--seam-h),100% 100%;
            mask-composite:intersect;
  }
}

/* Forced colours: the UA owns every surface, so an engraved band is noise
   at best and an opaque slab at worst. Withdraw it. */
@media (forced-colors:active){
  :is(.skin-onyx,.skin-char) + :is(.skin-creme,.skin-creme-deep)::before,
  :is(.skin-creme,.skin-creme-deep):has(+ :is(.skin-onyx,.skin-char))::after{display:none}
}

/* ---------- The seam that cannot be cut: the sticky sub-nav join ----------
   AUDITED, and this is the explicit exclusion the build order asked for.
   tarieven.html interposes the sticky category bar between the dark page
   header and the first cream section:
     header.section.skin-onyx  >  div.subnav  >  section.skin-creme#websites
   so the adjacent-sibling seam above never fires there, and that join is the
   only flat two-colour chapter break left on the site — on the page a buyer
   decides on.

   It is deliberately LEFT flat, because a seam there is provably invisible.
   Measured at 1440: .subnav is 51px tall, position:sticky with top:0
   (pricing.css), static box at document y 580-631, so the sheet's top edge is
   at 631 and a 14px band above it would occupy 617-631. Walking the scroll:

     scrollY   bar occupies    band 617-631
     0-580     doc 580-631     fully covered by the bar
     600       doc 600-651     fully covered by the bar
     631+      doc 631-682     already scrolled off the top of the viewport

   There is no scroll position at which the band is both on screen and not
   under the bar. Worse, .subnav carries backdrop-filter:blur(16px), so the
   only thing a seam there would contribute is a blurred gold smear across the
   bottom of the tab strip. Cost with no benefit, so it is not emitted.

   What DOES carry that join is the sheet's own lit cut edge, which is an inset
   on the cream box (document y 631-632) and therefore sits just BELOW the bar
   rather than under it — visible exactly while the reader is arriving at the
   boundary. So the paper-lip selector below is extended to the interposed
   case; the seam selector above is not. The bar's own 1px gold border-bottom
   is then the plate side of the joint and the lip is the paper side.

   The MIRROR case (a sheet whose bottom edge is separated from the dark ground
   by an unskinned element) is deliberately NOT handled: no such adjacency
   exists on any of the 11 pages (audited), and the right treatment depends
   entirely on what the interposed element turns out to be — a sticky bar wants
   the exclusion above, an ordinary block wants the seam. Do not add a
   speculative rule for it; add the measured one when a case appears. */

/* ---------- Paper on a plate ----------
   A sheet laid on an engraved plate has a lit cut edge along its top and its
   own underside shading along the bottom. Both live on the CREAM element, as
   inset box-shadows: no layout cost, no extra element, and nothing that can
   shift the section's box.

   Two departures from the plan here, both forced by measurement:

   (1) The plan put these on the seam pseudo-elements. They cannot go there.
       A mask clips EVERYTHING an element paints, box-shadow included, so a
       shadow on the masked band comes out sliced into the engraving rather
       than as a soft edge.
   (2) The plan wanted the lower edge to be a shadow CAST DOWN onto the dark
       ground. On this palette that is a no-op: --edge-shade over --onyx
       #0B0B0C (relative luminance .0034) lands on ~#040404, a difference no
       screen shows. The physical read that IS available is the sheet's own
       underside — the last few millimetres of paper turning away from the
       light — so the shade is an inset on the paper instead, in the warm
       open cast --lift-* already uses there (rgba(58,44,16,.34)), which is
       why it reads as thickness and not as dirt.
       An outer box-shadow on the cream section was also ruled out: the dark
       section is a later positioned sibling and paints over it, and the only
       way to win that is a z-index per band — the exact per-section stacking
       context that breaks .pi-meter, .sticky-col and tarieven's subnav.

   Geometry of `inset 0 -30px 22px -20px`: the -20px spread grows the shadow
   rect 20px past every edge so nothing bleeds in from the sides, and the
   -30px offset then lifts its bottom edge 10px INSIDE the box, giving a 10px
   core plus 22px of blur along the bottom only.

   Decor at 1px and a soft ramp; no text pair, so there is no ratio to clear.

   The two edges are composed through custom properties rather than by
   enumerating adjacencies. box-shadow does not accumulate across rules, so the
   first pass spelled the pair out in a third, more specific rule for the
   common both-sides case — which works, but the count of rules needed is the
   PRODUCT of the top and bottom adjacency variants, and adding the interposed
   top case documented above would have taken that from three rules to five.
   Setting one custom property per edge and reading both in a single
   box-shadow makes it the SUM instead, and each edge can then be given as
   many adjacency selectors as it needs without touching the other. Both
   default to a valid no-op shadow so the declaration is always well-formed on
   a cream section with dark ground on neither side. */
:is(.skin-creme,.skin-creme-deep){
  --sheet-lip:0 0 0 0 transparent;
  --sheet-shade:0 0 0 0 transparent;
}
/* The lit top cut edge: a sheet laid on dark ground. The second selector is
   the interposed case — dark section, one unskinned element (tarieven.html's
   sticky .subnav), then the sheet. :not() takes the specificity of its most
   specific argument, so that selector is (0,3,0) against the first's (0,2,0);
   both only set a custom property, so nothing competes. */
:is(.skin-onyx,.skin-char) + :is(.skin-creme,.skin-creme-deep),
:is(.skin-onyx,.skin-char)
  + :not(.skin-onyx,.skin-char,.skin-creme,.skin-creme-deep)
  + :is(.skin-creme,.skin-creme-deep){
  --sheet-lip:inset 0 1px 0 var(--edge-lip);
}
/* The underside, where the sheet turns away from the light. */
:is(.skin-creme,.skin-creme-deep):has(+ :is(.skin-onyx,.skin-char)){
  --sheet-shade:inset 0 -30px 22px -20px var(--edge-shade);
}
/* One declaration, so the two edges compose instead of overwriting each other.
   Custom properties resolve per element from whichever declaration won the
   cascade, independently of this rule's own position, so no ordering trap. */
:is(.skin-creme,.skin-creme-deep){
  box-shadow:var(--sheet-lip),var(--sheet-shade);
}

/* Nothing in the seam or the sheet edges animates, so there is deliberately
   no prefers-reduced-motion branch: the whole signature survives reduced
   motion intact. Noted so nobody goes looking for a guard that is absent on
   purpose. AUDIT NOTE for whoever extends this: the seam is painted OUTSIDE
   the cream section's box, so any cream section that ever acquires
   overflow:hidden will clip its own seams. As of this pass none does —
   .section and .pol-body are both overflow:visible, and no .cta-band on the
   site is cream (verified across all 11 pages). */

/* ---------- Layout ---------- */
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter)}
.wrap--narrow{max-width:940px}
.section{position:relative;padding-block:var(--sp-section)}
.section--tight{padding-block:var(--sp-section-tight)}

/* Rhythm, not metronome. Two sections that share a skin are one
   movement — close the seam between them so the *change of ground* is
   what reads as a chapter break, and the page gets a pulse instead of
   one uniform gap repeated eleven times. */
.section:is(.skin-onyx,.skin-char) + .section:is(.skin-onyx,.skin-char),
.section:is(.skin-creme,.skin-creme-deep) + .section:is(.skin-creme,.skin-creme-deep){
  padding-top:var(--sp-seam);
}
/* THE HERO IS A SECTION FOR THIS PURPOSE and was missing from the rule above.
   .hero carries background:var(--onyx) (components.css:195), so a dark section
   opening straight under it shares its ground and is the same one movement —
   but .hero is not .section, so the selector never reached it and the join took
   a full --sp-section.

   Measured on index.html at 1440px before this rule: the hero's box ends at
   y=900 and #essentie then added 172.8px of top padding, with a 63px band
   between them — 236px of unbroken near-black below a hero whose own composition
   already leaves 253px of air under its last line. An element was inserted into
   that gap to fix it, which made it 63px worse; the gap itself was always the
   fault. --sp-seam closes it to 66px, so the run from the hero's last line to the
   heading below goes 455px -> 323px.

   Scope: .hero exists on index.html and nowhere else — every other page's
   header uses its own class and only borrows .hero__bg / .hero__grain — so this
   is one join, not a site-wide change. It is written as a rule rather than as
   an override on #essentie because it is the same principle as the block above
   and should hold if that section is ever reordered or renamed. Creme after the
   hero is deliberately NOT matched: the ground changes there, tarieven.html
   relies on it, and pcg.css:493 already draws the engraved seam across it. */
.hero + .section:is(.skin-onyx,.skin-char){padding-top:var(--sp-seam)}
/* …and the closing address gets the opposite treatment on the way out:
   the page visibly slows down after it asks for something. Its TOP edge
   is deliberately left to the seam rule above — when the CTA sits on
   the same ground as the section before it, the two are one closing
   movement and should not be pushed apart. */
.section.cta-band{padding-block:var(--sp-section) var(--sp-section-loose)}

/* A section that ENDS on a full-bleed terminator — a price list, a card
   lattice, an accordion, the process rail — has already drawn its own bottom
   edge. A rule IS an ending, and a full --sp-section under one is air nothing
   earned, so those sections close on --sp-section-close instead.

   The three selectors walk the LAST-CHILD CHAIN one, two and three levels
   down, which is the honest reading of "the section ends on a rule": the
   markup is `section > .wrap > [terminator]` on most pages and
   `section > .wrap > div > [terminator]` on a few. Testing the chain rather
   than writing `:has(.cards:last-child)` is what stops a card grid that
   happens to be last inside some inner column from closing up a section
   whose real content continues below it.

   Specificity: .section plus :has() (which takes its most specific argument,
   here (0,2,0)) is (0,3,0), so this beats .section (0,1,0) and .section--tight
   — intended, a tight section that ends on a rule should also close up. It
   also beats .section.cta-band (0,2,0), which is why .cta-band is excluded
   explicitly: a closing address earns its --sp-section-loose whatever it
   happens to end on. */
.section:not(.cta-band):has(> :last-child:is(.price-rows,.rail,.faq,.deliver,.timeline,.cards,.tiers)),
.section:not(.cta-band):has(> :last-child > :last-child:is(.price-rows,.rail,.faq,.deliver,.timeline,.cards,.tiers)),
.section:not(.cta-band):has(> :last-child > :last-child > :last-child:is(.price-rows,.rail,.faq,.deliver,.timeline,.cards,.tiers)){
  padding-bottom:var(--sp-section-close);
}

/* Page hero shared by every inner page */
.page-hero{
  position:relative;overflow:hidden;
  padding-top:clamp(150px,16vw,230px);
  padding-bottom:clamp(60px,7vw,110px);
}
.page-hero__bg{position:absolute;inset:0;z-index:0;opacity:.62}
.page-hero__inner{position:relative;z-index:2}
.page-hero h1{max-width:18ch}
.page-hero .lead{margin-top:var(--sp-4)}
/* The hero already carries its own bottom air — don't stack a second
   full section pad straight underneath it. */
.page-hero + .section{padding-top:clamp(40px,5vw,72px)}

/* Breadcrumb */
/* .2em -> var(--ls-label) (.22em). Uppercase apparatus, so it routes through
   the declared label step rather than keeping its own drifted literal. */
.crumbs{display:flex;align-items:center;gap:.7em;font-size:var(--fs-micro);letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--muted);margin-bottom:var(--sp-4)}
/* Breadcrumbs are standalone navigation, not links inside prose, so they need a
   real target. The micro type left them 16px tall. */
.crumbs a{transition:color var(--dur-1);display:inline-flex;align-items:center;min-height:24px}
.crumbs a:hover{color:var(--gold-text)}
.crumbs span[aria-current]{color:var(--gold-text)}
/* No opacity here. On the cream skins there is NO value below 1.0 that
   keeps --muted at 4.5:1 (full opacity is 5.29:1 on --creme, so .95
   already lands at 4.77 and .8 at 3.53). Dimming ink to build hierarchy
   is structurally incompatible with this palette — the token has to
   carry the quietness, not an alpha on top of it. Same reasoning applies
   everywhere `opacity` used to sit on text in these sheets. */
.crumbs .sep{color:var(--muted)}

/* ---------- Type ---------- */
.eyebrow{
  font-family:var(--ff-body);font-weight:500;
  font-size:var(--fs-eyebrow);
  letter-spacing:var(--ls-eyebrow);text-transform:uppercase; /* was .34em, same value, now the declared step */
  color:var(--gold-text);
  display:inline-flex;align-items:center;gap:.85em;
}
.eyebrow::before{content:"";width:26px;height:1px;background:currentColor;opacity:.7}
.eyebrow.center::after{content:"";width:26px;height:1px;background:currentColor;opacity:.7}
.eyebrow.center{justify-content:center}
.eyebrow--bare::before,.eyebrow--bare::after{display:none}
/* When the eyebrow is a block-level label (form sections, contact
   details) it should own its line rather than sit inline with the
   value that follows it. */
div.eyebrow{display:flex;width:fit-content}

/* One family, so the hierarchy is entirely weight and scale. Display
   commits to 800, and .display alone to 900 — h1 deliberately stays at
   800 so heading <em> still has a real 900 step to make (see below).
   Tracking LOOSENS as the weight rises and tightens as the size does:
   -.032em at 118px up to -.012em at 23px. More stem weight closes the
   counters and eats the apparent letter gap, so the correct optical
   response to a heavier cut is more tracking, not less. Register floor
   is -.04em and nothing here goes near it. Leading opens slightly
   versus the old Cabinet values because Switzer's x-height is taller,
   so the same numeric line-height reads tighter. No font-optical-sizing
   and no font-variation-settings: Fontshare serves Switzer as static
   instances with no axes, so both would be inert. */
h1,h2,h3,.display{font-family:var(--ff-display);font-weight:800;line-height:1.06;letter-spacing:-.022em;text-wrap:balance}
.display{font-size:var(--fs-display);font-weight:900;line-height:1.02;letter-spacing:-.032em}
h1{font-size:var(--fs-h1);line-height:1.04;letter-spacing:-.026em}
h2{font-size:var(--fs-h2);line-height:1.07;letter-spacing:-.02em}
h3{font-size:var(--fs-h3);line-height:1.18;letter-spacing:-.012em}
/* .lead was the last place in these sheets still building hierarchy with an
   alpha on ink. The comment at .crumbs .sep documents why that is structurally
   wrong on this palette; the fix here is to drop the .86 and let WEIGHT carry
   the step instead, which is also the only lever a one-family system has.
   Measured, --ink on its own ground:
     dark  --creme-d #F4ECD8 on --onyx #0B0B0C   12.27:1 -> 16.72:1
     cream --inkt   #1A1610 on --papier #F8F3E9  11.03:1 -> 16.28:1
   Both already passed; this is a fidelity fix, not a rescue — a lede is
   quiet because it is lighter in WEIGHT than the heading above it, never
   because its ink has been faded. */
.lead{font-size:var(--fs-lead);line-height:1.62;font-weight:500;color:var(--ink);max-width:60ch;text-wrap:pretty}
.muted{color:var(--muted)}
.fine{font-size:var(--fs-fine);color:var(--muted);letter-spacing:.03em;line-height:1.55}
/* None of the six faces this site loads is an italic (see the token
   comment). font-style:italic therefore makes the browser synthesise an
   oblique by shearing the upright, which at 900 and display size looks
   broken rather than merely cheap. Emphasis steps up in weight inside
   the family instead. html also carries font-synthesis-style:none —
   VERIFIED working: with the real woff2 loaded, an upright and an
   "italic" span of the same string both measure 90.84px, i.e. no shear
   is applied. Keep this rule as well; it is the belt to that braces and
   it works in engines older than Chrome 97 / Safari 16.4 / Firefox 111. */
em,.italic{font-style:normal}
/* 900 against the 800 ladder — a real step. This is why h1 stays at 800
   rather than 900: h1/h2/h3 all keep a full 100-point move. On .display
   (already 900) the step is a no-op by construction, so emphasis there
   rests on --gold-text alone, which is what .gold-foil is for. */
h1 em,h2 em,h3 em,.display em,h1 .italic,h2 .italic,h3 .italic{font-weight:900}
p em,li em,.lead em,dd em,figcaption em{font-weight:600}
.gold{color:var(--gold-text)}
/* .display-face puts an element in the display voice without making it a
   heading. `.serif` is a deprecated alias kept in the SAME selector for
   one release — it covers the runtime generator in js/pcg-assistant.js
   and any markup not yet renamed, so nothing depends on a same-commit
   sweep. Delete `,.serif` from both rules below once the sweep lands.
   (The name was always a lie: there has never been a serif here.) */
.display-face,.serif{font-family:var(--ff-display)}
/* A .display-face that is NOT already a heading inherits BODY weight,
   which in a one-family system means it renders as body copy at display
   size — a 46px pull quote at 400 is just giant paragraph text. Pin it.
   The :not(:is(...)) guard is load-bearing: .display-face is (0,1,0) and
   would otherwise outrank the h1,h2,h3,.display element rule (0,0,1) and
   silently drop every heading carrying the class from 800 to 700. */
.display-face:not(:is(h1,h2,h3,.display)),
.serif:not(:is(h1,h2,h3,.display)){font-weight:700}
p{text-wrap:pretty}

/* Gold emphasis — SOLID. This used to be background-clip:text over
   --gold-grad, which is banned here for a measurable reason, not a
   stylistic one: the gradient's pale stop (#E6CF8F) fell to 1.39:1 on
   the cream skin, so the second half of every emphasised word on
   .skin-creme was effectively invisible, and its dark stop (#9c7a2f)
   fell to 3.62:1 there too. A gradient across a word also says nothing
   — it is decoration masquerading as emphasis.

   The class is redefined rather than removed because the markup uses it
   in 89 places. It now resolves to the skin's committed gold ink:
   champagne on the dark grounds (12.83:1 on --onyx, 12.00:1 on
   --houtskool, 13.14:1 on the footer's #070707) and bronze on the light
   ones (5.51:1 on --papier, 4.91:1 on --creme). All ≥4.5:1 at body
   size, so it is safe at every size it is used at.

   Emphasis comes from what was already there: the step up in weight
   (800 -> 900) and the size. With one family the face itself no longer
   distinguishes an emphasised word, so weight carries it. The only thing
   added back is a wide, low warm halo on dark ground, which is light on
   metal, not a drop shadow. */
.gold-foil{
  color:var(--gold-text);
  background:none;
  -webkit-background-clip:border-box;background-clip:border-box;
  -webkit-text-fill-color:currentColor;
  text-shadow:var(--halo-gold,none);
}

/* Section header block — the editorial "01 — Wat we doen" pattern.
   The measure lives on the heading itself: `ch` there is sized by the
   display face, whereas on the wrapper it would resolve against the
   much smaller body font. */
/* Tight inside the header, generous after it: the label and the heading
   are one object, the gap to the content is a real pause. */
.sec-head{display:flex;justify-content:space-between;align-items:flex-end;gap:var(--sp-4);flex-wrap:wrap;margin-bottom:clamp(52px,6.2vw,100px)}
.sec-head__title{flex:1 1 min(100%,420px);max-width:680px}
.sec-head h2{margin-top:.66rem;max-width:17ch}
.sec-head--center{flex-direction:column;align-items:center;text-align:center}
.sec-head--center .sec-head__title{flex:0 1 auto;max-width:820px}
.sec-head--center h2{max-width:20ch;margin-inline:auto}

/* ---------- Buttons ---------- */
.btn{
  --bd:var(--gold-text);
  display:inline-flex;align-items:center;justify-content:center;gap:.8em;
  font-family:var(--ff-body);font-weight:500;
  font-size:13px;letter-spacing:var(--ls-btn);text-transform:uppercase; /* was .16em, same value, now the declared step */
  line-height:1.2;text-align:center;
  padding:1.05em 1.9em;
  border:1px solid var(--bd);
  color:var(--ink);
  background:transparent;cursor:pointer;
  position:relative;overflow:hidden;isolation:isolate;
  transition:color var(--dur-2) var(--ease),border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-3) var(--ease),transform .16s var(--ease-soft);
}
.btn .arr{transition:transform var(--dur-2) var(--ease)}
.btn::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:var(--gold-grad);
  transform:translateY(101%);transition:transform .55s var(--ease);
}
/* The filled state is a lit surface, so it casts. Hover lights the
   button up; release presses it back into the page. */
/* The ink on a filled button is --ink-on-gold, not the old hard-coded
   #15110a. Measured against --gold-grad's darkest stop (--m-1 #96742C),
   which is what the two corners of the fill actually are: #15110a is
   4.33:1 there and .btn runs 13px/500, i.e. body text, so it needed
   4.5:1 and did not have it. --ink-on-gold (--onyx #0B0B0C) is 4.53:1
   at that same worst point, 8.13:1 on --m-2 and 12.80:1 on --m-3. */
.btn:hover,.btn:focus-visible{color:var(--ink-on-gold);border-color:transparent;box-shadow:var(--cast-gold)}
.btn:hover::before,.btn:focus-visible::before{transform:translateY(0)}
.btn:hover .arr,.btn:focus-visible .arr{transform:translateX(5px)}
.btn:active{transform:scale(.978);box-shadow:none}
/* 4.33:1 -> 4.53:1 at the gradient's darkest stop; see .btn:hover above. */
.btn--solid{border-color:transparent;color:var(--ink-on-gold);box-shadow:var(--cast-gold)}
.btn--solid::before{transform:translateY(0)}
.btn--solid:hover::before,.btn--solid:focus-visible::before{transform:translateY(-101%)}
.btn--solid:hover,.btn--solid:focus-visible{color:var(--gold-text);border-color:var(--gold-text);box-shadow:none}
.btn--solid:hover .arr,.btn--solid:focus-visible .arr{transform:translateX(5px)}
@media (prefers-reduced-motion:reduce){
  .btn,.btn .arr,.btn::before{transition-duration:.01ms}
  .btn:active{transform:none}
}
.btn--lg{padding:1.2em 2.3em;font-size:13.5px}
.btn--sm{padding:.78em 1.35em;font-size:11.5px}
.btn--ghost{border-color:var(--rule);color:var(--muted)}
/* A ghost button still fills with --gold-grad on hover (.btn::before), so
   its hover ink is the same 4.53:1 --ink-on-gold, not #15110a's 4.33:1. */
.btn--ghost:hover{color:var(--ink-on-gold)}
.btn[aria-disabled="true"],.btn:disabled{opacity:.45;pointer-events:none}

/* CTA pair — the standard primary + secondary arrangement */
.cta-pair{display:flex;align-items:center;gap:clamp(18px,2.4vw,34px);flex-wrap:wrap}
.cta-pair--center{justify-content:center}
.cta-note{font-size:var(--fs-fine);color:var(--muted);display:flex;align-items:center;gap:.6em}
.cta-note::before{content:"";width:6px;height:6px;background:var(--gold-grad);transform:rotate(45deg);flex:none}

.link-underline{
  position:relative;display:inline-flex;align-items:center;gap:.6em;
  font-weight:400;letter-spacing:.02em;color:var(--ink);
  padding-bottom:3px;
}
.link-underline::after{
  content:"";position:absolute;left:0;bottom:0;height:1px;width:100%;
  background:var(--gold-text);transform:scaleX(0);transform-origin:left;
  transition:transform var(--dur-2) var(--ease);
}
.link-underline:hover::after,.link-underline:focus-visible::after{transform:scaleX(1)}
.link-underline .arr{transition:transform var(--dur-2) var(--ease)}
.link-underline:hover .arr{transform:translateX(4px)}

/* ---------- Rules / dividers ---------- */
.hairline{height:1px;width:100%;background:var(--rule);transform-origin:left;}
.gold-tick{width:46px;height:1px;background:var(--gold-text)}

/* ---------- Photo placeholders ---------- */
.frame{
  position:relative;overflow:hidden;background:#121012;
  isolation:isolate;
}
.frame::after{ /* film grain + vignette */
  content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(120% 90% at 50% 30%,transparent 40%,rgba(0,0,0,.55) 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.06'/%3E%3C/svg%3E");
  mix-blend-mode:overlay;
}
.frame__media{
  position:absolute;inset:0;
  background:
    linear-gradient(135deg,#1c1813,#0d0c0d 55%,#241d12);
  background-size:cover;background-position:center;
}
.frame__stripes{
  position:absolute;inset:0;
  background:repeating-linear-gradient(135deg,rgba(201,161,74,.05) 0 2px,transparent 2px 13px);
}
.frame__cap{
  position:absolute;left:18px;bottom:16px;z-index:2;
  font-family:var(--ff-body);font-weight:400;
  font-size:11px;letter-spacing:var(--ls-label);text-transform:uppercase; /* .16em -> the declared label step */
  color:rgba(230,207,143,.72);
  display:flex;align-items:center;gap:.7em;
}
.frame__cap::before{content:"";width:16px;height:1px;background:currentColor}
.frame--gold{box-shadow:0 0 0 1px var(--rule)}

/* ---------- Reveal animations ---------- */
[data-rise]{opacity:0;transform:translateY(38px);transition:opacity 1s var(--ease),transform 1.1s var(--ease)}
[data-fade]{opacity:0;transition:opacity 1.3s var(--ease)}
[data-clip]{clip-path:inset(0 0 100% 0);transition:clip-path 1.25s var(--ease)}
[data-stagger]>*{opacity:0;transform:translateY(34px);transition:opacity .95s var(--ease),transform 1.05s var(--ease)}
.is-in[data-rise],.is-in[data-fade]{opacity:1;transform:none}
.is-in[data-clip]{clip-path:inset(0 0 0 0)}
.is-in[data-stagger]>*{opacity:1;transform:none}

/* draw-in hairline */
[data-draw]{transform:scaleX(0);transform-origin:left;transition:transform 1.3s var(--ease)}
.is-in[data-draw]{transform:scaleX(1)}

/* char-by-char display words */
.reveal-line{display:block;overflow:hidden;padding-bottom:.08em;margin-bottom:-.08em}
.reveal-line>span{display:block;transform:translateY(110%);transition:transform 1.15s var(--ease)}
.is-in .reveal-line>span{transform:translateY(0)}

@media (prefers-reduced-motion:reduce){
  *{scroll-behavior:auto !important}
  [data-rise],[data-fade],[data-clip],[data-stagger]>*,[data-draw],.reveal-line>span{
    opacity:1 !important;transform:none !important;clip-path:none !important
  }
}

/* ---------- Utility ---------- */
.center{text-align:center}
.mx-auto{margin-inline:auto}
.stack-sm>*+*{margin-top:1rem}
.stack>*+*{margin-top:1.6rem}
/* .grid sets NO columns on purpose — it is the gap-only base. If a page
   needs two columns, use one of the named utilities below rather than an
   inline `grid-template-columns`: an inline template outranks every
   stylesheet rule, so it survives the 860px collapse and leaves a 130px
   column on a phone. */
.grid{display:grid;gap:clamp(20px,3vw,40px)}
.grid-2{display:grid;gap:clamp(28px,4vw,72px);grid-template-columns:repeat(2,minmax(0,1fr))}
.grid-split{display:grid;gap:clamp(36px,5vw,90px);grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr);align-items:start}
/* The asymmetric splits the pages actually use, as classes so they
   collapse with everything else at 860. --wide is the narrow-lede /
   wide-content pair; --even is a plain halves pair with a tighter gap. */
.grid-split--wide{grid-template-columns:minmax(0,.7fr) minmax(0,1.3fr);gap:clamp(36px,5vw,80px)}
.grid-split--even{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.flex{display:flex}
.between{justify-content:space-between}
.items-center{align-items:center}
.gap-s{gap:.8rem}.gap-m{gap:1.6rem}.gap-l{gap:2.6rem}
.sticky-col{position:sticky;top:calc(var(--nav-h) + 40px)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
[hidden]{display:none !important}

@media (max-width:860px){
  /* Every modifier is listed explicitly. A media query adds NO specificity,
     so `.grid-split` alone here would lose to `.grid-split--wide` (0,2,0)
     and the wide variant would never collapse — the same trap that is
     documented at pricing.css's .tiers.cols-2 override. */
  .grid-2,.grid-split,.grid-split--wide,.grid-split--even{grid-template-columns:1fr}
  .sticky-col{position:static}
}

/* ---------- Bilingual (NL/EN) ----------
   The hide rule is deliberately specific (`html [data-lang]`) so a
   component rule such as `.card span{display:block}` can never leave
   the inactive language on screen.

   Because the visible language resolves to `display:revert`, never
   put a custom `display` on an element that also carries
   `data-lang` — style a wrapper around it instead. */
html [data-lang]:not(html){display:none}
html[data-lang="nl"] [data-lang="nl"],
html[data-lang="en"] [data-lang="en"]{display:revert}
html:not([data-lang="en"]) [data-lang="nl"]{display:revert}
