/* ==========================================================================
   WARMVAST — main stylesheet
   Design language: engineering-grade, calm, factual. Deep teal-green as the
   trust/primary color; warm yellow reserved as the energy accent (the live
   subsidy figure, the verdubbelaar). System font stack for zero render-blocking.
   ========================================================================== */

/* ---------- Self-hosted fonts ---------- */
@font-face {
	font-family: "Inter";
	font-style: normal;
	font-weight: 380 620;
	font-display: swap;
	src: url("../fonts/inter-latin-var.woff2") format("woff2");
}
@font-face {
	font-family: "Space Grotesk";
	font-style: normal;
	font-weight: 300 700;
	font-display: swap;
	src: url("../fonts/space-grotesk-latin-var.woff2") format("woff2");
}

/* ---------- Design tokens ---------- */
:root {
	--ink:            #1b1f21;
	--ink-2:          #3c4348;
	--ink-3:          #666e74; /* WCAG AA vs both --paper (4.79:1) and --surface (5.19:1); was #6c757b at 4.34:1 on paper (fails) */
	--paper:          #f6f6f2;
	--surface:        #ffffff;
	--surface-2:      #fbfbf9;
	--line:           #e6e7e1;
	--line-strong:    #d6d8d0;

	--primary:        #0b6b5b;
	--primary-600:    #0a5d4f;
	--primary-700:    #074a3f;
	--primary-050:    #e7f2ef;
	--primary-ink:    #ffffff;

	--accent:         #fed03d;
	--accent-strong:  #f6c200;
	--accent-700:     #e0a900;
	--accent-ink:     #2b2400;

	--success:        #1c7a45;
	--success-bg:     #e8f6ed;
	--warn:           #a35314;
	--warn-bg:        #fbf1e6;

	--radius-xs:  6px;
	--radius-sm:  8px;
	--radius-md:  11px;
	--radius:     14px;
	--radius-lg:  22px;
	--radius-pill: 999px;

	--shadow-sm:  0 1px 2px rgba(20,30,28,.06), 0 1px 3px rgba(20,30,28,.06);
	--shadow:     0 6px 20px rgba(20,40,36,.08), 0 2px 6px rgba(20,40,36,.05);
	--shadow-lg:  0 24px 60px rgba(12,40,34,.16), 0 6px 18px rgba(12,40,34,.08);

	/* Ambient thermal depth: the SAME soft teal/amber light the hero and
	   CTA band glow with, just cast onto a light backdrop instead of a dark
	   one -- so light sections read as part of one continuous atmosphere
	   instead of a different, flatter material. Was deliberately faint
	   ("lit paper, never coloured") which is exactly why it read as a
	   mismatch next to the hero's rich glow; turned up so it's actually felt. */
	--ambient-cool: rgba(11,107,91,.12);
	--ambient-warm: rgba(254,208,61,.13);

	/* Fine paper-grain only -- the ruled engineering-grid layer that used to
	   sit under this (measurement lines every 32px) read as technical
	   "kladpapier" against the hero/CTA's soft glow, a mismatched visual
	   language no amount of edge-fading could fix. Zero image requests: a
	   tiny inline SVG feTurbulence data-URI. */
	--noise-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");

	/* Glass system: a gradient hairline (cool->warm, echoing the thermal theme)
	   over a near-opaque fill so text contrast is never sacrificed for the effect. */
	--glass-fill:   rgba(255,255,255,.74);
	--glass-border: linear-gradient(150deg, rgba(255,255,255,.95), rgba(255,255,255,.18) 46%, rgba(11,107,91,.30));
	--glass-border-hot: linear-gradient(150deg, rgba(255,255,255,1), rgba(254,208,61,.5) 46%, rgba(11,107,91,.5));
	--shadow-glass: 0 22px 46px -22px rgba(12,40,34,.30), 0 8px 18px -12px rgba(12,40,34,.16);
	--shadow-float: 0 30px 60px -24px rgba(0,0,0,.55), 0 12px 26px -16px rgba(0,0,0,.4);

	--container: 1180px;
	--gutter: clamp(1rem, 4vw, 2.5rem);

	/* Motion system: one easing/duration vocabulary, used everywhere instead of bare "ease". */
	--ease-out:    cubic-bezier(.22,.61,.36,1);
	--ease-io:     cubic-bezier(.65,0,.35,1);
	--ease-spring: cubic-bezier(.34,1.4,.64,1);
	--dur-1: .16s;
	--dur-2: .26s;
	--dur-3: .5s;

	--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
	--font-display: "Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
	--font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

	--step--1: clamp(.82rem, .8rem + .1vw, .88rem);
	--step-0:  clamp(1rem, .96rem + .2vw, 1.075rem);
	--step-1:  clamp(1.15rem, 1.08rem + .35vw, 1.3rem);
	--step-2:  clamp(1.4rem, 1.28rem + .6vw, 1.7rem);
	--step-3:  clamp(1.75rem, 1.5rem + 1.1vw, 2.3rem);
	--step-4:  clamp(2.1rem, 1.7rem + 1.9vw, 3.2rem);
	--step-5:  clamp(2.5rem, 1.9rem + 2.8vw, 3.9rem);

	--header-h: 74px;

	/* Ambient haze channels, set by main.js. --wv-mx/my = smoothed pointer;
	   --wv-fx/fy = card autonomous float; --wv-gx/gy = CTA autonomous float.
	   All -1..1, default 0 (centred / static for reduced-motion). */
	--wv-mx: 0;
	--wv-my: 0;
	--wv-fx: 0;
	--wv-fy: 0;
	--wv-gx: 0;
	--wv-gy: 0;
}

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
/* Safety net: position:fixed elements that also carry a CSS transform (the
   off-canvas mobile nav, translateX(100%) when closed) can still register
   as horizontal overflow on some mobile engines even though they're
   visually off-screen -- the viewport then "zooms out" to fit that phantom
   width, which is the actual cause of "half the page is blank" on mobile.
   `clip` (not `hidden`) on both html and body: hidden alone on just html
   isn't enough to stop it (verified live), but hidden on body makes body a
   scroll container in its own right, which breaks .site-header's
   position:sticky (it stopped pinning to the top at all -- verified
   locally). `overflow-x: clip` gives the same paint-time clipping without
   turning the element into a scroll container, so sticky keeps working. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: clip; }
/* anchor targets clear the sticky header when jumped to */
:target, #warmvast-woningscan, [id].section { scroll-margin-top: 90px; }
@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }
	*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}
body {
	margin: 0;
	overflow-x: clip;
	font-family: var(--font-sans);
	font-size: var(--step-0);
	line-height: 1.62;
	color: var(--ink);
	background-color: #f4f4ef;
	background-image: var(--noise-texture);
	background-size: 220px 220px;
	background-repeat: repeat;
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { font-family: var(--font-display); line-height: 1.1; margin: 0 0 .5em; font-weight: 700; letter-spacing: -.02em; color: var(--ink); }
/* numeric "instrument" elements: display font + tabular figures */
.hero__title, .section-head__title, .page-hero__title,
.verdub__amount, .facts dd, .tariff__value,
.stat__num { font-family: var(--font-display); }
.verdub__amount, .facts dd, .tariff__value, .stat__num {
	font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; letter-spacing: -.01em;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-1); }
p { margin: 0 0 1rem; }
a { color: var(--primary-600); text-decoration: none; }
a:hover { color: var(--primary-700); }
img, svg, video { max-width: 100%; height: auto; display: block; }
ul, ol { margin: 0 0 1rem; padding-left: 1.2rem; }
strong { font-weight: 700; }
hr { border: 0; border-top: 1px solid var(--line); margin: 2rem 0; }
:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; border-radius: 4px; }

.screen-reader-text {
	position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
	position: absolute; left: -9999px; top: 0; z-index: 100;
	background: var(--ink); color: #fff; padding: .8rem 1.2rem; border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; color: #fff; }

/* ---------- Layout ---------- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
/* Slightly tighter than before (was clamp(3rem, 6vw, 5.5rem)) -- adjacent
   sections share this same padding top+bottom, so the gap between any two
   was 2x it; a modest ~18% cut across the board reads as "a bit closer
   together" without collapsing the rhythm. */
.section { padding-block: clamp(2.5rem, 5vw, 4.5rem); }
.section--tight { padding-block: clamp(2rem, 4vw, 3.5rem); }

/* ---- One continuous glowing sheet ----
   The whole light content area is a single unbroken surface: base paper
   tone + grain + several large, soft ambient light pools in the SAME
   teal/amber the hero and CTA band glow with -- so light sections read as
   the same atmosphere continuing at a brighter exposure, not a different,
   flatter material next to it. Because it lives on <main> (not per-section)
   the pools drift across section boundaries with no paper<->surface seams
   to smooth. Dark sections (hero, cta, footer, --ink) sit opaque on top and
   hide it. */
.site-main {
	position: relative;
	background-color: #f4f4ef;
	background-image:
		var(--noise-texture),
		radial-gradient(1900px 1300px at 102% 0%, var(--ambient-cool), transparent 72%),
		radial-gradient(1700px 1200px at -10% 26%, var(--ambient-warm), transparent 70%),
		radial-gradient(1800px 1250px at 110% 55%, var(--ambient-cool), transparent 70%),
		radial-gradient(1650px 1150px at -8% 80%, var(--ambient-warm), transparent 68%),
		radial-gradient(1750px 1200px at 102% 104%, var(--ambient-cool), transparent 70%);
	background-size: 220px 220px, auto, auto, auto, auto, auto;
	background-repeat: repeat, no-repeat, no-repeat, no-repeat, no-repeat, no-repeat;
}
/* light sections are now transparent windows onto the continuous sheet */
.section--paper,
.section--surface { background: transparent; }
.section--ink { background: var(--ink); color: #eef1ef; }
.section--ink h2, .section--ink h3 { color: #fff; }

/* .site-main's ambient-cool/-warm pools (see .site-main) are positioned by
   PERCENTAGE of the whole page's height, so how strongly one happens to
   tint the pixels right below the hero varies with how long the rest of
   the page is. The homepage is long enough that this was never visible --
   .usp-bar__grid's flat matched color already lines up there -- but the
   much shorter scan landing page lands a pool close enough to visibly
   mismatch it. Scoped to that page specifically: applying the same fade
   site-wide painted an unnecessary flat patch over the homepage's own
   already-correct blend, creating a new seam that hadn't been there.
   130px, not 48: measured, the tint keeps drifting until ~80px down before
   it settles at its true (cooler/greener) value -- a shorter fade only
   smoothed the first few pixels and still handed off into a visibly
   different flat tone right after, which read as its own color step even
   without a hard edge. */
body.is-scan-landing .hero + .section,
body.is-story-hero .hero + .section { background-image: linear-gradient(180deg, #f4f4ef 0%, transparent 100%); background-size: 100% 130px; background-position: top; background-repeat: no-repeat; }

.section-head { max-width: 62ch; margin-bottom: clamp(1.5rem, 3vw, 2.5rem); }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head__title { font-size: var(--step-3); }
.section-head__intro { font-size: var(--step-1); color: var(--ink-2); margin-bottom: 0; }
.section--ink .section-head__intro { color: #b9c4c0; }

.kicker {
	display: inline-flex; align-items: center; gap: .5rem;
	font-size: var(--step--1); font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
	color: var(--primary); margin: 0 0 .7rem;
}
.kicker::before { content: ""; width: 22px; height: 2px; background: var(--accent); border-radius: 2px; }
.kicker--light { color: var(--accent); }
.kicker--light::before { background: var(--accent); }

/* ---------- Icons ---------- */
.wv-icon { width: 22px; height: 22px; flex: 0 0 auto; }
.wv-icon--sm { width: 16px; height: 16px; }
.wv-icon--end { width: 18px; height: 18px; }

/* ---------- Buttons ---------- */
.btn {
	display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
	font: inherit; font-weight: 650; line-height: 1; text-align: center; cursor: pointer;
	padding: .95rem 1.5rem; border-radius: var(--radius-pill); border: 2px solid transparent;
	transition: transform var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
	white-space: nowrap; max-width: 100%;
}
.btn:hover { transform: translateY(-2px); }
@media (max-width: 560px) {
	/* nowrap is fine for short labels but a few CTAs run long ("Twijfelt u
	   welke maatregel past? Start de scan") -- on a narrow phone that just
	   pushes the pill past the viewport edge instead of ever wrapping.
	   Letting it wrap below this width keeps every button visually
	   contained; the short labels most buttons use don't reach a second
	   line anyway. */
	.btn { white-space: normal; line-height: 1.25; }
}
.btn:active { transform: translateY(0); }
.btn--sm { padding: .62rem 1rem; font-size: var(--step--1); }
.btn--lg { padding: 1.1rem 1.9rem; font-size: var(--step-1); }

.btn--primary { background: var(--primary); color: #fff; box-shadow: var(--shadow-sm); }
.btn--primary:hover { background: var(--primary-700); color: #fff; box-shadow: var(--shadow); }

.btn--accent { background: var(--accent); color: var(--accent-ink); box-shadow: 0 6px 16px rgba(246,194,0,.28); }
.btn--accent:hover { background: var(--accent-strong); color: var(--accent-ink); }

.btn--secondary { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn--secondary:hover { background: var(--surface); border-color: var(--ink); color: var(--ink); }

.btn--ghost { background: rgba(255,255,255,.08); color: #fff; border-color: rgba(255,255,255,.35); }
.btn--ghost:hover { background: rgba(255,255,255,.16); color: #fff; }

.btn:disabled { opacity: .6; cursor: not-allowed; transform: none; }

.link-arrow { display: inline-flex; align-items: center; gap: .4rem; font-weight: 650; color: var(--primary-600); }
.link-arrow:hover { gap: .6rem; }

/* ---------- Header / nav ---------- */
.site-header {
	position: sticky; top: 0; z-index: 60;
	border-bottom: 1px solid var(--line);
	transition: box-shadow var(--dur-2) var(--ease-out);
}
/* The frosted-glass fill lives on a pseudo-element, not .site-header itself.
   backdrop-filter (like transform/filter/perspective) makes the element that
   carries it a new containing block for any position:fixed descendant --
   which trapped the off-canvas mobile nav (a real descendant, #primary-nav)
   inside the header's own ~74px box instead of the viewport, collapsing it
   to a sliver. A ::before has no descendants of its own, so it can carry
   the same visual effect without turning .site-header into that trap. */
.site-header::before {
	content: ""; position: absolute; inset: 0; z-index: -1;
	background: rgba(255,255,255,.9);
	backdrop-filter: saturate(1.4) blur(10px);
}
.site-header.is-scrolled { box-shadow: var(--shadow-sm); }
.site-header__inner { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 1.5rem; min-height: var(--header-h); }

/* Transparent-over-hero header: on pages that open on the dark .hero,
   the header floats over it (same color as the hero, effectively invisible
   as its own bar) until the user scrolls, at which point it gets the usual
   frosted-white fill and dark text back. position:fixed (not the sticky
   above) so it's removed from flow and the hero can render full-bleed from
   y:0 underneath it instead of being pushed down by the header's own box --
   .hero's own top padding (88-128px) already clears the 74px header
   comfortably, so no further adjustment was needed there. */
body.has-dark-hero .site-header {
	position: fixed; left: 0; right: 0;
	border-bottom-color: transparent;
	transition: box-shadow var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}
body.has-dark-hero .site-header::before { opacity: 0; transition: opacity var(--dur-2) var(--ease-out); }
body.has-dark-hero .site-header.is-scrolled {
	border-bottom-color: var(--line);
}
body.has-dark-hero .site-header.is-scrolled::before { opacity: 1; }
/* min-width, not unscoped: below 960px .nav__list only ever renders inside
   the off-canvas panel (its own permanent white background, open or not),
   never inline over the hero -- white text there would just be invisible
   white-on-white the moment the panel opens.
   `> .menu-item > a` (direct children only), not a bare `.nav__list a`: the
   dropdown panels (.dropdown, .dropdown--wide) are ALSO descendants of
   .nav__list, and ALSO have their own permanent white background -- the
   same white-on-white trap as the off-canvas panel, just missed here the
   first time because it only breaks while unscrolled, at >=961px, with a
   dropdown actually open (the Gemeentes list going fully blank). Only the
   top-level trigger link ("Isolatie", "Gemeentes", ...) is an `a` that's a
   direct child of a `.menu-item` -- every dropdown-nested link is a
   grandchild-or-deeper, so this excludes them structurally rather than
   needing to know every dropdown class by name. */
@media (min-width: 961px) {
	body.has-dark-hero .site-header:not(.is-scrolled) .nav__list > .menu-item > a { color: rgba(255,255,255,.86); }
	body.has-dark-hero .site-header:not(.is-scrolled) .nav__list > .menu-item > a:hover,
	body.has-dark-hero .site-header:not(.is-scrolled) .nav__list > .current-menu-item > a {
		color: #fff; background: rgba(255,255,255,.14);
	}
}
/* logo crossfade: the dark wordmark reads fine on the white scrolled bar,
   but disappears into the hero's own near-black background when floating
   transparently -- swap to the light variant (already used in the footer)
   for that state instead of leaving it illegible. */
.brand { position: relative; display: inline-flex; align-items: center; }
.brand__logo--light {
	position: absolute; inset: 0; opacity: 0; pointer-events: none;
	transition: opacity var(--dur-2) var(--ease-out);
}
.brand__logo--dark { transition: opacity var(--dur-2) var(--ease-out); }
body.has-dark-hero .site-header:not(.is-scrolled) .brand__logo--dark { opacity: 0; }
body.has-dark-hero .site-header:not(.is-scrolled) .brand__logo--light { opacity: 1; }

/* Mobile menu button follows the same floating-over-dark-hero treatment as
   the logo/nav links above -- without this it stayed a solid white square
   (its always-on .nav-toggle background) sitting on top of the transparent
   header, the one element that never adapted to the dark backdrop. */
body.has-dark-hero .site-header:not(.is-scrolled) .nav-toggle { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.35); }
body.has-dark-hero .site-header:not(.is-scrolled) .nav-toggle__bars span { background: #fff; }

.brand { display: inline-flex; align-items: center; }
.brand__logo { display: block; height: 48px; width: auto; }
.brand__logo--footer { height: 60px; }

.site-nav { display: contents; }
.nav__list { display: flex; align-items: center; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.nav__list a {
	display: inline-flex; align-items: center; gap: .3rem; padding: .55rem .7rem; border-radius: var(--radius-sm);
	color: var(--ink-2); font-weight: 600; white-space: nowrap;
	transition: color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out);
}
.nav__list a:hover, .nav__list .current-menu-item > a { color: var(--ink); background: var(--primary-050); }
.nav__caret { transition: transform var(--dur-2) var(--ease-out); }

.menu-item--has-children { position: relative; }
.dropdown {
	position: absolute; top: calc(100% + 10px); left: 50%; transform: translateX(-50%) translateY(6px);
	width: min(560px, 92vw); background: var(--surface); border: 1px solid var(--line);
	border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: .8rem;
	opacity: 0; visibility: hidden; transition: opacity var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out); z-index: 70;
}
/* transparent bridge over the gap so the pointer never leaves the hover area */
.dropdown::before { content: ""; position: absolute; left: 0; right: 0; top: -14px; height: 14px; }
.menu-item--has-children:hover .dropdown,
.menu-item--has-children:focus-within .dropdown,
.dropdown.is-open { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
/* min-width, not unscoped: mobile taps don't fire a real mouseleave, so touch
   browsers (iOS Safari especially) can leave :hover "stuck" on the tapped
   <li> after the tap ends -- and since this selector's specificity beats the
   mobile aria-expanded-driven rule below (.nav-toggle etc.), that stuck hover
   would win and leave the caret rotated regardless of the dropdown's real
   open/closed state. Only meaningful >=861px anyway, where dropdowns are
   genuinely hover-driven (below that .dropdown is forced position:static and
   opened by tap, see the mobile block). */
@media (min-width: 861px) {
	.menu-item--has-children:hover .nav__caret { transform: rotate(180deg); }
}
/* minmax(0, 1fr), not a bare 1fr: a grid track's default minimum is auto (its
   content's own min-content size), so a long unbroken subtitle like
   "Uitleg over subsidie en isolatie" was demanding more than its fair half
   and pushing the whole grid -- and the item boxes with it -- past the
   dropdown panel's right edge instead of wrapping. minmax(0, 1fr) lets the
   track shrink below that and forces the text to wrap within its column. */
.dropdown__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .3rem; }
.dropdown__grid--single { grid-template-columns: 1fr; }
.dropdown--simple { width: min(300px, 92vw); }
.dropdown__item { display: flex; align-items: center; gap: .8rem; padding: .7rem .8rem !important; border-radius: var(--radius); background: transparent !important; transition: background-color var(--dur-1) var(--ease-out); min-width: 0; }
.dropdown__item:hover { background: var(--primary-050) !important; }
/* the text span is itself a flex item (of .dropdown__item) with the same
   min-width:auto default -- without this it also refuses to shrink and the
   overflow reappears one level down even after the grid track is fixed. */
.dropdown__item > span:last-child { min-width: 0; }
.dropdown__item strong { display: block; color: var(--ink); font-size: .98rem; }
.dropdown__item em { font-style: normal; color: var(--ink-3); font-size: .82rem; }
.dropdown__icon { display: inline-flex; width: 40px; height: 40px; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); }
.dropdown__item--all { grid-column: 1 / -1; background: var(--surface-2) !important; }

/* compact link-list variant: for menus with many plain items (e.g. Gemeentes)
   where the heavy icon+title+subtitle card style would make the dropdown
   unwieldy. */
.dropdown--wide { width: min(680px, 94vw); }
.dropdown__lead { padding: .3rem .5rem .7rem; color: var(--ink-3); font-size: var(--step--1); border-bottom: 1px solid var(--line); margin-bottom: .5rem; }
.dropdown__links { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .1rem .3rem; }
.dropdown__links a {
	display: block; padding: .5rem .7rem !important; border-radius: var(--radius-sm);
	color: var(--ink-2); font-weight: 600; font-size: .92rem;
	transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.dropdown__links a:hover { background: var(--primary-050); color: var(--ink); }
.dropdown__links-all { grid-column: 1 / -1; margin-top: .3rem; padding-top: .6rem; border-top: 1px solid var(--line); }
.dropdown__links-all a { color: var(--primary) !important; }

.site-nav__cta { display: flex; align-items: center; justify-content: flex-end; gap: 1rem; }
.nav__phone { display: inline-flex; align-items: center; gap: .45rem; font-weight: 700; color: var(--ink); white-space: nowrap; }
.nav__phone:hover { color: var(--primary); }
.nav__phone .wv-icon { width: 18px; height: 18px; color: var(--primary); }

.nav-toggle { display: none; }

/* ---------- Hero ---------- */
/* flex + justify-content:center (not a fixed top offset) so hero__inner
   sits centered in the space between the header and the floating USP bar,
   with equal breathing room above and below, instead of pinned to the top
   with whatever's left over dumped below it. padding-top/-bottom are that
   space's boundaries, not the content's own offset: top clears the fixed
   header (see .site-header), bottom reserves the USP bar's real height
   (main.js measures it live and writes --usp-clearance, since the bar's
   height depends on wrapped text that varies by viewport/content). */
.hero {
	position: relative; background: var(--ink); color: #eef2f0; overflow: hidden;
	min-height: 100vh; min-height: 100svh;
	display: flex; flex-direction: column; justify-content: center;
	padding-top: clamp(5.5rem, 10vh, 8rem);
	padding-bottom: var(--usp-clearance, clamp(7rem, 14vh, 10rem));
}
.hero::before {
	content: ""; position: absolute; inset: 0; z-index: 0;
	background:
		radial-gradient(120% 80% at 85% -10%, rgba(11,107,91,.55), transparent 55%),
		radial-gradient(90% 70% at 10% 110%, rgba(254,208,61,.10), transparent 50%),
		linear-gradient(180deg, #171b1d, #1d2426);
}
.hero__thermal {
	position: absolute; inset: 0; z-index: 0; opacity: .5; mix-blend-mode: screen;
	background:
		radial-gradient(closest-side at 78% 30%, rgba(254,208,61,.35), transparent),
		radial-gradient(closest-side at 90% 55%, rgba(226,88,34,.25), transparent),
		radial-gradient(closest-side at 68% 12%, rgba(11,107,91,.35), transparent);
	filter: blur(6px);
	animation: hero-drift 16s ease-in-out infinite alternate;
}
@keyframes hero-drift {
	from { transform: translate3d(0,0,0) scale(1); }
	to { transform: translate3d(-3%, 2%, 0) scale(1.08); }
}

/* sprawling ambient orbs: soft thermal light-sources that give the dark hero
   real depth without an image. Contained by the hero's overflow:hidden. */
.hero__orb {
	position: absolute; z-index: 0; border-radius: 50%; pointer-events: none;
	filter: blur(90px); mix-blend-mode: screen; will-change: transform;
}
.hero__orb--1 {
	width: 44vw; height: 44vw; max-width: 660px; max-height: 660px; top: -12%; right: -8%;
	background: radial-gradient(circle at center, rgba(11,107,91,.6), transparent 68%);
}
.hero__orb--2 {
	width: 36vw; height: 36vw; max-width: 540px; max-height: 540px; bottom: -16%; left: -6%;
	background: radial-gradient(circle at center, rgba(254,208,61,.22), transparent 68%);
}
@media (prefers-reduced-motion: reduce) { .hero__orb { will-change: auto; } }

@media (prefers-reduced-motion: reduce) {
	.hero__thermal { animation: none; }
}
.hero__inner {
	position: relative; z-index: 1;
	display: grid; grid-template-columns: 1.02fr .98fr; gap: clamp(1.5rem, 4vw, 3.5rem);
	/* top-align, and both columns start flush at the exact same row line —
	   copy and scan card line up tightly. top-align (not center) is deliberate:
	   the scan card's height varies a lot across its steps (address -> result ->
	   subsidie -> lead), so centering would make the left copy visibly jump
	   every time the user advances. */
	align-items: start;
	/* no padding of its own any more -- .hero itself now owns the top/bottom
	   reserve and centers this whole block within it (see .hero). Full width
	   comes for free: flex items default to align-self:stretch on the cross
	   axis (horizontal, since .hero is flex-direction:column). */
}
.hero__eyebrow {
	display: inline-flex; align-items: center; gap: .55rem;
	font-size: var(--step--1); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
	color: var(--accent); background: rgba(254,208,61,.1); border: 1px solid rgba(254,208,61,.25);
	padding: .4rem .8rem; border-radius: var(--radius-pill); margin-bottom: 1.2rem;
}
.hero__title { font-size: var(--step-5); color: #fff; margin-bottom: .8rem; }
.hero__title em { font-style: normal; color: var(--accent); }
.hero__sub { font-size: var(--step-1); color: #c6d0cc; max-width: 44ch; margin-bottom: 1.6rem; }
.hero__actions { display: flex; flex-wrap: wrap; gap: .9rem; margin-bottom: 1.6rem; }
.hero__trust { display: flex; flex-wrap: wrap; gap: 1.2rem; font-size: var(--step--1); color: #aeb9b5; }
.hero__trust span { display: inline-flex; align-items: center; gap: .45rem; }
.hero__trust .wv-icon { width: 16px; height: 16px; color: var(--accent); }
.hero__scan {
	position: relative;
	z-index: 0;
	isolation: isolate;
	/* Tuned for a hero__copy column sitting beside this one, back when both
	   pages' heroes had a visible eyebrow+h1 above the fold -- neither does
	   any more (see body.is-front/.is-scan-landing overrides below), so this
	   is really just "some top breathing room" now, kept as the shared
	   default rather than deleted since a future hero__copy could still
	   want it. margin-top (not padding-top): the ::before glow backdrop
	   below is positioned relative to this box's own edge, so the whole
	   box -- glow and card together -- must shift as one unit, not just the
	   inner content. */
	margin-top: 3rem;
}
/* Scan landing page: the card is the entire hero now, no copy column beside
   it to align with or share width against -- single centered column. The
   940px match to .usp-bar__grid belongs to the GLOW backdrop (.hero__scan
   ::before below), same as the homepage's proportions: the actual white
   card is narrower than its own glow by 44px (22px reveal on each side),
   so capping .hero__scan itself at 940px would have matched the card, not
   the glow, to the USP bar -- 896 (940-44) is what makes the glow land on
   940 once its own "+44px" is added back. */
body.is-scan-landing .hero__inner { grid-template-columns: 1fr; justify-items: center; }
body.is-scan-landing .hero__scan { width: 100%; max-width: 896px; margin-top: 0; }
/* ...and the glow's own 684px ceiling (tuned for the homepage's narrower
   grid-column context) needs to come off here, or it'd clamp smaller than
   the 896px card it's meant to frame. */
body.is-scan-landing .hero__scan::before { width: calc(100% + 44px); }
/* The homepage hero dropped its eyebrow pill -- h1 is hero__copy's first
   child there now, so hero__scan needs only a small manual nudge -- not the
   3rem tuned for the eyebrow's own height (that stays correct for
   template-scan.php, which still has one) -- to sit a touch below the h1's
   top line instead of dead-flush with it. */
body.is-front .hero__scan { margin-top: 2rem; }
.hero__scan::before {
	content: "";
	position: absolute;
	left: 50%;
	top: -18px;
	width: min(calc(100% + 44px), 684px);
	height: calc(100% + 36px);
	transform: translateX(-50%);
	border-radius: calc(var(--radius-lg) + 8px);
	background:
		linear-gradient(135deg, rgba(254,208,61,.13), rgba(255,255,255,.035) 24%, rgba(11,107,91,.18) 100%),
		rgba(255,255,255,.035);
	border: 1px solid rgba(255,255,255,.11);
	box-shadow:
		0 34px 90px -42px rgba(0,0,0,.74),
		inset 0 1px 0 rgba(255,255,255,.14);
	z-index: -1;
}

/* ---------- Shared form bits (used by the woningscan lead step) ---------- */
.wv-consent { display: flex; align-items: flex-start; gap: .6rem; font-weight: 500 !important; font-size: var(--step--1); color: var(--ink-2); margin-top: 1rem; }
.wv-consent input { margin-top: .2rem; accent-color: var(--primary); }
.wv-consent--inline { align-items: center; justify-content: space-between; gap: .9rem; margin-top: .15rem; }
.wv-consent--inline input { flex: 0 0 auto; width: 16px; height: 16px; margin: 0; }

.wv-success__mark { width: 56px; height: 56px; margin: 0 auto 1rem; border-radius: 50%; background: var(--success); color: #fff; display: flex; align-items: center; justify-content: center; }

/* ---------- Cards / services ---------- */
.grid { display: grid; gap: clamp(1rem, 2.5vw, 1.6rem); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }

.card {
	background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
	padding: 1.5rem; box-shadow: var(--shadow-sm);
	transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.card--link:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--line-strong); }

.problem-card { display: flex; align-items: flex-start; gap: .9rem; }
.problem-card__icon { flex: 0 0 auto; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); }
.problem-card p { margin: 0; font-weight: 600; }
.problem-card__body { min-width: 0; flex: 1; }
.problem-card__body p + p { margin-top: .3rem; font-weight: 400; color: var(--ink-3); font-size: .92rem; }

.service-card { display: flex; flex-direction: column; height: 100%; }
.service-card__icon { width: 52px; height: 52px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius); background: var(--primary); color: #fff; margin-bottom: 1rem; }
.service-card__icon .wv-icon { width: 26px; height: 26px; }
.service-card h3 { margin-bottom: .5rem; }
.service-card__problem { color: var(--ink-3); font-size: var(--step--1); margin-bottom: .5rem; }
.service-card__solution { margin-bottom: 1rem; }
.service-card__subsidy {
	display: inline-flex; align-items: center; gap: .45rem; align-self: flex-start;
	font-weight: 700; font-size: var(--step--1); color: var(--primary-700);
	background: var(--primary-050); padding: .4rem .75rem; border-radius: var(--radius-pill); margin-bottom: 1.2rem;
}
.service-card__foot { margin-top: auto; }

/* ---------- Verdubbelaar ---------- */
.verdub { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
.verdub__badge { display: inline-flex; align-items: center; gap: .5rem; background: var(--accent); color: var(--accent-ink); font-weight: 750; padding: .4rem .85rem; border-radius: var(--radius-pill); margin-bottom: 1rem; }
.verdub__calc { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow); overflow: clip; }
.verdub__row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1.1rem 1.4rem; border-bottom: 1px solid var(--line); }
.verdub__row:last-child { border-bottom: 0; }
.verdub__row--total { background: var(--ink); color: #fff; }
.verdub__row--total .verdub__amount { color: var(--accent); font-size: var(--step-2); }
.verdub__label { font-weight: 600; }
.verdub__label small { display: block; color: var(--ink-3); font-weight: 500; font-size: .82rem; }
.verdub__row--total .verdub__label small { color: #a9b6b1; }
.verdub__amount { font-weight: 800; font-size: var(--step-1); letter-spacing: -.02em; }
.verdub__note { font-size: .82rem; color: var(--ink-3); padding: .9rem 1.4rem; background: var(--surface-2); margin: 0; }

/* ---------- Stapelen: ISDE + gemeentelijke subsidie ---------- */
.stapel { position: relative; display: grid; grid-template-columns: 1fr auto 1fr; align-items: stretch; gap: clamp(1.25rem, 3vw, 2rem); }
.stapel__card {
	background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
	padding: clamp(1.5rem, 3vw, 2rem); box-shadow: var(--shadow-sm); display: flex; flex-direction: column;
}
.stapel__kicker {
	display: inline-flex; align-self: flex-start; align-items: center; gap: .4rem;
	font-size: var(--step--1); font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
	color: var(--primary-700); background: var(--primary-050); padding: .35rem .7rem; border-radius: var(--radius-pill);
	margin-bottom: 1rem;
}
.stapel__icon {
	width: 48px; height: 48px; display: flex; align-items: center; justify-content: center;
	border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); margin-bottom: 1rem;
}
.stapel__icon .wv-icon { width: 24px; height: 24px; }
.stapel__card h3 { font-size: var(--step-1); margin-bottom: .3rem; }
.stapel__card > p { color: var(--ink-3); font-size: var(--step--1); margin: 0; }
.stapel__amount { font-family: var(--font-display); font-weight: 800; font-size: var(--step-4); letter-spacing: -.03em; line-height: 1; color: var(--ink); margin: .2rem 0 .6rem; font-feature-settings: "tnum"; font-variant-numeric: tabular-nums; }
/* a plain colored line, not a filled pill: the full "bij energiearmoede..."
   sentence used to sit in a solid accent-yellow badge, which for the longer
   gemeenten wrapped to 2-3 lines of small bold text on a bright block -- the
   single biggest contributor to the card reading as cluttered. Same color
   coding, much less visual weight. */
.stapel__amount-sub { display: flex; align-items: flex-start; gap: .4rem; font-weight: 600; font-size: .82rem; color: var(--accent); margin: 0 0 .9rem; }
.stapel__amount-sub .wv-icon { flex: 0 0 auto; margin-top: .15rem; }
.stapel__desc { margin-bottom: .7rem !important; }
.stapel__card .link-arrow { margin-top: auto; padding-top: .5rem; }

/* voorwaarden/looptijd: real, carefully-sourced regulatory detail that must
   stay -- but shown by default it's what made the card feel like a wall of
   text. Tucked behind a native <details> so the card reads as a scannable
   headline number first; the fine print is one tap away, not gone. */
.stapel__details { margin: 0 0 1rem; }
.stapel__details summary {
	list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .35rem;
	font-size: .82rem; font-weight: 700; color: var(--ink-3);
}
.stapel__details summary::-webkit-details-marker { display: none; }
.stapel__details summary .wv-icon { width: 14px; height: 14px; transition: transform var(--dur-2) var(--ease-out); }
.stapel__details[open] summary .wv-icon { transform: rotate(180deg); }
.stapel__details__body { margin-top: .6rem; }
.stapel__details__body p { font-size: .82rem; color: var(--ink-3); margin: 0 0 .5rem; }
.stapel__card--lokaal .stapel__details summary { color: #9fb0ab; }
.stapel__card--lokaal .stapel__details__body p { color: #9fb0ab; }

.stapel__card--lokaal { background: var(--ink); border-color: var(--ink); color: #fff; }
.stapel__card--lokaal .stapel__kicker { background: rgba(254, 208, 61, .16); color: var(--accent); }
.stapel__card--lokaal .stapel__icon { background: rgba(254, 208, 61, .16); color: var(--accent); }
.stapel__card--lokaal h3 { color: #fff; }
.stapel__card--lokaal > p { color: #b9c4c0; }
.stapel__card--lokaal .stapel__amount { color: var(--accent); }
.stapel__card--lokaal .link-arrow { color: var(--accent); }
.stapel__card--lokaal .link-arrow:hover { color: var(--accent-strong); }

.stapel__plus {
	align-self: center; justify-self: center; width: 44px; height: 44px; flex: 0 0 auto;
	display: flex; align-items: center; justify-content: center; border-radius: 50%;
	background: var(--accent); color: var(--accent-ink); font-family: var(--font-display); font-weight: 800; font-size: 1.4rem;
	box-shadow: var(--shadow); z-index: 1;
}

.stapel__foot { display: flex; align-items: flex-start; gap: .6rem; margin-top: 1.5rem; padding-top: 1.2rem; border-top: 1px solid var(--line); }
.stapel__foot .wv-icon { flex: 0 0 auto; margin-top: .15rem; color: var(--ink-3); }
.stapel__foot p { margin: 0; font-size: .82rem; color: var(--ink-3); }

@media (max-width: 720px) {
	.stapel { grid-template-columns: 1fr; }
	.stapel__plus { justify-self: center; margin: -.4rem 0; }
}

/* ---------- Gemeenten (regio Zaandam) ---------- */
.kernen-list { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; grid-template-columns: repeat(2, 1fr); gap: .5rem .8rem; }
.kernen-list li { display: flex; align-items: center; gap: .5rem; font-weight: 600; font-size: var(--step--1); }
.kernen-list .wv-icon { color: var(--primary); flex: 0 0 auto; }
@media (max-width: 480px) { .kernen-list { grid-template-columns: 1fr; } }

.gemeenten-inline { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem .7rem; }
.gemeenten-inline a {
	display: inline-flex; padding: .5rem .9rem; border-radius: var(--radius-pill);
	background: var(--surface); border: 1px solid var(--line); color: var(--ink-2); font-weight: 600; font-size: var(--step--1);
	transition: border-color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.gemeenten-inline a:hover { background: var(--primary-050); border-color: var(--primary); color: var(--primary); }

/* ---------- Checklist ---------- */
.checklist { list-style: none; margin: 0 0 1.5rem; padding: 0; display: grid; gap: .6rem; }
.checklist li { position: relative; padding-left: 1.9rem; }
.checklist li::before {
	content: ""; position: absolute; left: 0; top: .15rem; width: 1.25rem; height: 1.25rem;
	background: var(--primary-050); border-radius: 50%;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%230b6b5b' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
	background-repeat: no-repeat; background-position: center;
}

/* Two-up variant: keeps each item's text left-aligned (checkmarks need a
   consistent left edge to scan) inside an otherwise centered block. */
.checklist--2 { grid-template-columns: repeat(2, 1fr); gap: .7rem 1.6rem; text-align: left; margin-bottom: 0; }
@media (max-width: 620px) { .checklist--2 { grid-template-columns: 1fr; } }

/* ---------- Werkwijze (steps) ---------- */
.steps { list-style: none; counter-reset: step; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1rem, 3vw, 1.8rem); }
.steps li { counter-increment: step; position: relative; padding-left: 3.4rem; }
.steps li::before {
	content: counter(step); position: absolute; left: 0; top: -.15rem;
	width: 2.5rem; height: 2.5rem; display: flex; align-items: center; justify-content: center;
	background: var(--primary); color: #fff; font-weight: 800; border-radius: var(--radius-md); font-size: 1.05rem;
}
.steps li h3 { font-size: var(--step-0); margin-bottom: .25rem; }
.steps li p { color: var(--ink-3); font-size: var(--step--1); margin: 0; }

/* ---------- Trust ---------- */
.trust-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; }
.trust-item { display: flex; align-items: center; gap: .9rem; padding: 1.1rem 1.3rem; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-sm); }
.trust-item__icon { flex: 0 0 auto; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); }
.trust-item__icon .wv-icon { width: 22px; height: 22px; }
.trust-item span:last-child { font-weight: 600; }

/* keurmerken: only rendered at all once warmvast_certificeringen() is non-empty */
.keurmerken { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .8rem 1.4rem; }
.keurmerken__item { display: flex; align-items: center; gap: .7rem; }
.keurmerken__item img { width: 44px; height: 44px; object-fit: contain; }
.keurmerken__item .wv-icon { width: 32px; height: 32px; color: var(--primary); flex: 0 0 auto; }
.keurmerken__item span { display: flex; flex-direction: column; line-height: 1.25; }
.keurmerken__item strong { font-size: .92rem; }
.keurmerken__item em { font-style: normal; color: var(--ink-3); font-size: .78rem; }
.site-footer .keurmerken__item strong { color: #fff; }
.site-footer .keurmerken__item em { color: #93a09b; }

/* trust-stats: only rendered once at least one of founded/homes/warranty is set */
.compare { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius-lg); overflow: clip; margin: 1.5rem 0; }
.compare__side { background: var(--surface); padding: 1.4rem; }
.compare__side--hot { background: var(--primary-050); }
.compare__icon { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); margin-bottom: .8rem; }
.compare__title { margin: 0 0 .4rem; font-size: var(--step-0); }
.compare__side p:last-child { margin: 0; color: var(--ink-3); font-size: var(--step--1); }

.trust-stats { display: flex; flex-wrap: wrap; gap: clamp(1.6rem, 5vw, 3.2rem); }
.trust-stats__item { text-align: left; }
.trust-stats__value { display: block; font-family: var(--font-display); font-size: var(--step-3); font-weight: 800; color: var(--primary); line-height: 1; letter-spacing: -.02em; }
.trust-stats__label { display: block; margin-top: .4rem; color: var(--ink-3); font-size: var(--step--1); font-weight: 600; }
.section--ink .trust-stats__value { color: var(--accent); }
.section--ink .trust-stats__label { color: #b9c4c0; }

/* trust-facts: one bordered card combining warmvast_trust_stats() (if any real
   ones are configured) with the always-on kernwaarden badges -- see
   warmvast_the_trust_facts(). auto-fit (not a fixed column count) because the
   item count varies with how many trust stats are configured; the divider
   rule below can draw a stray hairline at the left edge of a wrapped row's
   first item, an accepted trade-off for not hardcoding a count that drifts
   out of sync with the content. */
.trust-facts {
	list-style: none; margin: clamp(1.5rem, 3vw, 2.2rem) 0 0; padding: 0;
	display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
	background: var(--surface); border: 1px solid var(--line);
	border-radius: var(--radius-lg); box-shadow: var(--shadow); overflow: hidden;
}
.trust-facts__item { display: flex; align-items: center; gap: .8rem; padding: 1.15rem 1.4rem; position: relative; }
/* when an item is a real link (the scan landing page's "meer weten" tiles
   reuse this exact card for that) instead of the usual plain <li>, it needs
   its own text color reset (link color would otherwise tint strong/em) and
   a hover cue. */
a.trust-facts__item { color: inherit; transition: background-color var(--dur-1) var(--ease-out); }
a.trust-facts__item:hover { background: var(--primary-050); }
.trust-facts__item + .trust-facts__item::before { content: ""; position: absolute; left: 0; top: 22%; height: 56%; width: 1px; background: var(--line); }
.trust-facts__icon { flex: 0 0 auto; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); }
.trust-facts__item strong { display: block; font-size: .95rem; }
/* the homepage's only other user of this component (kernwaarden badges)
   never pairs strong with em, so this never got styled -- without it, the
   scan landing page's quick-link tiles (the first real strong+em use) ran
   both onto one line with no separation. */
.trust-facts__item em { display: block; margin-top: .3rem; font-style: normal; color: var(--ink-3); font-size: .82rem; }
.trust-facts__item--stat strong { display: block; font-family: var(--font-display); font-size: var(--step-2); font-weight: 800; color: var(--primary); line-height: 1; letter-spacing: -.02em; }
.trust-facts__item--stat span { display: block; margin-top: .35rem; color: var(--ink-3); font-size: .82rem; font-weight: 600; }

/* ---------- FAQ ---------- */
.faq { max-width: 820px; }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__item summary {
	list-style: none; cursor: pointer; padding: 1.1rem 0; display: flex; align-items: center; justify-content: space-between; gap: 1rem;
	font-weight: 700; font-size: var(--step-1); color: var(--ink);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary .wv-icon { color: var(--primary); transition: transform var(--dur-2) var(--ease-out); flex: 0 0 auto; }
.faq__item[open] summary .wv-icon { transform: rotate(180deg); }
.faq__item .faq__answer { padding: 0 0 1.2rem; color: var(--ink-2); }
.faq__item .faq__answer p:last-child { margin-bottom: 0; }

/* ---------- CTA band ---------- */
/* The same calming, warm dark backdrop as the hero (radial teal + amber over a
   dark base) — no grid here, and the ambient orbs instead of the house/steam. */
.cta-band {
	position: relative;
	overflow: clip;
	background:
		radial-gradient(120% 92% at 82% -25%, rgba(11,107,91,.5), transparent 56%),
		radial-gradient(96% 82% at 14% 125%, rgba(254,208,61,.12), transparent 54%),
		linear-gradient(180deg, #171b1d, #1d2426);
	color: #fff;
}
/* drifting thermal haze (the hero's warm shimmer, reduced-motion aware) */
.cta-band::before {
	content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
	opacity: .5; mix-blend-mode: screen; filter: blur(6px);
	background:
		radial-gradient(closest-side at 80% 16%, rgba(254,208,61,.28), transparent),
		radial-gradient(closest-side at 92% 64%, rgba(226,88,34,.20), transparent),
		radial-gradient(closest-side at 64% 6%, rgba(11,107,91,.30), transparent);
	animation: hero-drift 16s ease-in-out infinite alternate;
}
@media (prefers-reduced-motion: reduce) { .cta-band::before { animation: none; } }
/* the hero's ambient orbs (teal + amber), drifting with pointer + autonomous float */
.cta-band__orb {
	position: absolute; z-index: 0; border-radius: 50%; pointer-events: none;
	filter: blur(90px); mix-blend-mode: screen; will-change: transform;
}
.cta-band__orb--1 {
	width: 44vw; height: 44vw; max-width: 560px; max-height: 560px; top: -58%; right: -4%;
	background: radial-gradient(circle at center, rgba(11,107,91,.6), transparent 68%);
	transform: translate3d(calc(var(--wv-mx) * 90px + var(--wv-gx) * 46px), calc(var(--wv-my) * 60px + var(--wv-gy) * 34px), 0);
}
.cta-band__orb--2 {
	width: 36vw; height: 36vw; max-width: 460px; max-height: 460px; bottom: -58%; left: -3%;
	background: radial-gradient(circle at center, rgba(254,208,61,.22), transparent 68%);
	transform: translate3d(calc(var(--wv-mx) * -104px + var(--wv-fx) * 54px), calc(var(--wv-my) * -52px + var(--wv-fy) * 38px), 0);
}
@media (prefers-reduced-motion: reduce) { .cta-band__orb { transform: none !important; } }

/* Focused split: message left, one prominent action right, vertically aligned. */
.cta-band__inner {
	position: relative; z-index: 1;
	display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
	gap: clamp(1.4rem, 3.5vw, 3.2rem);
	padding-block: clamp(1.8rem, 3vw, 2.5rem);
}
.cta-band__copy { flex: 1 1 34ch; }
.cta-band h2 {
	color: #fff; margin: .15rem 0 .45rem; max-width: 24ch;
	font-size: var(--step-2); line-height: 1.14;
}
.cta-band h2 em { font-style: normal; color: var(--accent); }
.cta-band__sub { color: #cfe0da; margin: 0; max-width: 50ch; font-size: var(--step-0); }
.cta-band__proof {
	list-style: none; margin: .95rem 0 0; padding: 0;
	display: flex; flex-wrap: wrap; gap: .35rem 1.4rem;
}
.cta-band__proof li {
	display: inline-flex; align-items: center; gap: .45rem;
	color: #bcd3cc; font-size: var(--step--1); font-weight: 600;
}
.cta-band__proof .wv-icon { color: var(--accent); width: 16px; height: 16px; }

/* action cluster: one glowing primary CTA + a quiet phone fallback */
.cta-band__actions {
	flex: 0 0 auto; display: flex; flex-direction: column; align-items: stretch; gap: .7rem;
	text-align: center;
}
.cta-band__cta {
	font-size: var(--step-1); white-space: nowrap;
	padding-inline: clamp(1.5rem, 2.6vw, 2.4rem);
	box-shadow: 0 14px 36px -12px rgba(254,208,61,.6), 0 3px 10px rgba(0,0,0,.2);
	transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.cta-band__cta:hover { transform: translateY(-2px); box-shadow: 0 18px 44px -12px rgba(254,208,61,.75), 0 3px 10px rgba(0,0,0,.22); }
.cta-band__phone {
	display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
	color: #cfe0da; font-weight: 700; font-size: var(--step--1);
	transition: color var(--dur-1) var(--ease-out);
}
.cta-band__phone:hover { color: #fff; }
.cta-band__phone .wv-icon { color: var(--accent); width: 18px; height: 18px; }
.cta-band .keurmerken { margin-top: 1.1rem; gap: .6rem 1.6rem; }
.cta-band .keurmerken__item .wv-icon { color: var(--accent); width: 26px; height: 26px; }
.cta-band .keurmerken__item strong { color: #fff; font-size: .88rem; }
.cta-band .keurmerken__item em { color: #b9c4c0; }
@media (max-width: 620px) {
	.cta-band__actions { width: 100%; }
}

/* ---------- Footer ---------- */
.site-footer { background: var(--ink); color: #b9c4c0; }
.site-footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: 2rem; padding-block: clamp(2.5rem, 5vw, 3.8rem); }
.site-footer__tag { margin: 1rem 0 1.2rem; max-width: 40ch; font-size: var(--step--1); }
.site-footer__contact { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.site-footer__contact li { display: flex; align-items: center; gap: .5rem; font-size: var(--step--1); }
.site-footer__contact a { display: inline-flex; align-items: center; gap: .5rem; color: #dfe7e4; }
.site-footer__contact a:hover { color: var(--accent); }
.site-footer__contact .wv-icon { color: var(--accent); width: 18px; height: 18px; }
.site-footer__title { color: #fff; font-size: var(--step-0); margin-bottom: .9rem; }
.site-footer__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.site-footer__col a { color: #b9c4c0; font-size: var(--step--1); }
.site-footer__col a:hover { color: #fff; }
.site-footer__badge { display: flex; align-items: flex-start; gap: .5rem; margin-top: 1.2rem; font-size: .78rem; color: #93a09b; }
.site-footer__badge .wv-icon { color: var(--accent); width: 18px; height: 18px; flex: 0 0 auto; }
.site-footer__bar { border-top: 1px solid rgba(255,255,255,.1); }
.site-footer__bar-inner { display: flex; justify-content: space-between; gap: 1rem; padding-block: 1.2rem; font-size: .8rem; color: #8b9893; flex-wrap: wrap; }
.site-footer__bar p { margin: 0; }

/* ---------- Page hero / prose ---------- */
.page-hero { background: var(--surface); border-bottom: 1px solid var(--line); }
.page-hero--article { background: var(--paper); }
.page-hero__inner { padding-block: clamp(2rem, 4vw, 3.2rem); }
.page-hero__title { font-size: var(--step-4); max-width: 20ch; }
.page-hero__sub { font-size: var(--step-1); color: var(--ink-2); max-width: 60ch; }
.page-hero__meta { color: var(--ink-3); font-size: var(--step--1); }
.breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; font-size: var(--step--1); color: var(--ink-3); margin-bottom: 1rem; }
.breadcrumb a { color: var(--ink-3); }
.breadcrumb a:hover { color: var(--primary); }

.page-body { padding-block: clamp(2.2rem, 5vw, 3.5rem); }
.prose { max-width: 760px; }
.prose > * + * { margin-top: 1.1rem; }
.prose h2 { font-size: var(--step-2); margin-top: 2.2rem; }
.prose h3 { font-size: var(--step-1); margin-top: 1.6rem; }
.prose ul, .prose ol { padding-left: 1.3rem; }
.prose li { margin-bottom: .4rem; }
.prose img { border-radius: var(--radius); }
.prose blockquote { border-left: 4px solid var(--accent); background: var(--surface); padding: 1rem 1.3rem; margin: 1.5rem 0; border-radius: 0 var(--radius) var(--radius) 0; }
.prose a { text-decoration: underline; text-underline-offset: 2px; }
.prose .wp-block-table table { width: 100%; border-collapse: collapse; }
.prose table th, .prose table td { border: 1px solid var(--line); padding: .6rem .8rem; text-align: left; }
.prose table th { background: var(--surface-2); }

/* callout box usable inside prose */
/* article illustrations (custom SVG diagrams, not stock photography) */
.article-visual {
	margin: 0 0 1.8rem; border-radius: var(--radius-lg); overflow: clip;
	border: 1px solid var(--line); box-shadow: var(--shadow-sm); background: var(--surface);
}
.article-visual svg { width: 100%; height: auto; display: block; }
.article-visual--card { margin-bottom: 0; border-radius: var(--radius); aspect-ratio: 640/260; }
.article-visual--card svg { width: 100%; height: 100%; object-fit: cover; }

.wv-callout { background: var(--primary-050); border: 1px solid #cde3dd; border-radius: var(--radius); padding: 1.2rem 1.4rem; }
.wv-callout--warn { background: var(--warn-bg); border-color: #edd7c2; }
.wv-disclaimer { font-size: .82rem; color: var(--ink-3); border-top: 1px solid var(--line); padding-top: 1rem; }

/* article inline cta */
.article-cta { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; background: var(--surface); border: 1px solid var(--line); border-left: 4px solid var(--accent); border-radius: var(--radius); padding: 1.5rem; margin-top: 2rem; }
.article-cta h2 { font-size: var(--step-1); margin-bottom: .25rem; }
.article-cta p { margin: 0; color: var(--ink-3); }

/* posts grid */
.post-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
.card--post { padding: 0; overflow: clip; display: flex; flex-direction: column; }
.card--post__media img { width: 100%; height: 190px; object-fit: cover; }
.card--post__media--icon { position: relative; height: 190px; display: flex; align-items: center; justify-content: center; background: var(--surface-2); color: var(--primary); }
.card--post__media--icon .wv-icon { width: 44px; height: 44px; }
.card--post__body { padding: 1.3rem; display: flex; flex-direction: column; gap: .5rem; }
.card--post__title { font-size: var(--step-1); margin: 0; }
.project-card__tags { display: flex; flex-wrap: wrap; gap: .4rem; margin: 0; }
.project-card__tag { display: inline-flex; padding: .3rem .7rem; border-radius: var(--radius-pill); background: var(--primary-050); color: var(--primary); font-weight: 700; font-size: .78rem; }
.project-card__badge { position: absolute; top: .8rem; left: .8rem; padding: .3rem .7rem; border-radius: var(--radius-pill); background: var(--accent); color: var(--ink); font-weight: 700; font-size: .7rem; letter-spacing: .03em; text-transform: uppercase; }
.pagination { margin-top: 2.5rem; }
.pagination .nav-links { display: flex; gap: .5rem; flex-wrap: wrap; }
.pagination a, .pagination .current { padding: .5rem .9rem; border-radius: var(--radius-sm); border: 1px solid var(--line); }
.pagination .current { background: var(--primary); color: #fff; border-color: var(--primary); }

/* utilities */
.u-mt-0 { margin-top: 0; }
.u-center { text-align: center; }
.hidden { display: none !important; }

/* ---------- Service page ---------- */
.service-hero__grid { display: grid; grid-template-columns: 1.4fr .9fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
.service-hero__icon { display: inline-flex; width: 56px; height: 56px; align-items: center; justify-content: center; border-radius: var(--radius); background: var(--primary); color: #fff; margin-bottom: 1rem; }
.service-hero__icon .wv-icon { width: 28px; height: 28px; }
.service-hero__actions { display: flex; align-items: center; gap: 1.2rem; margin-top: 1.5rem; flex-wrap: wrap; }
.service-hero__phone { display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; color: var(--ink); }
.service-hero__phone .wv-icon { color: var(--primary); }
.service-hero__facts { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 1.5rem; }
.facts { margin: 1rem 0 0; display: grid; gap: 0; }
.facts > div { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding: .7rem 0; border-bottom: 1px solid var(--line); }
.facts > div:last-child { border-bottom: 0; }
.facts dt { color: var(--ink-3); font-size: var(--step--1); margin: 0; }
.facts dd { margin: 0; font-weight: 800; font-size: var(--step-1); letter-spacing: -.02em; }
.facts__hot { color: var(--primary); }
.facts__note { font-size: .78rem; color: var(--ink-3); margin: .8rem 0 0; }

.tariff { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; }
.tariff__cell { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.3rem; display: flex; flex-direction: column; gap: .5rem; }
.tariff__cell--hot { background: var(--ink); color: #fff; border-color: var(--ink); }
.tariff__label { font-size: var(--step--1); color: var(--ink-3); }
.tariff__cell--hot .tariff__label { color: #b7c2be; }
.tariff__value { font-size: var(--step-3); font-weight: 800; letter-spacing: -.03em; line-height: 1; }
.tariff__cell--hot .tariff__value { color: var(--accent); }
.tariff__value small { font-size: 1rem; font-weight: 600; color: var(--ink-3); margin-left: .2rem; }
.tariff__cell--hot .tariff__value small { color: #9fb0ab; }

.steps--4 { grid-template-columns: repeat(4, 1fr); }

/* rate table */
.table-wrap { overflow-x: auto; border-radius: var(--radius); border: 1px solid var(--line); }
.rate-table { width: 100%; border-collapse: collapse; background: var(--surface); min-width: 560px; }
.rate-table th, .rate-table td { padding: .95rem 1.1rem; text-align: left; border-bottom: 1px solid var(--line); }
.rate-table thead th { background: var(--surface-2); font-size: var(--step--1); text-transform: uppercase; letter-spacing: .04em; color: var(--ink-3); }
.rate-table tbody tr:last-child td { border-bottom: 0; }
.rate-table td { font-weight: 600; }
.rate-table__hot { color: var(--primary); font-weight: 800; }

/* contact */
.contact-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.contact-card { display: flex; align-items: center; gap: .9rem; text-decoration: none; color: var(--ink); }
.contact-card__icon { flex: 0 0 auto; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); }
.contact-card strong { display: block; }
.contact-card em { font-style: normal; color: var(--ink-3); font-size: var(--step--1); }

.service-card--wide { flex-direction: column; }
@media (max-width: 560px) { .contact-cards { grid-template-columns: 1fr; } }

/* ==========================================================================
   Woningscan (address-driven)
   ========================================================================== */
.ws {
	color: var(--ink); border: 1px solid transparent;
	/* solid fill (padding-box) keeps the form razor-legible; gradient hairline
	   (border-box) + float shadow + top light-catch make it read as glass. */
	background:
		linear-gradient(var(--surface), var(--surface)) padding-box,
		var(--glass-border) border-box;
	border-radius: var(--radius-lg); overflow: clip;
	box-shadow: var(--shadow-float), inset 0 1px 0 rgba(255,255,255,.65);
	padding: clamp(1.3rem, 3vw, 1.9rem);
}
.ws-head { margin-bottom: 1.1rem; }
.ws-head h2 { font-size: var(--step-2); margin-bottom: .3rem; }
.ws-head > p { color: var(--ink-3); font-size: var(--step--1); margin: 0; }
.ws-stage[hidden] { display: none; }

/* address form */
.ws-address__fields { display: grid; grid-template-columns: 1.4fr 1fr .8fr; gap: .7rem; margin-bottom: .9rem; }
.ws-address__fields label { display: block; font-size: var(--step--1); font-weight: 600; color: var(--ink-2); }
.ws-address input { width: 100%; font: inherit; margin-top: .3rem; padding: .75rem .8rem; background: var(--surface-2); border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm); }
.ws-address input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-050); }
.ws-address__btn { width: 100%; }
.ws-privacy { font-size: .8rem; color: var(--ink-3); text-align: center; margin: .8rem 0 0; }
.ws-error { color: var(--warn); background: var(--warn-bg); border-radius: var(--radius-sm); padding: .55rem .75rem; margin: .8rem 0 0; font-size: var(--step--1); }
.ws-error:empty { display: none; }

/* loading */
.ws-loading { text-align: center; padding: 2rem 1rem 2.35rem; color: var(--ink-3); }
.ws-loading[hidden] { display: none; }
.ws-loader {
	position: relative;
	width: 92px;
	height: 82px;
	margin: 0 auto .8rem;
	overflow: visible;
}
.ws-loader svg { position: relative; width: 100%; height: 100%; overflow: visible; }
.ws-loader__heat path {
	opacity: 0;
	animation: ws-heat-rise 1.8s ease-in-out infinite;
}
.ws-loader__heat path:nth-child(2) { animation-delay: .22s; }
.ws-loader__heat path:nth-child(3) { animation-delay: .44s; }
.ws-loader__house { filter: drop-shadow(0 4px 9px rgba(11,107,91,.13)); }
.ws-loading p {
	max-width: 30ch;
	margin: 0 auto;
	font-weight: 700;
	color: var(--ink-2);
}
@keyframes ws-heat-rise {
	0% { opacity: 0; transform: translateY(5px); }
	22% { opacity: 1; }
	68% { opacity: .75; }
	100% { opacity: 0; transform: translateY(-10px); }
}
@keyframes ws-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
	.ws-loader__heat path { animation: none; opacity: 1; }
}

/* back link */
.ws-back { display: inline-flex; align-items: center; gap: .3rem; background: none; border: 0; color: var(--ink-3); font: inherit; font-weight: 600; cursor: pointer; padding: 0 0 .6rem; }
.ws-back:hover { color: var(--primary); }
.ws-back .wv-icon { transform: rotate(90deg); }

/* verdict */
.ws-verdict { font-weight: 700; }
.ws-verdict.is-ok { color: var(--success); }
.ws-verdict.is-warn { color: var(--warn); }

/* visuals: house + aerial */
.ws-visuals { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; margin-bottom: 1.1rem; }
.ws-house { background: linear-gradient(160deg, #f2f6f4, #e7efec); border: 1px solid var(--line); border-radius: var(--radius); aspect-ratio: 4/3; display: flex; align-items: center; justify-content: center; }
.ws-house__svg { width: 100%; height: 100%; }
.ws-house__wall { fill: var(--primary); stroke: var(--primary-700); stroke-width: .4; stroke-linejoin: round; }
.ws-house__roof { fill: var(--accent); stroke: var(--accent-700); stroke-width: .4; stroke-linejoin: round; }
/* crisp ridge line along the roof/wall junction — reinforces the technical-drawing feel */
.ws-house__outline { fill: none; stroke: var(--primary-700); stroke-width: .6; opacity: .55; }
.ws-house__shadow { fill: rgba(10,40,34,.10); }
.ws-aerial { position: relative; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); aspect-ratio: 4/3; background: var(--surface-2); }
.ws-aerial img { width: 100%; height: 100%; object-fit: cover; }
.ws-aerial__overlay {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}
.ws-aerial__footprint {
	fill: rgba(254,208,61,.18);
	stroke: var(--accent);
	stroke-width: 1.15;
	vector-effect: non-scaling-stroke;
	filter: drop-shadow(0 0 4px rgba(0,0,0,.55));
}

/* details / surfaces */
.ws-details, .ws-label { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem 1.1rem; margin-bottom: 1rem; }
.ws-details h3, .ws-label h3 { display: flex; align-items: baseline; flex-wrap: wrap; gap: .25rem; font-size: var(--step-0); margin-bottom: .85rem; }
.ws-hint { font-weight: 500; font-size: .76rem; line-height: 1.3; color: var(--ink-3); }
.ws-surfaces { display: grid; gap: .5rem; }
.ws-surfaces label { display: flex; align-items: center; justify-content: space-between; gap: 1rem; font-weight: 600; }
.ws-suffix { display: inline-flex; align-items: center; gap: .4rem; color: var(--ink-3); font-weight: 500; }
.ws-suffix input { width: 92px; text-align: right; font: inherit; font-weight: 700; color: var(--ink); padding: .45rem .6rem; background: var(--surface); border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm); }
.ws-suffix input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-050); }
.ws-meta { font-size: .8rem; color: var(--ink-3); margin: .7rem 0 0; }

/* energy label scale */
.ws-label__scale {
	display: grid;
	grid-template-columns: repeat(11, minmax(0, 1fr));
	gap: 6px;
	margin-bottom: 1.45rem;
}
.ws-label__cell {
	position: relative;
	isolation: isolate;
	min-width: 0;
	min-height: 48px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--radius-sm);
	background: color-mix(in srgb, var(--c) 32%, #fff);
	color: color-mix(in srgb, var(--c) 58%, #333);
	font-family: var(--font-display);
	font-size: .76rem;
	font-weight: 800;
	line-height: 1;
	letter-spacing: 0;
	white-space: nowrap;
}
.ws-label__cell[data-label^="A+"] { font-size: .66rem; }
.ws-label__cell.is-active {
	background: var(--c);
	color: #fff;
	outline: 2px solid #fff;
	box-shadow: 0 0 0 4px var(--c);
	z-index: 2;
}
.ws-label__cell em {
	position: absolute;
	top: calc(100% + 7px);
	left: 50%;
	transform: translateX(-50%);
	font-family: var(--font-sans);
	font-style: normal;
	font-size: .61rem;
	font-weight: 800;
	line-height: 1;
	color: var(--ink-2);
	white-space: nowrap;
	pointer-events: none;
}
.ws-label__source { margin: 0; font-size: .8rem; line-height: 1.45; color: var(--ink-3); }

/* measures */
.ws-measures { list-style: none; margin: 0 0 1.1rem; padding: 0; display: grid; gap: .5rem; }
.ws-measure { display: flex; align-items: center; gap: .7rem; padding: .8rem 1rem; border: 1.5px solid var(--line-strong); border-radius: var(--radius); cursor: pointer; font-weight: 600; }
.ws-measure:has(input:checked) { border-color: var(--primary); background: var(--primary-050); }
.ws-measure input { accent-color: var(--primary); width: 1.15rem; height: 1.15rem; }
.ws-measure__name { display: inline-flex; align-items: center; gap: .45rem; }
.ws-measure__name .wv-icon { color: var(--primary); }
.ws-measure__m2 { margin-left: auto; font-weight: 700; color: var(--primary-700); }

/* subsidie / besparing figures */
.ws-figures { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }
.ws-figure { border-radius: var(--radius); padding: 1.1rem 1.2rem; }
.ws-figure { display: flex; flex-direction: column; align-items: flex-start; }
.ws-figure--subsidie { background: var(--ink); color: #fff; }
.ws-figure--besparing { background: var(--primary-050); border: 1px solid #cde3dd; }
.ws-figure__label { display: block; font-size: var(--step--1); color: var(--ink-3); margin-bottom: .35rem; }
.ws-figure--subsidie .ws-figure__label { color: #b7c2be; }
.ws-figure__val { font-family: var(--font-display); font-size: clamp(1.7rem, 5vw, 2.3rem); font-weight: 800; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.ws-figure--subsidie .ws-figure__val { color: var(--accent); }
.ws-figure--besparing .ws-figure__val { color: var(--primary-700); }
.ws-badge { display: none; margin-top: .6rem; align-items: center; gap: .3rem; background: var(--accent); color: var(--accent-ink); font-weight: 750; font-size: .72rem; padding: .25rem .55rem; border-radius: var(--radius-pill); }
.ws-badge.is-on { display: inline-flex; }
.ws-disclaimer { font-size: .78rem; color: var(--ink-3); margin: 1rem 0 1.1rem; }

/* lead form */
.ws-lead { display: grid; gap: .7rem; }
.ws-lead label { display: block; font-weight: 600; font-size: var(--step--1); }
.ws-lead .wv-consent--inline {
	display: block;
	margin-top: .1rem;
	font-weight: 500 !important;
	color: var(--ink-2);
	line-height: 1.5;
}
.ws-lead .wv-consent--inline span { display: inline; }
.ws-lead .wv-consent--inline input {
	display: inline-block;
	width: 16px;
	height: 16px;
	margin: 0 0 0 .45rem;
	vertical-align: -2px;
	accent-color: var(--primary);
}
.ws-lead input, .ws-lead textarea { width: 100%; font: inherit; margin-top: .25rem; padding: .7rem .8rem; background: var(--surface-2); border: 1.5px solid var(--line-strong); border-radius: var(--radius-sm); }
.ws-lead input:focus, .ws-lead textarea:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-050); }
.ws-honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.ws-full { width: 100%; }
.ws-success { text-align: center; padding: 1.5rem 1rem; }
.ws-success .wv-success__mark { margin-inline: auto; }

@media (max-width: 520px) {
	.ws-address__fields { grid-template-columns: 1fr; }
	.ws-visuals { grid-template-columns: 1fr; }
	.ws-figures { grid-template-columns: 1fr; }
	.ws-details, .ws-label { padding: .95rem; }
	.ws-label__scale { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 5px; row-gap: 1.65rem; margin-bottom: 1.5rem; }
	.ws-label__cell { min-height: 42px; font-size: .7rem; border-radius: 7px; }
	.ws-label__cell[data-label^="A+"] { font-size: .61rem; }
	.ws-label__cell em { top: calc(100% + 6px); font-size: .58rem; }
}

/* ---------- Reviews ---------- */
.reviews__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 1.5rem; margin-bottom: 2rem; }
.reviews__head .section-head { margin-bottom: 0; }
.reviews__score { display: flex; align-items: center; gap: .9rem; }
.reviews__num { font-family: var(--font-display); font-size: 3rem; font-weight: 700; line-height: 1; letter-spacing: -.03em; color: var(--ink); }
.reviews__score em { display: block; font-style: normal; color: var(--ink-3); font-size: var(--step--1); margin-top: .2rem; }
.stars { display: inline-flex; gap: 2px; }
.star { width: 18px; height: 18px; fill: var(--line-strong); }
.star--on { fill: var(--accent-strong); }
.review { display: flex; flex-direction: column; gap: .8rem; }
.review .stars .star { width: 16px; height: 16px; }
.review blockquote { margin: 0; font-size: 1.02rem; color: var(--ink); line-height: 1.55; }
.review figcaption { display: flex; align-items: center; gap: .7rem; margin-top: auto; }
.review__avatar { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--primary); color: #fff; font-weight: 700; font-family: var(--font-display); }
.review figcaption strong { display: block; }
.review figcaption em { font-style: normal; color: var(--ink-3); font-size: var(--step--1); }

/* ---------- USP bar ---------- */
/* A centred, rounded overview card that floats fully inside the hero,
   anchored flush to its bottom edge (bottom:0, not offset) and sharing the
   page sheet's own background color -- so the card's bottom simply becomes
   the page starting underneath it, no visible seam, instead of a gap of
   dark hero showing between the card and the page below. Absolutely
   positioned (not a sibling with negative margin) so it can never end up
   below the fold: the hero is exactly 100svh, so anything outside its own
   box would need scrolling to reach. Below the breakpoint where the hero
   stacks to one column, it drops back into normal flow. */
.usp-bar {
	position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
}
/* Concave corner fill: the card's square bottom corners (see .usp-bar__grid)
   sit flush against the hero's own flat bottom edge, which continues past
   the card's narrower width -- that junction (card's straight side edge
   meeting the hero's straight bottom edge) is itself a sharp inward corner,
   even though both the card and the hero-bottom are individually "seamless"
   now. mask-image (not a plain radial-gradient background) so the actual
   fill can be the card's real background -- flat color AND the same noise
   grain (color alone still mismatched the page, same reason as the card
   itself) -- clipped to a quarter-circle instead of trying to paint a
   second, separate approximation of it. The circle is centered on the
   corner AWAY from the card (0%/100% of this small box, i.e. deeper into
   the hero) with radius var(--radius-lg): everything closer than that stays
   masked out (hero's own background shows through, unaltered), everything
   at-or-beyond it is filled -- the fill reaches the junction point itself
   and curves away from it, not a bump centered ON the junction (that read
   as a ball, not a fillet -- the first version had the circle backwards).
   A thin border ring (::before, unmasked, 1px larger) gives it the same
   edge definition as the card's own top/side border. Two fixed
   940px-card-width halves (470px from center), not measured -- the card's
   max-width is itself a fixed value, so this stays exact at any viewport
   wide enough for the card to be at full width. */
.usp-bar__notch {
	position: absolute; bottom: 0; z-index: 1;
	width: var(--radius-lg); height: var(--radius-lg);
	pointer-events: none;
}
.usp-bar__notch::before {
	content: ""; position: absolute; inset: 0;
	background: var(--line);
}
.usp-bar__notch::after {
	content: ""; position: absolute; inset: 1px;
	background: var(--noise-texture) 0 0 / 220px 220px, #f4f4ef;
}
.usp-bar__notch--left {
	right: calc(50% + 470px);
}
.usp-bar__notch--left::before {
	-webkit-mask-image: radial-gradient(circle at 0% 0%, transparent calc(var(--radius-lg) - .5px), #fff var(--radius-lg));
	mask-image: radial-gradient(circle at 0% 0%, transparent calc(var(--radius-lg) - .5px), #fff var(--radius-lg));
}
.usp-bar__notch--left::after {
	--r: calc(var(--radius-lg) - 1px);
	-webkit-mask-image: radial-gradient(circle at 0% 0%, transparent calc(var(--r) - .5px), #fff var(--r));
	mask-image: radial-gradient(circle at 0% 0%, transparent calc(var(--r) - .5px), #fff var(--r));
}
.usp-bar__notch--right {
	left: calc(50% + 470px);
}
.usp-bar__notch--right::before {
	-webkit-mask-image: radial-gradient(circle at 100% 0%, transparent calc(var(--radius-lg) - .5px), #fff var(--radius-lg));
	mask-image: radial-gradient(circle at 100% 0%, transparent calc(var(--radius-lg) - .5px), #fff var(--radius-lg));
}
.usp-bar__notch--right::after {
	--r: calc(var(--radius-lg) - 1px);
	-webkit-mask-image: radial-gradient(circle at 100% 0%, transparent calc(var(--r) - .5px), #fff var(--r));
	mask-image: radial-gradient(circle at 100% 0%, transparent calc(var(--r) - .5px), #fff var(--r));
}
@media (max-width: 960px) {
	.usp-bar__notch { display: none; }
	/* position:static drops out of z-index entirely (it only applies to
	   positioned elements), so without this the always-on .hero__thermal /
	   .hero__orb glow layers (position:absolute, z-index:0) paint ABOVE this
	   card regardless of DOM order -- a translucent dark wash sitting on top
	   of a white card, exactly the "unreadable dark text" bug. `relative` +
	   a z-index keeps it in its own stacking layer above them. */
	.usp-bar { position: relative; z-index: 2; margin-top: clamp(1.5rem, 4vw, 2.5rem); }
	/* the square-bottom "cutout" look only makes sense flush against the
	   page below it (see .usp-bar__grid); here the card floats in normal
	   flow with a gap under it, so a squared-off bottom just reads as
	   accidentally clipped. Round it back. */
	.usp-bar__grid { border-radius: var(--radius-lg); border-bottom: 1px solid var(--line); }
}
.usp-bar__grid {
	display: grid; grid-template-columns: repeat(4, 1fr); gap: 0;
	max-width: 940px; margin-inline: auto;
	/* explicit reset: .usp-bar now lives inside .hero, which sets a near-white
	   color for its dark-background copy. Without this, .usp__text strong (no
	   color of its own) would inherit that near-white straight onto this white
	   card and become unreadable. */
	color: var(--ink);
	/* same literal color AND the same grain as body/.site-main's sheet
	   background (no shared variable exists for either yet) -- deliberately
	   not var(--surface)/white, so the card's bottom blends straight into
	   the page instead of reading as a white card sitting in front of it.
	   The color alone still measurably mismatched: .site-main's noise
	   texture (5% black grain) darkens its #f4f4ef by a few RGB points on
	   average, and this flat background didn't have that same grain --
	   without it, the seam was still visible as a faint but real color
	   step even with matching base colors. */
	background: #f4f4ef var(--noise-texture); background-size: 220px 220px;
	/* no border-bottom: a full 1px border on all four sides drew a visible
	   grey hairline right across the seam -- exactly the cut the color-match
	   was meant to erase. Top/sides still get one, for definition against
	   the hero. */
	border: 1px solid var(--line); border-bottom: none;
	/* rounded top only, square bottom -- the card visually rises out of the
	   page it shares a color with, instead of floating as a fully-rounded
	   card that happens to touch it. */
	border-radius: var(--radius-lg) var(--radius-lg) 0 0;
	/* var(--shadow-lg) is tuned for light backgrounds and all but disappears
	   against the hero's own near-black tone. Heavier alone wasn't enough
	   either: .hero's overflow:hidden clips right at this card's own bottom
	   edge, so any shadow layer reaching past it got sliced mid-fade -- a
	   second hard line, on top of the border one, on the light page right
	   below (only actually visible where that page happens to be light
	   enough for a dark smudge to stand out -- true on both the homepage
	   and the scan landing page, but only obvious on the latter). Negative
	   Y offsets + spread need every layer's reach (offset + blur + spread)
	   at or BELOW 0 for that to hold -- the two side layers were still
	   landing at +16 (a real miscalculation, not just theoretical) until
	   corrected here, which is what was actually leaking. */
	box-shadow:
		0 -32px 44px -14px rgba(0,0,0,.5),
		-26px -26px 44px -20px rgba(0,0,0,.4),
		26px -26px 44px -20px rgba(0,0,0,.4);
	overflow: hidden;
}
.usp { display: flex; align-items: center; gap: .8rem; padding: 1.15rem 1.4rem; position: relative; }
.usp + .usp::before { content: ""; position: absolute; left: 0; top: 22%; height: 56%; width: 1px; background: var(--line); }
.usp__icon { flex: 0 0 auto; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); }
.usp__text { display: flex; flex-direction: column; line-height: 1.25; }
.usp__text strong { font-size: .98rem; }
.usp__text em { font-style: normal; color: var(--ink-3); font-size: .82rem; }

/* ---------- Button sheen ----------
   Reserved for the single primary hero CTA (.btn--sheen) — a sweep on every
   accent/primary button site-wide reads as a cheap SaaS-promo tic, not premium. */
.btn--sheen { position: relative; overflow: hidden; }
.btn--sheen::after {
	content: ""; position: absolute; top: 0; left: -120%; width: 55%; height: 100%;
	background: linear-gradient(100deg, transparent, rgba(255,255,255,.38), transparent);
	transform: skewX(-18deg); transition: left var(--dur-3) var(--ease-out); pointer-events: none;
}
.btn--sheen:hover::after { left: 130%; }
@media (prefers-reduced-motion: reduce) { .btn--sheen::after { display: none; } }

/* ---------- Subsidie: ontzorging journey ---------- */
.ontzorg-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1rem, 2.5vw, 1.6rem); }
.ontzorg-card { position: relative; }
.ontzorg-card__step { position: absolute; top: 1.1rem; right: 1.3rem; font-family: var(--font-display); font-weight: 700; font-size: 1.35rem; line-height: 1; color: var(--line-strong); }
.ontzorg-card__icon { display: inline-flex; width: 48px; height: 48px; align-items: center; justify-content: center; border-radius: var(--radius-md); background: var(--primary-050); color: var(--primary); margin-bottom: .9rem; }
.ontzorg-card__icon .wv-icon { width: 24px; height: 24px; }
.ontzorg-card h3 { font-size: var(--step-1); margin-bottom: .4rem; }
.ontzorg-card p { color: var(--ink-3); font-size: var(--step--1); margin: 0; }
.ontzorg-note { display: flex; align-items: flex-start; gap: .6rem; max-width: 64ch; margin: 1.7rem auto 0; font-size: var(--step--1); color: var(--ink-3); }
.ontzorg-note .wv-icon { color: var(--primary); flex: 0 0 auto; margin-top: .15rem; }
@media (max-width: 860px) { .ontzorg-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .ontzorg-grid { grid-template-columns: 1fr; } }

/* ---------- Service card: warm top glow + full-perimeter hover trace ---------- */
@property --wv-trace { syntax: "<angle>"; inherits: false; initial-value: 0deg; }

.service-card { position: relative; isolation: isolate; }

/* warm hue glowing up from the top (echoes "trapped heat rising"): subtle by
   default so the brand hue is always a touch present, intensifies on hover. */
.service-card::before {
	content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: -1;
	background: radial-gradient(120% 78% at calc(50% + var(--wv-mx) * 30% + var(--wv-fx) * 14%) calc(-24% + var(--wv-my) * 16% + var(--wv-fy) * 9%), rgba(254,208,61,.24), transparent 60%);
	opacity: .38; transition: opacity var(--dur-3) var(--ease-out); pointer-events: none;
}
.card--link.service-card:hover::before { opacity: 1; }

/* a cool→warm light continuously traces the full outline on hover. inset:-1px
   + explicit radius makes the ring sit exactly on the card's rounded edge (no
   corner gap); a seamless linear loop means it never stops abruptly. A bright
   comet head plus a second, dimmer chaser keep it lively. */
.service-card::after {
	content: ""; position: absolute; inset: -1px; border-radius: var(--radius-lg); padding: 2px;
	background: conic-gradient(from var(--wv-trace),
		transparent 0deg,
		rgba(11,107,91,.5) 34deg,
		var(--primary) 60deg,
		var(--accent) 84deg,
		#fff7db 98deg,
		transparent 110deg,
		transparent 206deg,
		rgba(11,107,91,.32) 228deg,
		var(--accent) 252deg,
		transparent 272deg,
		transparent 360deg);
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	        mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor; mask-composite: exclude;
	opacity: 0; pointer-events: none; z-index: 2;
	transition: opacity .3s var(--ease-out);
}
.card--link.service-card:hover::after {
	opacity: 1;
	/* slower, and eased per quarter-turn so the light lingers ("magnet") as it
	   rounds each corner, then pulls smoothly along the straight edges. The 37°
	   phase places each slow-point with the comet head roughly on a corner. */
	animation: wv-card-trace 3.6s cubic-bezier(.66,0,.34,1) infinite;
}
@keyframes wv-card-trace {
	0%   { --wv-trace: 37deg; }
	25%  { --wv-trace: 127deg; }
	50%  { --wv-trace: 217deg; }
	75%  { --wv-trace: 307deg; }
	100% { --wv-trace: 397deg; }
}

@media (prefers-reduced-motion: reduce) {
	.service-card::after { display: none; }
}

/* ---------- Smooth FAQ open ---------- */
@keyframes wv-faq-down { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.faq__item[open] .faq__answer { animation: wv-faq-down var(--dur-2) var(--ease-out) both; }
@media (prefers-reduced-motion: reduce) { .faq__item[open] .faq__answer { animation: none; } }

/* ---------- Staggered reveal within grids ---------- */
/* capped 2-tier cascade: enough to read as a sequence without leaving the tail
   of a long row still fading in once it's already fully on screen */
.wv-js .grid > [data-reveal]:nth-child(n+2) { transition-delay: .04s; }
.wv-js .grid > [data-reveal]:nth-child(n+4) { transition-delay: .08s; }
.wv-js .steps > [data-reveal]:nth-child(n+2) { transition-delay: .04s; }
.wv-js .steps > [data-reveal]:nth-child(n+4) { transition-delay: .08s; }

/* ==========================================================================
   Premium layering: glass modules, asymmetric flow, ambient depth
   Scoped to the named high-value modules only — glass reads as hierarchy,
   not decoration, and stays cheap (≈5 backdrop-filters, not every chip).
   ========================================================================== */

/* Glass elevation for the 4 measures cards + the ISDE verdubbelaar logic card.
   Translucency is justified here: these sit over the ambient section gradients,
   so the blur samples real colour and reads as depth rather than frost. */
.section--surface .service-card,
.section--paper .verdub__calc {
	border: 1px solid transparent;
	background:
		linear-gradient(var(--glass-fill), var(--glass-fill)) padding-box,
		var(--glass-border) border-box;
	-webkit-backdrop-filter: blur(12px) saturate(1.1);
	backdrop-filter: blur(12px) saturate(1.1);
	box-shadow: var(--shadow-glass), inset 0 1px 0 rgba(255,255,255,.6);
}
/* hover: a higher lift and the hairline warms up (echoing "trapped heat"). */
.card--link.service-card:hover {
	transform: translateY(-6px);
	background:
		linear-gradient(rgba(255,255,255,.88), rgba(255,255,255,.88)) padding-box,
		var(--glass-border-hot) border-box;
	box-shadow: 0 32px 56px -26px rgba(12,40,34,.42), 0 12px 24px -14px rgba(12,40,34,.22), inset 0 1px 0 rgba(255,255,255,.7);
}
@supports not (backdrop-filter: blur(1px)) {
	.section--surface .service-card, .section--paper .verdub__calc { --glass-fill: rgba(255,255,255,.96); }
}

/* ---------- Responsive ---------- */
@media (max-width: 960px) {
	/* .hero's flex-centering (see .hero) is a desktop-only trick to balance
	   space around .hero__inner while .usp-bar floats absolutely outside
	   the flex flow. Below this breakpoint the bar drops into normal flow
	   as a second flex item (see .usp-bar's own mobile rule) -- centering
	   both together would bunch them in the middle instead of the expected
	   top-to-bottom stack, so back to plain block flow with a modest fixed
	   bottom gap instead of reserving the bar's full height. */
	.hero { display: block; padding-bottom: clamp(2.5rem, 6vw, 3.5rem); }
	.hero__inner { grid-template-columns: 1fr; }
	.verdub { grid-template-columns: 1fr; }
	.grid--4 { grid-template-columns: repeat(2, 1fr); }
	.grid--3 { grid-template-columns: repeat(2, 1fr); }
	.steps { grid-template-columns: repeat(2, 1fr); }
	.post-grid { grid-template-columns: repeat(2, 1fr); }
	.site-footer__grid { grid-template-columns: 1fr 1fr; }
	.site-footer__brand { grid-column: 1 / -1; }
	.service-hero__grid { grid-template-columns: 1fr; }
	.tariff { grid-template-columns: repeat(2, 1fr); }
	.steps--4 { grid-template-columns: repeat(2, 1fr); }
	.usp-bar__grid { grid-template-columns: repeat(2, 1fr); }
	.usp:nth-child(3)::before { display: none; }
	.usp:nth-child(odd)::before { display: none; }
}

/* hide the header phone at mid widths to prevent nav crowding (still in mobile menu + footer) */
@media (max-width: 1120px) and (min-width: 861px) {
	.site-nav { gap: 1rem; }
	.nav__phone { display: none; }
	.site-nav__cta { gap: .6rem; }
}

@media (max-width: 860px) {
	.site-header__inner { display: flex; }
	.nav-toggle {
		display: inline-flex; align-items: center; justify-content: center; margin-left: auto;
		width: 46px; height: 46px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm); cursor: pointer;
		transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
	}
	.nav-toggle__bars { position: relative; width: 22px; height: 16px; }
	.nav-toggle__bars span { position: absolute; left: 0; width: 100%; height: 2.2px; background: var(--ink); border-radius: var(--radius-xs); transition: transform var(--dur-2) var(--ease-out), opacity var(--dur-2) var(--ease-out), top var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out); }
	.nav-toggle__bars span:nth-child(1) { top: 0; }
	.nav-toggle__bars span:nth-child(2) { top: 7px; }
	.nav-toggle__bars span:nth-child(3) { top: 14px; }
	.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) { top: 7px; transform: rotate(45deg); }
	.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) { opacity: 0; }
	.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) { top: 7px; transform: rotate(-45deg); }

	.site-nav {
		display: flex;
		position: fixed; inset: var(--header-h) 0 0 auto; width: min(360px, 88vw);
		flex-direction: column; align-items: stretch; gap: 0; margin: 0; padding: 1.2rem;
		background: var(--surface); border-left: 1px solid var(--line); box-shadow: var(--shadow-lg);
		transform: translateX(100%); transition: transform var(--dur-2) var(--ease-out);
		overflow-y: auto; overscroll-behavior: contain;
	}
	.site-nav.is-open { transform: translateX(0); }

	/* Dimmed scrim behind the off-canvas panel -- standard mobile-drawer
	   pattern, and gives the "click outside to close" behaviour (already
	   handled by the document click listener in main.js, since this sits
	   outside both .site-nav and .nav-toggle) a visible, tappable surface
	   instead of an invisible dead zone. Always in the DOM so opacity can
	   transition both in and out; a class-gated element could only fade in.
	   z-index 59, one below .site-header's 60, so it dims the page content
	   without darkening the header bar/logo/toggle sitting above it. */
	body::after {
		content: ""; position: fixed; inset: 0; z-index: 59;
		background: rgba(10, 18, 16, .55);
		opacity: 0; visibility: hidden; pointer-events: none;
		transition: opacity var(--dur-2) var(--ease-out), visibility 0s linear var(--dur-2);
	}
	body.nav-open::after { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity var(--dur-2) var(--ease-out); }

	.nav__list { flex-direction: column; align-items: stretch; gap: .1rem; }
	.nav__list a { padding: .85rem .6rem; font-size: var(--step-1); }
	/* Cascading reveal as the panel opens, instead of every link/CTA just
	   being instantly "there" the moment the slide-in finishes -- items sit
	   translated/faded until .site-nav.is-open, then step in with a short
	   per-item delay. transition-delay isn't reset to 0 on close, so items
	   also step back out in the same order the panel slides away, rather
	   than all vanishing at once. */
	.nav__list > .menu-item, .site-nav__cta { opacity: 0; transform: translateX(16px); transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out); }
	.site-nav.is-open .nav__list > .menu-item, .site-nav.is-open .site-nav__cta { opacity: 1; transform: translateX(0); }
	.site-nav.is-open .nav__list > .menu-item:nth-child(1) { transition-delay: .05s; }
	.site-nav.is-open .nav__list > .menu-item:nth-child(2) { transition-delay: .09s; }
	.site-nav.is-open .nav__list > .menu-item:nth-child(3) { transition-delay: .13s; }
	.site-nav.is-open .nav__list > .menu-item:nth-child(4) { transition-delay: .17s; }
	.site-nav.is-open .nav__list > .menu-item:nth-child(5) { transition-delay: .21s; }
	.site-nav.is-open .nav__list > .menu-item:nth-child(n+6) { transition-delay: .25s; }
	.site-nav.is-open .site-nav__cta { transition-delay: .27s; }
	/* !important throughout: the desktop rule this overrides
	   (.dropdown.is-open { transform: translateX(-50%) ... }, meant to
	   center a hover dropdown under its trigger) kept winning here even
	   after matching this on equal specificity with later source order --
	   empirically confirmed via getComputedStyle, cause not fully pinned
	   down. Left unresolved it shifts the open mobile submenu half its own
	   width off-screen to the left.
	   Open/closed used to be driven by `display` alone (an instant show/
	   hide) specifically to dodge opacity/visibility -- those are what the
	   desktop hover-fade uses (.menu-item--has-children:hover .dropdown,
	   .dropdown.is-open below, unscoped by width), and an earlier, overly
	   broad attempt at animating this forced opacity/visibility to "shown"
	   unconditionally on BOTH .dropdown and .dropdown.is-open, cancelling
	   the JS's classList.toggle() out entirely -- every submenu got stuck
	   permanently open. This animates instead via max-height (display:none
	   can't transition), but stays correctly *gated*, unlike that earlier
	   attempt: the closed state below still resolves to opacity:0/hidden
	   -- the same values the desktop rule already uses for a closed
	   dropdown -- so there's nothing to fight, only .is-open flips them,
	   same as before. transition-delay on visibility keeps a closed
	   submenu's links out of tab order/screen readers (an inert max-height:0
	   box alone doesn't), instant on open, deferred on close until the
	   collapse finishes. 700px max-height ceiling: comfortably above every
	   current dropdown's real content height (~450px, the widest being
	   Gemeentes' lead + 8 links) with headroom for text wrapping -- bump it
	   if a dropdown's content grows enough to risk clipping. */
	.dropdown {
		position: static !important; transform: none !important; box-shadow: none !important;
		width: auto !important; border: 0 !important; padding: 0 0 .5rem .6rem !important; background: transparent !important;
		overflow: hidden !important; max-height: 0 !important; opacity: 0 !important; visibility: hidden !important;
		/* !important applies to the whole declaration, not per comma-separated
		   value -- can only appear once, at the end. Wins over the desktop
		   .dropdown transition (opacity/transform, --dur-1) by source order
		   alone (equal specificity, this rule is later in the file), so no
		   !important is actually needed here; kept unqualified so the syntax
		   stays valid. */
		transition: max-height var(--dur-2) var(--ease-out), opacity var(--dur-1) var(--ease-out), visibility 0s linear var(--dur-2);
	}
	.dropdown.is-open {
		max-height: 700px !important; opacity: 1 !important; visibility: visible !important;
		transition: max-height var(--dur-2) var(--ease-out), opacity var(--dur-1) var(--ease-out), visibility 0s linear;
	}
	/* Desktop only flips the caret on :hover (see .menu-item--has-children:hover
	   .nav__caret above) -- meaningless on a tap-to-open mobile submenu, which
	   has no hover state. Key it off the trigger's own aria-expanded instead,
	   which the JS below already keeps in sync with .is-open. */
	[aria-expanded="true"] > .nav__caret { transform: rotate(180deg); }
	.dropdown__grid { grid-template-columns: 1fr; }
	.dropdown__item--all { grid-column: auto; }
	.dropdown__links { grid-template-columns: 1fr; }
	.site-nav__cta { flex-direction: column; align-items: stretch; gap: .8rem; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
	.site-nav__cta .btn { width: 100%; }
	.nav__phone { justify-content: center; padding: .6rem; }

	/* Body scroll-lock (see main.js setOpen) pins body via position:fixed,
	   which breaks .site-header's position:sticky -- sticky only means
	   anything relative to a normally-scrolling ancestor, and once body
	   itself is pinned the header just sits wherever body's local flow
	   puts it: off-screen above the viewport by the frozen scroll offset,
	   taking the close button with it. Force it back to the true viewport
	   while the menu is open, the same way .site-nav already is. */
	body.nav-open .site-header { position: fixed; top: 0; left: 0; right: 0; }
}

@media (max-width: 560px) {
	.grid--4, .grid--3, .grid--2, .steps, .trust-grid, .post-grid, .dropdown__grid, .compare { grid-template-columns: 1fr; }
	.hero__scan::before { top: -12px; width: calc(100% + 20px); height: calc(100% + 24px); border-radius: calc(var(--radius-lg) + 4px); }
	.usp-bar__grid { grid-template-columns: 1fr; }
	.usp::before { display: none !important; }
	.usp + .usp { border-top: 1px solid var(--line); }
	.site-footer__grid { grid-template-columns: 1fr; }
	.cta-band__actions .btn { width: 100%; }
	.hero__actions .btn { width: 100%; }
}

/* body scroll lock when mobile nav open. overflow:hidden alone doesn't
   reliably block touch-drag scrolling on mobile, so JS (main.js) also
   freezes body with position:fixed + a negative top offset matching the
   scroll position at open time -- the standard mobile scroll-lock pattern,
   restored again on close. */
body.nav-open { overflow: hidden; }

/* ---------- scroll progress bar ---------- */
.scroll-progress {
	position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 200;
	background: linear-gradient(90deg, var(--primary), var(--accent));
	transform: scaleX(0); transform-origin: 0 50%; will-change: transform;
}
@media (prefers-reduced-motion: reduce) { .scroll-progress { display: none; } }

/* ---------- scroll reveal (progressive enhancement) ----------
   Only hidden when JS is present (html.wv-js). Without JS, or with reduced
   motion, content is fully visible. */
.wv-js [data-reveal] { opacity: 0; transform: translateY(12px); transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out); }
.wv-js [data-reveal="left"] { transform: translateX(-22px); }
.wv-js [data-reveal="right"] { transform: translateX(22px); }
.wv-js [data-reveal="scale"] { transform: scale(.96); }
/* CTA: action reveals a beat after the copy for a premium cascade */
.wv-js .cta-band__actions[data-reveal] { transition-delay: .12s; }
.wv-js [data-reveal].is-revealed { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
	.wv-js [data-reveal] { opacity: 1; transform: none; }
}

/* ==========================================================================
   OVER WARMVAST + ONS WERK — page makeovers
   Both pages open on the SAME plain .page-hero every other inner page uses
   (Contact, Subsidie, Kwaliteit, ...) -- no bespoke dark hero, no full-bleed
   banner. Photos throughout are deliberately SMALL: one shared .story-row
   component (photo max 300px wide, text beside it, sides alternating) used by
   About's origin story, Ons Werk's process story, and Subsidie's
   technische-schouw section. Reuses shared tokens/.card/.btn/kicker/checklist/
   data-reveal rather than inventing a parallel system.
   ========================================================================== */

/* ---------- Shared "small photo + text" story row ----------
   Used by: About's origin story, Ons Werk's process story, Subsidie's
   technische-schouw section. One pattern, one place to tune it.

   The photo column is deliberately SMALL (max 300px, 4:3) so text carries the
   page and the eye zigzags down it -- the row is a paragraph with a picture,
   not a picture with a caption.

   Movement is three effects, and each gets its OWN element, because all three
   animate `transform` and would otherwise overwrite each other (the JS
   parallax writes an inline transform, which beats any CSS transform on the
   same node -- including [data-reveal]'s slide-in and .is-revealed's reset):
     .story-row__media  -> data-reveal left/right  (slides in from its side)
     .story-row__drift  -> data-parallax           (drifts against the scroll)
     img                -> :hover                  (lifts + scales)
   Bonus from that split: the accent plate belongs to __media, so it stays put
   while the photo drifts across it -- reads as real depth, not a moving slab. */
/* Image column ~40% of the row: the convention comfort-isolatie.nl (the agreed
   reference) uses for every text-beside-photo block, at the restrained end of
   its 40-60% range. Never a percentage of the VIEWPORT -- that is what turns a
   photo into a banner on a wide screen. */
.story-row {
	display: grid;
	grid-template-columns: clamp(240px, 40%, 470px) minmax(0, 1fr);
	gap: clamp(1.5rem, 4vw, 3.5rem);
	align-items: center;
}
.story-row--alt { grid-template-columns: minmax(0, 1fr) clamp(240px, 40%, 470px); }
.story-row--alt .story-row__media { order: 2; }
.story-row + .story-row { margin-top: clamp(2.5rem, 6vw, 4.5rem); }

.story-row__media { position: relative; isolation: isolate; }
.story-row__drift { position: relative; z-index: 1; will-change: transform; }
.story-row__media img {
	display: block;
	/* Sources are pre-cropped to this exact 3:2 with face-aware framing (see
	   the build step in the repo notes), so cover is a no-op and there is no
	   object-position guess left to clip anyone's head. */
	width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
	border-radius: var(--radius-lg); box-shadow: var(--shadow);
	transition: transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) var(--ease-out);
}
.story-row__media:hover img { transform: translateY(-5px) scale(1.015); box-shadow: var(--shadow-lg); }
/* offset accent plate peeking out behind the photo -- gives a small image
   presence without making it bigger, and reads as intentional composition. */
.story-row__media::after {
	content: ""; position: absolute; z-index: 0; inset: 14px -14px -14px 14px;
	border-radius: var(--radius-lg); background: var(--primary-050);
	border: 1px solid #d9e8e3;
}
.story-row--alt .story-row__media::after { inset: 14px 14px -14px -14px; }

.story-row__step {
	display: inline-flex; align-items: center; gap: .5rem;
	font-family: var(--font-display); font-size: var(--step--1); font-weight: 700;
	letter-spacing: .09em; text-transform: uppercase; color: var(--primary); margin-bottom: .6rem;
}
.story-row__step::before { content: ""; width: 22px; height: 2px; background: var(--accent); border-radius: 2px; }
.story-row h3 { font-size: var(--step-2); margin-bottom: .5rem; }
.story-row p { color: var(--ink-2); margin: 0; max-width: 58ch; }

@media (max-width: 720px) {
	.story-row, .story-row--alt { grid-template-columns: 1fr; gap: 1.4rem; }
	.story-row--alt .story-row__media { order: 0; }
	/* cap the photo on mobile too -- edge-to-edge here is exactly the "huge
	   banner" look this component exists to avoid. */
	.story-row__media { max-width: 340px; }
	.story-row__media::after, .story-row--alt .story-row__media::after { inset: 12px -12px -12px 12px; }
}

/* ---------- Story pull-quote ---------- */
.about-story__quote {
	max-width: 640px; margin: 0 auto; padding: 1.7rem 1.9rem; background: var(--ink); color: #fff;
	border-radius: var(--radius-lg); font-family: var(--font-display); font-weight: 700;
	font-size: var(--step-2); line-height: 1.32; letter-spacing: -.01em; text-align: left; position: relative;
}
.about-story__quote::before { content: ""; position: absolute; left: 1.9rem; top: -.55rem; width: 26px; height: 4px; background: var(--accent); border-radius: 2px; }

/* ---------- Team grid ---------- */
.team-grid { grid-template-columns: repeat(3, 1fr); }
.team-card { padding: 0; overflow: clip; display: flex; flex-direction: column; transition: transform var(--dur-2) var(--ease-spring), box-shadow var(--dur-2) var(--ease-out); }
.team-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-lg); }
/* 3:2, not the source photos' own 4:5 -- a portrait-shaped crop made each card
   ~425px of photo before a word of text, which is what stacked them into a
   column of pictures. The shipped files are already cropped to this ratio
   around the detected face, so cover never has to guess. */
.team-card__photo { position: relative; aspect-ratio: 3 / 2; overflow: hidden; background: var(--ink); }
.team-card__photo img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease-out); }
.team-card:hover .team-card__photo img { transform: scale(1.06); }
.team-card__photo::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, transparent 55%, rgba(10,14,14,.55) 100%); }
.team-card__role-tag {
	position: absolute; left: 1rem; bottom: 1rem; z-index: 1;
	display: inline-flex; align-items: center; gap: .4rem;
	background: rgba(255,255,255,.16); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
	color: #fff; font-weight: 700; font-size: var(--step--1); letter-spacing: .01em;
	padding: .35rem .8rem; border-radius: var(--radius-pill); border: 1px solid rgba(255,255,255,.28);
}
.team-card__body { padding: 1.4rem 1.5rem 1.6rem; }
.team-card__name { font-size: var(--step-1); margin-bottom: .3rem; }
.team-card__bio { color: var(--ink-3); font-size: var(--step--1); margin: 0; }
@media (max-width: 860px) { .team-grid { grid-template-columns: 1fr; max-width: 420px; margin-inline: auto; } }

/* ---------- Ons werk: small extra-shots filmstrip ---------- */
.werk-filmstrip { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: clamp(2rem, 5vw, 3rem); }
.werk-filmstrip__item { position: relative; width: 118px; height: 118px; border-radius: var(--radius); overflow: hidden; flex: 0 0 auto; box-shadow: var(--shadow-sm); }
.werk-filmstrip__item img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease-out); }
.werk-filmstrip__item:hover img { transform: scale(1.08); }
.wv-js .werk-filmstrip[data-reveal] .werk-filmstrip__item { transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out); }
.wv-js .werk-filmstrip[data-reveal] .werk-filmstrip__item:nth-child(2) { transition-delay: .07s; }
.wv-js .werk-filmstrip[data-reveal] .werk-filmstrip__item:nth-child(3) { transition-delay: .14s; }

/* ---------- Scroll-linked parallax (data-parallax="0..1"; see main.js) ----------
   Only ever used on the small .story-row photos, at a low factor: enough that
   the page feels alive as you scroll, never enough to shift layout. */
[data-parallax] { will-change: transform; }
@media (prefers-reduced-motion: reduce) { [data-parallax] { transform: none !important; } }
