/* Base */
* {
	box-sizing: border-box;
}

body {
	margin: 0;
	overflow-x: hidden;
	font-family: var(--wp--custom--typography--body--font, var(--wp--preset--font-family--theme-body, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", Roboto, Arial, sans-serif));
	line-height: 1.6;
	color: var(--wp--custom--typography--body--color, #111111);
	background: #fff;
	-webkit-font-smoothing: antialiased;
}

a {
	color: #111;
	text-decoration: none;
}

a:hover {
	text-decoration: none;
}

.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* Header
 * data-header-style="solid" is the only mode implemented today
 * (matches the current white/sticky design); a future
 * data-header-style="transparent" variant (transparent until
 * .is-scrolled, e.g. sitting over a hero image) is a CSS-only addition
 * from here — .is-scrolled already exists and toggles independently of
 * this attribute, so nothing else needs to change to add it. */

/* Carries the sticky positioning itself (see header.php's own comment
 * for the full story) — deliberately NOT on <header>, because
 * <header> has no other in-flow content (the nav drawer/overlay are
 * position:fixed/absolute, contributing zero normal-flow height), so
 * <header> would be its own containing block's exact height, leaving
 * position:sticky no "travel room": it'd stay pinned for only the
 * first ~80px of scroll and then scroll away with the rest of the
 * page. This wrapper's containing block is <body> (as tall as the
 * whole page), so sticky here has all the room it needs. Also still
 * the plain positioning context for .main-navigation/.site-header__overlay,
 * its two `position:fixed` (mobile) / `position:absolute` (desktop)
 * siblings — any position other than static (sticky included)
 * establishes their containing block just as well. */
.site-header-wrap {
	position: sticky;
	top: 0;
	z-index: 1000;
}

/* Positioning context for .main-navigation's desktop `top:50%` centering
 * (see header.php's own comment) — scoped to just <header>'s own height,
 * not the topbar above it, so "Home / Zakelijk / Contact" center on the
 * white header bar itself regardless of whether the topbar is showing. */
.site-header-bar {
	position: relative;
}

.site-header {
	background: var(--pb-header-background, rgba(255, 255, 255, 0.98));
	backdrop-filter: blur(12px);
	border-bottom: 1px solid var(--pb-header-border, #ececec);
	border-radius: 0 0 1.25rem 1.25rem;
	box-shadow: none;
	transition: transform 0.3s ease, box-shadow 0.3s ease, background-color 0.3s ease;
	transform: translateY(0);
}

.site-header.is-scrolled {
	box-shadow: 0 2px 24px rgba(17, 17, 17, 0.08);
}

/* The header always stays pinned and fully visible while scrolling,
 * in either direction, at every screen size — assets/js/main.js still
 * toggles .is-hidden on scroll-down (and removes it on scroll-up), but
 * this makes that toggle a permanent no-op, neutralizing the
 * hide-on-scroll-down/reveal-on-scroll-up transform (and the jump it
 * introduced) entirely, matching the always-visible sticky bar
 * reference (e.g. westlandkaas.nl). */
.site-header.is-hidden {
	transform: translateY(0);
}

/* Mobile-only exception (2026-07-15) to the "always visible" rule
 * above — assets/js/main.js already toggles .is-hidden by scroll
 * direction (visible at top, hides on scroll-down past
 * SCROLL_THRESHOLD, reappears on scroll-up); this is the one real use
 * of that existing transform, scoped below the header's own
 * desktop/tablet breakpoint (see .custom-logo's `min-width: 768px`
 * above) so >=768px keeps the no-op override untouched. No JS change
 * needed — .site-header already has `transition: transform 0.3s ease`
 * (see base rule above) for the smooth slide. */
@media (max-width: 767.98px) {
	.site-header.is-hidden {
		transform: translateY(-100%);
	}
}

/* "Header sticky" Customizer off-state (header.php) — applied to both
 * .site-header-wrap (undoes the sticky positioning added above) and
 * <header> itself (below, so its own hide-on-scroll transform becomes
 * inert too). assets/js/main.js still adds/removes .is-scrolled/
 * .is-hidden regardless (it has no knowledge of this PHP setting) —
 * harmless, since a static-positioned element simply ignores a
 * transform meant to hide/reveal a sticky one; the .is-hidden override
 * below just makes that explicit/inert instead of relying on that
 * being incidentally true. */
.site-header-wrap.site-header--static {
	position: static;
}

.site-header--static.is-hidden {
	transform: none;
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	max-width: 1440px;
	margin: 0 auto;
	padding: 1rem 1.25rem;
}

.site-branding {
	display: flex;
	align-items: center;
	line-height: 0;
}

.custom-logo-link {
	display: inline-flex;
	align-items: center;
}

.custom-logo {
	display: block;
	height: auto;
	max-width: var(--pb-header-logo-width-mobile, 90px);
	width: auto;
}

@media (min-width: 768px) {
	.custom-logo {
		/* Spec: logo rendered size 78 x 30.47 on desktop. */
		max-width: var(--pb-header-logo-width-desktop, 78px);
	}
}

.site-title {
	display: inline-block;
	font-size: 1.2rem;
	line-height: 1;
	font-weight: 800;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--pb-header-text, #111111);
}

.menu-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background: transparent;
	color: var(--pb-header-hamburger, var(--pb-header-icon, #111111));
	cursor: pointer;
	transition: background-color 0.2s ease;
}

.menu-toggle:hover,
.menu-toggle:focus-visible {
	background: var(--pb-header-border, #f3f3f3);
	outline: none;
}

.menu-toggle__icon,
.menu-toggle__icon::before,
.menu-toggle__icon::after {
	display: block;
	width: 1.7rem;
	height: 2px;
	background: currentColor;
	border-radius: 999px;
	transition: transform 0.2s ease, opacity 0.2s ease;
}

.menu-toggle__icon {
	position: relative;
}

.menu-toggle__icon::before,
.menu-toggle__icon::after {
	content: "";
	position: absolute;
	left: 0;
}

.menu-toggle__icon::before {
	top: -0.35rem;
}

.menu-toggle__icon::after {
	top: 0.35rem;
}

body.nav-open .menu-toggle__icon {
	background: transparent;
}

body.nav-open .menu-toggle__icon::before {
	transform: translateY(0.35rem) rotate(45deg);
}

body.nav-open .menu-toggle__icon::after {
	transform: translateY(-0.35rem) rotate(-45deg);
}

/* Nike-style mobile drawer: white, full-height, slides in from the
 * right — same element/id/class the JS contract (assets/js/main.js)
 * already depends on, only the visual sizing/spacing below is new. */
.main-navigation {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	width: min(90vw, 26rem);
	padding: 6.5rem 2rem 2.5rem;
	background: var(--pb-header-background, #ffffff);
	border-left: 1px solid var(--pb-header-border, #ececec);
	box-shadow: -10px 0 30px rgba(17, 17, 17, 0.1);
	transform: translateX(100%);
	transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
	z-index: 1001;
	overflow-y: auto;
}

body.nav-open .main-navigation {
	transform: translateX(0);
}

/* Large close button, top-right. */
.main-navigation__close {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	position: absolute;
	top: 1.5rem;
	right: 1.5rem;
	width: 3rem;
	height: 3rem;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background: transparent;
	color: var(--pb-header-icon, #111111);
	cursor: pointer;
	transition: background-color 0.2s ease;
}

.main-navigation__close:hover,
.main-navigation__close:focus-visible {
	background: var(--pb-header-border, #f3f3f3);
	outline: none;
}

.main-navigation__close svg {
	width: 1.5rem;
	height: 1.5rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
}

/* PaperBrain mark, bottom-right of the mobile drawer (2026-07-23,
 * resized/repositioned per feedback — was 40px flush in the corner,
 * now bigger and pulled in from the edges to sit in the open space
 * below the last menu item). .main-navigation is already
 * `position:fixed` (the drawer itself), so this just anchors to its
 * own box. Hidden again at the >=768px desktop tier below, where
 * .main-navigation is repurposed as the inline desktop nav, not a
 * drawer. */
.main-navigation__mark {
	position: absolute;
right: 1rem;
bottom: 1rem;
width: 100px;
	height: auto;
pointer-events: none;
opacity: 0.6;
}

.main-navigation__menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}

.main-navigation__menu li {
	opacity: 0;
	transform: translateX(1rem);
	transition: opacity 0.3s ease, transform 0.3s ease;
}

body.nav-open .main-navigation__menu li {
	opacity: 1;
	transform: translateX(0);
}

.main-navigation__menu li:nth-child(1) { transition-delay: 0.05s; }
.main-navigation__menu li:nth-child(2) { transition-delay: 0.1s; }
.main-navigation__menu li:nth-child(3) { transition-delay: 0.15s; }
.main-navigation__menu li:nth-child(4) { transition-delay: 0.2s; }
.main-navigation__menu li:nth-child(5) { transition-delay: 0.25s; }
.main-navigation__menu li:nth-child(6) { transition-delay: 0.3s; }

/* Large, vertically stacked items with a trailing chevron
 * (.main-navigation__chevron, added via wp_nav_menu()'s "link_after" in
 * header.php) pushed to the far right via space-between — the desktop
 * override further down resets this back to inline-block, unaffected. */
.main-navigation__menu a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1.15rem 0;
	font-size: 17px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	color: #9ea3ae;
	border-bottom: 1px solid var(--pb-header-border, #f1f1f1);
}

.main-navigation__menu .current-menu-item > a {
	color: #111111;
	font-weight: 800;
}

/* .site-header-wrap .main-navigation prefix — genuinely higher
 * specificity (3 classes) than .woocommerce .site-header-wrap a
 * (assets/css/woocommerce.css, 2 classes + type — a plain
 * .site-header-wrap prefix alone only TIES that rule's specificity,
 * which isn't enough; source order then let woocommerce.css win since
 * it loads after this stylesheet). */
.site-header-wrap .main-navigation .main-navigation__menu a {
	color: #9ea3ae;
}

.site-header-wrap .main-navigation .main-navigation__menu .current-menu-item > a {
	color: #111111;
}

/* Zakelijk/Fundament's nested footer-style submenu links (2026-07-16)
 * are still <a> tags inside .main-navigation__menu, so the two rules
 * above (written when every menu <a> was a top-level item) otherwise
 * win over .site-footer__links a's own (unmodified) styling — same
 * .site-header-wrap .main-navigation prefix, same specificity,
 * positioned after so it wins the tie. unset for everything without a
 * competing same-element rule (lets normal inheritance/initial values
 * through, same result as the real footer, no duplicate literals).
 * color is the one exception: unset would also outrank
 * .site-footer__links a's own (0,1,1) color rule, not just the two
 * (0,3,1)/(0,1,1) leaks above — so this repeats that exact same
 * var()/fallback expression instead, the shared token, not an
 * independent literal. */
.site-header-wrap .main-navigation .site-footer__links a {
	display: unset;
	padding: unset;
	font-size: unset;
	font-weight: unset;
	text-transform: unset;
	letter-spacing: unset;
	color: var( --pb-footer-link, #6b6b6b );
	border-bottom: unset;
}

/* Zakelijk/Fundament/Contact's accordion rows (2026-07-16) doubled up
 * on dividers: .main-navigation__menu a's own border-bottom (the
 * pre-accordion per-item divider) plus .site-footer__col's border-top
 * both drew a line for the same row. .site-footer__col-title is
 * always a real <a>, so it inherited the first one directly — removed
 * here, .site-footer__col's border-top is the single source of truth
 * now for every item. */
.main-navigation .site-footer__col-title {
	border-bottom: 0;
}

/* .site-footer__col:last-of-type's own border-bottom (footer's own
 * "close the last column" rule) matched every single item here, not
 * just the true last one — each .site-footer__col is alone inside its
 * own <li>, so it's trivially "last of its type" among its own
 * (nonexistent) siblings, on top of its own border-top: a second
 * divider on every item. One divider per item (border-top, already
 * identical for all three) is the spec — removed outright, including
 * for the actual last item, rather than special-cased back in. */
.main-navigation .site-footer__col:last-of-type {
	border-bottom: 0;
}

.main-navigation__chevron {
	flex-shrink: 0;
	font-size: 1.25rem;
	opacity: 0.4;
}

/* Mobile: cart + hamburger only (Nike-style — no CTA, no search/
 * account/wishlist at this breakpoint). .pb-newsletter__cta--header hides
 * itself below 768px regardless of which icons a future variant re-adds here.
 * .menu-toggle already hides itself at 768px+ (existing rule above), so
 * no extra rule is needed to remove the hamburger on desktop. */
.site-header__actions {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.site-header__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: 999px;
	color: var(--pb-header-icon, #111111);
	transition: background-color 0.2s ease, transform 0.2s ease;
}

/* The cart icon (pb_wc_render_mini_cart_trigger()) is a <button>, the
 * other three are <a> — resetting browser button defaults here (not
 * in assets/css/woocommerce.css, so the mini-cart's own CSS/logic
 * stays untouched) is what makes all four actually look like the same
 * icon set instead of the cart one carrying a native button border/
 * background/font. */
button.site-header__icon {
	border: 0;
	background: none;
	padding: 0;
	font: inherit;
	cursor: pointer;
}

.site-header__icon:hover,
.site-header__icon:focus-visible {
	background: var(--pb-header-border, #f3f3f3);
	transform: translateY(-1px);
	outline: none;
}

.site-header__icon svg {
	width: 1.1rem;
	height: 1.1rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.8;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* Cart icon only, larger than the other header icons (Nike-style
 * shopping bag) — .pb-mini-cart-trigger is only ever on the cart
 * icon (pb_wc_render_mini_cart_trigger()/pb_header_render_cart_icon()'s
 * fallback), so this doesn't affect Help/Orders/Store icons. Fill-
 * based artwork (uploads/Addtocart.svg), not the stroke-based icon
 * set the other header icons use, so it resets fill/stroke back. */
.pb-mini-cart-trigger svg {
	width: 1.7rem;
	height: 1.7rem;
	fill: currentColor;
	stroke: none;
}

/* No grey circle behind the cart icon specifically on hover/focus —
 * the other header icons keep it (.site-header__icon:hover above). */
.pb-mini-cart-trigger:hover,
.pb-mini-cart-trigger:focus-visible {
	background: none;
}

/* Header CTA button (pb_header_render_cta(), inc/header-icons.php).
 * 2026-07-23: header CTA now renders the ghost variant of the shared
 * pb-newsletter__cta component — the exact same look as "Kom in
 * contact" (pb/newsletter on /verkooppunten/): transparent/outlined by
 * default, filling brand-purple on hover via the shared desktop hover
 * rule further down. This is that component's "--header" variant, not a
 * separate button; the only header-specific override left is the
 * default (non-hover) purple outline/text color for this light
 * background (see .pb-newsletter__cta--header.pb-button--ghost below),
 * still routed through the Customizer's --pb-header-cta-background
 * (inc/design-system-customizer.php, "CTA achtergrond"). Hidden by
 * default (mobile), revealed at the 768px breakpoint below (layout,
 * not visual style). */
.pb-newsletter__cta--header {
	display: none !important;
	white-space: nowrap;
}

/* WooCommerce's own bundled add-to-cart.js inserts a "Bekijk
 * winkelwagen" (View cart) link right after any .ajax_add_to_cart
 * button once the AJAX add completes — native behaviour, not something
 * this theme renders, so it can't be removed at the PHP render level.
 * The header CTA (pb_header_render_cta(), inc/header-icons.php)
 * already opens the mini-cart drawer itself on the same event, making
 * this extra link redundant here specifically; hidden, not removed, so
 * the same native behaviour is untouched everywhere else (PDP, "Past
 * goed bij", shop archive). */
.site-header__actions .added_to_cart {
	display: none;
}

.site-header__overlay {
	position: fixed;
	inset: 0;
	background: rgba(17, 17, 17, 0.25);
	backdrop-filter: blur(2px);
	opacity: 0;
	transition: opacity 0.3s ease;
	z-index: 1000;
}

body.nav-open .site-header__overlay {
	display: block;
	opacity: 1;
}

/* Optional thin top bar above the header (pb_render_header_topbar(),
 * inc/header-topbar.php) — off by default, small right-aligned links
 * only (Appearance -> Menus -> "Top Bar Menu"). Desktop only (hidden
 * below 768px, revealed in the media query below) — the mobile header
 * has no room for it. Not sticky itself: it scrolls away with the
 * page, only .site-header sticks. */
.site-topbar {
	display: none;
	background: var(--pb-topbar-background, #f5f5f5);
	color: var(--pb-topbar-text, #111111);
}

.site-topbar__inner {
	display: flex;
	justify-content: flex-end;
	max-width: 1440px;
	margin: 0 auto;
	padding: 0.5rem 1.25rem;
}

.site-topbar__menu {
	list-style: none;
	display: flex;
	align-items: center;
	margin: 0;
	padding: 0;
	font-size: 0.75rem;
}

/* Nike-style "|" separators between items (not before the first one) —
 * the separator's own margin provides all the spacing, so .site-topbar__menu
 * itself has no flex `gap` (avoids doubling up the spacing). */
.site-topbar__menu li + li::before {
	content: "|";
	margin: 0 0.75rem;
	opacity: 0.4;
}

.site-topbar__menu a {
	color: inherit;
}

.site-topbar__menu a:hover,
.site-topbar__menu a:focus-visible {
	text-decoration: underline;
}

@media (min-width: 768px) {
	.site-topbar {
		display: block;
	}

	.site-topbar__inner {
		padding: 0.5rem 2rem;
	}
}

@media (min-width: 1024px) {
	.site-topbar__inner {
		padding: 0.5rem 2.5rem;
	}
}

@media (min-width: 768px) {
	.site-header__inner {
		/* Spec: white header area stays compact, ~68-72px tall — with the
		 * CTA button (.pb-newsletter__cta--header) as the tallest row content,
		 * ~15px top/bottom lands in that range (horizontal breathing
		 * room per-breakpoint is unchanged). */
		padding: 15px 2rem;
		display: grid;
		grid-template-columns: auto minmax(0, 1fr) auto;
		align-items: center;
		column-gap: 2.5rem;
	}

	/* .main-navigation is no longer a grid child here (it's a sibling
	 * of <header> now — see header.php/.site-header-wrap's own
	 * comments), so the grid only has two actual items; pin them to
	 * the outer columns explicitly rather than relying on
	 * auto-placement, which would otherwise pack them into columns 1
	 * and 2 and leave column 3 empty. */
	.site-branding {
		grid-column: 1;
	}

	.site-header__actions {
		grid-column: 3;
	}

	.menu-toggle {
		display: none;
	}

	/* Nike-style: primary links centered over the header row.
	 * Positioned against .site-header-wrap (position:relative, see
	 * that rule's own comment) rather than as a CSS Grid child of
	 * .site-header__inner, since .main-navigation needs to be a DOM
	 * sibling of <header> for its mobile `position:fixed` drawer to
	 * size against the real viewport. */
	.main-navigation {
		position: absolute;
		top: 50%;
		right: auto;
		bottom: auto;
		left: 50%;
		/* The mobile base rule's `bottom: 0` (fullscreen drawer) isn't
		 * reset by anything else here otherwise — left combined with
		 * `top:50%`, it stretched this box all the way to the bottom of
		 * .site-header-bar, so its own height (and therefore the -50%
		 * translate's centering math) no longer matched its actual menu
		 * content, throwing "Home / Zakelijk / Contact" off the header's
		 * true vertical center by several pixels. */
		transform: translate(-50%, -50%);
		width: auto;
		padding: 0;
		background: transparent;
		border-left: 0;
		box-shadow: none;
		overflow: visible;
	}

	.main-navigation__close {
		display: none;
	}

	.main-navigation__mark {
		display: none;
	}

	/* Higher-specificity selectors, not just `.main-navigation__chevron`
	 * alone: that class shares equal (0,1,0) specificity with the later,
	 * unconditional `.material-symbols-rounded { display: inline-block; }`
	 * rule further down this file, which would otherwise win the
	 * cascade by source order despite this media query. */
	.main-navigation .main-navigation__chevron {
		display: none;
	}

	.main-navigation__menu {
		flex-direction: row;
		justify-content: center;
		align-items: center;
		gap: 2rem;
	}

	.main-navigation__menu li {
		opacity: 1;
		transform: none;
		transition: none;
	}

	.main-navigation__menu a {
		position: relative;
		display: inline-block;
		padding: 0.4rem 0.1rem;
		font-size: 14px;
		line-height: 1;
		font-weight: 600;
		letter-spacing: 0.02em;
		border-bottom: 0;
		color: #9ea3ae;
	}

	/* .site-header-wrap .main-navigation prefix — genuinely higher
	 * specificity (3 classes) than .woocommerce .site-header-wrap a
	 * (assets/css/woocommerce.css, 2 classes + type — a plain
	 * .site-header-wrap prefix alone only TIES that rule's specificity,
	 * which isn't enough; source order then let woocommerce.css win
	 * since it loads after this stylesheet, e.g. on the PDP). */
	.site-header-wrap .main-navigation .main-navigation__menu a {
		color: #9ea3ae;
	}

	.site-header-wrap .main-navigation .main-navigation__menu a:hover,
	.site-header-wrap .main-navigation .main-navigation__menu a:focus-visible {
		color: #7e6fef;
	}

	/* Desktop refinement: the active item's extra-bold 800 (base/mobile
	 * rule, further up) reads as too heavy at this smaller size — the
	 * underline (::after, below) is enough to mark it as current, so
	 * every desktop link stays the same 600 weight. */
	.main-navigation__menu .current-menu-item > a {
		font-weight: 600;
		color: #6848f4;
	}

	/* Active item (2026-07-15): site's Primary CTA color, same
	 * literal #6848F4 the CTA/hover states already hardcode nearby
	 * (line 706) rather than a Customizer-driven token, per explicit
	 * spec. This is the higher-specificity selector that actually wins
	 * over .site-header-wrap .main-navigation .main-navigation__menu a
	 * (see its own comment above) — hover on *inactive* items (line
	 * 704-707) is untouched, and mobile's own
	 * .main-navigation__menu .current-menu-item > a rule (base rules,
	 * outside this media query) is untouched too. */
	.site-header-wrap .main-navigation .main-navigation__menu .current-menu-item > a {
		color: #6848f4;
	}

	.main-navigation__menu a::after {
		content: "";
		position: absolute;
		left: 50%;
		bottom: -2px;
		width: 0;
		height: 2px;
		background: var(--pb-header-text, #111111);
		transition: width 0.25s ease, left 0.25s ease;
	}

	/* Hover no longer draws the underline (only color changes on hover,
	 * above) — focus-visible keeps it as a keyboard-navigation
	 * affordance. The current/active page no longer gets one either
	 * (2026-07-15) — color alone now marks it as current, per spec
	 * ("remove the underline/border indicator completely for the
	 * active item"); .current-menu-item > a::after simply stays at
	 * the base rule's width:0 above, same as any other non-hovered,
	 * non-focused link. */
	.main-navigation__menu a:focus-visible::after {
		left: 0;
		width: 100%;
	}

	.site-header__actions {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		justify-self: end;
	}

	.pb-newsletter__cta--header {
		display: inline-flex !important;
	}

	/* "Zakelijk"/"Fundament" reuse footer's .site-footer__col* markup for
	 * the mobile accordion (header.php) — footer's own desktop rules for
	 * those same (unscoped) classes would otherwise leak in here too.
	 * 2-class specificity beats footer's bare 1-class rules regardless
	 * of source order, so this always wins: no toggle/body, no
	 * border/padding from the footer column styling — .main-navigation__menu
	 * a's own desktop link styling (above) is all that's left, so these
	 * two items render byte-for-byte like Contact. */
	.main-navigation .site-footer__col,
	.main-navigation .site-footer__col-summary {
		border: 0;
		padding: 0;
		margin: 0;
	}

	.main-navigation .site-footer__col-toggle,
	.main-navigation .site-footer__col-body {
		display: none;
	}
}

@media (min-width: 1024px) {
	.site-header__inner {
		padding: 15px 2.5rem;
	}

	.main-navigation__menu {
		gap: 2.5rem;
	}
}

@media (min-width: 1440px) {
	.site-header__inner {
		padding: 15px 3rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	.site-header,
	.main-navigation,
	.main-navigation__menu li,
	.site-header__overlay,
	.menu-toggle__icon,
	.menu-toggle__icon::before,
	.menu-toggle__icon::after {
		transition: none !important;
	}
}

/* Main content */
.site-main {
	max-width: 800px;
	margin: 0 auto;
	padding: 2rem;
}

.site-main.front-page,
.site-main.verkooppunten-page-wrap,
.site-main.pb-blog-page-wrap,
.site-main.pb-blog-single-wrap,
.site-main.zakelijk-page-wrap {
	max-width: none;
	padding: 0;
}

/* Reusable PaperBrain image radius for plain content images (any Page's
 * own core/image blocks, e.g. inspiration pages) — same design-system
 * token other blocks already use, not a one-off/page-specific value. */
.entry-content .wp-block-image img {
	border-radius: var(--wp--custom--radius--large, 1.25rem);
}

/* "pb/benefits" — reusable 2x2 (1-col mobile) benefits grid. */
.pb-benefits {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 3rem 2rem;
	margin: 2.5rem auto;
}

@media (max-width: 767px) {
	.pb-benefits {
		grid-template-columns: 1fr;
	}
}

.pb-benefits__item {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 1rem;
}

.pb-benefits__icon {
	width: 64px;
	height: 64px;
	flex-shrink: 0;
	border-radius: 999px;
	background: rgba(104, 72, 244, 0.12);
	display: flex;
	align-items: center;
	justify-content: center;
}

.pb-benefits__icon-glyph {
	font-size: 28px;
	color: var(--wp--preset--color--primary, #6848f4);
}

.pb-benefits__text {
	display: block;
	width: 100%;
	max-width: 320px;
	margin: 0;
	padding: 0;
	background: transparent;
	border: none;
	resize: none;
	overflow: hidden;
	font-weight: 700;
	font-size: 1.05rem;
	line-height: 1.4;
	text-align: center;
	color: #111111;
}

article {
	margin-bottom: 2rem;
}

/* Reusable gradient-text eyebrow (opt-in via block "Additional CSS
 * Class") — not tied to any one page's content. Values (12px/500/12px
 * line-height, gradient stops) devtools-measured off
 * paperbrain.store/verbeter-je-nachtrust-met-deze-simpele-routine's
 * own ".wp-block-paragraph" eyebrow, an exact match, not eyeballed. */
.pb-gradient-text {
	font-size: 12px;
	font-weight: 500;
	line-height: 12px;
	text-align: center;
	background: linear-gradient(135deg, #0693e3 0%, #9b51e0 100%);
	-webkit-background-clip: text;
	background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
}

/* "Vier pijlers" card grid (fundament-ongedateerde-planner and similar
 * pages) — narrow card columns need the shared .pb-button--primary's
 * own min-width/uppercase wrap behaviour relaxed, without touching
 * that shared rule itself (used at full size elsewhere). */
.pb-pijler-grid .pb-button--primary {
	min-width: auto;
	white-space: nowrap;
}

/* Same self-contained full-bleed breakout as .pb-image-text.alignfull
 * (this theme has no generic .alignfull CSS of its own). */
.pb-pijler-grid.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Equal-height cards + aligned titles, regardless of title/body text
 * length. core/columns already stretches its own children by default;
 * this carries that stretch down through .wp-block-column into the
 * card Group itself (flex:1, no fixed height), and pins the button
 * row to the bottom via margin-top:auto — so icon/title stay anchored
 * at the same offset from the card top in every card, and only the
 * gap before the button grows/shrinks with content length. */
.pb-pijler-grid .wp-block-columns {
	align-items: stretch;
}

.pb-pijler-grid .wp-block-column {
	display: flex;
}

.pb-pijler-grid .wp-block-column > .wp-block-group {
	display: flex;
	flex-direction: column;
	flex: 1;
}

.pb-pijler-grid .wp-block-column > .wp-block-group > .wp-block-buttons {
	margin-top: auto;
}

/* Titles left-aligned so they start at the same X in every card,
 * regardless of title text length (center alignment made shorter
 * titles like "Je Denken" start further right than longer ones). */
.pb-pijler-grid .wp-block-column > .wp-block-group h3 {
	text-align: left;
}

/* Hero */
.hero {
	--hero-accent: #ff4d2d;
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: flex-end;
	min-height: 92vh;
	min-height: 92svh;
	padding: 6rem 1.25rem 4.5rem;
	overflow: hidden;
	background: #111;
	color: #fff;
	text-align: left;
}

.hero__media {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, #1f1f1f 0%, #000 55%, #1a1a1a 100%);
	background-size: cover;
	background-position: center;
	transform: scale(1.08);
	animation: hero-media-zoom 14s ease-out forwards;
}

@keyframes hero-media-zoom {
	to {
		transform: scale(1);
	}
}

.hero--has-image .hero__media {
	background-color: #000;
}

.hero__media--video {
	object-fit: cover;
	width: 100%;
	height: 100%;
}

.hero__media--video-error {
	display: none;
}

/* Triptych variant (2026-07-15, hero--triptych) — 3 side-by-side
 * cover-image panels instead of the single full-bleed .hero__media,
 * only rendered when backgroundImage2/3 are both set (see
 * pb_render_hero_block()). Centered content instead of the default
 * bottom-left stack — a 3-panel photo backdrop reads as a centered
 * "portrait wall", not a single asymmetric hero image. */
.hero--triptych {
	align-items: center;
	justify-content: center;
	text-align: center;
}

.hero__media-panels {
	position: absolute;
	inset: 0;
	display: flex;
}

.hero__media-panel {
	flex: 1 1 33.333%;
	background-size: cover;
	background-position: center;
	background-color: #000;
	transform: scale(1.08);
	animation: hero-media-zoom 14s ease-out forwards;
}

.hero--triptych .hero__content {
	align-items: center;
}

.hero__scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(0deg, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0.45) 40%, rgba(0, 0, 0, 0.15) 70%, rgba(0, 0, 0, 0.35) 100%);
}

.hero__content {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 1.25rem;
	max-width: 40rem;
}

.hero__badge,
.hero__badge-image,
.hero__title,
.hero__subtitle,
.hero__actions {
	margin: 0;
	opacity: 0;
	transform: translateY(1rem);
	animation: hero-reveal 0.7s ease forwards;
}

.hero__badge {
	animation-delay: 0.1s;
	display: inline-flex;
	align-items: center;
	padding: 0.4rem 0.9rem;
	border: 1px solid rgba(255, 255, 255, 0.4);
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: #fff;
	background: rgba(255, 255, 255, 0.08);
	backdrop-filter: blur(4px);
}

/* .hero__badge-image (2026-07-15) — optional logo-image alternative to
 * the text .hero__badge pill above (pb/hero's badgeImage attribute).
 * Shares the same reveal-animation timing/delay as the text badge it
 * replaces; width is tuned to the PaperBrain script-logo's own aspect
 * ratio rather than the block's actual pixel dimensions. */
.hero__badge-image {
	animation-delay: 0.1s;
	display: block;
	width: 170px;
	height: auto;
}

/* Typography now token-driven via PB Rich Heading's `.pb-type--h1`
 * class. "h1" is a shared, actively-used token (can't be retuned like
 * the display-* tokens were for other blocks), so this hero's own
 * much larger/tighter/uppercase look stays a scoped override here —
 * same pattern, just more overridden properties since h1 only
 * matches this block's font-weight (800) by coincidence. */
.hero__title {
	animation-delay: 0.2s;
	font-size: clamp(2.5rem, 8vw, 5.75rem);
	line-height: 0.92;
	letter-spacing: -0.03em;
	text-transform: uppercase;
	color: #fff;
}

/* "body" token already matches this subtitle's weight (400); size
 * (1.05rem) and color are this block's own nuance. */
.hero__subtitle {
	animation-delay: 0.3s;
	font-size: 1.05rem;
	color: rgba(255, 255, 255, 0.8);
	max-width: 30rem;
}

.hero__actions {
	animation-delay: 0.4s;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.75rem;
	width: 100%;
	max-width: 20rem;
	margin-top: 0.5rem;
}

/* .pb-button--primary inside .hero (2026-07-15) — used to force a
 * hardcoded white-fill/orange-hover look here regardless of the
 * "Knop: Primary" Customizer settings, so choosing "Primary" produced
 * a different size/shape/color inside pb/hero than everywhere else on
 * the site (pb/hero-centered, campaign CTAs, etc). Removed so "Primary"
 * means the same --wp--custom--button--primary--* driven look
 * everywhere, matching the reference look on the homepage's hero CTA.
 * Ghost keeps its own white-outline override below — untouched, only
 * Primary was reported inconsistent. */

.hero .pb-button--ghost {
	background: transparent;
	color: #ffffff;
	border-color: rgba(255, 255, 255, 0.55);
}

.hero .pb-button--ghost:hover,
.hero .pb-button--ghost:focus-visible {
	border-color: #ffffff;
	background: rgba(255, 255, 255, 0.1);
	color: #ffffff;
	transform: none;
	outline: none;
}

.hero__actions .button,
.hero__actions .pb-button {
	width: auto;
}

@keyframes hero-reveal {
	to {
		opacity: 1;
		transform: none;
	}
}

.button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: var(--wp--custom--button--primary--padding-y, 0.95rem) var(--wp--custom--button--primary--padding-x, 2.25rem);
	border-radius: var(--wp--custom--button--primary--radius, 999px);
	border: 1.5px solid transparent;
	font-size: var(--wp--custom--button--primary--font-size, 0.8rem);
	font-weight: var(--wp--custom--typography--button--weight, 700);
	text-transform: var(--wp--custom--typography--button--transform, none);
	letter-spacing: var(--wp--custom--button--primary--letter-spacing, 0.08em);
	cursor: pointer;
	transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

.button:active {
	transform: translateY(0) scale(0.97);
}

.button--primary {
	background: #fff;
	color: #111;
}

.button--primary:hover,
.button--primary:focus-visible {
	background: var(--hero-accent);
	color: #fff;
	transform: translateY(-2px);
	box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
	outline: none;
}

.button--ghost {
	background: transparent;
	color: #fff;
	border-color: rgba(255, 255, 255, 0.55);
}

.button--ghost:hover,
.button--ghost:focus-visible {
	border-color: #fff;
	background: rgba(255, 255, 255, 0.1);
	transform: translateY(-2px);
	outline: none;
}

/*
 * core/button applies its "Additional CSS class" (e.g. "button--primary")
 * to the block's own wrapper <div class="wp-block-button">, not the inner
 * <a class="wp-block-button__link">. Since .button/.button--primary/etc.
 * above are plain classes (not tag-scoped), they already style that
 * wrapper div correctly on their own; this rule only needs to neutralise
 * the inner link's default browser styling so it doesn't show through.
 */
.wp-block-button__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	color: inherit;
	text-decoration: none;
}

@media (min-width: 480px) {
	.hero__actions {
		flex-direction: row;
		width: auto;
		max-width: none;
	}
}

@media (min-width: 768px) {
	.hero {
		padding: 8rem 3rem 5rem;
	}
}

@media (min-width: 1440px) {
	.hero {
		padding: 8rem 4rem 5rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	.hero__badge,
	.hero__title,
	.hero__subtitle,
	.hero__actions {
		animation: none;
		opacity: 1;
		transform: none;
	}

	.hero__media {
		animation: none;
		transform: none;
	}
}

/* Campaign grid */
.campaign-grid {
	/* Symmetric top/bottom (was 1.5rem/4rem, 2026-07-17 fix) — the
	 * mismatched bottom padding produced a large, unbalanced gap when
	 * two Campaign Grid instances are stacked directly on a page. Still
	 * a real default (not 0), so a single instance keeps sensible
	 * breathing room before whatever follows; per-instance Spacing
	 * panel (blocks/campaign-grid/edit.js) overrides this when a
	 * specific placement needs more/less. */
	padding: 1.5rem 1.25rem;
	background: #fff;
	position: relative;
}

/* Optional background image/color + overlay (window.pbBackgroundControls,
 * same shared pattern as .pb-business-card__bg/__overlay) — absolutely
 * positioned layers behind the intro/cards, only visually present once
 * bgColor/bgImage/overlayColor is actually set (see edit.js/PHP render,
 * both leave these divs backgroundless/transparent otherwise so
 * .campaign-grid's own white background still shows through unchanged
 * for every existing instance). */
.campaign-grid__bg {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	z-index: 0;
}

.campaign-grid__overlay {
	position: absolute;
	inset: 0;
	z-index: 0;
}

/* Self-contained full/wide breakout (see .pb-image-text for the full
 * rationale) — never rely on ambient page-template width. */
.campaign-grid.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.campaign-grid.alignwide {
	max-width: 1200px;
	margin-left: auto;
	margin-right: auto;
}

.campaign-grid__intro {
	position: relative;
	z-index: 1;
	max-width: 1440px;
	margin: 0 auto 2rem;
}

.campaign-grid__eyebrow { margin: 0 0 0.5rem; }
.campaign-grid__heading { margin: 0 0 0.75rem; }
.campaign-grid__intro-text { margin: 0; max-width: 40rem; }

.campaign-grid__inner {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: 1fr;
	/* Mobile-only (this base rule only applies below the 640px
	 * min-width override further down, which switches to flex with its
	 * own 1.5rem gap) — reduced further (was 0.5rem) to sit noticeably
	 * closer on small screens. Desktop/tablet (>=640px) gap unchanged. */
	gap: 0.25rem;
	max-width: 1440px;
	margin: 0 auto;
}

.campaign-card {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: 22rem;
	overflow: hidden;
	border-radius: 0.75rem;
	background: var(--campaign-card-bg, transparent);
	color: #fff;
	/* Whole card is a click target now (see main.js) — .campaign-card__link
	 * itself already looks clickable via its underline; this signals the
	 * same affordance across the rest of the card. */
	cursor: pointer;
}

/* Desktop flip card (2026-07-23, opt-in per pb/campaign-grid instance
 * via its "Desktop flip card" toggle → .campaign-grid--flip on the
 * ancestor <section>). Neutral by default: .campaign-card__flip-inner/
 * --front use display:contents so .campaign-card__media/__body remain
 * direct flex children of .campaign-card exactly as before (every
 * existing, non-flip grid is untouched); .campaign-card__face--back is
 * display:none so it never renders visually or affects layout unless
 * flip mode is actually on. */
.campaign-card__flip-inner,
.campaign-card__face--front {
	display: contents;
}

.campaign-card__face--back {
	display: none;
}

/* Unconditional (desktop + mobile, 2026-07-23 fix): .pb-type--h3/
 * .pb-type--body each set their own explicit color (#111111 default,
 * design-system.css) — explicit author color always beats inherited
 * color regardless of specificity, so without this the back title/text
 * rendered near-black on .campaign-card__face--back's solid black
 * background (invisible) instead of inheriting its white/custom
 * --campaign-card-back-text. Same "color:inherit is load-bearing"
 * pattern already fixed elsewhere in this file (.animated-statement__
 * subtitle, .pb-image-text__body > *, etc.). Previously scoped only
 * inside the >=1024px block, which silently skipped mobile. */
.campaign-card__back-title,
.campaign-card__back-text {
	color: inherit;
}

/* Structural flip mechanics — desktop only (matches this theme's
 * >=1024px desktop breakpoint everywhere else); mobile/tablet keep the
 * neutral display:contents/none above untouched, i.e. the existing
 * non-flip layout, per "no hover flip on tablet/mobile". */
@media (min-width: 1024px) {
	.campaign-grid--flip .campaign-card {
		perspective: 1200px;
	}

	.campaign-grid--flip .campaign-card__flip-inner {
		display: block;
		position: absolute;
		inset: 0;
		/* z-index:1 (2026-07-23 fix) — .campaign-card::after's dark
		 * legibility gradient is generated content, painted last among
		 * .campaign-card's own children; .campaign-card__body used to
		 * beat it via its own z-index:1 back when __body was a direct
		 * sibling of ::after. Now that flip mode wraps __body inside
		 * this absolutely-positioned layer, THIS element is ::after's
		 * direct competitor for paint order — without its own z-index it
		 * defaulted to auto/DOM-order, so the overlay silently painted
		 * on top of the title, washing the white text out. */
		z-index: 1;
		transform-style: preserve-3d;
	}

	.campaign-grid--flip .campaign-card__face {
		position: absolute;
		inset: 0;
		-webkit-backface-visibility: hidden;
		backface-visibility: hidden;
		display: flex;
		flex-direction: column;
		justify-content: flex-end;
		overflow: hidden;
	}

	.campaign-grid--flip .campaign-card__face--back {
		display: flex;
		align-items: center;
		justify-content: center;
		text-align: center;
		padding: 2rem;
		transform: rotateY(180deg);
		background-color: var(--campaign-card-back-bg, #000000);
		/* Optional uploaded back-image (2026-07-23, backImage attribute)
		 * — a fixed dark scrim layered on top (first background-image
		 * layer painted above later ones) keeps the white title/text
		 * readable over any photo; --campaign-card-back-image defaults
		 * to "none" so cards without one show plain backBackgroundColor
		 * exactly as before. */
		background-image: linear-gradient(rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.45)), var(--campaign-card-back-image, none);
		background-size: cover;
		background-position: center;
		color: var(--campaign-card-back-text, #ffffff);
	}

	.campaign-grid--flip .campaign-card__back-inner {
		max-width: 100%;
		position: relative;
		z-index: 1;
	}

	.campaign-grid--flip .campaign-card__back-title {
		margin: 0 0 0.75rem;
	}

	/* Front face shows the title only — the full description text is
	 * reserved for the back (already rendered there, see
	 * .campaign-card__face--back above), so it doesn't show twice at
	 * once. */
	.campaign-grid--flip .campaign-card__face--front .campaign-card__text {
		display: none;
	}
}

/* Smooth 3D flip — reduced-motion: the rotateY() state change below
 * still happens (hover/focus still "works"), just instantly, same
 * no-preference-guard convention as .pb-reviews-carousel-scroll and
 * the Hero Centered underline elsewhere in this file. */
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
	.campaign-grid--flip .campaign-card__flip-inner {
		transition: transform 0.6s ease;
	}
}

/* Hover/focus flips only the hovered/focused card — plain :hover/
 * :focus-within, no JS: leaving a card (mouse-leave or focus moving
 * elsewhere) reverts it automatically, and hovering a different card
 * only ever affects that card's own .campaign-card__flip-inner. */
@media (min-width: 1024px) and (hover: hover) {
	.campaign-grid--flip .campaign-card:hover .campaign-card__flip-inner,
	.campaign-grid--flip .campaign-card:focus-within .campaign-card__flip-inner {
		transform: rotateY(180deg);
	}
}

/* Mobile/tablet (2026-07-23) — no hover/tap on touch, so a flip-enabled
 * grid shows the back (full black card, full text) permanently instead
 * of requiring an interaction that doesn't exist there. Desktop's
 * >=1024px rules above are untouched. */
@media (max-width: 1023.98px) {
	.campaign-grid--flip .campaign-card__flip-inner {
		display: block;
		/* position+z-index (fix, same root cause as the desktop flip-inner
		 * fix above): .campaign-card::after's dark overlay is positioned
		 * (absolute) with no z-index, and a positioned element always
		 * paints above non-positioned in-flow content regardless of
		 * DOM order/z-index — without this, ::after silently covered the
		 * now-static back-face text, making it unreadable/invisible. */
		position: relative;
		z-index: 1;
	}

	.campaign-grid--flip .campaign-card__face--front {
		display: none;
	}

	.campaign-grid--flip .campaign-card__face--back {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		text-align: center;
		min-height: 22rem;
		padding: 2rem;
		border-radius: inherit;
		background-color: var( --campaign-card-back-bg, #000000 );
		background-image: linear-gradient( rgba( 0, 0, 0, 0.45 ), rgba( 0, 0, 0, 0.45 ) ), var( --campaign-card-back-image, none );
		background-size: cover;
		background-position: center;
		color: var( --campaign-card-back-text, #ffffff );
	}

	.campaign-grid--flip .campaign-card__back-inner {
		position: relative;
		z-index: 1;
	}
}

/* Alternating slide-in per card (2026-07-23), same reveal mechanism/
 * "is-revealed" flip + IntersectionObserver already used for
 * .doelen-goals-grid .campaign-card above (main.js's shared observer
 * now also watches .campaign-grid--flip .campaign-card) — no separate
 * animation logic, once per page view. Desktop keeps its own hover-
 * flip interaction untouched; this opacity/slide only ever applies
 * below the 1024px breakpoint. */
@media (max-width: 1023.98px) {
	html.js .campaign-grid--flip .campaign-card {
		opacity: 0;
	}

	html.js .campaign-grid--flip .campaign-card.is-revealed {
		animation: pb-fade-in-left 0.8s ease forwards;
	}

	html.js .campaign-grid--flip .campaign-card:nth-child(even).is-revealed {
		animation-name: pb-fade-in-right;
	}
}

@media (max-width: 1023.98px) and (prefers-reduced-motion: reduce) {
	.campaign-grid--flip .campaign-card {
		opacity: 1;
		animation: none;
	}
}

.campaign-card__media {
	position: absolute;
	inset: 0;
	background: linear-gradient(160deg, #2b2b2b 0%, #000 100%);
	transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.campaign-card:hover .campaign-card__media,
.campaign-card:focus-within .campaign-card__media {
	transform: scale(1.07);
}

/* Dark text-legibility overlay — a dedicated ::after layer, not part of
 * .campaign-card__body's own background. It used to live on __body
 * itself, which worked fine while __body stretched full-width; once
 * __body got a fixed max-width (so title/text/link never reflow while
 * the card's own width animates on hover — see __body below), a
 * background living ON __body would only ever cover that fixed width,
 * leaving the newly-revealed, wider part of the card's image
 * undimmed on hover. inset:0 + border-radius:inherit means this layer
 * always matches the card's *current* box exactly, at any width, and
 * respects the card's own corner radius. pointer-events:none so it
 * never intercepts clicks meant for the real content/link in __body. */
.campaign-card::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
	pointer-events: none;
	background: linear-gradient(0deg, rgba(0, 0, 0, 0.65) 0%, rgba(0, 0, 0, 0) 55%);
	transition: background 0.5s ease;
}

.campaign-card:hover::after,
.campaign-card:focus-within::after {
	background: linear-gradient(0deg, rgba(0, 0, 0, 0.82) 0%, rgba(0, 0, 0, 0.05) 55%);
}

/* Alternating card background, cycling every 4 cards — driven by real
 * sibling DOM position (:nth-child), not a PHP-computed
 * `campaign-card--N` class. Each card is its own pb/campaign-grid-item
 * child block since the Framework v1.0 editor-UX pass, so "which
 * position in the grid" is exactly what :nth-child already expresses;
 * nothing needs to be threaded from parent to child for this. */
.campaign-grid__inner .campaign-card:nth-child(4n + 1) .campaign-card__media {
	background: linear-gradient(160deg, #3a3a3a 0%, #0d0d0d 100%);
}

.campaign-grid__inner .campaign-card:nth-child(4n + 2) .campaign-card__media {
	background: linear-gradient(160deg, #ff4d2d 0%, #7a1500 100%);
}

.campaign-grid__inner .campaign-card:nth-child(4n + 3) .campaign-card__media {
	background: linear-gradient(160deg, #2b2b2b 0%, #000 100%);
}

.campaign-grid__inner .campaign-card:nth-child(4n + 4) .campaign-card__media {
	background: linear-gradient(160deg, #444 0%, #111 100%);
}

.campaign-card__body {
	position: relative;
	z-index: 1;
	max-width: 22rem;
	padding: 1.75rem;
}

/* Typography token-driven via PB Rich Heading's `.pb-type--h3` class —
 * "h3" reused as-is (weight 800 and letter-spacing -0.01em already
 * match) with a scoped override for size/transform/color. */
.campaign-card__title {
	margin: 0 0 0.4rem;
	font-size: clamp(1.35rem, 2.5vw, 1.75rem);
	text-transform: uppercase;
	color: #fff;
	/* Guaranteed legibility (2026-07-23) — the ::after gradient below
	 * already darkens this area, but a lighter patch of a given photo
	 * can still leave solid white looking washed out; this subtle dark
	 * shadow keeps the title readable regardless of what's directly
	 * behind it, without changing its color/size/position. */
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

/* "body" token already matches this text's weight (400); size and
 * color are this card's own nuance. Solid white (2026-07-23 — was
 * rgba(255,255,255,0.85)), same legibility reasoning as the title
 * above. */
.campaign-card__text {
	margin: 0 0 1rem;
	font-size: 0.9rem;
	color: #fff;
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
	max-width: 22rem;
	transition: opacity 0.45s ease, transform 0.45s ease;
}

/* Animated underline (same left-to-right reveal technique already
 * used for .main-navigation__menu a::after) instead of a static
 * border-bottom + opacity fade. */
/* Plain text-link CTA style only (cardCtaStyle "link") — a real button
 * (.pb-button, primary/secondary/ghost) carries its own complete look
 * and must not inherit this animated-underline treatment, so every
 * selector here is scoped to :not(.pb-button). */
.campaign-card__link:not(.pb-button) {
	position: relative;
	display: inline-block;
	font-size: 0.8rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #fff;
	padding-bottom: 0.2rem;
}

.campaign-card__link:not(.pb-button)::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	height: 1.5px;
	background: currentColor;
	transform: scaleX(0.35);
	transform-origin: left;
	transition: transform 0.35s ease;
}

.campaign-card__link:not(.pb-button):hover::after,
.campaign-card__link:not(.pb-button):focus-visible::after,
.campaign-card:hover .campaign-card__link:not(.pb-button)::after,
.campaign-card:focus-within .campaign-card__link:not(.pb-button)::after {
	transform: scaleX(1);
}

/* PB Coaching Card (2026-07-20) — pb/campaign-grid-item's InnerBlocks
 * path: Number/Image/Overlay/Heading/Paragraph as real child blocks.
 * Image/Overlay are full-bleed layers (image reuses .pb-hc-image — see
 * blocks/hero-centered-image — normally a contained "floating" image,
 * overridden to cover here); Heading/Paragraph reuse .pb-hc-heading/
 * .pb-hc-body and sit above them via z-index, bottom-anchored by
 * .campaign-card's own flex column + justify-content:flex-end. */
.campaign-card > .pb-hc-image {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
}

.campaign-card > .pb-hc-image .pb-hc-image__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.campaign-card > .pb-card-overlay {
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
}

.campaign-card > .pb-card-number {
	position: absolute;
	top: 1.25rem;
	left: 1.25rem;
	z-index: 2;
	display: block;
	background: #fff;
	color: #111;
	font-weight: 800;
	font-size: 1.5rem;
	line-height: 1;
	padding: 0.6rem 0.85rem 0.5rem;
	border-radius: 0.75rem 0.75rem 0.75rem 0;
}

.campaign-card > .pb-hc-heading,
.campaign-card > .pb-hc-body {
	position: relative;
	z-index: 1;
	padding: 0 1.75rem;
}

.campaign-card > .pb-hc-heading {
	margin: 0 0 0.4rem;
}

.campaign-card > .pb-hc-body {
	margin: 0 0 1.75rem;
}

.campaign-card > .pb-hc-heading .pb-hc-heading__text {
	font-size: clamp(1.35rem, 2.5vw, 1.75rem);
	text-transform: uppercase;
}

.campaign-card > .pb-hc-body .pb-hc-body__text {
	font-size: 0.9rem;
	opacity: 0.85;
}

/* >=640px: Coca-Cola-style hover expansion. Switching from CSS grid to
 * a wrapping flex row is what makes this possible with no JS and no
 * markup change: flex-wrap treats each wrapped line as its own
 * independent flex-grow context, so growing the hovered card's
 * flex-grow only redistributes space against its own row sibling (2
 * cards per row, same 2x2 layout as before) — the other row is
 * unaffected. Mobile (<640px) keeps its original display:grid;
 * grid-template-columns:1fr stack untouched. */
@media (min-width: 640px) {
	.campaign-grid__inner {
		display: flex;
		flex-wrap: wrap;
		gap: 1.5rem;
	}

	/* flex-basis is 0, not calc(50% - gap) — with a fixed ~50% basis,
	 * 2 cards + the gap already consume exactly 100% of the row, leaving
	 * zero free space for flex-grow to redistribute on hover (a hovered
	 * card's higher flex-grow had nothing to actually grow into, so
	 * cards never visibly resized). With basis:0, the entire row width
	 * is allocated via flex-grow ratios instead — equal ratios (1:1) at
	 * rest still split 50/50, but a higher hover ratio now has the
	 * whole row to redistribute against, producing a real, visible
	 * size change. */
	.campaign-card {
		flex: 1 1 0%;
		transition: flex-grow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
	}

	.campaign-card:hover,
	.campaign-card:focus-within {
		flex-grow: 1.6;
	}

	/* Preview (first paragraph) at rest, full text on hover/focus — a
	 * generous max-height on hover (not a tight 6rem cap) so real content
	 * is never clipped, instead of the previous hidden-until-hover state. */
	.campaign-card__text {
		max-height: 4.5rem;
		opacity: 1;
		overflow: hidden;
		transform: none;
	}

	.campaign-card:hover .campaign-card__text,
	.campaign-card:focus-within .campaign-card__text {
		max-height: 40rem;
	}
}

/* Confirmed still showing the full 1.5rem gap on real mobile/tablet
 * devices at exactly 768px (a common width — e.g. iPad portrait —
 * excluded by a 767.98px cutoff) and throughout the rest of the
 * 640-1023px range. Applies the tighter gap for the entire span this
 * component treats as non-desktop (up to the 1024px breakpoint where
 * .campaign-grid's own padding/.campaign-card sizing next changes),
 * without moving the 640px 2-column flex-wrap switch itself. Only true
 * desktop (>=1024px) keeps the unchanged 1.5rem gap from the block
 * above. */
@media (min-width: 640px) and (max-width: 1023.98px) {
	.campaign-grid__inner {
		gap: 0.25rem;
	}
}

@media (min-width: 1024px) {
	.campaign-grid {
		/* Symmetric top/bottom (was 2rem/6rem, 2026-07-17 fix, same
		 * reasoning as the mobile-first base rule above). */
		padding: 2rem 3rem;
	}

	.campaign-card {
		min-height: 26rem;
	}
}

@media (min-width: 1440px) {
	.campaign-grid {
		padding: 2rem 4rem;
	}
}

@media (min-width: 640px) and (prefers-reduced-motion: reduce) {
	.campaign-card,
	.campaign-card::after,
	.campaign-card__media,
	.campaign-card__body,
	.campaign-card__text,
	.campaign-card__link::after {
		transition: none !important;
	}

	.campaign-card:hover,
	.campaign-card:focus-within {
		flex-grow: 1;
	}

	.campaign-card__text {
		max-height: none;
		margin-bottom: 1rem;
		opacity: 1;
		transform: none;
	}
}

/* "icon" card style (2026-07-15, cardStyle:"icon") — a small, flat,
 * always-static card (icon + title + text, optional accent-color top
 * border), not the original full-bleed photo card with its Coca-Cola
 * hover-expansion. Scoped entirely to .campaign-card--icon so the
 * unmodified .campaign-card rules above (still the default, cardStyle
 * "photo") are completely unaffected. */
.campaign-card--icon {
	flex-grow: 1 !important;
	justify-content: flex-start;
	align-items: center;
	min-height: 0;
	padding: 2rem 1.5rem;
	text-align: center;
	background: var(--campaign-card-bg, #17171c);
	border-top: 3px solid var(--campaign-card-accent, #6848f4);
	border-radius: 0.75rem;
}

/* Bottom-align each card's CTA regardless of how many lines the title/
 * text above it take — cards in the same row already stretch to equal
 * height (.campaign-grid__inner's default flex align-items:stretch),
 * so pushing just the CTA to the bottom of .__body (not the whole
 * body block) lines every button up on the same baseline. */
.campaign-card--icon .campaign-card__body {
	display: flex;
	flex-direction: column;
	flex: 1;
	align-self: stretch;
}

.campaign-card--icon .campaign-card__link {
	margin-top: auto;
}

.campaign-card--icon:hover,
.campaign-card--icon:focus-within {
	flex-grow: 1 !important;
}

.campaign-card--icon::after {
	content: none;
}

.campaign-card--icon .campaign-card__icon {
	width: 2.75rem;
	height: 2.75rem;
	margin-bottom: 1.25rem;
}

.campaign-card--icon .campaign-card__icon img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
}

.campaign-card--icon .campaign-card__body {
	position: static;
	max-width: none;
	padding: 0;
}

.campaign-card--icon .campaign-card__title {
	text-transform: none;
	font-size: 1.15rem;
}

.campaign-card--icon .campaign-card__text {
	max-height: none !important;
	margin-bottom: 0 !important;
	opacity: 1 !important;
	transform: none !important;
	overflow: visible;
}

/* Campaign banner */
.campaign-banner {
	/* Same custom property .hero also defines — .button--primary:hover
	 * references it with no fallback, so without redefining it here
	 * too, hovering this block's CTA resolved to an invalid custom
	 * property (background-color's initial value, transparent),
	 * making the button appear to vanish into the dark backdrop. */
	--hero-accent: #ff4d2d;
	position: relative;
	display: flex;
	align-items: flex-end;
	min-height: 34rem;
	padding: 3rem 1.25rem;
	overflow: hidden;
	color: #fff;
	text-align: left;
}

.campaign-banner.alignfull {
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.campaign-banner.alignwide {
	max-width: 1200px;
	margin-left: auto;
	margin-right: auto;
}

.campaign-banner__media {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, #1a1a1a 0%, #000 60%, #262626 100%);
}

.campaign-banner__scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(0deg, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.15) 60%);
}

.campaign-banner__content {
	position: relative;
	z-index: 1;
	max-width: 34rem;
}

.campaign-banner__eyebrow {
	margin: 0 0 0.5rem;
	font-size: 0.8rem;
	font-weight: 700;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: #ff4d2d;
}

/* Typography token-driven via PB Rich Heading's `.pb-type--h1` class —
 * "h1" reused as-is (its weight 800 matches) with a scoped override
 * for the rest, same pattern/reasoning as pb/hero's own title (every
 * Display token is already claimed by other blocks). */
.campaign-banner__title {
	margin: 0 0 0.6rem;
	font-size: clamp(2rem, 7vw, 3.75rem);
	line-height: 0.98;
	text-transform: uppercase;
	letter-spacing: -0.02em;
	color: #fff;
}

/* "body" token already matches this subtitle's weight (400); size and
 * color are this block's own nuance. */
.campaign-banner__subtitle {
	margin: 0 0 1.75rem;
	font-size: 1.05rem;
	color: rgba(255, 255, 255, 0.85);
}

/* 2026-07-16: white-fill scoped override removed — campaign-banner's
 * "primary" CTA now renders the exact same sitewide .pb-button--primary
 * (design-system.css, purple #6848F4 / hover #7E6FEF) as every other
 * Primary CTA. No local color override left; default :hover/:focus
 * already comes from the shared rule. */

@media (min-width: 768px) {
	.campaign-banner {
		padding: 5rem 3rem;
		min-height: 38rem;
	}
}

@media (min-width: 1440px) {
	.campaign-banner {
		padding: 5rem 4rem;
	}
}

/* ==========================================================================
 * pb/campaign-banner-v2 — redesigned Campaign Banner (2026-07-15):
 * full-bleed dark 2-column split (text left, image right, stacks on
 * mobile — text first), recreating paperbrain.store's homepage
 * "Ontdek de kracht van een PaperBrain®" section. The 4 reorderable
 * child blocks (heading/body/cta/image) stay fully independent in the
 * editor, but the PHP render (inc/campaign-banner-v2-block.php) always
 * splits them into these two wrapper columns by TYPE rather than DOM
 * position — .pb-cb-text (normal block-stacking, auto height) and
 * .pb-cb-media (the image, vertically centered against it). A flat
 * "every child is its own CSS Grid item, image row-spans column 2"
 * version was tried first and dropped: an item spanning several auto
 * grid rows forces the browser to inflate every one of those rows to
 * jointly fit its height (CSS Grid track-sizing spec), which pushed
 * huge gaps between the short text children instead of them stacking
 * normally. Reuses the exact pb-hc-hide-(desktop|tablet|mobile)/pb-hc-animate responsive-
 * visibility + entrance-animation system already built for
 * pb/hero-centered's children rather than a duplicate set of rules —
 * those class names are attached by pb_hc_child_classes()/
 * pb-hc-common.js regardless of which parent block a child belongs to.
 * ========================================================================*/
/* Full-bleed positioning moved inline (pb_render_campaign_banner_v2_block(),
 * inc/campaign-banner-v2-block.php) — the stylesheet version of this rule
 * was empirically not being applied in testing; inline verified working. */

/* Entrance animation override (2026-07-16, updated 2026-07-17) — scoped
 * to this block only, does not touch .pb-hc-animate's own rule (shared
 * with pb/hero-centered). Desktop: spins in from the right (image sits
 * on the right side of this layout); mobile below spins in from the
 * left instead — same pb-cb-spin-in-* technique, mirrored direction. */
.pb-campaign-banner .pb-hc-animate {
	animation: pb-cb-spin-in-right 1s ease 0s 1 forwards;
}

@keyframes pb-cb-spin-in-right {
	0% {
		opacity: 0;
		transform: translateX(100%) rotate(0deg);
	}

	100% {
		opacity: 1;
		transform: translateX(0) rotate(360deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.pb-campaign-banner .pb-hc-animate {
		animation: none;
	}
}

/* Mobile-only scroll-triggered override (2026-07-17) — the desktop
 * spin above plays on paint (page load), which is fine on desktop
 * where the banner is already in view, but on mobile the banner sits
 * further down the stacked layout: the animation had already finished
 * before the user scrolled to it, so it just appeared static. Reuses
 * the site's existing IntersectionObserver + "is-revealed" class-flip
 * (main.js, same mechanism as .goal-step/.pb-image-text-compact — no
 * new observer). Mobile spins in from the left (opposite side from
 * desktop's spin-in-from-right above, matching each breakpoint's own
 * stacking order). */
@media (max-width: 767.98px) {
	html.js .pb-campaign-banner .pb-hc-animate {
		opacity: 0;
		animation: none;
	}

	html.js .pb-campaign-banner.is-revealed .pb-hc-animate {
		animation: pb-cb-spin-in-left 1s ease 0s 1 forwards;
	}
}

@keyframes pb-cb-spin-in-left {
	0% {
		opacity: 0;
		transform: translateX(-100%) rotate(0deg);
	}

	100% {
		opacity: 1;
		transform: translateX(0) rotate(360deg);
	}
}

@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {
	html.js .pb-campaign-banner .pb-hc-animate {
		opacity: 1;
		animation: none;
	}
}

/* Gap/padding measured live from paperbrain.store's own "Ontdek de
 * kracht van een PaperBrain®" section (2026-07-16, 1512px viewport):
 * section 96px top / 64px bottom padding, 0 horizontal (the 1200px
 * max-width + auto margins alone create the desktop gutter); 96px gap
 * between two ~552px columns (46/46% of 1200px). Horizontal padding
 * comes back at the mobile breakpoint below, where there's no 1200px
 * container left to create a gutter on its own. */
.pb-campaign-banner__inner {
	display: flex;
	align-items: center;
	gap: 6rem;
	max-width: 1200px;
	margin: 0 auto;
	padding: 6rem 0 4rem;
}

.pb-cb-text {
	flex: 1 1 50%;
	min-width: 0;
}

/* Desktop column position is controlled by the "direction" attribute
 * (pb-campaign-banner--image-left), independent of DOM order — DOM
 * order itself follows List View (mobile stacking), see
 * pb_render_campaign_banner_v2_block(). Only set above the mobile
 * stack breakpoint so mobile keeps natural DOM/List-View order. */
@media (min-width: 901px) {
	.pb-campaign-banner .pb-cb-text { order: 1; }
	.pb-campaign-banner .pb-cb-media { order: 2; }
	.pb-campaign-banner--image-left .pb-cb-text { order: 2; }
	.pb-campaign-banner--image-left .pb-cb-media { order: 1; }
}

.pb-cb-eyebrow {
	margin: 0 0 0.75rem;
	font-size: 0.8rem;
	font-weight: 700;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: #ff4d2d;
}

/* Per-instance typography override (2026-07-16, pb/campaign-banner-v2-*
 * children) — same pb_typography_css_vars()/pbSectionTypography pattern
 * already used by pb/hero-image-split's own sections. Only active when
 * the child's "Typografie override" panel is actually engaged (the
 * ".pb-cb-*--override" modifier class + "--cb?-*" vars are only ever
 * output then, see inc/campaign-banner-v2-block.php) — every existing
 * instance with no override stays on the plain rules above, untouched. */
.pb-cb-eyebrow--override .pb-cb-eyebrow {
	font-size: var(--cbe-eyebrow-desktop-size);
	line-height: var(--cbe-eyebrow-desktop-line-height);
	font-weight: var(--cbe-eyebrow-desktop-weight);
	letter-spacing: var(--cbe-eyebrow-desktop-spacing);
}

.pb-cb-heading {
	margin: 0 0 1.25rem;
}

/* !important required: .pb-cb-heading__text renders a real semantic tag
 * (h1-h6), and design-system.css's own bare h1..h6 rules already force
 * these 4 properties with !important — same conflict class already
 * fixed for pb/hero-image-split's own heading (inc/hero-image-split-block.php). */
.pb-cb-heading--override .pb-cb-heading__text {
	font-size: var(--cbh-heading-desktop-size) !important;
	line-height: var(--cbh-heading-desktop-line-height) !important;
	font-weight: var(--cbh-heading-desktop-weight) !important;
	letter-spacing: var(--cbh-heading-desktop-spacing) !important;
}

/* color:inherit (not a hardcoded white) — .pb-campaign-banner's own
 * "Tekstkleur" setting (bgColor's counterpart, default #ffffff for the
 * original dark-banner look) sets the actual color on the section
 * element; a hardcoded white here would go invisible the moment an
 * instance uses a light bgColor instead (e.g. the coachingstool page's
 * #efebe6 banner, which needs dark text) — same "color:inherit is
 * load-bearing" lesson as .animated-statement__text/__subtitle. */
/* Regular base weight, <strong> stands out — same pattern already used
 * by .pb-image-text__heading/.animated-statement__text. Without this,
 * the h2 preset's own 800 base weight (design-system.css) left almost
 * no visible difference against <strong>'s +100 (900) — bold looked
 * "already on" everywhere, so toggling it felt broken. !important
 * required for the same reason as .pb-cb-heading--override above:
 * design-system.css's bare h1..h6 rules set font-weight with
 * !important themselves. */
.pb-cb-heading__text {
	margin: 0;
	color: inherit;
	font-weight: 400 !important;
}

.pb-cb-heading__text strong {
	font-weight: 700;
}

.pb-cb-body {
	margin: 0 0 2rem;
}

.pb-cb-body__text {
	margin: 0;
	color: inherit;
	opacity: 0.75;
	max-width: 32rem;
}

.pb-cb-body--override .pb-cb-body__text {
	font-size: var(--cbb-body-desktop-size);
	line-height: var(--cbb-body-desktop-line-height);
	font-weight: var(--cbb-body-desktop-weight);
	letter-spacing: var(--cbb-body-desktop-spacing);
}

/* A bit more breathing room above the CTA (on top of .pb-cb-body's own
 * 2rem bottom margin) — was sitting too close to the paragraph above. */
.pb-cb-cta {
	margin-top: 2rem;
}

.pb-cb-cta--center {
	display: flex;
	justify-content: center;
	/* Standalone usage (not nested in .pb-image-text__body, e.g. right
	 * after PB FAQ): FAQ already ends with 4rem of its own bottom
	 * padding, so the base 2rem margin-top on top of that was too much;
	 * margin-bottom keeps it from sitting flush against whatever
	 * section follows. */
	margin-top: 0;
	margin-bottom: 3rem;
}

.pb-cb-media {
	flex: 1 1 50%;
	min-width: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

.pb-cb-image__img {
	display: block;
	width: var(--pcb-image-width, 420px);
	max-width: 100%;
	height: auto;
	/* No effect until a future instance sets a fixed aspect-ratio/crop
	 * (object-position only matters together with object-fit) — see
	 * inc/campaign-banner-v2-block.php's pb_render_campaign_banner_v2_image_block(). */
	object-position: var(--pcb-focal-position, 50% 50%);
}

.pb-cb-image.has-shadow .pb-cb-image__img {
	filter: drop-shadow(0 30px 40px rgba(0, 0, 0, 0.35));
}

/* Opt-in square crop (2026-07-16) — for source images authored as wide
 * backgrounds (object-fit:cover + the focal point picks out the square
 * worth showing), instead of the default natural-aspect-ratio render. */
.pb-cb-image--crop .pb-cb-image__img {
	aspect-ratio: 1 / 1;
	height: auto;
	object-fit: cover;
}

@media (max-width: 900px) {
	/* !important: .pb-campaign-banner__inner/.pb-cb-text/.pb-cb-media now
	 * carry inline layout styles (see pb_render_campaign_banner_v2_block(),
	 * inc/campaign-banner-v2-block.php — same empirical reason as the
	 * section's own full-bleed rule above), so this override must win
	 * against inline, not just against another stylesheet rule. */
	.pb-campaign-banner__inner {
		flex-direction: column !important;
		align-items: stretch !important;
		padding: 4rem 1.5rem !important;
		gap: 2.5rem !important;
	}

	.pb-cb-text, .pb-cb-media {
		flex-basis: auto !important;
		width: 100% !important;
	}

	.pb-cb-media {
		margin-top: 0;
	}

	.pb-cb-image__img {
		width: min(var(--pcb-image-width, 420px), 320px);
	}

	/* This block only has the one (900px) responsive breakpoint, so the
	 * typography-override panel's separate "tablet"/"mobile" fields both
	 * resolve here — mobile takes priority when both are filled in. */
	.pb-cb-eyebrow--override .pb-cb-eyebrow {
		font-size: var(--cbe-eyebrow-mobile-size, var(--cbe-eyebrow-tablet-size, var(--cbe-eyebrow-desktop-size)));
	}

	.pb-cb-heading--override .pb-cb-heading__text {
		font-size: var(--cbh-heading-mobile-size, var(--cbh-heading-tablet-size, var(--cbh-heading-desktop-size))) !important;
	}

	.pb-cb-body--override .pb-cb-body__text {
		font-size: var(--cbb-body-mobile-size, var(--cbb-body-tablet-size, var(--cbb-body-desktop-size)));
	}
}

/* Team / category selector */
.team-selector {
	padding: 4rem 1.25rem;
	text-align: center;
	background: #f7f7f7;
}

.team-selector.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.team-selector.alignwide {
	max-width: 1200px;
	margin-left: auto;
	margin-right: auto;
}

.team-selector__title {
	margin: 0 0 2.5rem;
}

.team-selector__title.pb-type--h2 {
	font-size: clamp(1.75rem, 3.5vw, 2.25rem);
	text-transform: uppercase;
}

.team-selector__grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 2rem 1rem;
	max-width: 960px;
	margin: 0 auto 2.5rem;
}

.team-selector__item {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.75rem;
	color: #111;
}

.team-selector__logo {
	display: block;
	width: 4.5rem;
	height: 4.5rem;
	border-radius: 50%;
	background: linear-gradient(135deg, #ff4d2d 0%, #111 100%);
	transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s ease;
}

.team-selector__item:hover .team-selector__logo,
.team-selector__item:focus-visible .team-selector__logo {
	transform: scale(1.08);
	box-shadow: 0 8px 20px rgba(17, 17, 17, 0.15);
}

.team-selector__name {
	font-size: 0.85rem;
	font-weight: 600;
	transition: opacity 0.2s ease;
}

.team-selector__item:hover .team-selector__name {
	opacity: 0.7;
}

.button--ghost-dark {
	background: transparent;
	color: #111;
	border-color: rgba(17, 17, 17, 0.55);
}

.button--ghost-dark:hover,
.button--ghost-dark:focus-visible {
	border-color: #111;
	background: rgba(17, 17, 17, 0.08);
	transform: translateY(-2px);
	outline: none;
}

.button--dark {
	background: #111;
	color: #fff;
}

.button--dark:hover,
.button--dark:focus-visible {
	background: #ff4d2d;
	color: #fff;
	transform: translateY(-2px);
	box-shadow: 0 8px 20px rgba(17, 17, 17, 0.2);
	outline: none;
}

@media (min-width: 480px) {
	.team-selector__grid {
		grid-template-columns: repeat(4, 1fr);
	}
}

@media (min-width: 768px) {
	.team-selector__grid {
		grid-template-columns: repeat(8, 1fr);
	}
}

@media (min-width: 1024px) {
	.team-selector {
		padding: 6rem 3rem;
	}
}

/* Featured products carousel */
.products {
	padding: 4rem 1.25rem;
	background: #fff;
}

.products.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.products.alignwide {
	max-width: 1200px;
	margin-left: auto;
	margin-right: auto;
}

.products__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	max-width: 1440px;
	margin: 0 auto 1.75rem;
}

.products__title {
	margin: 0;
}

.products__title.pb-type--h2 {
	font-size: clamp(1.75rem, 3.5vw, 2.25rem);
	text-transform: uppercase;
}

.products__nav {
	display: flex;
	gap: 0.5rem;
}

.products__nav-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.5rem;
	height: 2.5rem;
	border: 1.5px solid rgba(17, 17, 17, 0.25);
	border-radius: 50%;
	background: transparent;
	font-size: 1.25rem;
	line-height: 1;
	color: #111;
	cursor: pointer;
	transition: border-color 0.2s ease, background-color 0.2s ease, transform 0.2s ease;
}

.products__nav-btn:hover,
.products__nav-btn:focus-visible {
	border-color: #111;
	background: rgba(17, 17, 17, 0.06);
	transform: translateY(-1px);
	outline: none;
}

.products__nav-btn:active {
	transform: translateY(0) scale(0.94);
}

.products__track {
	display: flex;
	gap: 1.25rem;
	max-width: 1440px;
	margin: 0 auto;
	padding-bottom: 0.5rem;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
}

.products__track::-webkit-scrollbar {
	display: none;
}

.product-card {
	flex: 0 0 15rem;
	scroll-snap-align: start;
}

.product-card__media {
	position: relative;
	height: 15rem;
	margin-bottom: 1rem;
	overflow: hidden;
	border-radius: 0.75rem;
	background: linear-gradient(160deg, #eee 0%, #ccc 100%);
}

.product-card__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: rgba(17, 17, 17, 0);
	transition: background-color 0.3s ease;
}

.product-card:hover .product-card__media::after,
.product-card:focus-within .product-card__media::after {
	background: rgba(17, 17, 17, 0.06);
}

.product-card__media--1 {
	background: linear-gradient(160deg, #f4d9d0 0%, #ff4d2d 100%);
}

.product-card__media--2 {
	background: linear-gradient(160deg, #e5e5e5 0%, #999 100%);
}

.product-card__media--3 {
	background: linear-gradient(160deg, #dfe7f0 0%, #7b93ab 100%);
}

.product-card__media--4 {
	background: linear-gradient(160deg, #eee 0%, #2b2b2b 100%);
}

.product-card__pill {
	position: absolute;
	top: 0.75rem;
	left: 0.75rem;
	padding: 0.3rem 0.7rem;
	border-radius: 999px;
	background: #fff;
	color: #111;
	font-size: 0.7rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.product-card__name {
	margin: 0 0 0.3rem;
}

.product-card__name.pb-type--h3 {
	font-size: 0.95rem;
	font-weight: 700;
	letter-spacing: normal;
}

.product-card__price {
	margin: 0;
}

.product-card__price.pb-type--small {
	font-size: 0.9rem;
	font-weight: 400;
	letter-spacing: normal;
	color: #666;
}

@media (min-width: 768px) {
	.product-card {
		flex-basis: 17rem;
	}
}

@media (min-width: 1024px) {
	.products {
		padding: 6rem 3rem;
	}
}

@media (min-width: 1440px) {
	.products {
		padding: 6rem 4rem;
	}
}

/* Verkooppunten */
.verkooppunten {
	padding: 4rem 1.25rem;
	background: #fff;
}

.verkooppunten__inner {
	display: grid;
	grid-template-columns: 1fr;
	gap: 2.5rem;
	max-width: 1440px;
	margin: 0 auto 3rem;
}

.verkooppunten__title {
	margin: 0 0 0.75rem;
	font-size: clamp(1.75rem, 4vw, 2.5rem);
	font-weight: 800;
	text-transform: uppercase;
	letter-spacing: -0.02em;
}

.verkooppunten__subtitle {
	margin: 0 0 1.5rem;
	font-size: 1rem;
	color: #666;
	max-width: 30rem;
}

.verkooppunten__search {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.75rem;
	max-width: 26rem;
}

.verkooppunten__input {
	width: 100%;
	padding: 0.9rem 1.15rem;
	border: 1.5px solid #ddd;
	border-radius: 999px;
	font-size: 0.9rem;
	font-family: inherit;
	color: #111;
	background: #f7f7f7;
	transition: border-color 0.2s ease, background-color 0.2s ease;
}

.verkooppunten__input::placeholder {
	color: #999;
}

.verkooppunten__input:focus-visible,
.verkooppunten__input:focus {
	border-color: #111;
	background: #fff;
	outline: none;
}

.verkooppunten__partner-link {
	display: inline-block;
	margin-top: 1.25rem;
	font-size: 0.8rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #111;
	border-bottom: 1.5px solid #111;
	padding-bottom: 0.15rem;
	transition: opacity 0.2s ease;
}

.verkooppunten__partner-link:hover,
.verkooppunten__partner-link:focus-visible {
	opacity: 0.6;
}

.verkooppunten__map {
	position: relative;
	min-height: 23rem;
	border-radius: 0.75rem;
	overflow: hidden;
	background: linear-gradient(135deg, #eef1f4 0%, #d7dde3 100%);
}

.verkooppunten__map .leaflet-container {
	width: 100%;
	height: 100%;
	min-height: 23rem;
	font-family: inherit;
	background: transparent;
}

@media (min-width: 768px) {
	.verkooppunten__map,
	.verkooppunten__map .leaflet-container {
		min-height: 28rem;
	}
}

/*
 * Custom PaperBrain marker (replaces the default Leaflet pin): a
 * circular red badge with a small white logo mark centered inside,
 * similar in feel to Nike's circular store-locator pins.
 */
.pb-marker {
	background: transparent;
	border: 0;
}

.pb-marker__pin {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: var(--sl-marker-bg, #ea3325);
	border: 2.5px solid #fff;
	box-shadow: 0 3px 10px rgba(17, 17, 17, 0.35);
	transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s ease, background-color 0.2s ease;
}

.pb-marker__pin img {
	display: block;
	width: 16px;
	height: 16px;
}

.pb-marker.is-active .pb-marker__pin {
	background: var(--sl-marker-active, #ea3325);
	transform: scale(1.3);
	box-shadow: 0 6px 16px rgba(17, 17, 17, 0.45);
}

/*
 * Custom popup: a large Google Maps-style card (image left, info
 * right) floating above the marker. Strips almost all default
 * Leaflet popup chrome and replaces it with our own rounded,
 * shadowed card — only the small centered tail is kept (restyled)
 * so the card still visually "points" at its marker.
 */
.pb-popup-wrapper {
	margin-bottom: 14px;
}

.pb-popup-wrapper .leaflet-popup-content-wrapper {
	padding: 0;
	border-radius: var(--sl-popup-radius, 18px);
	box-shadow: var(--sl-popup-shadow, 0 22px 48px rgba(17, 17, 17, 0.28));
	overflow: hidden;
}

.pb-popup-wrapper .leaflet-popup-content {
	margin: 0;
	width: 340px !important;
}

.pb-popup-wrapper .leaflet-popup-tip-container {
	margin-top: -1px;
}

.pb-popup-wrapper .leaflet-popup-tip {
	width: 16px;
	height: 16px;
	background: #fff;
	box-shadow: 0 8px 16px rgba(17, 17, 17, 0.18);
}

.pb-popup-wrapper .leaflet-popup-close-button {
	top: 0.6rem;
	right: 0.6rem;
	width: 1.75rem;
	height: 1.75rem;
	border-radius: 50%;
	background: rgba(17, 17, 17, 0.55);
	color: #fff;
	font-size: 1.1rem;
	line-height: 1.75rem;
	text-align: center;
}

.pb-popup-wrapper .leaflet-popup-close-button:hover {
	background: rgba(17, 17, 17, 0.8);
	color: #fff;
}

.pb-popup {
	display: flex;
	align-items: stretch;
	font-family: inherit;
	color: #111;
}

/*
 * Google Maps popup only (dedicated Store Locator page) — added via
 * buildPopupHtml()'s `compact` flag, never present on the homepage's
 * Leaflet popup. Fixes width + height so every popup is identically
 * sized regardless of image aspect ratio or how much text a given
 * store has (e.g. "Bruna" vs "Nijenoord") — overflow is clipped
 * rather than growing the box.
 */
.pb-popup--compact {
	width: 300px;
	height: 110px;
	overflow: hidden;
}

.pb-popup__media {
	position: relative;
	flex: 0 0 100px;
	background: linear-gradient(160deg, #eee 0%, #ccc 100%);
}

.pb-popup__media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.pb-popup__info {
	flex: 1;
	min-width: 0;
	padding: 0.55rem 0.75rem;
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.pb-popup__logo {
	display: block;
	max-height: 24px;
	margin-bottom: 0.3rem;
}

a.pb-popup__name:hover,
a.pb-popup__name:focus-visible {
	text-decoration: underline;
}

.pb-popup__name {
	display: block;
	margin-bottom: 0.15rem;
	font-size: 0.92rem;
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: 1.2;
}

.pb-popup__address,
.pb-popup__hours {
	margin: 0 0 0.15rem;
	font-size: 0.76rem;
	line-height: 1.3;
	color: #767676;
}

.pb-popup__address {
	margin-bottom: 0.25rem;
}

.pb-popup__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.4rem;
	margin-top: 0.2rem;
}

/*
 * Homepage popup (Leaflet): the compact spacing above targets the
 * Google Maps popup on /verkooppunten. .pb-popup__info/__name/etc.
 * are shared classes, so restore the original, taller homepage
 * spacing here via the higher-specificity Leaflet-scoped selector —
 * the homepage popup stays pixel-identical to before.
 */
.pb-popup-wrapper .leaflet-popup-content .pb-popup__media img,
.pb-popup-wrapper .leaflet-popup-content .pb-popup__media--placeholder {
	min-height: 120px;
}

.pb-popup-wrapper .leaflet-popup-content .pb-popup__info {
	padding: 0.85rem 0.9rem;
	display: block;
}

.pb-popup-wrapper .leaflet-popup-content .pb-popup__logo {
	margin-bottom: 0.4rem;
}

.pb-popup-wrapper .leaflet-popup-content .pb-popup__name {
	margin-bottom: 0.3rem;
}

.pb-popup-wrapper .leaflet-popup-content .pb-popup__address,
.pb-popup-wrapper .leaflet-popup-content .pb-popup__hours {
	margin-bottom: 0.2rem;
	line-height: 1.35;
}

.pb-popup-wrapper .leaflet-popup-content .pb-popup__address {
	margin-bottom: 0.5rem;
}

.pb-popup-wrapper .leaflet-popup-content .pb-popup__actions {
	margin-top: 0.5rem;
}

/*
 * Homepage popup (Leaflet): this higher-specificity selector beats
 * Leaflet's own `.leaflet-container a { color: #0078A8; }` base rule,
 * which otherwise wins and shows through as bright blue link text.
 * Left unchanged — the homepage keeps its black/orange CTA.
 */
.pb-popup-wrapper .leaflet-popup-content a.pb-popup__route {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0.45rem 0.85rem;
	border-radius: 999px;
	background: #111;
	font-size: 0.68rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #fff;
	transition: background-color 0.2s ease, transform 0.2s ease;
}

.pb-popup-wrapper .leaflet-popup-content a.pb-popup__route:hover,
.pb-popup-wrapper .leaflet-popup-content a.pb-popup__route:focus-visible {
	background: #ff4d2d;
	color: #fff;
	transform: translateY(-1px);
}

/*
 * Locator page popup (Google Maps InfoWindow): the plain
 * `.pb-popup__route` selector only ever matches here, since the
 * homepage's popup content is always additionally wrapped in
 * `.pb-popup-wrapper .leaflet-popup-content`, which the more specific
 * rule above already covers.
 */
.pb-popup__route {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0.32rem 0.8rem;
	border-radius: 999px;
	background: var(--sl-popup-cta-bg, #6848f4);
	font-size: 0.68rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--sl-popup-cta-text, #fff);
	transition: background-color 0.2s ease, transform 0.2s ease;
}

.pb-popup__route:hover,
.pb-popup__route:focus-visible {
	background: var(--sl-popup-cta-hover-bg, #5636c9);
	color: var(--sl-popup-cta-hover-text, #fff);
	transform: translateY(-1px);
}

.pb-popup-wrapper .leaflet-popup-content a.pb-popup__website,
.pb-popup__website {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0.32rem 0.8rem;
	border-radius: 999px;
	border: 1px solid rgba(17, 17, 17, 0.15);
	background: transparent;
	font-size: 0.68rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #111;
	transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.pb-popup-wrapper .leaflet-popup-content a.pb-popup__website:hover,
.pb-popup-wrapper .leaflet-popup-content a.pb-popup__website:focus-visible,
.pb-popup__website:hover,
.pb-popup__website:focus-visible {
	background: #111;
	border-color: #111;
	color: #fff;
}

/* Google Maps InfoWindow: strip default chrome so it matches the
   Leaflet popup's rounded, shadowed card look. */
.gm-style .gm-style-iw-c {
	padding: 0 !important;
	top: 0 !important;
	border-radius: var(--sl-popup-radius, 18px);
	box-shadow: var(--sl-popup-shadow, 0 22px 48px rgba(17, 17, 17, 0.28));
}

.gm-style .gm-style-iw-d {
	overflow: hidden !important;
	padding: 0 !important;
	max-width: none !important;
}

.gm-style .gm-style-iw-ch {
	padding-top: 0 !important;
}

.gm-style .gm-style-iw-tc::after {
	background: #fff;
}

.gm-style .gm-ui-hover-effect {
	top: 6px !important;
	right: 6px !important;
	opacity: 0.9;
}

@media (max-width: 480px) {
	.pb-popup-wrapper .leaflet-popup-content {
		width: 270px !important;
	}

	.pb-popup__media {
		flex-basis: 85px;
	}

	.pb-popup__media img,
	.pb-popup__media--placeholder {
		min-height: 100px;
	}
}

.verkooppunten__list {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
	max-width: 1440px;
	margin: 0 auto;
}

.verkooppunt-card {
	padding: 1.5rem;
	border: 1px solid #ececec;
	border-radius: 0.75rem;
	transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.verkooppunt-card:hover,
.verkooppunt-card:focus-within {
	border-color: #111;
	box-shadow: 0 12px 28px rgba(17, 17, 17, 0.08);
	transform: translateY(-3px);
}

.verkooppunt-card.is-active {
	border-color: #ff4d2d;
	box-shadow: 0 12px 28px rgba(255, 77, 45, 0.18);
}

.verkooppunt-card__name {
	margin: 0 0 0.4rem;
	font-size: 1.05rem;
	font-weight: 700;
}

.verkooppunt-card__address,
.verkooppunt-card__hours {
	margin: 0 0 0.25rem;
	font-size: 0.85rem;
	color: #666;
}

.verkooppunt-card__link {
	display: inline-block;
	margin-top: 0.75rem;
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #111;
	border-bottom: 1.5px solid #111;
	padding-bottom: 0.1rem;
	transition: opacity 0.2s ease;
}

.verkooppunt-card__link:hover,
.verkooppunt-card__link:focus-visible {
	opacity: 0.6;
}

/*
 * Extended card elements used on the full locator page: a full-width,
 * text-only list row — Nike-style clean rows separated by a subtle
 * border. Name (clickable, opens the single Verkooppunt page) sits
 * above the address on the left; "Bekijk locatie" sits as a plain
 * text link on the right (same target as the title). Selected state
 * is a subtle red accent bar, not a bold outline.
 */
.verkooppunt-card--full {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	width: 100%;
	margin: 0;
	padding: 0.65rem 0.25rem;
	border: none;
	border-bottom: 1px solid #ececec;
	border-radius: var(--sl-list-radius, 0px);
	background: transparent;
	box-shadow: none;
	cursor: pointer;
	transition: background-color 0.2s ease;
}

/*
 * .verkooppunt-card--full sets its own `display: flex` — an author
 * rule, which (per the CSS cascade) always wins over the browser's
 * built-in `[hidden] { display: none }` default regardless of
 * selector specificity. Without this override, setting `.hidden =
 * true` in JS (filtering) had no visible effect on these rows.
 */
.verkooppunt-card--full[hidden] {
	display: none;
}

.verkooppunten-page__list .verkooppunt-card--full:last-child {
	border-bottom: none;
}

.verkooppunt-card--full:hover,
.verkooppunt-card--full:focus-within {
	background: #fafafa;
	box-shadow: none;
	transform: none;
}

.verkooppunt-card--full.is-active {
	border-color: #ececec;
	background: var(--sl-list-active-bg, #fdf1ef);
	box-shadow: inset 3px 0 0 var(--sl-list-active-border, #ea3325);
}

.verkooppunt-card--full .verkooppunt-card__body {
	margin: 0;
	min-width: 0;
}

.verkooppunt-card--full .verkooppunt-card__name {
	margin: 0 0 0.2rem;
	font-size: 16px !important;
	font-weight: 700;
	line-height: 1.15;
}

.verkooppunt-card--full .verkooppunt-card__name a {
	color: inherit;
}

.verkooppunt-card--full .verkooppunt-card__name a:hover,
.verkooppunt-card--full .verkooppunt-card__name a:focus-visible {
	text-decoration: underline;
}

.verkooppunt-card--full .verkooppunt-card__address {
	margin: 0;
	font-size: 0.76rem;
	line-height: 1.2;
}

.verkooppunt-card--full .verkooppunt-card__link {
	flex-shrink: 0;
	margin-top: 0;
	color: #000000;
	border-bottom: none;
}

.verkooppunt-card--full .verkooppunt-card__link:hover,
.verkooppunt-card--full .verkooppunt-card__link:focus-visible {
	opacity: 1;
	color: var(--sl-button-primary-hover-bg, #5636c9);
	border-bottom-color: var(--sl-button-primary-hover-bg, #5636c9);
}

@media (min-width: 640px) {
	.verkooppunten__search {
		flex-direction: row;
		align-items: center;
		max-width: none;
	}

	.verkooppunten__input {
		flex: 1;
	}

	.verkooppunten__submit {
		white-space: nowrap;
	}

	.verkooppunten__list {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (min-width: 900px) {
	.verkooppunten__inner {
		grid-template-columns: 0.85fr 1.15fr;
		align-items: center;
		gap: 3rem;
	}
}

@media (min-width: 1024px) {
	.verkooppunten {
		padding: 6rem 3rem;
	}
}

@media (min-width: 1440px) {
	.verkooppunten {
		padding: 6rem 4rem;
	}
}

/* Verkooppunten: full locator page (page-verkooppunten.php) */
.verkooppunten-page-wrap .entry-content > *:first-child {
	margin-top: 0;
}

.verkooppunten-page-wrap .entry-content > p:empty {
	display: none;
}

.verkooppunten-page-wrap .entry-content > .verkooppunten-page + * {
	margin-top: 0 !important;
}

.verkooppunten-page {
	padding: 2rem 1.25rem 30px;
	max-width: 1600px;
	margin: 0 auto;
}

.verkooppunten-page__breadcrumb {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	margin-bottom: 1.25rem;
}

.verkooppunten-page__breadcrumb.pb-type--small {
	color: #888;
	font-weight: 400;
	letter-spacing: normal;
}

.verkooppunten-page__breadcrumb a {
	color: #888;
	transition: color 0.2s ease;
}

.verkooppunten-page__breadcrumb a:hover,
.verkooppunten-page__breadcrumb a:focus-visible {
	color: var(--wp--preset--color--secondary, #111);
}

.verkooppunten-page__breadcrumb [aria-current] {
	color: var(--wp--preset--color--secondary, #111);
	font-weight: 600;
}

.verkooppunten-page__title {
	margin: 0 0 0.6rem;
	font-size: var(--wp--preset--font-size--h-2, clamp(1.65rem, 3vw, 2.1rem));
	font-weight: 800;
	letter-spacing: -0.02em;
}

.verkooppunten-page__subtitle {
	margin: 0 0 1.5rem;
	color: #666;
	font-size: 0.92rem;
	line-height: 1.55;
}

/*
 * Mobile: single column, source order = map, partner CTA, sidebar.
 * !important on `display` only: when this layout is built from the
 * core Columns block (composable /verkooppunten blocks) instead of the
 * monolithic pb/store-locator block, WordPress's block-supports layout
 * feature adds its own generated `.wp-container-core-columns-is-layout-*`
 * class alongside `.is-layout-flex` — a 2-class compound selector in
 * wp-block-library's CSS that outranks this single class on `display`
 * and keeps the row flex, which collapses the map to 0 height at the
 * desktop breakpoint below. Every other property here already wins
 * normally (equal or higher specificity / later in source order).
 */
.verkooppunten-page__layout {
	display: flex !important;
	flex-direction: column;
	gap: 1.5rem;
}

.verkooppunten-page__map {
	order: 0;
	position: relative;
	margin-top: -2rem;
	margin-right: calc(-1.25rem - max(0px, (100vw - 1600px) / 2));
	min-height: var(--sl-map-height-mobile, 26rem);
	border-radius: 20px;
	overflow: hidden;
	background: linear-gradient(135deg, #eef1f4 0%, #d7dde3 100%);
}

@media (max-width: 1023.98px) {
	.verkooppunten-page__map {
		margin-left: -1.25rem;
		margin-right: -1.25rem;
		border-radius: 0;
		width: auto;
		height: 600px;
	}

	.verkooppunten-page__partner-cta {
		display: none;
	}

	.verkooppunten-page__sidebar {
		height: 26rem;
		overflow: hidden;
	}

	.verkooppunten-page__list {
		flex: 1 1 auto;
		min-height: 0;
		overflow-y: auto;
	}
}

.verkooppunten-page__map .leaflet-container {
	width: 100%;
	height: 100%;
	min-height: var(--sl-map-height-mobile, 26rem);
	font-family: inherit;
	background: transparent;
}

.verkooppunten-page__map-fallback {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 2rem;
	text-align: center;
}

.verkooppunten-page__map-fallback p {
	margin: 0;
	color: #666;
	font-size: 0.9rem;
}

.verkooppunten-page__partner-cta {
	order: 2;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	padding: 1.25rem 1.5rem;
	border-radius: 16px;
	background: var(--wp--preset--color--surface, #f7f7f7);
}

.verkooppunten-page__partner-text {
	margin: 0;
}

.verkooppunten-page__partner-text.pb-type--body {
	font-size: 0.95rem;
	font-weight: 700;
	color: var(--wp--preset--color--secondary, #111);
}

.verkooppunten-page__partner-link {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	padding: 0.65rem 1.25rem;
	border-radius: var(--wp--custom--radius--pill, 999px);
	background: var(--sl-button-primary-bg, var(--wp--preset--color--primary, #6848f4));
	color: var(--sl-button-primary-text, #fff);
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	white-space: nowrap;
	transition: background-color 0.2s ease, transform 0.2s ease;
}

.verkooppunten-page__partner-link:hover,
.verkooppunten-page__partner-link:focus-visible {
	background: var(--sl-button-primary-hover-bg, #5636c9);
	transform: translateY(-1px);
}

/*
 * Scoped to the locator sidebar only, so the shared .button--dark
 * class used elsewhere on the site (homepage included) keeps its own
 * black/orange styling untouched.
 */
.verkooppunten-page__sidebar .verkooppunten__submit {
	background: var(--sl-button-primary-bg, #6848f4);
	color: var(--sl-button-primary-text, #fff);
}

.verkooppunten-page__sidebar .verkooppunten__submit:hover,
.verkooppunten-page__sidebar .verkooppunten__submit:focus-visible {
	background: var(--sl-button-primary-hover-bg, #5636c9);
	color: var(--sl-button-primary-text, #fff);
}

/* Desktop and mobile — "Zoek verkooppunten" button removed,
 * search-as-you-type (assets/js/main.js, the input's own `input`
 * event listener) already runs the search without it, so
 * functionality is unchanged. Scoped to this page's sidebar only,
 * same convention as the rule above — the homepage/other search
 * instances are untouched. */
	.verkooppunten-page__sidebar .verkooppunten__submit {
		display: none;
	}

	.verkooppunten-page__sidebar .verkooppunten__input {
		box-sizing: border-box;
		width: 100%;
		max-width: 416px;
		height: 56px;
		line-height: 56px;
		padding: 0 1.25rem 0 3.25rem;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
		background-repeat: no-repeat;
		background-position: 20px center;
		background-size: 18px 18px;
	}

.verkooppunten-page__sidebar {
	order: 1;
	display: flex;
	flex-direction: column;
}

.verkooppunten__filters {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	margin: 1rem 0 1.25rem;
}

.verkooppunten__filter-type,
.verkooppunten__filter-province {
	padding: 0.55rem 1rem;
	border: 1px solid rgba(17, 17, 17, 0.1);
	border-radius: 999px;
	font-size: 0.8rem;
	font-family: inherit;
	color: #111;
	background: #f7f7f7;
}

.verkooppunten__filter-province:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

/* Counter + filter share one row, at every viewport width (confirmed
 * 2026-07-11): .wp-block-pb-store-locator-filter (the filter block's
 * own wrapper — .verkooppunten__filters sits one level deeper, so
 * that class itself is never a direct flex item here) and
 * .verkooppunten-page__count are direct flex-column items of
 * .verkooppunten-page__sidebar (the count is exposed as one via the
 * list block's own .sl-passthrough/display:contents wrapper). CSS
 * Grid auto-placement was tried first to line them up without any
 * fixed pixel math, but proved unreliable in combination with that
 * display:contents wrapper (confirmed broken via direct measurement);
 * a fixed-height pull-up is the reliable fallback instead: the filter
 * is taken right-aligned (align-self:flex-end) with the exact "Row
 * margin: 16px 0", and .verkooppunten__filter-type's own height below
 * is fixed at 39px, so the count paragraph's `margin-top: -55px`
 * (39px content + 16px of the filter's own bottom margin) reliably
 * pulls it up to align with the filter's row regardless of content
 * above it. align-self:flex-start (overriding this rule's own
 * pre-existing align-self:center) plus a max-width keep the count
 * text starting at the left edge and wrapping within the space left
 * of the fixed-width filter, instead of overlapping it, at every
 * viewport width. */
.verkooppunten-page__sidebar .wp-block-pb-store-locator-filter {
	align-self: flex-end;
	margin: 16px 0;
}

/* Base .verkooppunten__filters rule above still carries its own
 * `margin: 1rem 0 1.25rem` — reset to 0 here so the wrapper's own
 * 16px/0 margin above is the only spacing contributing to its total
 * height, matching the -55px pull-up math on the count paragraph
 * below exactly. */
.verkooppunten-page__sidebar .verkooppunten__filters {
	margin: 0;
}

.verkooppunten-page__sidebar .verkooppunten-page__count {
	align-self: flex-start;
	max-width: calc(100% - 133px);
	margin-top: -55px;
	margin-bottom: 16px;
}

.verkooppunten-page__sidebar .verkooppunten__filter-type {
	width: 113.36px;
	height: 39px;
	padding: 6px 20px;
	box-sizing: border-box;
	border-radius: 999px;
	background: #ffffff;
	/* Sliders/filter icon replaces the native chevron on the right. */
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23111111' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='4' y1='6' x2='20' y2='6'/%3E%3Ccircle cx='9' cy='6' r='2' fill='%23111111' stroke='none'/%3E%3Cline x1='4' y1='12' x2='20' y2='12'/%3E%3Ccircle cx='15' cy='12' r='2' fill='%23111111' stroke='none'/%3E%3Cline x1='4' y1='18' x2='20' y2='18'/%3E%3Ccircle cx='11' cy='18' r='2' fill='%23111111' stroke='none'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 14px center;
	background-size: 16px 16px;
}

.verkooppunten-page__count {
	margin: 0 0 1rem;
	font-size: 0.8rem;
	color: #888;
}

.verkooppunten-page__count-number {
	color: #111;
	font-weight: 700;
}

.verkooppunten-page__list {
	display: flex;
	flex-direction: column;
	gap: 0;
	width: 100%;
	max-width: none;
	margin: 0;
}

.verkooppunten-page__empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	padding: 3rem 1.5rem;
	text-align: center;
	color: #666;
	border: none;
	border-radius: 16px;
	background: #f7f7f7;
}

.verkooppunten-page__empty[hidden] {
	display: none;
}

/* "Bekijk alle verkooppunten" — mobile-only (main.js only ever reveals
 * it below the same 1023.98px breakpoint used elsewhere in this
 * component); [hidden] already covers the default/desktop/expanded
 * states, this just gives the visible mobile state a tappable, on-
 * brand look matching .verkooppunt-card--full's own link styling. */
.verkooppunten-page__show-all {
	display: block;
	width: 100%;
	margin: 1rem 0 0;
	padding: 0.9rem 1.25rem;
	border: 1.5px solid #111;
	border-radius: 999px;
	background: transparent;
	font: inherit;
	font-size: 0.8rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	text-align: center;
	color: #111;
	cursor: pointer;
	transition: background-color 0.2s ease, color 0.2s ease;
}

.verkooppunten-page__show-all:hover,
.verkooppunten-page__show-all:focus-visible {
	background: #111;
	color: #fff;
	outline: none;
}

.verkooppunten-page__show-all[hidden] {
	display: none;
}

.verkooppunten-page__empty-icon {
	font-size: 1.75rem;
}

.verkooppunten-page__empty strong {
	color: #111;
	font-size: 1.05rem;
}

/*
 * /verkooppunten built from the standalone Store Locator blocks inside
 * a core Columns block: .verkooppunten-page__layout's CSS Grid above
 * and .verkooppunten-page__list's flex:1 below both require their
 * target element to be a DIRECT child of a specific ancestor (the
 * layout grid / the sidebar flex box) — exactly like the original
 * single pb/store-locator block always rendered them. Columns and each
 * block's own render_callback each add one wrapper <div> around that
 * target element, which breaks that direct-child requirement. Adding
 * this class (via a block's "Additional CSS Class(es)" field) makes
 * that one wrapper box invisible to layout without changing anything
 * it wraps — used on the right-hand Column, and on the Map/Partner CTA/
 * List blocks placed inside Columns.
 */
.sl-passthrough {
	display: contents;
}

@media (min-width: 1024px) {
	.verkooppunten-page {
		padding-top: 8px;
	}

	.verkooppunten-page__layout {
		display: grid !important;
		grid-template-columns: minmax(320px, 37%) 1fr;
		grid-template-rows: minmax(0, 1fr) auto;
		grid-template-areas:
			"sidebar map"
			"sidebar cta";
		align-items: stretch;
		gap: 1.5rem 2.5rem;
		height: var(--sl-map-height-desktop, 85vh);
		min-height: 20rem;
	}

	.verkooppunten-page__sidebar {
		grid-area: sidebar;
		min-height: 0;
		overflow: hidden;
		display: flex;
		flex-direction: column;
	}

	.verkooppunten-page__map {
		grid-area: map;
		min-height: 0;
		width: 900px !important;
		height: 630px !important;
		margin-top: -8px;
		justify-self: end;
		overflow: hidden;
		border-top-left-radius: 0;
		border-top-right-radius: 0;
		border-bottom-right-radius: 0;
		border-bottom-left-radius: 0;
	}

	.verkooppunten-page__map .leaflet-container {
		width: 900px !important;
		height: 630px !important;
		min-height: 0;
	}

	.verkooppunten-page__partner-cta {
		grid-area: cta;
	}

	.verkooppunten-page__list {
		flex: 1 1 auto;
		min-height: 0;
		overflow-y: auto;
		box-sizing: border-box;
		padding-right: 0.5rem;
		/* Fades the top/bottom edge instead of hard-cutting a row in
		 * half mid-scroll — purely visual, doesn't affect the actual
		 * scrollable height or content. */
		-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
		mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
	}
}

/* Footer (rebuilt 2026-07-09, npdigital.com/nl-inspired reference) —
 * every color below is a --pb-footer-* custom property (Appearance ->
 * Customize -> PaperBrain Design -> Footer / Footer kolom 1-4 / Footer
 * social links); nothing here is a final, unchangeable hardcoded
 * color. Structure: logo+pattern brand block, up to
 * PB_FOOTER_MAX_COLUMNS <details>/<summary> columns (mobile-collapsible,
 * force-expanded on desktop — see the breakpoint override further
 * down), then a bottom bar with social icons + copyright. */
.site-footer {
	background: var(--pb-footer-background, #f6f3ee);
	color: var(--pb-footer-text, #6b6b6b);
	font-size: 0.9rem;
}

/* Base Material Symbols Rounded rule (Google's own recommended
 * baseline) — global (header icons, hero-split icons), not footer-
 * specific; kept here only because this used to be the footer USP
 * bar's own copy before the 2026-07-09 rebuild removed that bar. Also
 * defined in assets/css/woocommerce.css for PDP use — same rule, no
 * conflict, WooCommerce's copy just isn't guaranteed loaded everywhere
 * this one needs to work (every page, via the header). */
.material-symbols-rounded {
	font-family: 'Material Symbols Rounded';
	font-weight: normal;
	font-style: normal;
	display: inline-block;
	line-height: 1;
	white-space: nowrap;
	direction: ltr;
	-webkit-font-smoothing: antialiased;
}

.site-footer__main {
	padding-top: 3rem;
	padding-bottom: 0.5rem;
}

/* Horizontal padding lives here, directly on the max-width:auto
 * element — same structure as .site-header__inner (2026-07-11; was on
 * the outer .site-footer__main instead, which only matches the header
 * pixel-for-pixel below ~1512px viewports — above that, max-width
 * caps at different points depending on which element the padding
 * sits on, since border-box padding subtracted before vs. after the
 * width comparison isn't the same math). */
.site-footer__inner {
	max-width: 1440px;
	margin: 0 auto;
	padding-left: 1.25rem;
	padding-right: 1.25rem;
}

/* ---- Brand block: logo only — .site-footer__logo's --pb-footer-logo-width
 * custom property is set inline per pb_design_footer_logo_size
 * (Appearance -> Customize -> PaperBrain Design -> Footer), see
 * pb_footer_logo_size_px() in footer.php; no decorative pattern
 * (removed 2026-07-09). Two logo blocks always render
 * (--desktop/--mobile, each already resolved to Footer Image / Mobile
 * Footer Image / site logo in footer.php) — only one is ever visible
 * per breakpoint, toggled below and in the desktop media query
 * further down. */
.site-footer__brand {
	position: relative;
	margin-bottom: 2rem;
}

.site-footer__logo {
	position: relative;
	max-width: var(--pb-footer-logo-width, 144px);
}

.site-footer__logo--desktop {
	display: none;
}

.site-footer__logo img {
	display: block;
	height: auto;
	max-width: 100%;
}

@media (min-width: 1024px) {
	.site-footer__logo--desktop img {
		width: 310px;
		height: 310px;
		max-width: none;
	}
}

.site-footer__logo a {
	color: var(--pb-footer-heading, #111111);
	font-family: var(--wp--preset--font-family--theme-heading, inherit);
	font-size: 1.5rem;
	text-decoration: none;
}

/* ---- Columns: two separate, Customizer-managed column sets
 * (--desktop / --mobile — see pb_footer_get_column()'s $mobile param
 * and pb_footer_mobile_has_config() in inc/design-system-customizer.php),
 * both always in the DOM, CSS shows only the one matching the current
 * breakpoint (same "render both, toggle via CSS" technique already
 * used for the desktop/mobile logo blocks above). Column-level
 * accordion (open/close) is identical either way — only the content
 * differs. ---- */
.site-footer__columns {
	display: flex;
	flex-direction: column;
}

.site-footer__columns--desktop {
	display: none;
}

.site-footer__col {
	border-top: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
}

.site-footer__col:last-of-type {
	border-bottom: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
}

/* Either a plain <button> (no title-link) or a <div> holding a real
 * <a> plus a small icon-only toggle <button> (title-link set) — see
 * pb_footer_render_column()'s own doc comment for why a real <a> can't
 * nest inside the row-wide button. Both shapes carry `aria-expanded`
 * on this element either way, so nothing else needs to know which one
 * rendered. */
.site-footer__col-summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	width: 100%;
	padding: 1.1rem 0;
	background: none;
	border: 0;
	font: inherit;
	text-align: left;
	text-decoration: none;
	color: inherit;
	cursor: pointer;
}

.site-footer__col-toggle {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	background: none;
	border: 0;
	padding: 0;
	cursor: pointer;
}

.site-footer__col-title {
	font-size: 16px;
	font-weight: 700;
	color: var(--pb-footer-heading, #111111);
	text-decoration: none;
}

.site-footer__col-icon {
	flex-shrink: 0;
	display: inline-block;
	font-size: 1rem;
	line-height: 1;
	color: var(--pb-footer-heading, #111111);
}

/* Own "+"/"−" glyphs (not a browser disclosure triangle), on the
 * RIGHT of the title — mobile-only in practice, since desktop hides
 * .site-footer__col-icon entirely (Zakelijk/Fundament/Contact never
 * collapse there — see the desktop media query further down).
 * Reverted to +/- on the right 2026-07-10 (had briefly become a
 * rotating "›" on the left for a desktop-only request that no longer
 * applies to mobile). */
.site-footer__col-icon::before {
	content: "+";
}

.site-footer__col-summary[aria-expanded="true"] .site-footer__col-icon::before {
	content: "\2212";
}

/* Inspiratie/Contact-style flat link rows (no groups, just a
 * title-link — see pb_footer_render_column()) always show a plain
 * "›", never the +/- accordion glyphs above — there's nothing to
 * expand. */
.site-footer__col-icon--link::before {
	content: "\203A";
}

.site-footer__col-body {
	padding: 0 0 1.25rem;
	display: flex;
	flex-direction: column;
	gap: 1rem;
}

/* Accordion collapse — progressive enhancement gated behind html.js
 * exactly like pb/animated-statement's own reveal (see
 * pb_print_js_class_script() in functions.php): without JavaScript
 * every column simply renders fully expanded, never permanently
 * hidden content. assets/js/footer-accordion.js only ever flips
 * aria-expanded; it owns no visibility logic itself. max-height (not
 * display) so opening/closing animates instead of jumping. Applies on
 * every breakpoint now (2026-07-10 — was mobile-only; desktop columns
 * are collapsible too per the corrected design). */
html.js .site-footer__col-body {
	max-height: 0;
	/* padding-bottom is part of .site-footer__col-body's own base rule
	 * (1.25rem) — border-box still can't compress padding below its own
	 * value, so a collapsed max-height:0 box was rendering a 20px sliver
	 * (its bottom padding) with the first item's text bleeding through
	 * it (2026-07-16). Never surfaced before: desktop's own override
	 * further down sets max-height:none!important (never actually
	 * collapses there), and every other max-height:0 use of this class
	 * is inside .site-footer__columns--mobile, which footer-accordion.js
	 * additionally hides via inline display:none — this is the first
	 * context relying on the max-height mechanism alone. */
	padding-bottom: 0;
	overflow: hidden;
	transition: max-height 0.25s ease;
}

/* Keyed off .site-footer__col--open (set server-side from open_mobile,
 * kept in sync by footer-accordion.js), not a ~ sibling-combinator
 * reacting to aria-expanded — older WebKit/Safari has known style-
 * invalidation flakiness with attribute-driven sibling combinators,
 * disproportionately hitting mobile users (2026-07-14). aria-expanded
 * itself is unchanged, still there for accessibility. */
html.js .site-footer__col--open .site-footer__col-body {
	max-height: 40rem;
	padding-bottom: 1.25rem;
}

/* B2B/Retail-style named groups as their own +/- accordion — desktop
 * and mobile both (2026-07-15 — was desktop-only, gated inside the
 * @media(min-width:768px) block below; relocated here, universal,
 * same class-based technique as .site-footer__col--open above, so
 * mobile gets it too and neither relies on the flakier ~ combinator).
 * Only headed groups ($collapsible_groups + a heading, see
 * pb_footer_render_column()) render the toggle; headingless groups
 * (Fundament/Contact's flat lists) keep the plain, non-toggleable
 * .site-footer__group-heading <p> either way. */
.site-footer__group-toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	width: 100%;
	background: none;
	border: 0;
	padding: 0;
	margin: 0 0 0.6rem;
	font: inherit;
	text-align: left;
	color: inherit;
	cursor: pointer;
}

.site-footer__group-icon {
	flex-shrink: 0;
	font-size: 1rem;
	line-height: 1;
	color: var(--pb-footer-heading, #111111);
}

.site-footer__group-icon::before {
	content: "+";
}

.site-footer__group-toggle[aria-expanded="true"] .site-footer__group-icon::before {
	content: "\2212";
}

html.js .site-footer__group-toggle ~ .site-footer__links {
	max-height: 0;
	overflow: hidden;
	transition: max-height 0.25s ease;
}

html.js .site-footer__group--open .site-footer__links {
	max-height: 20rem;
}

.site-footer__group {
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
}

.site-footer__group + .site-footer__group {
	padding-top: 1rem;
	border-top: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
}

/* Groups (B2B / Retail, etc.) are plain, static labels by default —
 * always visible whenever their column is open. Desktop turns a named
 * group into its own +/- accordion instead (.site-footer__group-toggle,
 * inside the desktop media query further down) — this plain <p>
 * variant is what mobile (and any headingless group, e.g.
 * Fundament/Contact) always uses. */
.site-footer__group-heading {
	margin: 0;
	font-size: 13px;
	font-weight: 700;
	color: var(--pb-footer-heading, #111111);
}

.site-footer__links {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
	font-size: 13px;
}

.site-footer__links a,
.site-footer__address-line {
	color: var(--pb-footer-link, #6b6b6b);
}

.site-footer__links a {
	text-decoration: none;
	transition: color 0.2s ease;
}

.site-footer__links a:hover,
.site-footer__links a:focus-visible {
	color: var(--pb-footer-link-hover, #111111);
}

/* Trailing right-arrow items (an editor flags these with the "| arrow"
 * suffix in a group's Links textarea, e.g. Contact's "Neem contact
 * op") — moved out of the >=768px media query (2026-07-23) so the
 * Mobile Footer's own accordion gets the exact same vertically-
 * centered arrow, not just desktop/tablet. Real Material Symbol glyph
 * (design system's existing .material-symbols-rounded icon font,
 * already used by pb/benefits, header utility links, etc.) rendered by
 * pb_footer_render_column() — replaces the old ::after Unicode "›"
 * pseudo-element so it's a proper icon, not plain text. */
.site-footer__link--arrow {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
}

.site-footer__link-icon {
	font-size: 18px;
	line-height: 1;
}

/* Mobile Footer (2026-07-23) — the desktop/tablet layout above pushes
 * the icon to the far end of the row (space-between) since the link
 * spans a much wider column there; on a narrow phone row that reads as
 * one big separate arrow, disconnected from "Neem contact op". Mobile
 * keeps the arrow directly after the text instead, as plain inline
 * text (">") rather than the Material Symbol glyph, matching the
 * same font/color/spacing as the rest of the link — no separate icon
 * element on this breakpoint. */
@media (max-width: 767.98px) {
	.site-footer__link--arrow {
		display: inline-flex;
		justify-content: flex-start;
		gap: 0.35em;
	}

	.site-footer__link--arrow .site-footer__link-icon {
		display: none;
	}

	.site-footer__link--arrow::after {
		content: ">";
	}
}

/* ---- Bottom bar: social icons + copyright ---- */
.site-footer__bottom {
	max-width: 1440px;
	margin: 0 auto;
	padding: 1.5rem 1.25rem;
}

.site-footer__bottom-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
}

.site-footer__social {
	display: flex;
	gap: 0.75rem;
}

.site-footer__social-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: 50%;
	background: var(--pb-footer-divider, rgba(17, 17, 17, 0.08));
	color: var(--pb-footer-icon, #111111);
	transition: background-color 0.2s ease;
}

.site-footer__social-icon:hover,
.site-footer__social-icon:focus-visible {
	background: rgba(17, 17, 17, 0.16);
}

.site-footer__social-icon svg {
	width: 1.1rem;
	height: 1.1rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.site-footer__copyright {
	margin: 0;
	font-size: 12px;
	color: var(--pb-footer-text, #6b6b6b);
}

/* Desktop-only Social grid column (2026-07-11) — .site-footer__bottom
 * above still renders the exact same social+copyright content and
 * stays the one mobile shows (hidden on desktop instead, see the
 * media query further down); this is the "render both, CSS shows the
 * one matching the breakpoint" pattern already used for the logo. */
.site-footer__social-col {
	display: none;
}

/* ---- Desktop: logo column + up to 4 divided columns side by side,
 * using the separate .site-footer__columns--desktop content (falls
 * back to the desktop columns for --mobile too when the Mobile footer
 * Customizer set is empty — see pb_footer_mobile_has_config()).
 * Corrected 2026-07-10: on desktop, Zakelijk/Fundament/Contact and
 * their B2B/Retail-style group headings are NEVER collapsible — only
 * mobile has a column-level accordion. So the toggle/icon is hidden
 * and .site-footer__col-body is force-expanded here regardless of
 * aria-expanded (JS still flips aria-expanded on click, it's just
 * inert on desktop — no HTML/JS change needed for that). The only
 * "clickable" affordance on desktop is the ">" marker in front of
 * each individual nav item (.site-footer__links li), not the column
 * or group headings. ---- */
@media (min-width: 768px) {
	.site-footer__main {
		/* 2026-07-12: shifted content up a bit (was 4rem/1rem) and gave
		 * the bottom more breathing room so it doesn't sit flush against
		 * the page's bottom edge. */
		padding-top: 3.5rem;
		padding-bottom: 2rem;
	}

	.site-footer__inner {
		display: grid;
		/* Horizontal padding matches .site-header__inner's own
		 * breakpoint-by-breakpoint values (2026-07-11 — was 3rem/4rem on
		 * .site-footer__main, didn't line up with the header at all) so
		 * the logo aligns with the header logo. Right side gets a bit
		 * extra (2026-07-12, +0.5rem at every tier below) so the last
		 * icon/copyright aren't flush against the viewport edge — "Bestel
		 * nu" alignment is now approximate rather than pixel-exact, a
		 * deliberate, explicitly-requested trade-off. */
		padding-left: 2rem;
		padding-right: 2.5rem;
		/* Wider than the columns' own minmax below (was 14rem/1fr) so the
		 * logo block gets more breathing room and the columns sit further
		 * right, per design. 4 equal content tracks — Zakelijk/Fundament/
		 * Contact (nested grid, spans the first 3) + Social (the 4th,
		 * 2026-07-12: reuses this same track instead of a separate
		 * trailing one, so it's genuinely the same width as the other 3
		 * — see .site-footer__columns--desktop / .site-footer__social-col). */
		grid-template-columns: minmax(20rem, 1.6fr) repeat(4, minmax(0, 1fr));
		gap: 0;
		align-items: start;
	}

	.site-footer__brand {
		margin-bottom: 0;
		/* Stretches to the row's full height (its siblings' tallest
		 * column) so the flex centering below can center the logo
		 * vertically within that space, without affecting the columns'
		 * own top-aligned .site-footer__inner. Horizontally left-aligned
		 * (2026-07-11 — was centered, which only coincidentally matched
		 * the header logo's position before the Social column changed
		 * this track's own computed width) so the footer logo's left
		 * edge always matches the header logo's, regardless of how wide
		 * this fr-based column ends up being. */
		align-self: stretch;
		display: flex;
		align-items: center;
		justify-content: flex-start;
	}

	.site-footer__logo--desktop {
		display: block;
		/* Bigger on desktop than the Customizer size setting's own base
		 * (mobile) value — same --pb-footer-logo-width custom property,
		 * just scaled up here. */
		max-width: calc(var(--pb-footer-logo-width, 144px) * 1.6);
	}

	.site-footer__logo--mobile {
		display: none;
	}

	.site-footer__columns--desktop {
		/* Spans only the first 3 of the outer grid's 4 content tracks
		 * (was 2/-2, i.e. all 4) — Social now takes the 4th instead of a
		 * separate trailing track, see .site-footer__social-col below.
		 * Its own repeat(3,...) matches the 3 columns actually rendered
		 * (Zakelijk/Fundament/Contact) — was repeat(4,...), which
		 * reserved a phantom 4th track with nothing in it, the root
		 * cause of the "Social column too detached" / uneven spacing
		 * reported 2026-07-12. */
		grid-column: 2 / 5;
		display: grid;
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	.site-footer__columns--mobile {
		display: none;
	}

	/* The outer grid's 4th (last) content track — same width as each of
	 * Zakelijk/Fundament/Contact (all four are equal 1fr shares of the
	 * same repeat(4,...) declared on .site-footer__inner), same
	 * border-left divider + left gutter, so the whole row reads as one
	 * consistent grid (2026-07-12 correction — was a separate,
	 * narrower, content-shrunk track). No right padding: this is the
	 * terminal column, so its content (the icon row) reaches the exact
	 * same right edge "Bestel nu" sits on in the header, same as how
	 * the other columns' left edges/dividers already line up. */
	.site-footer__social-col {
		display: block;
		grid-column: 5 / 6;
		/* Outer grid's own align-items is "start" (columns stay top-
		 * aligned instead of stretching to the row's tallest sibling —
		 * see .site-footer__inner below), so without this override
		 * Social's own height (and therefore its border-left divider)
		 * only matched its own short content, not the taller Zakelijk/
		 * Fundament/Contact columns next to it. */
		align-self: stretch;
		border-left: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
		padding: 0 0 0 2rem;
		/* Positioning context for the absolutely-positioned copyright
		 * below — has no right padding of its own (unlike
		 * .site-footer__inner), so its padding-box right edge is exactly
		 * where the icon row/track already ends. */
		position: relative;
	}

	/* Matches .site-footer__col-summary's own divider under
	 * Zakelijk/Fundament/Contact's heading exactly (same padding/
	 * border/margin), so "SOCIAL" continues the same rhythm. */
	.site-footer__social-heading {
		padding-bottom: 1rem;
		margin: 0 0 1.25rem;
		border-bottom: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
	}

	/* Column is now full-width (matching its siblings) rather than
	 * shrunk to the icon row's own content width, so the icon row needs
	 * to actively span that width itself for the first/last icon to
	 * still line up with the heading's left edge and the column's true
	 * right edge, respectively. */
	.site-footer__social-col .site-footer__social {
		justify-content: space-between;
	}

	/* Pinned to the bottom of .site-footer__social-col itself (2026-07-12
	 * — was in-flow directly under the icons, which read as "belonging"
	 * to them). left:0/right:0 (not a bare right:0) so its box is
	 * exactly the column's own width — keeps it left-aligned with the
	 * icons and never overflowing into Contact, wrapping to 2 lines
	 * where the column (240px) is too narrow for the sentence on one
	 * line, same as any other in-column text. Taken out of flow, so it
	 * no longer affects .site-footer__social-col's own (stretched, see
	 * above) height. */
	.site-footer__social-col .site-footer__copyright {
		position: absolute;
		/* left:2rem (not 0) — absolutely positioned children are placed
		 * relative to the container's padding-box OUTER edge, ignoring
		 * its own padding-left:2rem, unlike the heading/icons above
		 * (normal-flow children, which DO sit inset by that padding);
		 * matching it here is what makes this line up with the icons'
		 * own left edge instead of sitting 2rem further left. */
		left: 2rem;
		right: 0;
		bottom: 0;
		margin: 0;
	}

	.site-footer__bottom {
		display: none;
	}

	.site-footer__col {
		border-top: 0;
		border-left: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
		padding: 0 2rem;
	}

	.site-footer__col:last-of-type {
		border-bottom: 0;
	}

	.site-footer__col-summary {
		cursor: default;
		padding: 0 0 1rem;
		border-bottom: 1px solid var(--pb-footer-divider, rgba(17, 17, 17, 0.12));
		margin-bottom: 1.25rem;
	}

	.site-footer__col-title {
		text-transform: uppercase;
		letter-spacing: 0.04em;
	}

	.site-footer__col-toggle,
	.site-footer__col-icon {
		display: none;
	}

	.site-footer__col-body {
		padding: 0;
		max-height: none !important;
		overflow: visible !important;
	}

	/* .site-footer__group-toggle (B2B/Retail-style accordion) styling +
	 * collapse now lives in the universal section above (2026-07-15 —
	 * was desktop-only here; mobile needs the same accordion now).
	 * .site-footer__link--arrow itself moved to the unconditional rules
	 * above main.css's first @media block (2026-07-23) — now shared by
	 * the Mobile Footer accordion too instead of >=768px only. */

	.site-footer__bottom-inner {
		align-items: flex-end;
	}
}

/* ---- Tablet / iPad (2026-07-10, reworked 2026-07-23): the base
 * desktop grid above packs the logo + all 4 columns into one row,
 * which at iPad Pro widths (portrait ~1024px, landscape ~1366px)
 * squeezed each column down to roughly 150px. Wrapping the 3 content
 * columns into 2+2 (the previous approach here) still wasn't enough
 * room. Instead: drop the logo column entirely at this tier (hidden,
 * not just shrunk) and give its freed-up space to the 4 content
 * columns as one flat row — Zakelijk / Fundament / Contact / Social,
 * Contact sitting directly next to Social — same as the base desktop
 * tier's column count/order, just without the logo taking a track.
 * Nothing else (icons, accordion state, PHP structure) changes. ---- */
@media (min-width: 768px) and (max-width: 1366px) {
	.site-footer__brand {
		display: none;
	}

	.site-footer__inner {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}

	.site-footer__columns--desktop {
		grid-column: 1 / 4;
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	.site-footer__social-col {
		grid-column: 4 / 5;
	}
}

@media (min-width: 1024px) {
	.site-footer__inner {
		/* Matches .site-header__inner's own 1024px horizontal-padding
		 * tier on the left; right gets the same +0.5rem as the 768px
		 * tier (2026-07-12). */
		padding-left: 2.5rem;
		padding-right: 3rem;
	}
}

@media (min-width: 1440px) {
	.site-footer__main {
		padding-top: 4rem;
		padding-bottom: 2.5rem;
	}

	.site-footer__inner {
		/* Matches .site-header__inner's own 1440px horizontal-padding
		 * tier on the left (was 4rem on .site-footer__main, didn't line
		 * up with the header's 3rem); right gets the same +0.5rem as the
		 * other tiers (2026-07-12). */
		padding-left: 3rem;
		padding-right: 3.5rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	.site-footer__social-icon,
	.site-footer__links a {
		transition: none;
	}
}

/* FAQ (pb/faq block) */
.faq {
	max-width: 48rem;
	margin: 0 auto;
	padding: 4rem 1.25rem;
}

.faq__title {
	width: fit-content;
	margin: 0 auto 1.5rem;
	text-align: center;
}

.faq__title.pb-type--h2 {
	font-size: clamp(1.75rem, 4vw, 2.5rem);
	text-transform: uppercase;
}

/* "Regular" toolbar format (pb/weight-formats.js, opt-in via edit.js's
 * extraAllowedFormats) — e.g. "Vragen?" stepped down to 400 while the
 * rest of the same h2 keeps its bold preset weight. !important needed
 * because it's a real <h2>: design-system.css's sitewide
 * `h2{font-weight:…!important}` reset otherwise wins regardless of
 * this class's specificity (same issue already documented above for
 * .pb-reviews-carousel__title.pb-type--h2 and the Hero Centered h1). */
.faq__title .has-weight-regular {
	font-weight: 400 !important;
}

.faq__list {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.faq__item {
	border: 1px solid #e2e2e2;
	border-radius: 0.75rem;
	padding: 1rem 1.25rem;
}

.faq__contact-note {
	margin: 1.5rem 0 0;
	text-align: center;
	font-size: 16px;
}

.faq__contact-link {
	color: #6848F4;
	font-weight: 700;
	text-decoration: none;
}

.faq__question {
	cursor: pointer;
	font-weight: 700;
	list-style: none;
}

.faq__question::-webkit-details-marker {
	display: none;
}

.faq__question::after {
	content: "+";
	float: right;
	font-weight: 400;
}

.faq__item[open] .faq__question::after {
	content: "\2212";
}

.faq__answer {
	margin-top: 0.75rem;
}

.faq__answer-text.pb-type--body {
	color: #555;
}

.faq__answer p {
	margin: 0 0 0.75rem;
}

.faq__answer p:last-child {
	margin-bottom: 0;
}

/* ==========================================================================
 * Logo Slider (pb/logo-slider) — CSS-only infinite marquee, no JS.
 * Per-instance layout (visible count, gap, speed, colors) comes from
 * CSS custom properties set inline by the block's render_callback
 * (inc/logo-slider-block.php); this file only defines the mechanism.
 *
 * Loop technique: the track holds two identical copies of the logo
 * row (.logo-slider__group), side by side, with `width: max-content`
 * so the track is exactly as wide as both copies combined. Animating
 * translateX(0) -> translateX(-50%) therefore always scrolls exactly
 * one full copy's width, no matter how many logos there are — a
 * seamless loop with a single keyframe and zero JS.
 *
 * "Visible count" (desktop/tablet/mobile) is implemented with
 * container query width units (cqw) on .logo-slider__viewport, so
 * each logo's box is sized to fit exactly N per row of the block's
 * own width — not the browser viewport — regardless of where the
 * block is placed on the page.
 * ========================================================================*/
.logo-slider {
	overflow: hidden;
}

.logo-slider.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.logo-slider.alignwide {
	max-width: 1200px;
	margin-left: auto;
	margin-right: auto;
}

.logo-slider__viewport {
	container-type: inline-size;
	overflow: hidden;
}

/* Soft edge fade (MindJournal-style) — a CSS mask, not extra DOM/JS:
 * cheap for the browser to composite and never affects layout/CLS. */
.logo-slider--fade-edges .logo-slider__viewport {
	-webkit-mask-image: linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
	mask-image: linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
}

.logo-slider__track {
	display: flex;
	width: max-content;
	gap: var(--logo-slider-gap, 3.5rem);
	animation: pb-logo-slider-scroll var(--logo-slider-duration, 30s) linear infinite;
}

.logo-slider--pause-on-hover:hover .logo-slider__track {
	animation-play-state: paused;
}

.logo-slider__group {
	display: flex;
	align-items: center;
	flex-shrink: 0;
	gap: var(--logo-slider-gap, 3.5rem);
}

.logo-slider__item {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	height: var(--logo-slider-max-height, 40px);
	width: calc((100cqw - (var(--logo-slider-desktop, 5) - 1) * var(--logo-slider-gap, 3.5rem)) / var(--logo-slider-desktop, 5));
}

.logo-slider__item a {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
}

.logo-slider__item img {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
	opacity: var(--logo-slider-opacity, 1);
	transition: opacity 0.2s ease, filter 0.2s ease;
}

.logo-slider--grayscale .logo-slider__item img {
	filter: grayscale(100%);
}

/* Hover: subtle opacity change on any logo item, always. */
.logo-slider__item:hover img,
.logo-slider__item:focus-within img {
	opacity: var(--logo-slider-hover-opacity, 1);
}

/* Hover: additionally drop grayscale to reveal full colour — only
 * when "Grijstinten bij hover" is left off (the default), so editors
 * can choose to keep logos grayscale even on hover instead. */
.logo-slider--hover-color .logo-slider__item:hover img,
.logo-slider--hover-color .logo-slider__item:focus-within img {
	filter: grayscale(0%);
}

@media (max-width: 1024px) {
	.logo-slider__item {
		width: calc((100cqw - (var(--logo-slider-tablet, 3) - 1) * var(--logo-slider-gap, 3.5rem)) / var(--logo-slider-tablet, 3));
	}
}

@media (max-width: 640px) {
	.logo-slider__item {
		width: calc((100cqw - (var(--logo-slider-mobile, 2) - 1) * var(--logo-slider-gap, 3.5rem)) / var(--logo-slider-mobile, 2));
	}
}

@keyframes pb-logo-slider-scroll {
	from {
		transform: translateX(0);
	}

	to {
		transform: translateX(-50%);
	}
}

/* Static, fully accessible fallback: no animation, no duplicated
 * (aria-hidden) copy, logos wrap onto multiple centered rows instead
 * of relying on horizontal motion. */
@media (prefers-reduced-motion: reduce) {
	.logo-slider__viewport {
		overflow: visible;
	}

	.logo-slider__track {
		animation: none;
		transform: none;
		width: 100%;
	}

	.logo-slider__group {
		flex-wrap: wrap;
		justify-content: center;
		width: 100%;
	}

	.logo-slider__group[aria-hidden="true"] {
		display: none;
	}

	.logo-slider__item {
		width: auto;
		max-width: calc((100% - (var(--logo-slider-desktop, 5) - 1) * var(--logo-slider-gap, 3.5rem)) / var(--logo-slider-desktop, 5));
	}
}

/* ==========================================================================
 * pb/reviews-carousel — reviews carousel, Trustpilot-inspired visual
 * style (inc/reviews-carousel-block.php). The block server-renders
 * exactly one copy of each review — no duplicate markup on load, so a
 * 2-review carousel never looks like it has 4 — but a perfectly
 * seamless one-direction scroll of finite, non-repeating content
 * isn't possible with CSS alone (the track has to "reset" somewhere).
 * assets/js/reviews-carousel.js clones the real cards once, after
 * initial render (aria-hidden + tabindex="-1", so assistive tech and
 * keyboard nav only ever encounter each review once), then adds the
 * `--ready` class that's what actually gates the animation below —
 * so the track is always fully doubled *before* it starts moving,
 * and `translateX(-50%)` always lands on a seamless repeat rather
 * than a visible jump.
 *
 * - Cards use `align-items: stretch` (the track is itself the flex
 *   row now — there is no separate group wrapper) so every card in a
 *   row is equal height regardless of review text length, with
 *   `flex-grow` on the text and a 3-line clamp so a long review can
 *   never make one card taller than its neighbours.
 * - On mobile (<=640px) the animation is turned off entirely and the
 *   viewport becomes a native horizontally-scrollable, scroll-snapped
 *   list instead — continuous auto-animation and "swipe to browse"
 *   can't coexist on a touch surface, so touch input wins there; the
 *   JS-cloned duplicate cards are hidden there too, so a swipe never
 *   scrolls into a repeat.
 * - The viewport (only the viewport, not the section/heading above
 *   it) breaks out to full viewport width — same `100vw` / `left:50%`
 *   / `margin:-50vw` technique already used by .pb-blog-preview
 *   elsewhere in this file — so cards can slide in from fully
 *   off-screen instead of clipping against the normal content column.
 * ========================================================================*/
/* No overflow:hidden here — the viewport just below breaks out to
 * full viewport width via left:50%/margin:-50vw (see its own comment),
 * and an overflow:hidden on this parent would clip that breakout
 * right back down to the normal content width. Clipping happens on
 * .pb-reviews-carousel__viewport itself instead; body already has its
 * own overflow-x:hidden (see top of file) as the outer safety net. */
.pb-reviews-carousel {
	padding: clamp(2.5rem, 5vw, 4.5rem) 0;
}

.pb-reviews-carousel.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.pb-reviews-carousel__title {
	max-width: var(--wc-container-narrow, 1200px);
	margin: 0 auto 2rem;
	padding: 0 1rem;
}

.pb-reviews-carousel__title.pb-type--h2 {
	font-size: clamp(1.6rem, 1.4vw + 1.4rem, 2.25rem);
	font-weight: 400;
	line-height: 1.25;
	letter-spacing: normal;
	text-transform: none;
}

/* This is a real <h2>, so design-system.css's sitewide `h2 {…!important}`
 * rule was winning over the clamp() above (same issue as the PDP
 * title — see assets/css/woocommerce.css), rendering at that rule's
 * flat ~1.65rem mobile value regardless of viewport width. At that
 * size "zeggen over PaperBrain®" (the manually placed second line —
 * see post 199's sectionTitle) didn't fit one line on narrow phones and
 * wrapped into an unwanted third line. Sized to reliably fit that
 * specific line at ~320–390px widths. */
@media (max-width: 767px) {
	.pb-reviews-carousel__title.pb-type--h2 {
		font-size: 1.3rem !important;
	}
}

/* Title as InnerBlocks children (pb/hero-centered-heading +
 * pb/hero-centered-body — see pb_render_reviews_carousel_block()) —
 * same container width/spacing and h2 sizing as .pb-reviews-carousel__title
 * above, just split across the two real child blocks instead of one
 * RichText field, so each line gets its own level/size/color controls. */
.pb-reviews-carousel .pb-hc-heading,
.pb-reviews-carousel .pb-hc-body,
.pb-reviews-carousel__title-blocks .pb-hc-heading,
.pb-reviews-carousel__title-blocks .pb-hc-body {
	max-width: var(--wc-container-narrow, 1200px);
	margin-left: auto;
	margin-right: auto;
	padding: 0 1rem;
}

.pb-reviews-carousel .pb-hc-body,
.pb-reviews-carousel__title-blocks .pb-hc-body {
	margin-top: 0;
	margin-bottom: 2rem;
}

/* .pb-reviews-carousel__title-blocks is the editor-only InnerBlocks
 * wrapper (blocks/reviews-carousel/edit.js) — the ServerSideRender
 * preview inside the same hybrid block already gets the real
 * .pb-reviews-carousel section from PHP, so both selectors are needed
 * to match editor canvas and frontend. */
.pb-reviews-carousel .pb-hc-heading__text,
.pb-reviews-carousel .pb-hc-body__text,
.pb-reviews-carousel__title-blocks .pb-hc-heading__text,
.pb-reviews-carousel__title-blocks .pb-hc-body__text {
	font-size: clamp(1.6rem, 1.4vw + 1.4rem, 2.25rem);
	font-weight: 400;
	line-height: 1.25;
	letter-spacing: normal;
	text-transform: none;
}

.pb-reviews-carousel__viewport {
	container-type: inline-size;
	overflow: hidden;
	box-sizing: border-box;
	width: 100vw;
	/* Same container gutter scale already used for other full-bleed
	 * viewports in this file (e.g. .pb-business-split's own
	 * clamp(1.5rem, 5vw, 3rem)) — keeps both edge cards off the viewport
	 * edge without capping the 100vw breakout the autoplay animation
	 * needs (transform: translateX(-50%) is relative to .__track's own
	 * width, not the viewport, so padding here never affects it). */
	padding: 0 clamp(1.5rem, 4vw, 3rem);
	position: relative;
	left: 50%;
	right: 50%;
	margin-left: -50vw;
	margin-right: -50vw;
}

.pb-reviews-carousel__track {
	display: flex;
	align-items: stretch;
	width: max-content;
	gap: var(--reviews-carousel-gap, 1.5rem);
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Wrapped in prefers-reduced-motion: no-preference (rather than an
 * !important override) so this rule simply doesn't exist for a user
 * who has reduced motion on — no specificity fight with the reduced-
 * motion block further down. */
@media (prefers-reduced-motion: no-preference) {
	.pb-reviews-carousel--autoplay.pb-reviews-carousel--ready .pb-reviews-carousel__track {
		animation: pb-reviews-carousel-scroll var(--reviews-carousel-duration, 35s) linear infinite;
	}
}

/* Autoplay off: instead of leaving anything past the visible count
 * clipped by the viewport's own overflow:hidden, it becomes a
 * manually browsable, native horizontally-scrollable list — same
 * treatment as the mobile breakpoint below. */
.pb-reviews-carousel:not(.pb-reviews-carousel--autoplay) .pb-reviews-carousel__viewport {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	scroll-snap-type: x proximity;
}

.pb-reviews-carousel:not(.pb-reviews-carousel--autoplay) .pb-reviews-carousel__card {
	scroll-snap-align: start;
}

.pb-reviews-carousel--pause-on-hover:hover .pb-reviews-carousel__track,
.pb-reviews-carousel--pause-on-hover:focus-within .pb-reviews-carousel__track {
	animation-play-state: paused;
}

.pb-reviews-carousel__card {
	display: flex;
	flex: 0 0 auto;
	flex-direction: column;
	width: calc((100cqw - (var(--reviews-carousel-desktop, 4) - 1) * var(--reviews-carousel-gap, 1.5rem)) / var(--reviews-carousel-desktop, 4));
	background: var(--reviews-carousel-card-bg, #dceee4);
	color: var(--reviews-carousel-text, #111111);
	border-radius: var(--wc-radius-card, 1.25rem);
	padding: 1.25rem 1.5rem;
}

.pb-reviews-carousel__card-top {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.35rem 0.75rem;
	margin-bottom: 0.85rem;
}

/* Trustpilot-style rating badge: 5 boxes, each with a white star. The
 * first `rating` boxes get the tier color (set inline per filled
 * star, --reviews-carousel-rating-color — see
 * pb_reviews_carousel_rating_color()); the rest stay a plain,
 * visually inactive light grey. */
.pb-reviews-carousel__rating {
	display: flex;
	gap: 0.15rem;
}

.pb-reviews-carousel__star {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 1.15rem;
	height: 1.15rem;
	border-radius: 0.2rem;
	background: #d8d8d8;
}

.pb-reviews-carousel__star.is-filled {
	background: var(--reviews-carousel-rating-color, #00b67a);
}

.pb-reviews-carousel__star svg {
	width: 0.7rem;
	height: 0.7rem;
	fill: #ffffff;
	display: block;
}

/* Deliberately quiet: small icon, small muted text, no color/weight
 * emphasis — a footnote, not a badge. */
.pb-reviews-carousel__verified {
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
	font-size: 0.68rem;
	font-weight: 400;
	color: var(--reviews-carousel-text, #111111);
	opacity: 0.55;
	white-space: nowrap;
}

.pb-reviews-carousel__verified svg {
	width: 0.7rem;
	height: 0.7rem;
	fill: currentColor;
	flex-shrink: 0;
}

.pb-reviews-carousel__card-title {
	margin: 0 0 0.4rem;
	font-weight: 700;
	font-size: 1.15rem;
	line-height: 1.3;
}

/* Clamped to a configurable line count (--reviews-carousel-desktop-lines/
 * -mobile-lines, set inline per instance — see pb_render_reviews_carousel_block(),
 * inc/reviews-carousel-block.php) so one long review can no longer force
 * every card in the row to match its height; assets/js/reviews-carousel.js's
 * "Lees meer" toggle adds .is-expanded (below) to let that one card grow
 * back to its full content on demand, independent of its siblings. */
.pb-reviews-carousel__card-text {
	flex-grow: 1;
	margin: 0 0 0.5rem;
	font-size: 0.9rem;
	line-height: 1.55;
	opacity: 0.85;
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: var(--reviews-carousel-desktop-lines, 6);
	overflow: hidden;
}

.pb-reviews-carousel__card.is-expanded .pb-reviews-carousel__card-text {
	display: block;
	-webkit-line-clamp: unset;
	overflow: visible;
}

.pb-reviews-carousel__toggle {
	display: inline-block;
	align-self: flex-start;
	margin: 0 0 1rem;
	padding: 0;
	border: none;
	background: none;
	color: var(--reviews-carousel-star, #00b67a);
	text-decoration: underline;
	cursor: pointer;
}

@media (max-width: 640px) {
	.pb-reviews-carousel__card-text {
		-webkit-line-clamp: var(--reviews-carousel-mobile-lines, 5);
	}
}

.pb-reviews-carousel__card-meta {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.75rem;
	font-size: 0.8rem;
	opacity: 0.7;
}

.pb-reviews-carousel__reviewer {
	font-weight: 700;
}

@media (max-width: 1024px) {
	.pb-reviews-carousel__card {
		width: calc((100cqw - (var(--reviews-carousel-tablet, 2) - 1) * var(--reviews-carousel-gap, 1.5rem)) / var(--reviews-carousel-tablet, 2));
	}
}

/* Mobile: same autoplay/infinite-loop animation as desktop (mobile
 * card width via --reviews-carousel-mobile), *plus* the viewport stays
 * natively swipeable — overflow-x:auto and a `transform`-driven
 * animation don't conflict: the animation only ever paints a visual
 * offset, it never changes the track's own scrollable layout size, so
 * a manual swipe still scrolls normally on top of it. scroll-snap
 * only affects where a swipe gesture settles, not whether the
 * animation keeps running. */
@media (max-width: 640px) {
	.pb-reviews-carousel__viewport {
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		scroll-snap-type: x proximity;
	}

	.pb-reviews-carousel__card {
		width: calc((100cqw - (var(--reviews-carousel-mobile, 1) - 1) * var(--reviews-carousel-gap, 1.5rem)) / var(--reviews-carousel-mobile, 1));
		scroll-snap-align: start;
	}
}

@keyframes pb-reviews-carousel-scroll {
	from {
		transform: translateX(0);
	}

	to {
		transform: translateX(-50%);
	}
}

/* Static, fully accessible fallback: no animation, cards wrap onto
 * multiple rows instead of relying on horizontal motion — same
 * convention as .logo-slider. */
@media (prefers-reduced-motion: reduce) {
	.pb-reviews-carousel__viewport {
		overflow: visible;
	}

	.pb-reviews-carousel__track {
		animation: none;
		transform: none;
		width: 100%;
		flex-wrap: wrap;
	}

	.pb-reviews-carousel__card {
		width: auto;
		max-width: calc((100% - (var(--reviews-carousel-desktop, 4) - 1) * var(--reviews-carousel-gap, 1.5rem)) / var(--reviews-carousel-desktop, 4));
		min-width: 220px;
		flex-grow: 1;
	}

	/* The JS-cloned duplicate (see assets/js/reviews-carousel.js) only
	 * exists to give the animation a seamless loop — with the
	 * animation off here, it would otherwise show every review twice,
	 * wrapped onto extra rows. */
	.pb-reviews-carousel__card[aria-hidden="true"] {
		display: none;
	}
}

/* ==========================================================================
 * pb/hero-split — sticky, scroll-progress homepage hero
 * (inc/hero-split-block.php). Not a slider: on desktop/tablet
 * (>640px) the section is a tall "scroll track" — one
 * --hero-split-height per slide, via --hero-split-slide-count — and
 * .pb-hero-split__viewport is pinned with position:sticky while the
 * page scrolls naturally (no wheel/touch interception, no locked
 * scroll) through that track. assets/js/hero-split.js reads how far
 * the track has scrolled past the viewport top and crossfades slides
 * accordingly — see that script's own doc comment. Once the track is
 * fully scrolled, the sticky viewport un-pins and the page continues
 * into whatever follows, exactly like any normal section; scrolling
 * back up reverses it.
 *
 * On mobile (≤640px) this is intentionally reverted below to the
 * previous single-viewport, non-sticky behaviour (JS still
 * wheel/touch-intercepts there) — see that media query block.
 *
 * Without JS (or if it fails to load), slide 1 (server-rendered with
 * `is-active` already) is simply what visitors see; the extra
 * scroll-track height would sit empty below it, so the fallback is
 * still non-trapping but not ideal — acceptable since this only
 * matters with JS disabled/blocked, same tradeoff as before.
 *
 * Desktop: 3-column grid (left text/CTAs, center image + dots, right
 * features). Mobile: a single stacked column — no reordering needed,
 * since the natural source order inside .pb-hero-split__left already
 * is eyebrow -> heading -> paragraph -> buttons, followed by
 * .pb-hero-split__center (image + dots) and .pb-hero-split__right
 * (features), which is exactly the requested stack order.
 * ========================================================================*/
/* Self-contained full-bleed (2026-07-10): the block's own attribute
 * defaults to align:full, but this theme has no generic .alignfull
 * breakout CSS (see the near-identical note elsewhere in this file) —
 * the block previously only rendered full-width because the homepage
 * template's own wrapper (.site-main.front-page) happens to remove
 * the site's default 800px content max-width. Copied to any other
 * page (plain .site-main, 800px), the block was silently trapped
 * inside that width instead. Same left:50%/-50vw breakout already
 * used by .hero above, scoped to .pb-hero-split itself so it renders
 * identically regardless of which page/template/container it's in. */
.pb-hero-split {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	margin-top: 0;
	height: calc(var(--hero-split-height, 100vh) * var(--hero-split-slide-count, 1));
	color: var(--hero-split-text, #ffffff);
}

@media (min-width: 641px) {
	.site-main.front-page {
		padding-top: 0 !important;
		margin-top: 0 !important;
	}

	.pb-hero-split {
		margin-top: 0 !important;
	}

	.pb-hero-split__grid {
		padding-top: 2px !important;
	}
}

.pb-hero-split__viewport {
	position: sticky;
	top: 0;
	height: var(--hero-split-height, 100vh);
	overflow: hidden;
}

.pb-hero-split__slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	visibility: hidden;
	overflow-y: auto;
	transition: opacity var(--hero-split-duration, 600ms) ease;
}

.pb-hero-split__slide.is-active {
	opacity: 1;
	visibility: visible;
}

.pb-hero-split__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
	background-image: var(--hero-split-bg-desktop, none);
	background-size: cover;
	background-position: var(--hero-split-bg-position, center);
	background-repeat: no-repeat;
}

.pb-hero-split__overlay {
	position: absolute;
	inset: 0;
}

.pb-hero-split__grid {
	position: relative;
	z-index: 1;
	min-height: 100%;
	max-width: var(--wc-container-width, 1600px);
	margin: 0 auto;
	/* Top/bottom padding kept close to each other (rather than a huge top
	 * value dwarfing a small bottom one) so vertical centering below
	 * distributes evenly: shrinking the hero (via the "Hero hoogte"
	 * slider) then reduces the free space above AND below the content by
	 * roughly the same amount, instead of only eating into the bottom
	 * gap. Top is still slightly taller than bottom, just enough to
	 * clear the sticky topbar+header bar sitting on top of the hero
	 * (they overlap it, rather than pushing it down, since the hero's
	 * own sticky viewport fills the full hero height) — enough room that
	 * the eyebrow/heading never renders underneath it. */
	padding: 0 clamp(1.5rem, 4vw, 3rem) clamp(3rem, 5vw, 4rem);
	display: grid;
	/* Center track used to be `auto`, which sizes to the image's own
	 * intrinsic/natural pixel dimensions rather than to the space
	 * actually available in the hero — so the product image could never
	 * grow past its upload resolution no matter how high "Breedte
	 * afbeelding (%)" was set. A real, generously-weighted fr track
	 * gives .pb-hero-split__center an actual computed width to size
	 * against, which is what makes the max-size/scale system below
	 * (imageWidth/imageMaxHeight/imageScale) actually able to make the
	 * product large. */
	grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) minmax(0, 1fr);
	/* Vertically centered (not `start`) so shrinking/growing the hero
	 * height redistributes evenly above and below the content — see the
	 * padding comment above for why this no longer means "needs a
	 * scroll to see everything": with the reduced, closer-together top/
	 * bottom padding here, centering no longer leaves a large empty gap
	 * above the content on typical desktop heights. */
	align-items: center;
	gap: clamp(1.5rem, 3vw, 3rem);
}

.pb-hero-split__left {
	max-width: 32rem;
}

/* color: inherit is load-bearing on every text element below, not
 * decorative: this theme's global heading/paragraph styles (theme.json
 * + main.css base typography) set their own explicit color, which
 * otherwise wins over simple inheritance from .pb-hero-split's own
 * `color: var(--hero-split-text)` — without it, headings/paragraphs
 * silently render in the site's default near-black text color
 * regardless of this block's own "Tekstkleur" setting or how dark the
 * slide's background is. */
.pb-hero-split__eyebrow {
	display: inline-block;
	margin-bottom: 1.25rem;
	padding: 0.4rem 0.9rem;
	border: 1px solid currentColor;
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	opacity: 0.85;
	color: var(--hero-split-eyebrow-color, inherit);
}

.pb-hero-split__heading {
	margin: 0 0 1.25rem;
	color: var(--hero-split-heading-color, inherit);
}

.pb-hero-split__heading.pb-type--h1 {
	font-size: clamp(2.1rem, 2.2vw + 1.5rem, 3.75rem);
	font-weight: 700;
	line-height: 1.08;
	letter-spacing: normal;
}

.pb-hero-split__paragraph {
	max-width: 30rem;
	margin: 0 0 2rem;
	opacity: 0.85;
	color: var(--hero-split-body-color, inherit);
}

.pb-hero-split__paragraph.pb-type--body {
	font-size: 1.1rem;
}

.pb-hero-split__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
}

/* Scoped overrides so this block's own per-slide CTA colors (falling
 * back to "Accentkleur"/"Tekstkleur" when unset) drive the primary
 * button, instead of the global .button--primary default (white-on-
 * hover and not configurable per hero instance). Also overrides the
 * global .button class's own uppercase/wide-tracking/bold styling —
 * the reference design uses plain sentence case at a normal letter
 * spacing, not the shouty all-caps pill used elsewhere on the site. */
.pb-hero-split .button--primary {
	background: var(--hero-split-cta1-bg, var(--hero-split-accent, #6848f4));
	color: var(--hero-split-cta1-text, #ffffff);
	text-transform: none;
	letter-spacing: normal;
	font-weight: 600;
	font-size: 0.95rem;
}

.pb-hero-split .button--primary:hover,
.pb-hero-split .button--primary:focus-visible {
	filter: brightness(1.12);
	background: var(--hero-split-cta1-bg, var(--hero-split-accent, #6848f4));
	color: var(--hero-split-cta1-text, #ffffff);
}

/* Secondary CTA — an inline text link with a trailing arrow, not a
 * button (no border/background/pill shape), matching the reference
 * layout: primary button + "Text link →" side by side. Deliberately
 * its own styles, not extending .button/.button--ghost at all. */
.pb-hero-split__cta-secondary {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0;
	border: 0;
	background: none;
	border-radius: 0;
	font-size: 1rem;
	font-weight: 600;
	text-transform: none;
	letter-spacing: normal;
	color: var(--hero-split-cta2-text, var(--hero-split-text, inherit));
	transition: opacity 0.2s ease;
}

.pb-hero-split__cta-secondary::after {
	content: "\2192";
	display: inline-block;
	color: var(--hero-split-cta2-arrow, currentColor);
	transition: transform 0.2s ease;
}

.pb-hero-split__cta-secondary:hover,
.pb-hero-split__cta-secondary:focus-visible {
	opacity: 0.75;
	outline: none;
}

.pb-hero-split__cta-secondary:hover::after,
.pb-hero-split__cta-secondary:focus-visible::after {
	transform: translateX(0.25rem);
}

/* Desktop only (mobile keeps its own, separately-designed stacked
 * layout — see the max-width:640px block further down): guarantees
 * the primary button and secondary text link always sit side by side,
 * as one tight horizontal group, perfectly centered against each
 * other (not baseline — the reference shows the plain-text link
 * vertically centered against the filled button's full height, not
 * pinned to its text baseline), matching the reference layout/spacing
 * exactly. */
@media (min-width: 641px) {
	.pb-hero-split__actions {
		flex-wrap: nowrap;
		align-items: center;
		gap: 32px;
	}

	/* Fixed height + horizontal-only padding (rather than sizing off
	 * vertical padding) so the button is a predictable ~54px tall
	 * regardless of font metrics, and `white-space`/`flex-shrink`
	 * guarantee "START VANDAAG" never wraps or gets squeezed narrower
	 * than its content — both were happening before because this flex
	 * row's items were allowed to shrink below their natural size once
	 * `gap:32px` + `flex-wrap:nowrap` left less room than the text
	 * needed. */
	.pb-hero-split .button--primary {
		height: 54px;
		padding: 0 32px;
		white-space: nowrap;
		flex-shrink: 0;
	}

	.pb-hero-split__cta-secondary {
		gap: 11px;
		flex-shrink: 0;
		/* Reads as one tight inline-flex unit ("Text →") — never let it
		 * wrap onto two lines just because the left column happens to
		 * be narrow at some in-between desktop width. */
		white-space: nowrap;
	}

	/* Reference shows the eyebrow as plain small-caps tracked text, no
	 * pill/badge around it — strips the badge's padding/border/radius
	 * (all still set in the base rule above) back to a bare label.
	 * Mobile is untouched: it only overrides margin-bottom (see the
	 * max-width:640px block further down), so it keeps the original
	 * pill look there. */
	.pb-hero-split__eyebrow {
		padding: 0;
		border: 0;
		border-radius: 0;
	}
}

.pb-hero-split__center {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
}

.pb-hero-split__image {
	display: block;
	width: 100%;
	height: auto;
	max-height: var(--hero-split-image-max-height, 80vh);
	object-fit: contain;
	/* Per-slide "Afbeelding schaal" control (block Inspector): scales the
	 * product up beyond its own box without affecting grid/flex layout
	 * flow, so it stays perfectly centered (both axes, via the flex
	 * centering above) while becoming the hero's dominant visual
	 * element. Reset to none on mobile below — mobile keeps its
	 * existing, unscaled sizing. */
	transform: scale(var(--hero-split-image-scale, 1));
	transform-origin: center;
}

/* Pinned to a fixed distance from the bottom of .pb-hero-split__center
 * (a deterministic, always-full-height box — see above) instead of
 * flowing directly under the image, so the dots land at the exact
 * same spot on every slide regardless of that slide's own image
 * aspect ratio/rendered height — the one thing here that's genuinely
 * "layout", not "content", so it shouldn't drift between slides.
 * Reverted to normal in-flow stacking on mobile below, unchanged from
 * before. */
.pb-hero-split__dots {
	position: absolute;
	left: 50%;
	bottom: clamp(2rem, 6vh, 3.5rem);
	transform: translateX(-50%);
	display: flex;
	align-items: center;
	gap: 0.6rem;
	z-index: 2;
}

.pb-hero-split__dot {
	width: 0.55rem;
	height: 0.55rem;
	padding: 0;
	border: none;
	border-radius: 999px;
	background: var(--hero-split-dot, rgba(255, 255, 255, 0.4));
	cursor: pointer;
	transition: background-color 0.2s ease, transform 0.2s ease;
}

.pb-hero-split__dot:hover,
.pb-hero-split__dot:focus-visible {
	transform: scale(1.2);
	outline: none;
}

.pb-hero-split__dot.is-active {
	background: var(--hero-split-dot-active, #ffffff);
}

.pb-hero-split__right {
	display: flex;
	flex-direction: column;
	gap: 1.75rem;
	max-width: 22rem;
	justify-self: end;
}

.pb-hero-split__feature {
	display: flex;
	align-items: flex-start;
	gap: 1rem;
}

.pb-hero-split__feature-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 2.75rem;
	height: 2.75rem;
	border: 1px dashed var(--hero-split-feature-icon-border-color, currentColor);
	border-radius: 999px;
	opacity: 0.9;
}

.pb-hero-split__feature-icon-glyph {
	font-size: 1.25rem;
	line-height: 1;
	color: var(--hero-split-feature-icon-color, inherit);
}

.pb-hero-split__feature-text {
	display: flex;
	flex-direction: column;
	gap: 0.2rem;
	padding-top: 0.35rem;
}

.pb-hero-split__feature-title {
	font-size: 1rem;
	font-weight: 700;
	color: var(--hero-split-feature-title-color, inherit);
}

.pb-hero-split__feature-description {
	opacity: 0.8;
	color: var(--hero-split-feature-description-color, inherit);
}

.pb-hero-split__feature-description.pb-type--body {
	font-size: 0.9rem;
	line-height: 1.5;
}

@media (max-width: 1024px) {
	.pb-hero-split__bg {
		background-image: var(--hero-split-bg-tablet, none);
	}

	.pb-hero-split__grid {
		gap: 1.5rem;
	}

	.pb-hero-split__right {
		max-width: 16rem;
	}
}

/* Mobile: single stacked column, natural source order already matches
 * the requested "Heading (eyebrow+heading+paragraph), Buttons, Image,
 * Features" sequence — see this block's own CSS doc comment above. */
@media (max-width: 640px) {
	/* Reverts the desktop/tablet "tall scroll track + sticky pinned
	 * viewport" (base rules above) back to the original single-viewport,
	 * non-sticky setup — mobile keeps the previous wheel/touch-intercept
	 * behaviour (assets/js/hero-split.js branches on this same 640px
	 * boundary), which needs this exact geometry to still work. */
	.pb-hero-split {
		height: var(--hero-split-height, 100vh);
	}

	.pb-hero-split__viewport {
		position: static;
		height: 100%;
		overflow: hidden;
	}

	.pb-hero-split__bg {
		background-image: var(--hero-split-bg-mobile, none);
	}

	.pb-hero-split__grid {
		display: flex;
		flex-direction: column;
		justify-content: center;
		padding-top: clamp(4rem, 12vw, 5rem);
		padding-bottom: 1.5rem;
		gap: 1.5rem;
		text-align: center;
	}

	.pb-hero-split__left {
		max-width: none;
	}

	.pb-hero-split__eyebrow {
		margin-bottom: 0.85rem;
	}

	.pb-hero-split__heading {
		margin-bottom: 0.75rem;
		font-size: clamp(1.8rem, 6vw, 2.5rem);
	}

	.pb-hero-split__paragraph {
		max-width: none;
		margin: 0 auto 1.25rem;
		font-size: 1rem;
	}

	.pb-hero-split__actions {
		justify-content: center;
	}

	.pb-hero-split__image {
		max-height: 32vh;
		transform: none;
	}

	/* Undoes the desktop-only "pin dots to a fixed spot" rule above —
	 * mobile keeps dots flowing normally right after the image, exactly
	 * as before that rule existed. */
	.pb-hero-split__center {
		gap: 1.5rem;
	}

	.pb-hero-split__dots {
		position: static;
		left: auto;
		bottom: auto;
		transform: none;
	}

	.pb-hero-split__right {
		max-width: none;
		gap: 1rem;
	}

	.pb-hero-split__feature {
		text-align: left;
	}
}

/* Static, fully accessible fallback: every slide shown at once,
 * stacked, no absolute positioning/transition/scroll-jacking — same
 * "respect the user's OS-level preference" convention as
 * .logo-slider/.pb-reviews-carousel. assets/js/hero-split.js also
 * checks this same media query and skips wiring up scroll interception
 * entirely, so this CSS state is the complete, final rendering. */
@media (prefers-reduced-motion: reduce) {
	.pb-hero-split {
		height: auto;
	}

	.pb-hero-split__viewport {
		position: static;
		height: auto;
		overflow: visible;
	}

	.pb-hero-split__slide {
		position: static;
		opacity: 1;
		visibility: visible;
		transition: none;
		overflow-y: visible;
	}

	.pb-hero-split__slide + .pb-hero-split__slide {
		border-top: 1px solid rgba(255, 255, 255, 0.2);
	}
}

/* ==========================================================================
 * Blog / Insights (pb/blog-preview, pb/blog-grid, single.php, archive.php)
 * ========================================================================*/

/*
 * Shared card — same media/hover-zoom technique as .campaign-card.
 * min-width: 0 is required here: as a CSS Grid item (.pb-blog-grid__grid)
 * this defaults to min-width: auto, and the min-height + aspect-ratio
 * combination on .pb-blog-card__link below would otherwise force a
 * wider intrinsic size than its 1fr track and overflow the grid/page.
 */
.pb-blog-card {
	min-width: 0;
	margin: 0;
}

.pb-blog-card__link {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	aspect-ratio: 3 / 4;
	overflow: hidden;
	border-radius: var(--wp--custom--radius--large, 1.25rem);
	color: #fff;
}

.pb-blog-card__media {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, #eef1f4 0%, #d7dde3 100%);
	transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.pb-blog-card__link:hover .pb-blog-card__media,
.pb-blog-card__link:focus-visible .pb-blog-card__media {
	transform: scale(1.06);
}

.pb-blog-card__link::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(0deg, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.15) 55%, rgba(0, 0, 0, 0) 100%);
}

.pb-blog-card__body {
	position: relative;
	z-index: 1;
	padding: clamp(1.5rem, 3vw, 2.25rem);
}

.pb-blog-card__category {
	display: inline-block;
	margin-bottom: 0.6rem;
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: rgba(255, 255, 255, 0.85);
}

.pb-blog-card__title {
	margin: 0;
	font-size: clamp(1.2rem, 1.6vw + 0.7rem, 1.75rem);
	font-weight: 800;
	letter-spacing: -0.01em;
	line-height: 1.3;
	/* Same reasoning as .hero__title: explicit white so the global
	 * heading rule (Theme Default) can't win over inheritance from
	 * .pb-blog-card__link's dark photo-overlay text color. Shared by
	 * both Blog Preview and Blog Grid cards. */
	color: #fff;
}

@media (prefers-reduced-motion: reduce) {
	.pb-blog-card__media {
		transition: none;
	}
}

/*
 * Homepage "Insights" preview — a premium horizontal carousel on every
 * breakpoint (not just mobile). CSS scroll-snap drives the actual
 * alignment/swipe; assets/js/main.js only drives the arrow buttons'
 * one-card scroll + disabled state, and optional autoplay.
 */
/*
 * Full-bleed section breakout: the outermost wrapper (and therefore
 * its background-color, set via the "Kleuren" panel) always spans the
 * true viewport width, regardless of which page/template this block
 * is placed on or whether "Full width" alignment is toggled — the
 * same technique the theme's other edge-to-edge sections rely on.
 * body already has `overflow-x: hidden` (see top of file), which is
 * what safely absorbs the scrollbar-width rounding this trick can
 * introduce. Inner content (header/viewport) keeps its own
 * max-width + margin: 0 auto below, unaffected by this.
 */
.pb-blog-preview {
	width: 100vw;
	position: relative;
	left: 50%;
	right: 50%;
	margin-left: -50vw;
	margin-right: -50vw;
	padding: 5rem 1.25rem;
	background: var(--wp--preset--color--background, #fff);
}

.pb-blog-preview__header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	max-width: 1600px;
	margin: 0 auto 1.75rem;
}

.pb-blog-preview__title {
	margin: 0;
}

.pb-blog-preview__title.pb-type--h2 {
	font-size: clamp(1.65rem, 3vw, 2.1rem);
}

.pb-blog-preview__controls {
	display: flex;
	align-items: center;
	gap: 1.5rem;
}

.pb-blog-preview__link {
	display: inline-block;
	font-size: var(--wp--preset--font-size--button, 0.8rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--wp--preset--color--secondary, #111);
	border-bottom: 1.5px solid var(--wp--preset--color--secondary, #111);
	padding-bottom: 0.15rem;
	white-space: nowrap;
	transition: opacity 0.2s ease;
}

.pb-blog-preview__link:hover,
.pb-blog-preview__link:focus-visible {
	opacity: 0.6;
}

.pb-blog-preview__arrows {
	display: flex;
	gap: 0.5rem;
	flex-shrink: 0;
}

.pb-blog-preview__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	padding: 0;
	border: 1.5px solid rgba(17, 17, 17, 0.16);
	border-radius: var(--wp--custom--radius--pill, 999px);
	background: transparent;
	color: var(--wp--preset--color--secondary, #111);
	font-size: 1.35rem;
	line-height: 1;
	cursor: pointer;
	transition: border-color 0.2s ease, background-color 0.2s ease, opacity 0.2s ease, transform 0.15s ease;
}

.pb-blog-preview__arrow:hover:not(:disabled),
.pb-blog-preview__arrow:focus-visible:not(:disabled) {
	border-color: currentColor;
	background: rgba(17, 17, 17, 0.06);
	outline: none;
}

.pb-blog-preview__arrow:active:not(:disabled) {
	transform: scale(0.94);
}

.pb-blog-preview__arrow:disabled {
	opacity: 0.3;
	cursor: default;
}

.pb-blog-preview__viewport {
	max-width: 1600px;
	margin: 0 auto;
}

.pb-blog-preview__track {
	--pb-blog-preview-gap: 1rem;
	display: flex;
	gap: var(--pb-blog-preview-gap);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	padding-bottom: 0.25rem;
}

.pb-blog-preview__track::-webkit-scrollbar {
	display: none;
}

.pb-blog-preview__track:focus-visible {
	outline: 2px solid var(--wp--preset--color--secondary, #111);
	outline-offset: 6px;
}

.pb-blog-preview__track .pb-blog-card {
	flex: 0 0 84%;
	scroll-snap-align: start;
}

@media (min-width: 480px) {
	.pb-blog-preview__track .pb-blog-card {
		flex-basis: 62%;
	}
}

@media (min-width: 768px) {
	.pb-blog-preview {
		padding: 6rem 2rem;
	}

	.pb-blog-preview__track {
		--pb-blog-preview-gap: 1.5rem;
	}

	.pb-blog-preview__track .pb-blog-card {
		flex-basis: calc((100% - (var(--pb-blog-preview-tablet-cards, 2) - 1) * var(--pb-blog-preview-gap)) / var(--pb-blog-preview-tablet-cards, 2));
	}
}

@media (min-width: 1024px) {
	.pb-blog-preview {
		padding: 7rem 3rem;
	}

	.pb-blog-preview__track {
		--pb-blog-preview-gap: 1.75rem;
	}

	.pb-blog-preview__track .pb-blog-card {
		flex-basis: calc((100% - (var(--pb-blog-preview-desktop-cards, 3) - 1) * var(--pb-blog-preview-gap)) / var(--pb-blog-preview-desktop-cards, 3));
	}
}

@media (prefers-reduced-motion: reduce) {
	.pb-blog-preview__track {
		scroll-behavior: auto;
	}
}

/* Blog overview grid (pb/blog-grid + archive.php) */
.pb-blog-grid {
	padding: 3rem 1.25rem 4rem;
	max-width: 1800px;
	margin: 0 auto;
}

.pb-blog-grid.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
}

.pb-blog-grid__header {
	margin-bottom: 2.25rem;
}

.pb-blog-grid__title {
	margin: 0 0 0.75rem;
}

.pb-blog-grid__title.pb-type--h2 {
	font-size: clamp(1.9rem, 1.2vw + 1.6rem, 2.75rem);
}

.pb-blog-grid__intro {
	margin: 0;
	max-width: 42rem;
}

.pb-blog-grid__intro.pb-type--body {
	color: #666;
	font-size: 1.05rem;
	line-height: 1.65;
}

.pb-blog-grid__filter {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
	margin-bottom: 2.75rem;
}

.pb-blog-grid__filter-link {
	padding: 0.5rem 1rem;
	border: 1px solid var(--wp--preset--color--border, rgba(17, 17, 17, 0.12));
	border-radius: var(--wp--custom--radius--pill, 999px);
	font-size: var(--wp--preset--font-size--button, 0.8rem);
	font-weight: 600;
	color: #444;
	background: var(--wp--preset--color--background, #fff);
	transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.pb-blog-grid__filter-link:hover,
.pb-blog-grid__filter-link:focus-visible {
	border-color: var(--wp--preset--color--secondary, #111);
	color: var(--wp--preset--color--secondary, #111);
}

.pb-blog-grid__filter-link.is-active {
	background: var(--wp--preset--color--secondary, #111);
	border-color: var(--wp--preset--color--secondary, #111);
	color: var(--wp--preset--color--background, #fff);
}

.pb-blog-grid__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.5rem;
}

/* Mobile-only swipeable track (<768px, matches this grid's own next
 * breakpoint below) — same native scroll + scroll-snap technique
 * already used by .pb-blog-preview__track, reused here rather than a
 * new implementation. display:flex/scroll-snap only apply below
 * 768px; the min-width:768px/1024px/1440px grid rules further down are
 * untouched, so tablet/desktop keep their exact current grid layout. */
@media (max-width: 767.98px) {
	.pb-blog-grid__grid {
		display: flex;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		scroll-behavior: smooth;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
	}

	.pb-blog-grid__grid::-webkit-scrollbar {
		display: none;
	}

	.pb-blog-grid__grid:focus-visible {
		outline: 2px solid var(--wp--preset--color--secondary, #111);
		outline-offset: 6px;
	}

	.pb-blog-grid__grid .pb-blog-card {
		flex: 0 0 100%;
		scroll-snap-align: start;
	}
}

@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {
	.pb-blog-grid__grid {
		scroll-behavior: auto;
	}
}

.pb-blog-grid__empty {
	padding: 3rem 1.5rem;
	border-radius: 16px;
	background: var(--wp--preset--color--surface, #f7f7f7);
	color: #666;
	text-align: center;
}

.pb-blog-grid__pagination {
	margin-top: 2.5rem;
}

.pb-blog-grid__pagination ul.page-numbers {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.pb-blog-grid__pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.5rem;
	height: 2.5rem;
	padding: 0 0.75rem;
	border-radius: var(--wp--custom--radius--pill, 999px);
	border: 1px solid var(--wp--preset--color--border, rgba(17, 17, 17, 0.12));
	font-size: 0.85rem;
	font-weight: 600;
	color: #444;
	transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.pb-blog-grid__pagination a.page-numbers:hover,
.pb-blog-grid__pagination a.page-numbers:focus-visible {
	border-color: var(--wp--preset--color--secondary, #111);
	color: var(--wp--preset--color--secondary, #111);
}

.pb-blog-grid__pagination .page-numbers.current {
	background: var(--wp--preset--color--secondary, #111);
	border-color: var(--wp--preset--color--secondary, #111);
	color: var(--wp--preset--color--background, #fff);
}

.pb-blog-grid__pagination .page-numbers.dots {
	border-color: transparent;
}

@media (min-width: 768px) {
	.pb-blog-grid {
		padding: 4.5rem 2.5rem 5.5rem;
	}

	.pb-blog-grid__grid {
		grid-template-columns: repeat(2, 1fr);
		gap: 2rem;
	}

	.pb-blog-grid__grid .pb-blog-card__link {
		min-height: 26rem;
	}
}

@media (min-width: 1024px) {
	.pb-blog-grid {
		padding: 5rem 2rem 6rem;
	}

	.pb-blog-grid__grid {
		grid-template-columns: repeat(var(--pb-blog-grid-columns, 3), 1fr);
		gap: 2.5rem;
	}

	.pb-blog-grid__grid .pb-blog-card__link {
		min-height: 30rem;
	}
}

@media (min-width: 1440px) {
	.pb-blog-grid {
		padding: 5.5rem 3rem 7rem;
	}

	.pb-blog-grid__grid .pb-blog-card__link {
		min-height: 34rem;
	}
}

/* Single blog post (single.php) */
.pb-blog-single-wrap {
	max-width: 100%;
}

.pb-blog-single {
	max-width: 44rem;
	margin: 0 auto;
	padding: 2.5rem 1.25rem 4rem;
}

.pb-blog-single__header {
	margin-bottom: 2.5rem;
	text-align: center;
}

.pb-blog-single__breadcrumb {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	margin-bottom: 1.75rem;
	font-size: var(--wp--preset--font-size--small, 0.78rem);
	color: #888;
}

.pb-blog-single__breadcrumb a {
	color: #888;
	transition: color 0.2s ease;
}

.pb-blog-single__breadcrumb a:hover,
.pb-blog-single__breadcrumb a:focus-visible {
	color: var(--wp--preset--color--secondary, #111);
}

.pb-blog-single__breadcrumb [aria-current] {
	color: var(--wp--preset--color--secondary, #111);
	font-weight: 600;
}

.pb-blog-single__category {
	display: inline-block;
	margin-bottom: 1rem;
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.1em;
	color: var(--wp--preset--color--accent, #ea3325);
}

.pb-blog-single__title {
	margin: 0 0 1.1rem;
	font-size: var(--wp--preset--font-size--h-1, clamp(2.1rem, 1.6vw + 1.8rem, 3.25rem));
	font-weight: 800;
	letter-spacing: -0.02em;
	line-height: 1.1;
}

.pb-blog-single__meta {
	margin: 0;
	font-size: 0.85rem;
	font-weight: 500;
	letter-spacing: 0.02em;
	color: #888;
}

.pb-blog-single__media {
	max-width: 68rem;
	margin: 0 auto 3rem;
	padding: 0 1.25rem;
}

.pb-blog-single__media img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: 1.5rem;
	box-shadow: var(--wp--preset--shadow--medium, 0 24px 60px rgba(17, 17, 17, 0.12));
	object-fit: cover;
}

.pb-blog-single__content {
	font-size: 1.125rem;
	line-height: 1.8;
	color: var(--wp--preset--color--body-text, #222);
}

.pb-blog-single__content > * {
	margin: 0 0 1.75rem;
}

.pb-blog-single__content > p:first-of-type {
	font-size: var(--wp--preset--font-size--h-4, 1.3rem);
	line-height: 1.65;
	color: #333;
}

.pb-blog-single__content h2 {
	margin-top: 3rem;
	font-size: var(--wp--preset--font-size--h-3, 1.6rem);
	font-weight: 800;
	letter-spacing: -0.01em;
}

.pb-blog-single__content h3 {
	margin-top: 2.25rem;
	font-size: var(--wp--preset--font-size--h-4, 1.3rem);
	font-weight: 700;
}

.pb-blog-single__content blockquote {
	margin: 0 0 1.5rem;
	padding: 0.25rem 0 0.25rem 1.25rem;
	border-left: 3px solid var(--wp--preset--color--accent, #ea3325);
	color: #444;
	font-style: italic;
}

.pb-blog-single__content ul,
.pb-blog-single__content ol {
	padding-left: 1.25rem;
}

.pb-blog-single__content img {
	max-width: 100%;
	height: auto;
	border-radius: 1rem;
}

.pb-blog-single__content a {
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

.pb-blog-single__share {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 1rem;
	margin-top: 3rem;
	padding-top: 2.25rem;
	border-top: 1px solid #ececec;
	text-align: center;
}

.pb-blog-single__share-label {
	font-size: 0.78rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: #888;
}

.pb-blog-single__share-links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.6rem;
}

.pb-blog-single__share-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.5rem;
	height: 2.5rem;
	border: 1.5px solid rgba(17, 17, 17, 0.15);
	border-radius: var(--wp--custom--radius--pill, 999px);
	color: var(--wp--preset--color--secondary, #111);
	transition: border-color 0.2s ease, background-color 0.2s ease, transform 0.15s ease;
}

.pb-blog-single__share-link:hover,
.pb-blog-single__share-link:focus-visible {
	border-color: var(--wp--preset--color--secondary, #111);
	background: rgba(17, 17, 17, 0.06);
	transform: translateY(-2px);
	outline: none;
}

@media (min-width: 640px) {
	.pb-blog-single__share {
		justify-content: space-between;
		text-align: left;
	}
}

.pb-blog-single__nav {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: space-between;
	gap: 1.5rem 1rem;
	margin-top: 3rem;
	padding-top: 2rem;
	border-top: 1px solid #ececec;
}

.pb-blog-single__nav-item {
	flex: 1 1 10rem;
	min-width: 0;
}

.pb-blog-single__nav-item--next {
	text-align: right;
}

.pb-blog-single__nav-item a {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	transition: opacity 0.2s ease;
}

.pb-blog-single__nav-item--next a {
	align-items: flex-end;
}

.pb-blog-single__nav-item a:hover,
.pb-blog-single__nav-item a:focus-visible {
	opacity: 0.6;
}

.pb-blog-single__nav-eyebrow {
	font-size: 0.72rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: #888;
}

.pb-blog-single__nav-title {
	font-size: 0.98rem;
	font-weight: 700;
	color: var(--wp--preset--color--secondary, #111);
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}

.pb-blog-single__back {
	order: -1;
	flex: 1 1 100%;
	text-align: center;
	font-size: var(--wp--preset--font-size--button, 0.8rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	border-bottom: 1.5px solid var(--wp--preset--color--secondary, #111);
	padding-bottom: 0.15rem;
	transition: opacity 0.2s ease;
}

.pb-blog-single__back:hover,
.pb-blog-single__back:focus-visible {
	opacity: 0.6;
}

@media (min-width: 640px) {
	.pb-blog-single__back {
		order: 0;
		flex: 0 0 auto;
	}
}

@media (min-width: 768px) {
	.pb-blog-single {
		padding: 3.5rem 1.25rem 5rem;
	}

	.pb-blog-single__title {
		line-height: 1.1;
	}
}

/* ==========================================================================
 * Animated Statement (pb/animated-statement)
 * ========================================================================*/
.animated-statement {
	position: relative;
	z-index: 0;
	text-align: center;
}

/* Optional decorative background image (2026-07-15, bgImage attribute).
 * z-index:-1 (not the default "auto") is load-bearing: an auto/0
 * z-index on a position:absolute element paints *after* — i.e. on top
 * of — static in-flow siblings like .animated-statement__inner,
 * regardless of DOM order (same stacking-order bug already fixed once
 * for PB Hero Centered's own background layer). The parent's own
 * z-index:0 above gives it a stacking context, so the negative z-index
 * here stays contained and never bleeds into sibling sections on the
 * page. Only ever rendered when bgImage is set, so untouched instances
 * are unaffected. */
.animated-statement__bg-image {
	position: absolute;
	z-index: -1;
	inset: 0;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	pointer-events: none;
}

/* Optional small corner logo mark (2026-07-23, logoImage attribute) —
 * decorative watermark, bottom-right of the section. Same z-index
 * reasoning as .animated-statement__bg-image above: the parent's own
 * z-index:0 (base rule) gives this a contained stacking context, so a
 * plain default z-index here already paints above the bg-image
 * (z-index:-1) without needing one itself. Size/opacity/offset are all
 * per-instance inline styles (PHP/JS already build them), so this rule
 * only needs the positioning mechanics. */
.animated-statement__logo {
	position: absolute;
	height: auto;
	display: block;
	pointer-events: none;
}

.animated-statement.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.animated-statement.alignwide {
	max-width: 1200px;
	margin-left: auto;
	margin-right: auto;
}

.animated-statement--align-left {
	text-align: left;
}

.animated-statement--align-right {
	text-align: right;
}

.animated-statement__inner {
	margin-left: auto;
	margin-right: auto;
	max-width: var( --wp--custom--section--content-width, 800px );
	padding-left: 1.25rem;
	padding-right: 1.25rem;
}

.animated-statement--align-left .animated-statement__inner {
	margin-left: 0;
}

.animated-statement--align-right .animated-statement__inner {
	margin-right: 0;
}

/* Coachingstool page only (block-instance "Additional CSS class" —
 * not a global .animated-statement override): wider content column
 * so the first (bold) statement wraps across more of the page,
 * matching the requested reference layout. Text stays centered;
 * typography/animation/spacing untouched. */
.animated-statement--coaching-wide .animated-statement__inner {
	max-width: 1100px;
}

.animated-statement__text {
	margin: 0 0 1.75rem;
	/* color:inherit is load-bearing (2026-07-15 fix): a bare `p{color:…}`
	 * rule in design-system.css otherwise always wins over the "Tekstkleur"
	 * setting's inherited color from .animated-statement__inner — explicit
	 * author rules beat inheritance regardless of specificity, so this
	 * text was silently stuck at #111111 on every instance, invisible the
	 * moment someone used a dark backgroundColor (only ever looked
	 * "correct" by coincidence when textColor was also left at its own
	 * #111111 default). Same fix/reasoning already applied to
	 * .animated-statement__subtitle and other PB text elements. */
	color: inherit;
	font-family: var( --wp--preset--font-family--theme-heading, -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", Roboto, Arial, sans-serif );
	/* Regular weight by default (was a hardcoded 800, making every
	 * statement permanently "extra bold" and leaving RichText's own
	 * <strong> mark nowhere heavier to go) — reuses the same
	 * --wp--custom--typography--body-large--weight token
	 * .pb-type--body-large already resolves to (design-system.css),
	 * the closest existing role to this large centered statement text
	 * (same 1.15 line-height, same "regular by default, strong stands
	 * out" intent), rather than inventing a new weight value here.
	 * The --as-intro-desktop-* custom properties (2026-07-14, per-
	 * instance typography controls) are only ever set by
	 * pb_animated_statement_typography_vars() when that instance
	 * actually has an Intro override — unset elsewhere (e.g. Home),
	 * so var()'s fallback (this exact original value) applies there
	 * unchanged. See inc/animated-statement-block.php. */
	font-weight: var( --as-intro-desktop-weight, var( --wp--custom--typography--body-large--weight, 400 ) );
	line-height: var( --as-intro-desktop-line-height, 1.15 );
	letter-spacing: var( --as-intro-desktop-spacing, -0.02em );
	font-size: var( --as-intro-desktop-size, clamp( 1.75rem, 1rem + 3vw, 3.5rem ) );
}

/* Mirrors .pb-type--body-large strong (and every other .pb-type--*
 * role) elsewhere in design-system.css — the same sitewide "true bold
 * for inline emphasis, regardless of the role's own base weight" rule,
 * applied here since .animated-statement__text isn't itself a
 * .pb-type--* element. RichText's core/bold format outputs a real
 * <strong>, so no markup/JS change is needed for this to apply.
 * The --as-bold-* custom properties (see .animated-statement__text
 * above) are set on the section root and simply inherit down to this
 * nested <strong> — no separate class/markup hook needed for "Bold
 * statement" to be independently stylable per instance. */
.animated-statement__text strong {
	font-weight: var( --as-bold-desktop-weight, 700 );
	font-size: var( --as-bold-desktop-size, inherit );
	line-height: var( --as-bold-desktop-line-height, inherit );
	letter-spacing: var( --as-bold-desktop-spacing, inherit );
}

/* "Regular"/"Extra bold" RichText formats (assets/js/pb-weight-formats.js)
 * — needed because this block's base weight is the Intro typography
 * preset's own (often 800, e.g. h2), so core/bold's <strong> can't step
 * *down* to regular and there's no native format to step above bold. */
.animated-statement__text .has-weight-regular {
	font-weight: 400;
}

.animated-statement__text .has-weight-extrabold {
	font-weight: 900;
}

/* Optional line under the statement (pb-rich-paragraph's "body" role —
 * see block.json's subtitleText / inc/animated-statement-block.php).
 * .pb-type--body already gives the smaller size/weight/line-height off
 * the same design tokens; the --as-subtitle-* custom properties below
 * (2026-07-14, per-instance typography controls) simply layer on top
 * via the exact same tokens as their own fallback, so an instance that
 * has never touched the "Typografie — Subtitle" panel renders
 * identically to before. `color: inherit` stays reset so the block's
 * "Tekstkleur" setting (applied via .animated-statement__inner's
 * inline style) still wins here, same reasoning as
 * .animated-statement__text. */
.animated-statement__subtitle {
	margin: -0.75rem 0 1.75rem;
	color: inherit;
	font-size: var( --as-subtitle-desktop-size, var( --wp--preset--font-size--body, 1rem ) );
	line-height: var( --as-subtitle-desktop-line-height, var( --wp--custom--typography--body--line-height, 1.6 ) );
	font-weight: var( --as-subtitle-desktop-weight, var( --wp--custom--typography--body--weight, 400 ) );
	letter-spacing: var( --as-subtitle-desktop-spacing, var( --wp--custom--typography--body--letter-spacing, 0em ) );
}

/* RichText <strong> inside the subtitle (e.g. "bereiken"/"volhouden"/
 * "door wilt geven") was falling through to design-system.css's generic
 * `.pb-type--body strong { font-weight: min(calc(400+100),900) }` = 500
 * (Medium) — a +100 step over body's own 400 base, same formula every
 * .pb-type--* role uses, but too subtle to read as "bold" against a
 * Poppins body face (unlike h1-h3's 800-base roles, where +100 lands at
 * 900). Same specificity as that rule (class+element); wins on source
 * order since main.css loads after design-system.css. Poppins 700 is
 * already loaded (functions.php's Google Fonts enqueue), so no extra
 * font request is needed. */
.animated-statement__subtitle strong {
	font-weight: 700;
}

/* Tablet/mobile tiers for the 3 typography-controllable sections
 * (Intro/Bold/Subtitle) — each breakpoint falls back to the next-wider
 * one, then finally to the same hardcoded default as the unconditional
 * rules above, exactly mirroring design-system.css's own .pb-type--*
 * nested var() fallback chain. Only ever active when an instance's
 * introTypography/boldTypography/subtitleTypography attribute actually
 * sets something for that breakpoint — otherwise every var() here
 * resolves straight through to the original default, so Home
 * (untouched) renders identically. */
@media (max-width: 1024px) {
	.animated-statement__text {
		font-size: var( --as-intro-tablet-size, var( --as-intro-desktop-size, clamp( 1.75rem, 1rem + 3vw, 3.5rem ) ) );
		line-height: var( --as-intro-tablet-line-height, var( --as-intro-desktop-line-height, 1.15 ) );
		font-weight: var( --as-intro-tablet-weight, var( --as-intro-desktop-weight, var( --wp--custom--typography--body-large--weight, 400 ) ) );
		letter-spacing: var( --as-intro-tablet-spacing, var( --as-intro-desktop-spacing, -0.02em ) );
	}

	.animated-statement__text strong {
		font-weight: var( --as-bold-tablet-weight, var( --as-bold-desktop-weight, 700 ) );
		font-size: var( --as-bold-tablet-size, var( --as-bold-desktop-size, inherit ) );
		line-height: var( --as-bold-tablet-line-height, var( --as-bold-desktop-line-height, inherit ) );
		letter-spacing: var( --as-bold-tablet-spacing, var( --as-bold-desktop-spacing, inherit ) );
	}

	.animated-statement__subtitle {
		font-size: var( --as-subtitle-tablet-size, var( --as-subtitle-desktop-size, var( --wp--preset--font-size--body, 1rem ) ) );
		line-height: var( --as-subtitle-tablet-line-height, var( --as-subtitle-desktop-line-height, var( --wp--custom--typography--body--line-height, 1.6 ) ) );
		font-weight: var( --as-subtitle-tablet-weight, var( --as-subtitle-desktop-weight, var( --wp--custom--typography--body--weight, 400 ) ) );
		letter-spacing: var( --as-subtitle-tablet-spacing, var( --as-subtitle-desktop-spacing, var( --wp--custom--typography--body--letter-spacing, 0em ) ) );
	}
}

@media (max-width: 767px) {
	/* Manual <br> tags in the statement/subtitle text are tuned for
	 * desktop's much wider line — on mobile they force very short,
	 * often single-word lines regardless of font-size. Hiding the <br>
	 * itself (not touching the actual text/content) lets each line wrap
	 * naturally against the real available width instead. */
	.animated-statement__text br,
	.animated-statement__subtitle br {
		display: none;
	}

	.animated-statement__inner {
		padding-left: 1rem;
		padding-right: 1rem;
	}

	.animated-statement__text {
		font-size: var( --as-intro-mobile-size, var( --as-intro-tablet-size, var( --as-intro-desktop-size, clamp( 1.75rem, 1rem + 3vw, 3.5rem ) ) ) );
		line-height: var( --as-intro-mobile-line-height, var( --as-intro-tablet-line-height, var( --as-intro-desktop-line-height, 1.15 ) ) );
		font-weight: var( --as-intro-mobile-weight, var( --as-intro-tablet-weight, var( --as-intro-desktop-weight, var( --wp--custom--typography--body-large--weight, 400 ) ) ) );
		letter-spacing: var( --as-intro-mobile-spacing, var( --as-intro-tablet-spacing, var( --as-intro-desktop-spacing, -0.02em ) ) );
	}

	.animated-statement__text strong {
		font-weight: var( --as-bold-mobile-weight, var( --as-bold-tablet-weight, var( --as-bold-desktop-weight, 700 ) ) );
		font-size: var( --as-bold-mobile-size, var( --as-bold-tablet-size, var( --as-bold-desktop-size, inherit ) ) );
		line-height: var( --as-bold-mobile-line-height, var( --as-bold-tablet-line-height, var( --as-bold-desktop-line-height, inherit ) ) );
		letter-spacing: var( --as-bold-mobile-spacing, var( --as-bold-tablet-spacing, var( --as-bold-desktop-spacing, inherit ) ) );
	}

	.animated-statement__subtitle {
		font-size: var( --as-subtitle-mobile-size, var( --as-subtitle-tablet-size, var( --as-subtitle-desktop-size, var( --wp--preset--font-size--body, 1rem ) ) ) );
		line-height: var( --as-subtitle-mobile-line-height, var( --as-subtitle-tablet-line-height, var( --as-subtitle-desktop-line-height, var( --wp--custom--typography--body--line-height, 1.6 ) ) ) );
		font-weight: var( --as-subtitle-mobile-weight, var( --as-subtitle-tablet-weight, var( --as-subtitle-desktop-weight, var( --wp--custom--typography--body--weight, 400 ) ) ) );
		letter-spacing: var( --as-subtitle-mobile-spacing, var( --as-subtitle-tablet-spacing, var( --as-subtitle-desktop-spacing, var( --wp--custom--typography--body--letter-spacing, 0em ) ) ) );
	}
}

/* 2026-07-24: coachingstool (page-id-968) wants its closing
 * "Daar gebeurt de echte verandering." statement on its
 * own line with vertical spacing above it, on desktop AND
 * mobile. This page has a SECOND, unrelated pb/animated-
 * statement instance further down ("PaperBrain als
 * verlengstuk" heading) that also has a lone <strong> --
 * scoping by an auto-generated per-instance class
 * (.as-xxxxxxxxxx) does NOT work here, it is regenerated on
 * every save/render and silently stops matching, so instead
 * scope by shape: only the text block that actually contains
 * a strong+strong pair (:has(strong + strong)) is this one --
 * the verlengstuk block has just one lone <strong>, no pair,
 * so it never matches. The bold run itself is five separate
 * per-word <strong> spans (see pb_animated_statement_word_spans(),
 * inc/animated-statement-block.php) with no shared wrapper,
 * so target only the first one in the run
 * (:not(strong + strong) excludes every strong that itself
 * follows another strong) and push a block-level, margin-topped
 * ::before in front of just that word. */
.page-id-968 .animated-statement__text:has(strong + strong) strong:not(strong + strong)::before {
	content: "";
	display: block;
	margin-top: 0.75em;
}

@media (max-width: 767px) {
	/* 2026-07-24: coachingstool (page-id-968) subtitle also loses
	 * its own manual line breaks to the sitewide "hide every
	 * .animated-statement__subtitle br on mobile" rule further
	 * up -- re-enable just the pair before "Hoe voorkom je..."
	 * (3rd/4th br) and the one between it and "Hoe zorg je..."
	 * (5th). Scoped the same way as the rule above: the verlengstuk
	 * block's subtitle ("Zo versterkt...") has zero <br> at all,
	 * so :has(br) alone already excludes it -- no auto-generated
	 * per-instance class involved, so this keeps matching across
	 * saves/re-renders. */
	.page-id-968 .animated-statement__subtitle:has(br) br:nth-of-type(3),
	.page-id-968 .animated-statement__subtitle:has(br) br:nth-of-type(4),
	.page-id-968 .animated-statement__subtitle:has(br) br:nth-of-type(5) {
		display: initial;
	}
}

.animated-statement__cta {
	display: inline-block;
}

/* Words, subtitle and CTA all start "subtly present" — dimmed,
 * softly blurred and slightly offset/scaled down, coming into sharp
 * focus on reveal rather than appearing from nothing (Coca-Cola-style
 * "text comes into focus" effect). Only html.js (added as early as
 * possible by a tiny inline script, see pb_print_js_class_script() in
 * functions.php) opts a statement into this treatment, so a statement
 * never stays permanently unreadable without JavaScript — without it,
 * the plain default (visible, no filter/transform) applies.
 * opacity/filter/transform only — none of these affect box size/
 * position in the layout, so nothing here can cause layout shift.
 * The subtitle (2026-07-09) joins this exact same rule set rather
 * than getting its own animation — its own transition-delay (set
 * server-side, right after the statement's last word) is what times
 * it into the same cascade, not a separate CSS treatment. */
html.js .animated-statement__word,
html.js .animated-statement__subtitle,
html.js .animated-statement__cta {
	opacity: 0.26;
	filter: blur(8px);
	transform: translateY(10px) scale(0.985);
	transition: opacity 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94), filter 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94), transform 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.animated-statement__word {
	display: inline-block;
	will-change: opacity, filter, transform;
}

/* A <u>-wrapped word never showed its underline (2026-07-15 fix): a
 * text-decoration set on an ancestor doesn't paint through a
 * display:inline-block descendant's own "decorating box" (CSS Text
 * Decoration spec) — the .animated-statement__word span above breaks
 * it for every underlined word. text-decoration-line isn't inherited
 * either, so it has to be declared directly on the span itself,
 * scoped to only the <u> case so plain words are unaffected. */
.animated-statement__text u .animated-statement__word {
	text-decoration: underline;
}

html.js .animated-statement.is-revealed .animated-statement__word,
html.js .animated-statement.is-revealed .animated-statement__subtitle,
html.js .animated-statement.is-revealed .animated-statement__cta {
	opacity: 1;
	filter: blur(0);
	transform: translateY(0) scale(1);
}

@media (prefers-reduced-motion: reduce) {
	html.js .animated-statement__word,
	html.js .animated-statement__subtitle,
	html.js .animated-statement__cta {
		opacity: 1;
		filter: none;
		transform: none;
		transition: none;
	}
}

/* ==========================================================================
 * pb/business-split — two full-height blocks side by side (Retail/B2B
 * style landing page), see inc/business-split-block.php.
 * ========================================================================*/
.pb-business-split {
	display: grid;
	grid-template-columns: 1fr;
	/* --pb-header-height is kept in sync with the real, current header
	 * (topbar + header combined) by assets/js/business-split.js — 71px
	 * is just the fallback for the very first paint, before that script
	 * has run once. */
	min-height: calc(100vh - var(--pb-header-height, 71px));
}

.pb-business-split.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

@media (min-width: 768px) {
	.pb-business-split {
		grid-template-columns: 1fr 1fr;
	}
}

.pb-business-split__block {
	position: relative;
	display: flex;
	align-items: center;
	overflow: hidden;
	min-height: 70vh;
}

@media (min-width: 768px) {
	.pb-business-split__block {
		min-height: inherit;
	}
}

.pb-business-split__bg {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.pb-business-split__overlay {
	position: absolute;
	inset: 0;
}

.pb-business-split__content {
	position: relative;
	z-index: 1;
	width: 100%;
	max-width: 512px;
	margin: 0 auto;
	padding: 2rem clamp(1.5rem, 5vw, 3rem);
	color: #ffffff;
}

/* Desktop only: gives both cards a shared reference height (matching
 * the "content area ~512x512" spec) with text top-aligned inside it —
 * two cards with different amounts of text (one-line vs two-line
 * title) would otherwise each center within their own, different
 * content height and start at different points side by side, even
 * though the block itself is centered as a whole. This keeps the
 * eyebrow/title/text starting at the exact same point on both cards,
 * while the box is still centered by .pb-business-split__block's own
 * align-items:center, giving the same "sits in the middle" look as
 * the reference. */
@media (min-width: 768px) {
	.pb-business-split__content {
		display: flex;
		flex-direction: column;
		justify-content: flex-start;
		min-height: 512px;
	}
}

/* color: inherit is load-bearing on all three text elements below, not
 * decorative: this theme's global heading/paragraph styles (theme.json
 * + main.css base typography) set their own explicit color, which
 * otherwise wins over simple inheritance from .pb-business-split__content's
 * own `color: #fff` — same issue, same fix, as pb/hero-split's own
 * eyebrow/heading/paragraph (see that block's CSS for the longer
 * version of this note). */
.pb-business-split__eyebrow {
	display: block;
	margin-bottom: 0.75rem;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	opacity: 0.85;
	color: inherit;
}

.pb-business-split__title {
	margin: 0 0 1rem;
	font-size: 24px;
	line-height: 1.25;
	font-weight: 700;
	color: inherit;
}

.pb-business-split__text {
	margin: 0 0 1.75rem;
	font-size: 14px;
	line-height: 1.6;
	opacity: 0.9;
	color: inherit;
}

/* Outlined/light button — deliberately its own class, not the shared
 * pill-shaped .button/.button--primary/.button--ghost used elsewhere
 * (see pb/hero-split's own CTA for the same reasoning): the reference
 * uses a plain rectangular outline, not this theme's usual pill. */
.pb-business-split__cta {
	display: inline-flex;
	align-items: center;
	padding: 0.85rem 1.75rem;
	border: 1px solid currentColor;
	border-radius: 4px;
	background: transparent;
	color: #ffffff;
	font-size: 14px;
	font-weight: 600;
	transition: background-color 0.2s ease, color 0.2s ease;
}

.pb-business-split__cta:hover,
.pb-business-split__cta:focus-visible {
	background: #ffffff;
	color: #111111;
	outline: none;
}

/* ==========================================================================
 * pb/business-card — a single full-height card, meant to be placed
 * twice inside a plain Gutenberg Columns block (50/50) — see
 * inc/business-card-block.php. .pb-business-columns (an "Additional
 * CSS class" added to that Columns block in the editor) is what turns
 * the row itself into a full-height, no-gap row; the core Columns
 * block already stretches both columns to equal height by default,
 * which is what makes two of these cards line up correctly without
 * any cross-card alignment logic.
 * ========================================================================*/
.pb-business-columns.wp-block-columns {
	margin-bottom: 0;
	/* --pb-header-height is kept in sync with the real, current header
	 * by assets/js/business-split.js (shared with pb/business-split). */
	min-height: calc(100vh - var(--pb-header-height, 71px));
	gap: 0;
}

.pb-business-columns > .wp-block-column {
	margin: 0;
}

.pb-business-card {
	position: relative;
	display: flex;
	align-items: flex-start;
	overflow: hidden;
	min-height: 70vh;
	height: 100%;
}

@media (min-width: 768px) {
	.pb-business-card {
		align-items: center;
		min-height: inherit;
	}
}

/* Mobile: both cards square (aspect-ratio 1/1) and stacked, sized to
 * exactly half the available viewport height each — the two together
 * always fill the screen without overflow regardless of device
 * dimensions (deriving the square from height, not 100vw width, is
 * what guarantees this on narrower/taller phones too), so both are
 * visible on load with no scrolling needed. Content stays vertically
 * centered like desktop; .pb-business-card__content's own padding is
 * what gives each card breathing room, no extra row/column gap needed. */
@media (max-width: 767px) {
	/* This Columns instance has no align:"full" set (desktop/tablet
	 * intentionally stay content-width, unchanged) — mobile-only
	 * breakout so both squares can reach the actual screen edges, same
	 * position:relative/100vw/-50vw technique used elsewhere in this
	 * file (e.g. .campaign-grid.alignfull). */
	.pb-business-columns.wp-block-columns {
		position: relative;
		left: 50%;
		width: 100vw;
		max-width: 100vw;
		margin-left: -50vw;
		min-height: calc(100dvh - var(--pb-header-height, 71px));
	}

	.pb-business-card {
		align-items: center;
		aspect-ratio: 1 / 1;
		width: 100%;
		height: auto;
		min-height: 0;
		margin: 0;
		flex: 0 0 auto;
	}
}

.pb-business-card__bg {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.pb-business-card__overlay {
	position: absolute;
	inset: 0;
}

.pb-business-card__content {
	position: relative;
	z-index: 1;
	width: 100%;
	max-width: 512px;
	margin: 0 auto;
	padding: 2rem clamp(1.5rem, 5vw, 3rem);
	color: #ffffff;
}

/* color: inherit is load-bearing on all three text elements below —
 * see pb/business-split's own identical note for the full story. */
.pb-business-card__eyebrow {
	display: block;
	margin-bottom: 0.75rem;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	opacity: 0.85;
	color: inherit;
}

/* Typography token-driven via PB Rich Heading/Paragraph's
 * `.pb-type--*` classes — "displayXl" retuned to this title's original
 * 24px/700/1.25 values (unused elsewhere; zero regression risk). */
.pb-business-card__title {
	margin: 0 0 1rem;
	color: inherit;
}

/* "body" token already matches this text's weight (400)/line-height
 * (1.6); font-size (14px) and color (inherit) are this block's own
 * nuance, same pattern as pb/hero-centered and pb/newsletter. */
.pb-business-card__text {
	margin: 0 0 1.75rem;
	font-size: 14px;
	opacity: 0.9;
	color: inherit;
}

/* Neutralize pb-button--ghost's uppercase/letter-spacing/hover-lift —
 * this outline CTA predates the shared button system. */
.pb-business-card__cta.pb-button--ghost {
	display: inline-flex;
	align-items: center;
	padding: 0.6rem 1.25rem;
	border: 1px solid currentColor;
	border-radius: 4px;
	background: transparent;
	color: #ffffff;
	font-size: 13px;
	font-weight: 600;
	text-transform: none;
	letter-spacing: normal;
	transition: background-color 0.2s ease, color 0.2s ease;
}

.pb-business-card__cta.pb-button--ghost:hover,
.pb-business-card__cta.pb-button--ghost:focus-visible {
	background: #ffffff;
	color: #111111;
	transform: none;
	outline: none;
}

/* ==========================================================================
 * pb/newsletter — full-width newsletter sign-up banner (reference:
 * Elvou's "Join het Elvou Friends-programma"), see inc/newsletter-block.php.
 * Desktop: 1400x528 section, centered 1376x400 rounded card inside it.
 * ========================================================================*/
.pb-newsletter {
	max-width: 1400px;
	margin: 0 auto;
	/* Set inline per-instance by inc/newsletter-block.php from the
	 * block's own "Sectie-padding" Inspector controls (default 64px
	 * each, giving the 1400x528 section around a 1400x400 container —
	 * 528 - 400 = 128 = 64 top + 64 bottom). */
	padding: var(--pb-newsletter-padding-top, 64px) 0 var(--pb-newsletter-padding-bottom, 64px);
}

/* Self-contained, environment-independent width (2026-07-14): the
 * max-width/margin:auto above only produces the intended 1400px
 * centered card once this section's own ambient container is already
 * at least that wide. On the front page (.site-main.front-page has no
 * width cap — see the .site-main rule) that's true, but a Page using
 * the plain page.php template gets the generic .site-main's own
 * 800px reading-width box instead — an identical, byte-for-byte
 * copied newsletter block was silently trapped inside ~736px there
 * instead of 1400px (same root cause/fix shape as .pb-hero-split's own
 * note above: "the block was silently trapped inside that width
 * instead" of its intended size, because it depended on which
 * template happened to remove the site's default content max-width).
 * Breaks the section out to the full viewport width regardless of
 * ambient container/template, then relies on .pb-newsletter__container
 * below — which already has its own independent max-width:1400px/
 * margin:auto — to re-center the visible card at exactly 1400px on
 * every page. Home's own rendering is unchanged: its wrapper was
 * already unconstrained, so this resolves to the same effective width
 * there. Excluded when nested inside the two-newsletters-side-by-side
 * Columns layout below — there the *column* is meant to define the
 * available width, not the viewport. */
.pb-newsletter:not(.wp-block-column .pb-newsletter) {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.pb-newsletter__container {
	max-width: 1400px;
	height: 400px;
	margin: 0 auto;
	padding: 0 12px;
}

/* Self-contained width, independent of .site-main's per-template cap
 * (2026-07-16): this Columns pattern previously just inherited
 * .site-main's ambient width, which is uncapped on the front-page
 * template but capped to 800px on the plain page.php template (see
 * the .site-main rule near the top of this file) — so an identical
 * copied Columns > Column > pb/newsletter structure rendered far
 * narrower on any other page. Scoped only to this exact selector
 * (not a generic .alignwide/.alignfull rule, so it can't affect any
 * other Columns usage, including Home's own other content), using
 * the same self-contained left:50%/-50vw breakout already proven
 * elsewhere in this file. On the front-page template this resolves
 * to the same width Home already had (its ambient was already
 * uncapped), so Home's rendered output is unchanged. */
.wp-block-columns:has(> .wp-block-column > .pb-newsletter) {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Two pb/newsletter blocks side by side in a core 50/50 Columns block
 * — :has scopes this to that case only, so unrelated wp-block-columns
 * usage elsewhere (e.g. Campaign Grid) is untouched.
 * .pb-newsletter__container's own padding is still untouched; the
 * outer left/right inset is added on the *column* wrapper instead
 * (so each card sits further in from the page edge), and the shared
 * middle gap is smaller than that outer inset — was inverted (small
 * outer, large middle). Desktop only — Columns stacks on mobile via
 * its own responsive behaviour. */
@media (min-width: 1025px) {
	.wp-block-columns:has(> .wp-block-column > .pb-newsletter) {
		gap: 16px;
	}

	.wp-block-columns:has(> .wp-block-column > .pb-newsletter) > .wp-block-column:first-child {
		padding-left: 40px;
	}

	.wp-block-columns:has(> .wp-block-column > .pb-newsletter) > .wp-block-column:last-child {
		padding-right: 40px;
	}
}

.pb-newsletter__card {
	position: relative;
	width: 100%;
	height: 100%;
	border-radius: 16px;
	overflow: hidden;
}

.pb-newsletter__bg {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.pb-newsletter__overlay {
	position: absolute;
	inset: 0;
}

.pb-newsletter__content {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	height: 100%;
	padding: 2rem clamp(1.5rem, 5vw, 4rem);
	text-align: center;
	color: #ffffff;
}

/* color: inherit is load-bearing on all three text elements below —
 * see pb/hero-split's own identical note for the full story (this
 * theme's global heading/paragraph styles otherwise win over simple
 * inheritance from .pb-newsletter__content's own `color: #fff`). */
.pb-newsletter__eyebrow {
	display: block;
	margin-bottom: 0.75rem;
	font-size: 12px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	opacity: 0.85;
	color: inherit;
}

/* Typography is now token-driven via PB Rich Heading/PB Rich
 * Paragraph's `.pb-type--*` classes — this rule only keeps layout
 * (margin) and the load-bearing `color: inherit` override (the
 * "displayL" token was retuned to this title's original 36px/600/1.25
 * values, since nothing else referenced it yet — zero regression risk
 * elsewhere; see theme.json). */
.pb-newsletter__title {
	margin: 0 0 0.75rem;
	color: inherit;
}

/* "body" token already matches this text's original weight (400) and
 * line-height (1.6) exactly — only font-size (14px, smaller than
 * body's 1rem) and color (inherit, vs. body's own dark default) are
 * this block's own nuance, same pattern as pb/hero-centered's
 * description. */
.pb-newsletter__text {
	max-width: 40rem;
	margin: 0 0 1.5rem;
	font-size: 14px;
	opacity: 0.9;
	color: inherit;
}

/* Second, optional CTA (2026-07-17) — .pb-newsletter__content has no
 * flex `gap` of its own (every element's spacing is its own margin, see
 * __title/__text above), so this wrapper carries the "below CTA 1"
 * spacing; the button inside still gets the exact same .pb-newsletter__cta
 * classes/styling as CTA 1, nothing duplicated. */
.pb-newsletter__cta-extra {
	margin-top: 0.75rem;
}

/* Outlined/transparent CTA matching the reference exactly (plain
 * rectangular outline, not this theme's usual pill-shaped
 * .button/.button--ghost) — same reasoning/pattern as pb/hero-split's
 * and pb/business-card's own CTA. Now layered on top of the shared
 * pb-button/pb-button--ghost classes (see PHP: pb_button_class()) —
 * text-transform/letter-spacing/transform are explicitly neutralized
 * below since pb-button--ghost's own uppercase/letter-spacing/hover-
 * lift would otherwise leak through on any property this rule doesn't
 * already redeclare. */
/* Scoped to --ghost only (2026-07-16): this used to be a bare
 * .pb-newsletter__cta selector, which meant it also silently overrode
 * Primary/Secondary CTAs with ghost's own box model — pb-button--primary/
 * --secondary must render byte-identical to Hero Header's, unmodified. */
.pb-newsletter__cta.pb-button--ghost {
	display: inline-flex;
	align-items: center;
	padding: 0.6rem 1.25rem;
	border: 1px solid currentColor;
	border-radius: 4px;
	background: transparent;
	color: #ffffff;
	font-size: 12px;
	font-weight: 600;
	text-transform: none;
	letter-spacing: normal;
	transition: background-color 0.2s ease, color 0.2s ease;
}

.pb-newsletter__cta.pb-button--ghost:hover,
.pb-newsletter__cta.pb-button--ghost:focus-visible {
	background: #ffffff;
	color: #111111;
	transform: none;
	outline: none;
}

/* Header CTA variant on the ghost style (2026-07-23) — same component/
 * markup as "Kom in contact" (pb/newsletter on /verkooppunten/), still
 * the header's own look: filled purple by default (not the block's
 * transparent/outline default), white text, same border color as the
 * fill. Same specificity (two classes) as the base ghost rule above and
 * declared after it, so this wins on the header's element (which
 * carries both classes) without needing higher specificity tricks. */
.pb-newsletter__cta--header.pb-button--ghost {
	background-color: var(--pb-header-cta-background, #6848f4);
	border-color: var(--pb-header-cta-background, #6848f4);
	color: #ffffff;
}

/* Sitewide PB Newsletter CTA hover, desktop only (2026-07-23): one shared
 * brand-purple hover for every PB Newsletter CTA regardless of which
 * .pb-button--ghost/--primary/--secondary style is selected, so this is
 * the default everywhere the block is used, not a per-page rule. Wins
 * over the mobile-safe rules above/the sitewide .pb-button--*:hover via
 * the min-width query + equal-or-higher selector specificity, cascading
 * later — mobile (below this breakpoint) keeps its existing hover as-is.
 * Only background/border/text color change; border-radius, padding and
 * typography still come from whichever .pb-button--* variant is active,
 * and no other button component (outside .pb-newsletter__cta) is touched.
 * The header CTA (.pb-newsletter__cta--header) is intentionally included
 * here (not excluded) — it now shares this exact hover too. */
@media (min-width: 1025px) {
	.pb-newsletter__cta.pb-button--ghost:hover,
	.pb-newsletter__cta.pb-button--ghost:focus-visible,
	.pb-newsletter__cta.pb-button--primary:hover,
	.pb-newsletter__cta.pb-button--primary:focus-visible,
	.pb-newsletter__cta.pb-button--secondary:hover,
	.pb-newsletter__cta.pb-button--secondary:focus-visible {
		background-color: #6848f4;
		border-color: #6848f4;
		color: #ffffff;
		transition: background-color 0.22s ease, border-color 0.22s ease, color 0.22s ease;
		transform: none;
		outline: none;
	}
}

/* Header hover (2026-07-23) — since the header is filled purple by
 * default (unlike every other PB Newsletter ghost CTA, which starts
 * transparent), the shared desktop hover fill above would be invisible
 * on it; this lightens instead, same mechanism/spec as the header's own
 * pre-redesign hover. Same specificity as the shared hover block above
 * (two classes + pseudo) but declared after it, so it wins the tie
 * regardless of viewport width. */
.pb-newsletter__cta--header.pb-button--ghost:hover,
.pb-newsletter__cta--header.pb-button--ghost:focus-visible {
	background-color: #7e6fef;
	border-color: #7e6fef;
	color: #ffffff;
}

@media (max-width: 1024px) {
	.pb-newsletter {
		padding: clamp(32px, 6vw, 48px) 0;
	}

	.pb-newsletter__container {
		height: 340px;
		padding: 0 1rem;
	}
}

@media (max-width: 767px) {
	.pb-newsletter {
		padding: 24px 0;
	}

	.pb-newsletter__container {
		height: auto;
		padding: 0 1rem;
	}

	.pb-newsletter__card {
		display: flex;
		align-items: center;
		height: auto;
		min-height: 320px;
	}

	.pb-newsletter__content {
		height: auto;
		width: 100%;
		padding: 2rem 1.25rem;
	}

	/* Title's mobile size now comes from .pb-type--display-l's own
	 * sizeMobile token (theme.json), not a block-level override.
	 * Text's mobile size (14px) was already identical to its own
	 * unconditional base rule above — genuinely redundant, removed. */
	.pb-newsletter__cta.pb-button--ghost {
		font-size: 12px;
	}
}

/* ==========================================================================
 * pb/hero-image-split — new, standalone hero (text left / large
 * floating image right), NOT based on pb/hero-split — see
 * inc/hero-image-split-block.php. Reference: "Van goede intenties
 * naar concrete stappen." notebook hero.
 * ========================================================================*/
/* Full-bleed breakout — same established technique as this theme's
 * own .hero block (position:relative;left:50%;width:100vw;margin-left:
 * -50vw), not a naive max-width:1400px;margin:0 auto (which only ever
 * centers within whatever width the parent page template already
 * gives it — on a page whose content column is narrower than 1400px,
 * that would never actually reach full width at all). The theme has
 * no generic .alignfull/.alignwide breakout CSS of its own; every
 * full-width block achieves it this same way, individually. The
 * 1400px cap itself lives on .pb-hero-image-split__grid below, so the
 * *background* spans the full viewport while the *content* stays
 * readably centered — matching "Volledige breedte binnen de container
 * zoals huidige site". */
.pb-hero-image-split {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	overflow: hidden;
	min-height: var(--his-min-height, auto);
	padding: var(--his-padding-top, 96px) 0 var(--his-padding-bottom, 96px);
	display: flex;
	align-items: center;
}

.pb-hero-image-split__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
}

.pb-hero-image-split__overlay {
	position: absolute;
	inset: 0;
}

.pb-hero-image-split__grid {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: 1.1fr 1fr;
	align-items: center;
	gap: 3rem;
	width: 100%;
	max-width: var(--his-content-max-width, 1400px);
	margin: 0 auto;
	padding: 0 var(--his-padding-right, 80px) 0 var(--his-padding-left, 80px);
}

.pb-hero-image-split__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	max-width: var(--his-text-column-width, 34rem);
}

.pb-hero-image-split__eyebrow {
	margin-bottom: 1.25rem;
	font-size: var(--his-eyebrow-desktop-size, 0.8rem);
	line-height: var(--his-eyebrow-desktop-line-height, normal);
	font-weight: var(--his-eyebrow-desktop-weight, 600);
	letter-spacing: var(--his-eyebrow-desktop-spacing, 0.2em);
	text-transform: uppercase;
	color: var(--his-title-color, #1c2333);
	opacity: 0.65;
}

/* Typography now token-driven via PB Rich Heading's `.pb-type--*`
 * class ("bodyLarge" retuned to this title's original clamp/weight/
 * line-height/letter-spacing values — the last unclaimed token; every
 * Display tier was already claimed by earlier, smaller blocks). Serif
 * is still a deliberate scoped override, unrelated to the token
 * system — the theme has no serif preset, and this design is
 * intentionally different from every other heading on the site. Bold
 * (<strong>, from the shared `.pb-type--* strong` rule) plus italic
 * (<em>, italic by default) together reproduce the old dedicated
 * "italic part" styling — now just normal Gutenberg toolbar
 * formatting instead of a second text field. */
/* font-size/line-height/font-weight/letter-spacing use !important: this
 * renders a real semantic tag (titleLevel, h1-h6), and design-system.css's
 * own bare h1..h6 rules already force those 4 properties with
 * !important, which would otherwise always win regardless of this
 * class's own specificity/source order (same conflict class already
 * fixed for pb/image-text's heading). Fallback is
 * --his-heading-fallback-desktop-* (inc/hero-image-split-block.php),
 * dynamically resolved per this instance's own titleLevel — so any
 * other instance with no headingTypography override renders byte-
 * identical to before, whichever level it uses. */
.pb-hero-image-split__title {
	margin: 0 0 1.5rem;
	font-family: Georgia, "Times New Roman", Times, serif;
	color: var(--his-title-color, #1c2333);
	font-size: var(--his-heading-desktop-size, var(--his-heading-fallback-desktop-size)) !important;
	line-height: var(--his-heading-desktop-line-height, var(--his-heading-fallback-desktop-line-height)) !important;
	font-weight: var(--his-heading-desktop-weight, var(--his-heading-fallback-desktop-weight)) !important;
	letter-spacing: var(--his-heading-desktop-spacing, var(--his-heading-fallback-desktop-spacing)) !important;
}

/* "body" token already matches this description's weight (400) and
 * line-height (1.6); size (18px) and color are this block's own
 * nuance. */
.pb-hero-image-split__description {
	margin: 0 0 var(--his-paragraph-gap, 2rem);
	max-width: 30rem;
	font-size: var(--his-body-desktop-size, 18px);
	line-height: var(--his-body-desktop-line-height, 1.6);
	font-weight: var(--his-body-desktop-weight, 400);
	letter-spacing: var(--his-body-desktop-spacing, normal);
	color: #1c2333;
	opacity: 0.72;
}

.pb-hero-image-split__description strong {
	font-weight: 700;
	opacity: 1;
}

.pb-hero-image-split__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 2rem;
}

/* Child-layer architecture (2026-07-15): pb/his-cta-primary/-secondary
 * are separate InnerBlocks siblings inside .pb-hero-image-split__content
 * (a flex column) instead of both living inside one .pb-hero-image-
 * split__actions row — laid out the same way via their own generated
 * wp-block-pb-his-cta-* wrapper class instead. */
.pb-hero-image-split__content > .wp-block-pb-his-cta-primary,
.pb-hero-image-split__content > .wp-block-pb-his-cta-secondary {
	display: inline-flex;
	align-items: center;
	margin-right: 1.5rem;
}

/* 2026-07-16: scoped dark-navy override removed — this block's
 * "primary" CTA now renders the exact same sitewide .pb-button--primary
 * (design-system.css) as every other Primary CTA, per "Start vandaag"
 * being the single site-wide Primary reference. No local styling left
 * to override; only layout (white-space) stays, matching the block's
 * single-line CTA label intent. */
.pb-hero-image-split .pb-button--primary {
	white-space: nowrap;
}

/* Secondary CTA — plain text link with a trailing arrow, same visual
 * pattern as pb/hero-split's own __cta-secondary (not shared code,
 * since this block deliberately doesn't depend on pb/hero-split). */
.pb-hero-image-split__cta-secondary {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0;
	border: 0;
	background: none;
	border-radius: 0;
	font-size: var(--his-cta2-desktop-size, 0.95rem);
	line-height: var(--his-cta2-desktop-line-height, normal);
	font-weight: var(--his-cta2-desktop-weight, 600);
	letter-spacing: var(--his-cta2-desktop-spacing, normal);
	color: var(--his-cta2-color, #1c2333);
	white-space: nowrap;
	transition: opacity 0.2s ease;
}

.pb-hero-image-split__cta-secondary::after {
	content: "\2192";
	display: inline-block;
	color: var(--his-cta2-color, currentColor);
	transition: transform 0.2s ease;
}

.pb-hero-image-split__cta-secondary:hover,
.pb-hero-image-split__cta-secondary:focus-visible {
	opacity: 0.7;
	outline: none;
}

.pb-hero-image-split__cta-secondary:hover::after,
.pb-hero-image-split__cta-secondary:focus-visible::after {
	transform: translateX(0.25rem);
}

.pb-hero-image-split__media {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100%;
}

.pb-hero-image-split__image-wrap {
	position: relative;
	width: var(--his-image-width, 440px);
	max-width: 100%;
	transform: translate(var(--his-image-offset-x, 0), var(--his-image-offset-y, 0)) rotate(4deg);
}

.pb-hero-image-split__image {
	display: block;
	width: 100%;
	height: auto;
}

/* Soft, floating drop-shadow beneath the image (not a plain box —
 * drop-shadow follows the image's own alpha silhouette, so it reads as
 * the object floating above the background rather than a hard-edged
 * box shadow), matching the reference's notebook exactly. */
.pb-hero-image-split__image-wrap.has-shadow .pb-hero-image-split__image {
	filter: drop-shadow(0 30px 40px rgba(20, 20, 30, 0.28)) drop-shadow(0 8px 12px rgba(20, 20, 30, 0.16));
}

.pb-hero-image-split__image-wrap.has-float {
	animation: his-image-bob 4.5s ease-in-out infinite;
}

@keyframes his-image-bob {
	0%,
	100% {
		transform: translate(var(--his-image-offset-x, 0), var(--his-image-offset-y, 0)) rotate(4deg);
	}

	50% {
		transform: translate(var(--his-image-offset-x, 0), calc(var(--his-image-offset-y, 0px) - 14px)) rotate(4deg);
	}
}

/* Slide-in-on-load animation — content from the left, image from the
 * right — same established "opacity:0 + transform, animation: ...
 * forwards" pattern as the classic .hero block's own .hero__title
 * etc. (see above): runs unconditionally on paint, no scroll-trigger
 * and no JS dependency at all (this is a top-of-page hero, always
 * visible immediately — unlike pb/animated-statement further down a
 * page, there's nothing to "scroll into view"). That also means it
 * animates correctly inside the block editor's own ServerSideRender
 * preview, where enqueued front-end scripts never actually run — a
 * JS/IntersectionObserver-gated version would otherwise leave the
 * editor preview permanently stuck at opacity:0. Gated only behind
 * this instance's own "Animatie inschakelen" Inspector toggle
 * (--animate only present when on). */
.pb-hero-image-split--animate .pb-hero-image-split__content,
.pb-hero-image-split--animate .pb-hero-image-split__media {
	opacity: 0;
	animation: his-slide-in 0.8s ease forwards;
}

.pb-hero-image-split--animate .pb-hero-image-split__content {
	transform: translateX(-40px);
}

.pb-hero-image-split--animate .pb-hero-image-split__media {
	transform: translateX(40px);
	animation-delay: 0.15s;
}

@keyframes his-slide-in {
	to {
		opacity: 1;
		transform: none;
	}
}

@media (prefers-reduced-motion: reduce) {
	.pb-hero-image-split--animate .pb-hero-image-split__content,
	.pb-hero-image-split--animate .pb-hero-image-split__media {
		opacity: 1;
		transform: none;
		animation: none;
	}

	.pb-hero-image-split__image-wrap.has-float {
		animation: none;
	}
}

@media (max-width: 1024px) {
	.pb-hero-image-split {
		padding: clamp(48px, 8vw, 72px) 0;
	}

	.pb-hero-image-split__grid {
		gap: 2rem;
		padding: 0 clamp(24px, 5vw, 48px);
	}

	.pb-hero-image-split__image-wrap {
		width: min(var(--his-image-width, 440px), 340px);
	}

	.pb-hero-image-split__eyebrow { font-size: var(--his-eyebrow-tablet-size, var(--his-eyebrow-desktop-size, 0.8rem)); }
	.pb-hero-image-split__title { font-size: var(--his-heading-tablet-size, var(--his-heading-desktop-size, var(--his-heading-fallback-tablet-size))) !important; }
	.pb-hero-image-split__description { font-size: var(--his-body-tablet-size, var(--his-body-desktop-size, 18px)); }
	.pb-hero-image-split__cta-secondary { font-size: var(--his-cta2-tablet-size, var(--his-cta2-desktop-size, 0.95rem)); }
}

@media (max-width: 767px) {
	.pb-hero-image-split {
		padding: 40px 0;
		min-height: auto;
	}

	.pb-hero-image-split__grid {
		grid-template-columns: 1fr;
		gap: 2.5rem;
		padding: 0 24px;
	}

	.pb-hero-image-split__content {
		max-width: none;
		align-items: flex-start;
		text-align: left;
	}

	/* Title's mobile size now comes from .pb-type--body-large's own
	 * sizeMobile token; description's mobile 18px was already
	 * identical to its unconditional base rule — both redundant,
	 * removed. */
	.pb-hero-image-split__actions {
		flex-wrap: wrap;
		gap: 1rem 1.5rem;
	}

	.pb-hero-image-split__media {
		order: 2;
	}

	.pb-hero-image-split__image-wrap {
		width: min(var(--his-image-width, 440px), 260px);
		margin: 0 auto;
	}

	.pb-hero-image-split__eyebrow { font-size: var(--his-eyebrow-mobile-size, var(--his-eyebrow-tablet-size, var(--his-eyebrow-desktop-size, 0.8rem))); }
	.pb-hero-image-split__title { font-size: var(--his-heading-mobile-size, var(--his-heading-tablet-size, var(--his-heading-desktop-size, var(--his-heading-fallback-mobile-size)))) !important; }
	.pb-hero-image-split__description { font-size: var(--his-body-mobile-size, var(--his-body-tablet-size, var(--his-body-desktop-size, 18px))); }
	.pb-hero-image-split__cta-secondary { font-size: var(--his-cta2-mobile-size, var(--his-cta2-tablet-size, var(--his-cta2-desktop-size, 0.95rem))); }
}

/* ==========================================================================
 * pb/hero-centered — standalone centered homepage hero (reference:
 * paperbrain.store's current homepage hero). One centered column:
 * logo, small subtitle, large floating product image, headline,
 * description, CTA. See inc/hero-centered-block.php. Full-bleed
 * breakout — same established technique as .hero/.pb-hero-image-split
 * (this theme has no generic .alignfull CSS of its own).
 * ========================================================================*/
.pb-hero-centered {
	position: relative;
	z-index: 0;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	margin-top: 0;
	overflow: hidden;
	display: flex;
	align-items: center;
}

/* Residual sub-header gap on plain page templates that isn't
 * attributable to any margin/padding in the header/site-main/article/
 * entry-content chain (all confirmed 0 — verified live, including with
 * the sticky header forced to position:static, which didn't change
 * it either). No cleaner rule found; negative margin closes it. */
.entry-content > .pb-hero-centered:first-child {
	margin-top: -22px;
}

/* On a plain page template (no .front-page/.verkooppunten-page-wrap/
 * etc. class), .site-main keeps its default 2rem padding on every
 * side — margin-top:0 above only cancels the hero's OWN margin, it
 * can't reach an ancestor's padding. Zero just that top padding when
 * this full-bleed hero is the very first thing in the content, same
 * effect as the page-template override list already does for those
 * specific templates, without touching spacing for any other content. */
.site-main:has(> article > .entry-content > .pb-hero-centered:first-child),
.site-main:has(> .entry-content > .pb-hero-centered:first-child),
.site-main:has(> article > .entry-content > .hero:first-child),
.site-main:has(> .entry-content > .hero:first-child),
.site-main:has(> article > .entry-content > .pb-contact:first-child),
.site-main:has(> .entry-content > .pb-contact:first-child) {
	padding-top: 0;
}

.pb-hero-centered__inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	max-width: 640px;
	margin: 0 auto;
	padding-top: var(--pb-hc-padding-top, 96px);
	padding-bottom: var(--pb-hc-padding-bottom, 96px);
	padding-left: 24px;
	padding-right: 24px;
	text-align: center;
}

/* Mobile-only cap on the section's own top/bottom padding (shared,
 * responsive — same technique already used by .pb-newsletter's own
 * <=767px override): per-instance paddingTop/paddingBottom (px, set
 * via the block's Inspector) is authored for desktop framing and can
 * be 200-300px+, which was never reduced at narrower widths, leaving
 * an oversized gap before the next block on mobile. Overrides the
 * custom-property value entirely at this breakpoint; tablet (768px+)
 * and desktop keep the instance's own value unchanged. */
@media (max-width: 767.98px) {
	.pb-hero-centered__inner {
		padding-top: clamp(48px, 12vw, 96px);
		padding-bottom: clamp(48px, 12vw, 96px);
	}
}

/* ---- Responsive visibility (every pb-hc-* child) ---- */
@media (min-width: 1024px) {
	.pb-hc-hide-desktop { display: none !important; }
}
@media (min-width: 768px) and (max-width: 1023.98px) {
	.pb-hc-hide-tablet { display: none !important; }
}
@media (max-width: 767.98px) {
	.pb-hc-hide-mobile { display: none !important; }
}

/* ---- Optional entrance animation (every pb-hc-* child) ----
 * Same timing/easing/fill measured from the live paperbrain.store hero
 * (figure.has-animation.animate): 1s, ease, 0s delay, forwards, single
 * run, no scale/rotation. The reference's own translate3d(-100%,0,0)
 * is relative to the ELEMENT's own width, which only clears the hero
 * block, not the viewport, for a small centered image — using
 * -100vw here instead guarantees a fully off-screen start regardless
 * of the image's width or position, while the end state (transform:
 * none) — i.e. the final resting position — is unchanged. */
@keyframes pb-hc-fade-in-left {
	0% { opacity: 0; transform: translate3d(-100vw, 0, 0); }
	100% { opacity: 1; transform: none; }
}

.pb-hc-animate {
	animation: pb-hc-fade-in-left 1s ease 0s 1 forwards;
}

@media (prefers-reduced-motion: reduce) {
	.pb-hc-animate {
		animation: none;
	}
}

/* ---- Child: Background Pattern — the wrapper is always full-width/
 * full-height at fixed position/z-index; opacity is applied ONLY to
 * the ::before pseudo-element carrying the actual pattern image, so
 * the opacity control can never affect the wrapper's own size,
 * position, scale, section background or any sibling content. */
/* Full-bleed regardless of DOM nesting: this child renders inside
 * .pb-hero-centered__inner (max-width:640px, its own positioning
 * context), so a plain `inset:0` would constrain the pattern to that
 * 640px content column instead of the viewport. Same self-contained
 * left:50%/-50vw breakout used by .hero/.pb-hero-split/.pb-image-text
 * etc. — top/bottom stay inset:0 (still sized to the hero's own
 * content height via its nearest positioned ancestor); only the
 * horizontal span is made viewport-relative. */
/* Anchored to .pb-hero-centered (the section — see __inner above,
 * which is no longer `position:relative` for exactly this reason): a
 * plain block container's auto height reliably includes ALL of
 * __inner's content, CTA included, unlike stretching against an
 * auto-height flex container. z-index MUST be negative, not 0/auto —
 * per CSS painting order, static in-flow content (logo/heading/body/
 * CTA) paints before z-index:0/auto positioned siblings, so a
 * non-negative z-index here would paint the pattern ON TOP of the
 * content instead of behind it. */
.pb-hc-background {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	z-index: -1;
	pointer-events: none;
}

.pb-hc-background::before {
	content: "";
	position: absolute;
	inset: 0;
	background-image: var(--phc-bg-image, none);
	background-repeat: no-repeat;
	background-size: var(--phc-bg-size, cover);
	background-position: var(--phc-bg-position, center);
	background-attachment: fixed;
	opacity: var(--phc-bg-opacity, 0.4);
}

@media (max-width: 781.98px) {
	/* background-attachment:fixed is unreliable on mobile browsers
	 * (iOS Safari in particular) — falls back to normal scroll there. */
	.pb-hc-background::before {
		background-attachment: scroll;
	}
}

/* ---- Child: Logo ---- */
.pb-hc-logo {
	margin: 0 0 2rem;
}

.pb-hc-logo__image {
	display: block;
	height: auto;
	margin: 0 auto;
}

/* ---- Child: Subtitle (16px/400 — PB "body" type token, matches the
 * live reference exactly) ---- */
.pb-hc-subtitle {
	margin: 0 0 1.5rem;
}

.pb-hc-subtitle__text {
	margin: 0;
	color: #6b6b6b;
}

/* ---- Child: Floating Product Image ---- */
.pb-hc-image {
	display: flex;
	align-items: center;
	justify-content: center;
	margin: 0 0 1.5rem;
}

.pb-hc-image__img {
	display: block;
	width: var(--phc-image-width, 267px);
	max-width: 100%;
	height: auto;
	border-radius: var(--phc-image-radius, 0px);
}

/* Soft, floating drop-shadow beneath the image — same technique as
 * pb/hero-image-split's own has-shadow (drop-shadow follows the
 * image's own alpha silhouette rather than a hard-edged box shadow). */
.pb-hc-image.has-shadow .pb-hc-image__img {
	filter: drop-shadow(0 30px 40px rgba(20, 20, 30, 0.25)) drop-shadow(0 8px 12px rgba(20, 20, 30, 0.14));
}

/* ---- Child: Heading — PB typography system, own margin only ---- */
.pb-hc-heading {
	margin: 0 0 1rem;
}

/* Regular by default (design-system's .pb-type--h1 is 800 by default,
 * shared site-wide — scoped here only, so other headings keep their
 * bold default). Doesn't touch .pb-type--h1 strong{font-weight:700}
 * (design-system.css): that rule targets <strong> directly, which
 * always wins over this inherited weight for manually bolded words. */
.pb-hc-heading__text {
	margin: 0;
	font-weight: 400 !important;
}

/* Desktop-only override: Hero Centered's own heading size — scoped to
 * body.page-id-363 (https://pb.local/zakelijk/retail/) ONLY. This was
 * briefly sitewide (any .pb-hero-centered instance, e.g. the homepage
 * hero) — narrowed back to the one page it was actually requested for
 * (2026-07-23). Same 1024px desktop breakpoint as .pb-type--h1's own
 * rule (design-system.css) so mobile/tablet keep that rule's existing
 * sizes untouched. !important needed because design-system.css's
 * sitewide `h1{…!important}` reset (real <h1> element, same issue
 * already documented for h2 above at .pb-reviews-carousel__title.pb-
 * type--h2) otherwise wins regardless of this rule's higher class
 * specificity. */
@media (min-width: 1024px) {
	body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 {
		font-size: 65px !important;
		white-space: nowrap;
	}
}

/* Bolded word(s) inside the Retail hero heading (e.g. "bij jou") get an
 * underline that draws in left-to-right on page load — currentColor so
 * it matches whatever textColor this instance uses, not a hardcoded
 * brand color. Scoped to body.page-id-363 — see note above. Reduced-
 * motion: draw is skipped and the underline just shows fully (same
 * no-preference-guard convention as .pb-reviews-carousel-scroll above). */
body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong {
	position: relative;
	display: inline-block;
}

body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -0.06em;
	width: 100%;
	height: 0.07em;
	background-color: currentColor;
	transform: scaleX(0);
	transform-origin: left;
}

@media (prefers-reduced-motion: no-preference) {
	body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong::after {
		animation: pb-hero-centered-underline 0.7s ease-out 0.5s forwards;
	}
}

@media (prefers-reduced-motion: reduce) {
	.pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong::after {
		transform: scaleX(1);
	}
}

@keyframes pb-hero-centered-underline {
	to {
		transform: scaleX(1);
	}
}

/* Coachingstool hero heading (body.page-id-968) ONLY, same page-scoped
 * pattern as page-id-363 above — .pb-hero-centered__inner's shared
 * max-width:640px (every hero-centered instance sitewide) leaves far
 * less usable width on real phones than on desktop, so the same
 * fontSizeOverride px value that fits "Als coach leg jij het
 * fundament." / "PaperBrain® borgt dat resultaat." on one line each at
 * desktop (36px, set via the block's own Inspector) wraps to 3-4 lines
 * on mobile. Smaller mobile-only size measured to keep both halves on
 * one line each (2 lines total) from ~360px phone width up; very old
 * ~320px devices may still wrap to 3. */
@media (max-width: 767.98px) {
	body.page-id-968 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 {
		font-size: 18px !important;
	}
}

/* ---- Child: Body text ---- */
.pb-hc-body {
	margin: 0 0 2rem;
}

.pb-hc-body__text {
	margin: 0 auto;
	/* Same content-width token as .animated-statement__inner (2026-07-17
	 * fix, was a hardcoded 30rem/480px — visibly narrower than the
	 * Animated Statement block directly above it on the same page).
	 * Reusing the var() reference (not copying its 800px fallback as a
	 * new literal) keeps the two permanently in sync if this token is
	 * ever set globally elsewhere. Text-align/font/line-height/spacing
	 * are untouched — only the width changed. */
	max-width: var( --wp--custom--section--content-width, 800px );
}

/* RichText <strong> inside this shared body area (e.g. "Dé coachingstool")
 * was falling through to design-system.css's generic `.pb-type--body
 * strong { font-weight: min(calc(400+100),900) }` = 500 (Medium) — a
 * +100 step over body's own 400 base, too subtle to read as bold (same
 * issue already fixed for .animated-statement__subtitle strong and
 * .pb-image-text__body strong). Same specificity as that rule
 * (class+element); wins on source order since main.css loads after
 * design-system.css. Poppins 700 is already loaded (functions.php's
 * Google Fonts enqueue), so no extra font request is needed. Applies to
 * every pb/hero-centered-body instance sitewide (hero, campaign-card,
 * reviews-carousel, pricing-section reuse it too), current and future
 * content alike. */
.pb-hc-body__text strong {
	font-weight: 700;
}

/* ---- Child: CTA — no scoped override: uses the shared
 * .pb-button--primary Design System component exactly as-is (same
 * border-radius/height/padding/typography/hover/focus/transitions as
 * every other PB CTA on the site), per "reuse existing PB button
 * component/tokens, no duplicate CSS". */
.pb-hc-cta {
	display: flex;
	justify-content: center;
}

/* Space between multiple stacked CTAs (e.g. a primary + secondary
 * button) — only applies when there's more than one, so a single CTA
 * elsewhere is unaffected. */
.pb-hc-cta + .pb-hc-cta {
	margin-top: 1rem;
}

/* Same gap when a CTA is followed by a body text (e.g. "Shop nu" ->
 * "Start vandaag") — neither element has spacing toward the other by
 * default. */
.pb-hc-cta + .pb-hc-body {
	margin-top: 2rem;
}

@media (max-width: 1024px) {
	.pb-hero-centered__inner {
		padding-left: 20px;
		padding-right: 20px;
	}

	.pb-hc-image__img {
		width: min(var(--phc-image-width, 267px), 240px);
	}
}

@media (max-width: 767px) {
	.pb-hero-centered__inner {
		padding-left: 20px;
		padding-right: 20px;
	}

	.pb-hc-logo,
	.pb-hc-subtitle,
	.pb-hc-image {
		margin-bottom: 1.25rem;
	}

	.pb-hc-image__img {
		width: min(var(--phc-image-width, 267px), 200px);
	}
}

/* ---- Core Columns block, image + text (test-blokken page) ----
 * Standard wp:columns/wp:column/wp:image/wp:heading/wp:paragraph —
 * only scoped via this "Additional CSS class" (pb-image-text-block,
 * a normal core block attribute, not a custom block). 64px gap,
 * text column vertically centered against the image. !important is
 * required on align-items: core's own block-library CSS ships
 * `.wp-block-columns{align-items:normal!important}`, which silently
 * beat a plain (non-important) override here regardless of
 * specificity/source order. Mobile/tablet stacking is core's own
 * default Columns behaviour (isStackedOnMobile defaults true) — no
 * extra CSS needed for that. Heading/paragraph typography and the
 * image's own border-radius/width come from the block markup itself
 * (theme.json's plain h2/p element rules, and the image's inline
 * style), not from anything here. */
.pb-image-text-block {
	align-items: center !important;
	gap: 64px;
}

/* Border-radius only, via external CSS — the image's own inline
 * style (block markup) must stay exactly what core's Image block
 * save() produces from its attributes, or the block shows as invalid
 * in the editor. No width/max-width here: the native resize/width
 * controls (which write their own inline width/aspect-ratio via the
 * block's own attributes) stay in full control of sizing. */
.pb-image-text-block .wp-block-image img {
	border-radius: 16px;
}

/* The right column's own content (heading + both paragraphs) stays
 * one centered group, not stretched/spread — column-level
 * align-items:center above already centers the column as a whole
 * against the taller image; this keeps its children hugging each
 * other rather than being stretched to the column's full height. */
.pb-image-text-block > .wp-block-column:last-child {
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.gm-ui-hover-effect {
	display: none !important;
}

/* pb/image-text — reusable two-column image/text block. All spacing
 * driven by the --pit-* custom properties the render_callback/edit.js
 * set inline per instance (ratio, gap, vertical align, padding,
 * radius); typography/button classes come from the shared PB
 * component library (pb_typography_class()/pb_button_class()), never
 * hardcoded here. Column order in the markup already matches the
 * chosen image-left/text-left layout, so mobile stacking below just
 * follows natural source order — no responsive `order` overrides
 * needed. */
.pb-image-text {
	box-sizing: border-box;
	width: 100%;
	max-width: var(--pit-max-width, none);
	margin-left: auto;
	margin-right: auto;
	padding: var(--pit-padding, var(--wp--preset--spacing--section)) clamp(1.25rem, 4vw, 3rem);
}

/* When two consecutive sections share the same bgImage (e.g. PB
 * Campaign Banner -> PB Image Text -> PB Hero Centered all using the
 * same backdrop, see .pb-hc-background::before), fixed attachment
 * makes it one continuous image behind all of them instead of each
 * section re-rendering its own independently-cropped copy — that
 * mismatch at the shared edge is what showed as a visible seam/line. */
.pb-image-text,
.pb-campaign-banner {
	background-attachment: fixed;
}

@media (max-width: 781.98px) {
	.pb-image-text,
	.pb-campaign-banner {
		background-attachment: scroll;
	}
}

/* Self-contained full/wide breakout — same established technique as
 * .hero/.pb-hero-split/.pb-hero-centered/.pb-hero-image-split (this
 * theme has no generic .alignfull/.alignwide CSS of its own). Without
 * this, the block only appeared full-width on the homepage because
 * .site-main.front-page blankets every block with max-width:none at
 * the page-template level — on any other page (normal 800px content
 * column) the block fell back to that narrow width instead. */
.pb-image-text.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.pb-image-text.alignwide {
	max-width: 1200px;
}

.pb-image-text__grid {
	display: grid;
	width: 100%;
	grid-template-columns: var(--pit-columns, 1fr 1fr);
	align-items: var(--pit-align, center);
	gap: var(--pit-gap, var(--wp--preset--spacing--lg));
}

.pb-image-text__media {
	min-width: 0;
}

.pb-image-text__image {
	display: block;
	width: var(--pit-image-width, 100%);
	height: var(--pit-image-height, auto);
	max-width: none;
}

.pb-image-text__content {
	min-width: 0;
	width: var(--pit-content-width, auto);
	height: var(--pit-content-height, auto);
}

.pb-image-text__eyebrow {
	display: block;
	margin: 0 0 0.5rem;
}

/* pb/image-text-compact — grid/columns are deliberately UNCHANGED here
 * (2026-07-17 correction): a previous attempt shrank the image
 * column's own track (max-content), which also squeezed the text
 * column onto whatever was left over — visibly broken (text wrapping
 * one word per line). The grid keeps the exact shared .pb-image-
 * text__grid behaviour (grid-template-columns: var(--pit-columns, 1fr
 * 1fr), same as plain pb/image-text) — column widths, gap, padding,
 * responsive behaviour all untouched. Only the IMAGE's own render size
 * is overridden: .pb-image-text__media becomes a flex box that fills
 * its (unchanged-width) column and centers a smaller image inside it,
 * both axes; .pb-image-text__image caps at 1000x1380 (or the column's
 * own width if narrower — min(1000px,100%) — so it's never wider than
 * its column either), object-fit:contain so it's never stretched/
 * cropped, `!important` needed to win over the width/height inline
 * style pb/image-text-image's own render always sets (its normal,
 * correct behaviour for every other caller of that shared component;
 * this is the one deliberate override for this specific block). White
 * space around the (now smaller) image inside its column is expected.
 * Remaining grid-column rules below are editor-canvas-only fallback:
 * frontend (inc/image-text-compact-block.php) nests Image/Eyebrow/
 * Heading/Paragraph/CTA into real .pb-image-text__media/__content
 * wrappers (2 direct grid children, same as plain pb/image-text) so
 * they're inert there; on canvas, Gutenberg's single InnerBlocks
 * region renders all five as flat DOM siblings instead (can't wrap
 * only some of one region's own children) — placed into the same two
 * columns by class here, same editor-fallback pattern already used for
 * pb/goal-step. */
.pb-image-text-compact .pb-image-text__grid {
	grid-auto-rows: min-content;
}

.pb-image-text-compact .pb-image-text__media {
	display: flex;
	align-items: center;
	justify-content: center;
	align-self: stretch;
}

/* No !important, no width/height override here (2026-07-17 fix): that
 * had made pb/image-text-image's own Breedte/Hoogte Inspector controls
 * dead — the inline width/height they set was always discarded. max-
 * width/max-height only add a ceiling (higher specificity than the
 * shared .pb-image-text__image's own max-width:none is enough to win,
 * no !important needed) — an explicit Breedte/Hoogte still applies
 * exactly as set, just never exceeds 1000x1380. object-fit:contain
 * here is only a fallback for when objectFit isn't explicitly set
 * (the inline style, when present, already wins on its own). */
.pb-image-text-compact .pb-image-text__image {
	max-width: min(1000px, 100%);
	max-height: 1380px;
	object-fit: contain;
}

.pb-image-text-compact .pb-image-text__grid > .pb-image-text__media {
	grid-column: 1;
	grid-row: 1 / -1;
}

.pb-image-text-compact .pb-image-text__grid > .pb-image-text__eyebrow,
.pb-image-text-compact .pb-image-text__grid > .pb-image-text__heading,
.pb-image-text-compact .pb-image-text__grid > .pb-image-text__body-text,
.pb-image-text-compact .pb-image-text__grid > .wp-block-paragraph,
.pb-image-text-compact .pb-image-text__grid > .wp-block-list,
.pb-image-text-compact .pb-image-text__grid > .pb-image-text__actions {
	grid-column: 2;
}

.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .pb-image-text__media {
	grid-column: 2;
}

.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .pb-image-text__eyebrow,
.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .pb-image-text__heading,
.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .pb-image-text__body-text,
.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .wp-block-paragraph,
.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .wp-block-list,
.pb-image-text-compact.pb-image-text-compact--text-left .pb-image-text__grid > .pb-image-text__actions {
	grid-column: 1;
}

/* Regular by default so RichText's own bold toggle (core/bold,
 * blocks/image-text/edit.js -> window.pbRichHeading) actually has
 * something to contrast against — the "Visuele stijl" token
 * (.pb-type--h1..h6/display-*, design-system.css) sets font-weight as
 * high as 800 unconditionally, which made every heading in this block
 * render uniformly bold regardless of which words the editor actually
 * marked bold/not-bold. Scoped to this block's own heading class only
 * (not the shared .pb-type--* tokens themselves, which other blocks
 * still rely on being bold-by-default) — same "regular base, <strong>
 * stands out" pattern as .animated-statement__text/strong. !important
 * is required here: headingLevel renders a real semantic tag (h1-h6),
 * and design-system.css's own bare h1/h2/h3.../h6 rules already set
 * font-weight with !important (same precedent as this session's other
 * heading-weight overrides), which would otherwise win regardless of
 * this rule's class-based specificity/source order. */
.pb-image-text__heading {
	margin: 0 0 1rem;
	font-weight: 400 !important;
}

.pb-image-text__heading strong {
	font-weight: 700;
}

/* Paragraph/list elements inside .pb-image-text__body have their own
 * explicit color from the theme's global typography styles, which
 * otherwise wins over the inherited color set on this wrapper when a
 * custom textColor is used (e.g. on a dark background) — same trap as
 * heading color elsewhere in this theme. */
.pb-image-text__body > * {
	color: inherit;
}

.pb-image-text__body > :first-child {
	margin-top: 0;
}

.pb-image-text__body > :last-child {
	margin-bottom: 0;
}

/* RichText/core <strong> inside this shared body area (e.g. "PaperBrain®
 * kan dat wel.") was falling through to design-system.css's generic
 * `.pb-type--body strong { font-weight: min(calc(400+100),900) }` = 500
 * (Medium) — a +100 step over body's own 400 base, too subtle to read
 * as bold (same issue already fixed for .animated-statement__subtitle
 * strong). Same specificity as that rule (class+element); wins on
 * source order since main.css loads after design-system.css. Poppins
 * 700 is already loaded (functions.php's Google Fonts enqueue), so no
 * extra font request is needed. Applies to every pb/image-text (and
 * pb/goal-step, which reuses this exact body wrapper) instance
 * sitewide, current and future content alike. */
.pb-image-text__body strong {
	font-weight: 700;
}

.pb-image-text__actions {
	margin-top: 1.5rem;
}

/* CTA as a direct child of the shared Text Group body (pb/campaign-
 * banner-v2's Heading/Paragraph/CTA nesting, and any other .pb-button
 * placed directly here) — .pb-button itself carries no margin, so it
 * otherwise sat flush against the paragraph above it. Same 1.5rem gap
 * .pb-image-text__actions already uses elsewhere for this exact
 * purpose, reused instead of a new value. */
.pb-image-text__body > .pb-button {
	margin-top: 1.5rem;
}

@media (max-width: 781.98px) {
	.pb-image-text__grid {
		grid-template-columns: 1fr;
	}
}


/* ==========================================================================
 * pb/goal-steps + pb/goal-step — numbered "how it works" section
 * (eyebrow/title/subtitle header + reorderable steps). Built for the
 * /doelen-bereiken/ page clone; see inc/goal-steps-block.php.
 * ========================================================================*/
.goal-steps {
	padding: 5rem 1.25rem;
}

.goal-steps.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.goal-steps__header {
	max-width: 640px;
	margin: 0 auto 4rem;
	text-align: center;
}

.goal-steps__eyebrow {
	display: block;
	margin-bottom: 0.75rem;
	font-size: 0.8rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--wp--preset--color--primary, #6848f4);
}

.goal-steps__title {
	margin: 0 0 0.75rem;
}

.goal-steps__subtitle {
	margin: 0;
	color: rgba(17, 17, 17, 0.7);
}

.goal-steps__list {
	max-width: 1100px;
	margin: 0 auto;
	counter-reset: goal-step;
}

.goal-step {
	counter-increment: goal-step;
	position: relative;
	display: grid;
	grid-template-columns: 1fr 1fr;
	/* --goal-step-* custom properties are set inline per instance from
	 * the block's own Layout/Vormgeving Inspector controls (edit.js/
	 * goal-steps-block.php) — same "attribute drives the var, CSS
	 * supplies the fallback" pattern as --pit-* on pb/image-text, so an
	 * instance with none of these set keeps today's exact appearance.
	 * Legacy 2-child steps default to "center" (their original,
	 * unchanged behaviour); the new wrapper-based structure overrides
	 * this to "stretch" below — see that rule for why. */
	align-items: var(--goal-step-align, center);
	margin-top: var(--goal-step-margin, 0);
	margin-bottom: var(--goal-step-margin, 0);
	column-gap: var(--goal-step-gap, 3rem);
	padding: var(--goal-step-padding, 3rem) 0;
}


/* New InnerBlocks structure: pb/image-text-image renders in column 1;
 * every other child (pb/image-text-heading, pb/image-text-paragraph,
 * core/paragraph/core/list — whatever's actually in InnerBlocks) is
 * concatenated in real List View order inside one PHP-rendered
 * .pb-image-text__body wrapper (inc/goal-steps-block.php) — no fixed
 * heading-then-paragraph order, no per-child CSS grid-row placement
 * needed. The wrapper is layout-only (parent-controlled), not a
 * Gutenberg block itself — Image/Heading/Paragraph stay three separate
 * List View layers. A legacy step (.goal-step__media/__body, no
 * pb-image-text__* classes) is unaffected. */
.goal-step > .pb-image-text__media {
	grid-column: 1;
	grid-row: 1;
}

.goal-step > .pb-image-text__body {
	grid-column: 2;
	/* Explicit grid-row (both here and on __media above) — without it,
	 * CSS Grid's row-flow auto-placement cursor advances past column 2
	 * once __media is placed there (goal-step--image-right swaps
	 * __media to column 2), and since __media renders before __body in
	 * source order, __body's column 1 then falls "behind" that cursor
	 * and gets pushed onto an entirely new row instead of sharing row 1
	 * — the stacked/skewed layout on every image-right step. Pinning
	 * both to row 1 makes column order irrelevant to row placement. */
	grid-row: 1;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.25rem;
}

/* "stretch" (not "center") only when the wrapper is present, so the row
 * is exactly as tall as the image and the flexbox above can center
 * heading/paragraph as a tight-packed unit within that height — plain
 * Grid item "center" alone can't do that without either inflating the
 * row tracks (old bug: huge heading↔paragraph gap) or losing centering
 * against the image. Higher specificity than the base rule above, so
 * --goal-step-align still wins when a user explicitly sets it. */
.goal-step:has(> .pb-image-text__body) {
	align-items: var(--goal-step-align, stretch);
}

.goal-step > .pb-image-text__body > .pb-image-text__heading {
	margin-bottom: 0;
}

.goal-step > .pb-image-text__body > .pb-image-text__body-text,
.goal-step > .pb-image-text__body > .wp-block-paragraph {
	margin-top: 0;
}

/* Alternating media/body side per step — real sibling position
 * (:nth-child), same established pattern as pb/campaign-grid-item's
 * own alternating background (main.css). */
.goal-step:nth-child(even) .goal-step__media {
	order: 2;
}

/* Explicit per-step image position (imageLayout) — wins over the
 * automatic :nth-child alternation above when a step sets it directly. */
.goal-step.goal-step--image-left .goal-step__media {
	order: 0;
}

.goal-step.goal-step--image-right .goal-step__media {
	order: 2;
}

/* Same image-right override for the new flat InnerBlocks structure —
 * grid-column (not order) drives placement there. */
.goal-step.goal-step--image-right > .pb-image-text__media {
	grid-column: 2;
}

.goal-step.goal-step--image-right > .pb-image-text__body {
	grid-column: 1;
}

/* Editor-canvas fallback only: a single InnerBlocks region always
 * renders every child as a flat DOM sibling (Gutenberg can't wrap only
 * some of one region's own children in an extra element), so
 * .pb-image-text__body above never exists on canvas — only in the
 * PHP-rendered frontend markup. Placing heading/paragraph by class
 * (not position) still respects List View order either way; the row
 * tracks here just keep them tight on canvas too so editing stays
 * usable. Exact against-image vertical centering is the one visual
 * detail that can differ from the (correct) frontend — an inherent
 * single-InnerBlocks-region limitation, not a layout bug. */
.goal-step > .pb-image-text__heading,
.goal-step > .pb-image-text__body-text,
.goal-step > .wp-block-paragraph,
.goal-step > .wp-block-list {
	grid-column: 2;
}

.goal-step:has(> .pb-image-text__heading, > .pb-image-text__body-text, > .wp-block-paragraph, > .wp-block-list) {
	grid-template-rows: min-content minmax(min-content, 1fr);
}

.goal-step:has(> .pb-image-text__heading, > .pb-image-text__body-text, > .wp-block-paragraph, > .wp-block-list) > .pb-image-text__media {
	grid-row: 1 / -1;
	align-self: start;
}

.goal-step.goal-step--image-right > .pb-image-text__heading,
.goal-step.goal-step--image-right > .pb-image-text__body-text,
.goal-step.goal-step--image-right > .wp-block-paragraph,
.goal-step.goal-step--image-right > .wp-block-list {
	grid-column: 1;
}

.goal-step__media {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 14rem;
}

.goal-step__image,
.goal-step .pb-image-text__image {
	width: 85%;
	max-width: 100%;
	height: auto;
	border-radius: 0.5rem;
}

.goal-step__annotation {
	position: absolute;
	bottom: -0.5rem;
	right: 10%;
	max-width: 12rem;
	margin: 0;
	padding: 0.5rem 0.75rem;
	font-size: 0.8rem;
	font-weight: 600;
	background: #fff;
	border-radius: 0.5rem;
	box-shadow: 0 6px 16px rgba(17, 17, 17, 0.12);
}

.goal-step__annotation:empty {
	display: none;
}

.goal-step__body {
	position: relative;
}

.goal-step__title {
	margin: 0 0 1rem;
  position: relative;
  padding-left: 2rem;
}

.goal-step__title::before {
	content: counter(goal-step) ".";
	font-weight: 800;
  position: absolute;
  left: 0;
  top: 0;
}

/* 2026-07-24: partnerpagina-2 (page-id-2048) specifically does not want the step number - scoped to this page only, other pages using pb/goal-steps keep it. */
.page-id-2048 .goal-step__title {
  padding-left: 0;
}

.page-id-2048 .goal-step__title::before {
  display: none;
}

.goal-step__text {
	color: rgba(17, 17, 17, 0.75);
}

.goal-step__text p {
	margin: 0 0 1rem;
}

.goal-step__text p:last-child {
	margin-bottom: 0;
}

@media (max-width: 781.98px) {
	.goal-step,
	.goal-step:nth-child(even) .goal-step__media {
		grid-template-columns: 1fr;
		order: 0;
	}

	/* display:block turns the grid into a plain stacked column, so
	 * every grid-column/grid-row placement above (image side, .pb-
	 * image-text__body's flex centering) stops applying — children
	 * simply stack in real DOM/source order instead, which now always
	 * matches List View order (inc/goal-steps-block.php renders
	 * children in true $block->inner_blocks sequence, image included,
	 * not "image always first"). imageLayout (left/right) is a
	 * desktop-only concern for exactly this reason — it doesn't
	 * override List View order on mobile. */
	.goal-step {
		display: block;
	}

	.goal-step__media,
	.goal-step > .pb-image-text__media,
	.goal-step > .pb-image-text__body {
		margin-bottom: 1.5rem;
	}

	.goal-step > .pb-image-text__body:last-child,
	.goal-step > .pb-image-text__media:last-child {
		margin-bottom: 0;
	}
}

/* .doelen-goals-section (2026-07-15) — the "Waar wil jij voor gaan?"
 * dark section on the /doelen-bereiken/ page clone is a native
 * core/group, not a pb/* custom block, so it has no self-contained
 * breakout of its own (this theme has no generic .alignfull rule —
 * see the near-identical note on every pb/* full-bleed block; core
 * blocks rely on that, which this theme deliberately doesn't provide).
 * Same established left:50%/-50vw technique, scoped to this one
 * instance via its own "Additional CSS class" instead of a blanket
 * .wp-block-group.alignfull rule (which would full-bleed every group
 * block sitewide). */
.doelen-goals-section.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* .doelen-goals-grid (2026-07-15) — .campaign-grid's own unconditional
 * white background (main.css, ".campaign-grid { background:#fff; }")
 * would otherwise paint over .doelen-goals-section's dark pattern
 * background behind it. Scoped to this one instance only — every other
 * pb/campaign-grid usage keeps its own white background unchanged. */
.doelen-goals-grid {
	background: transparent;
}

/* .doelen-hero-centered (2026-07-15) — pb/hero's own default is a
 * bottom-left content stack; the /doelen-bereiken/ source hero is
 * centered instead. Scoped to this one instance's own "Additional CSS
 * class" (not pb/hero's global default, which other instances rely on
 * staying left-aligned) — same technique already used for .hero--
 * triptych's own centered variant. */
.doelen-hero-centered {
	align-items: center;
	justify-content: center;
	text-align: center;
}

.doelen-hero-centered .hero__content {
	align-items: center;
}

.doelen-hero-centered .hero__actions {
	/* align-items (not just justify-content): .hero__actions is
	 * flex-direction:column on mobile (<480px), where the main axis is
	 * vertical — justify-content only centers that axis, leaving
	 * align-items:flex-start (pb/hero's own default) still pinning the
	 * button to the left. align-items is the cross-axis property that
	 * actually centers it horizontally in the column layout; at >=480px
	 * (flex-direction:row) justify-content:center is what centers it
	 * there instead — both kept so either axis is covered. */
	justify-content: center;
	align-items: center;
}

/* .doelen-midcta-section (2026-07-15) — the source page has a centered
 * "Start vandaag" CTA button directly after the 5-steps section, on the
 * same light background, before the closing dark statement. This theme
 * has no generic .alignfull rule, so this one-off group needs its own
 * scoped breakout (same pattern as .doelen-goals-section above). */
.doelen-midcta-section.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
	background-color: #f7f7f9;
}

/* Scroll-reveal for "Waar wil jij voor gaan?" cards + the 5 "Hoe
 * PaperBrain werkt" steps (2026-07-15) — the source site (Blockify's
 * own block-animation feature) fades each card/step in from the side
 * as it scrolls into view; this reproduces that using the theme's own
 * existing reveal mechanism instead (same IntersectionObserver +
 * "is-revealed" class-flip pattern as pb/animated-statement — see
 * main.js). Scoped to .doelen-goals-grid specifically so ordinary
 * pb/campaign-grid usage elsewhere keeps its normal, unanimated
 * appearance; .goal-step is already page-specific so needs no extra
 * scoping. Source alternates each card's slide direction per-card
 * (left/left/right/left) and always slides each step's text column in
 * from the right while leaving its image column static — matched here
 * 1:1 rather than picking a single generic direction for all. */
.doelen-goals-grid .campaign-card {
	opacity: 0;
}

.doelen-goals-grid .campaign-card.is-revealed {
	animation: pb-fade-in-right 0.8s ease forwards;
}

.doelen-goals-grid .campaign-card:nth-child(1).is-revealed,
.doelen-goals-grid .campaign-card:nth-child(2).is-revealed,
.doelen-goals-grid .campaign-card:nth-child(4).is-revealed {
	animation-name: pb-fade-in-left;
}

/* Goal-step reveal — same IntersectionObserver + "is-revealed" class-
 * flip mechanism as pb/animated-statement/.campaign-card above (see
 * main.js), reused as-is, no separate animation logic. Root-cause fix
 * (previously the text was permanently invisible/never animated on the
 * live frontend): every other opacity:0-by-default rule in this file
 * is gated behind html.js (added synchronously in <head>, see
 * pb_print_js_class_script() in functions.php) so content only ever
 * hides when JS has actually run — .goal-step__body/.pb-image-text__body
 * were missing that gate, so if the observer never fired (or hadn't
 * run yet), the heading/paragraph stayed opacity:0 forever. Animates
 * the whole text block as one unit (not heading/paragraph separately)
 * — identical grouping to the legacy .goal-step__body, and what keeps
 * them tight together while sliding in. Per-instance on/off + direction
 * read data-goal-step-animate(-direction) (block.json animationEnabled/
 * animationDirection, default "on"/"right" — unset instances keep
 * today's exact behaviour). */
html.js .goal-step[data-goal-step-animate="on"] > .goal-step__body,
html.js .goal-step[data-goal-step-animate="on"] > .pb-image-text__body,
html.js .pb-image-text-compact[data-goal-step-animate="on"] .pb-image-text__content {
	opacity: 0;
}

html.js .goal-step[data-goal-step-animate="on"].is-revealed > .goal-step__body,
html.js .goal-step[data-goal-step-animate="on"].is-revealed > .pb-image-text__body,
html.js .pb-image-text-compact[data-goal-step-animate="on"].is-revealed .pb-image-text__content {
	animation: pb-fade-in-right var(--pb-reveal-duration, 0.8s) ease forwards;
}

html.js .goal-step[data-goal-step-animate-direction="left"].is-revealed > .goal-step__body,
html.js .goal-step[data-goal-step-animate-direction="left"].is-revealed > .pb-image-text__body,
html.js .pb-image-text-compact[data-goal-step-animate-direction="left"].is-revealed .pb-image-text__content {
	animation-name: pb-fade-in-left;
}

/* Image slide-in — separate on/off toggle from the text animation
 * above (data-goal-step-image-animate, block.json imageAnimationEnabled,
 * default "off" so no existing step changes appearance until explicitly
 * turned on), same reveal mechanism/keyframe reused as-is (pb-fade-in-
 * left — "van links naar rechts"), no separate animation logic. */
html.js .goal-step[data-goal-step-image-animate="on"] > .goal-step__media,
html.js .goal-step[data-goal-step-image-animate="on"] > .pb-image-text__media,
html.js .pb-image-text-compact[data-goal-step-image-animate="on"] .pb-image-text__media {
	opacity: 0;
}

html.js .goal-step[data-goal-step-image-animate="on"].is-revealed > .goal-step__media,
html.js .goal-step[data-goal-step-image-animate="on"].is-revealed > .pb-image-text__media,
html.js .pb-image-text-compact[data-goal-step-image-animate="on"].is-revealed .pb-image-text__media {
	animation: pb-fade-in-left var(--pb-reveal-duration, 0.8s) ease forwards;
}

@keyframes pb-fade-in-left {
	from {
		opacity: 0;
		transform: translateX(-140px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pb-fade-in-right {
	from {
		opacity: 0;
		transform: translateX(140px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pb-fade-in-top {
	from {
		opacity: 0;
		transform: translateY(-140px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pb-fade-in-bottom {
	from {
		opacity: 0;
		transform: translateY(140px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pb-fade-in {
	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

/* Generic PB scroll-reveal (2026-07-23) — shared by any block that
 * opts in via a `data-pb-reveal` wrapper attribute (see
 * pb_reveal_attrs(), inc/block-helpers.php, and pb-animation-
 * controls.js for the matching editor-side Inspector panel), instead
 * of a new one-off implementation per block. Same IntersectionObserver
 * + "is-revealed" class-flip mechanism as .goal-step/.pb-image-text-
 * compact above (main.js's shared observer now also watches
 * [data-pb-reveal]) — one trigger per page view, same html.js gating
 * convention (content only ever hides once JS has actually run).
 * Direction/duration/delay are per-instance CSS custom properties
 * (--pb-reveal-name/--pb-reveal-duration/--pb-reveal-delay) set inline
 * per element, so one rule covers every direction instead of one
 * selector per direction. */
html.js [data-pb-reveal] {
	opacity: 0;
}

html.js [data-pb-reveal].is-revealed {
	animation: var( --pb-reveal-name, pb-fade-in ) var( --pb-reveal-duration, 0.8s ) ease var( --pb-reveal-delay, 0s ) forwards;
}

@media (prefers-reduced-motion: reduce) {
	.doelen-goals-grid .campaign-card,
	html.js .goal-step[data-goal-step-animate="on"] > .goal-step__body,
	html.js .goal-step[data-goal-step-animate="on"] > .pb-image-text__body,
	html.js .goal-step[data-goal-step-image-animate="on"] > .goal-step__media,
	html.js .goal-step[data-goal-step-image-animate="on"] > .pb-image-text__media,
	html.js .pb-image-text-compact[data-goal-step-animate="on"] .pb-image-text__content,
	html.js .pb-image-text-compact[data-goal-step-image-animate="on"] .pb-image-text__media,
	html.js [data-pb-reveal] {
		opacity: 1;
		animation: none;
	}
}

/* ==========================================================================
 * pb/contact (2026-07-15, unified-card revision) — /contact/ page: ONE
 * black card (eyebrow/heading/intro/form-tabs/ConvertKit forms/social
 * links + image all inside the same container, no visual seam).
 * Mobile-first: single column, content then image; desktop (>900px)
 * becomes two equal-width columns, content left/image right. The
 * parent block's own "Background" attrs (color/image/overlay/radius —
 * --pbc-* custom properties, set inline on .pb-contact by
 * inc/contact-block.php, unchanged) now paint .pb-contact__inner (the
 * single shared card), not .pb-contact__content — moving which
 * selector *reads* those same custom properties is a pure CSS change,
 * nothing in the block.json/edit.js/PHP structure moved. .pb-contact__image
 * no longer has its own border-radius; .pb-contact__inner's own
 * overflow:hidden + border-radius clips both children to one shared
 * rounded shape, and the flex gap between them is 0 so the image sits
 * flush against the content side.
 * ========================================================================*/
.pb-contact {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
	padding: 1.5rem;
	box-sizing: border-box;
	/* Same black as the inner card (var(--pbc-bg-color), already set
	 * inline on this exact element by inc/contact-block.php — reusing
	 * it here, not a second source of truth). Without this, only
	 * .pb-contact__inner (the narrower, rounded card one level in) was
	 * black — this element's own padding is the ring AROUND that card,
	 * which was rendering the page's white background straight through
	 * it. Solid color only (no pattern/overlay here) — those stay on
	 * .pb-contact__inner exactly as before, this is purely the gutter
	 * backdrop so nothing white shows anywhere behind the card. */
	background-color: var( --pbc-bg-color, #111111 );
}

.pb-contact__inner {
	display: flex;
	flex-direction: column;
	gap: 0;
	max-width: 1280px;
	margin: 0 auto;
	background-color: var( --pbc-bg-color, #111111 );
	background-image: var( --pbc-bg-image, none );
	background-position: var( --pbc-bg-position, center );
	background-size: var( --pbc-bg-size, cover );
	border-radius: var( --pbc-radius, 24px );
	position: relative;
	overflow: hidden;
}

/* Overlay tints the pattern/photo background without touching text —
 * a separate ::before layer (not a shorthand background blend) so
 * overlayOpacity can go to 0 (no ::before painted content at all,
 * opacity:0 element) without affecting --pbc-bg-color underneath. Now
 * spans the whole unified card (content + image), not just the text
 * side — content/image both get z-index:1 below to stay above it. */
.pb-contact__inner::before {
	content: "";
	position: absolute;
	inset: 0;
	background-color: var( --pbc-overlay-color, transparent );
	opacity: var( --pbc-overlay-opacity, 0 );
	pointer-events: none;
	z-index: 0;
}

.pb-contact__content {
	position: relative;
	z-index: 1;
	color: var( --pbc-text-color, #111111 );
	padding: var( --pbc-padding-mobile, 20px );
	box-sizing: border-box;
	min-width: 0;
}

.pb-contact__eyebrow {
	margin: 0 0 0.5rem;
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: #ff4d2d;
}

.pb-contact__heading {
	margin: 0 0 0.75rem;
}

.pb-contact__heading-text {
	margin: 0;
	color: var( --pbc-text-color, #111111 );
	font-size: var( --pbc-heading-text-mobile-size, var( --pbc-heading-text-desktop-size, 1.75rem ) );
	line-height: var( --pbc-heading-text-mobile-line-height, var( --pbc-heading-text-desktop-line-height, 1.15 ) );
	font-weight: var( --pbc-heading-text-mobile-weight, var( --pbc-heading-text-desktop-weight, 700 ) );
	letter-spacing: var( --pbc-heading-text-mobile-spacing, var( --pbc-heading-text-desktop-spacing, normal ) );
}

.pb-contact__intro {
	margin: 0 0 1.25rem;
}

.pb-contact__intro-text {
	margin: 0 0 0.75rem;
	color: var( --pbc-text-color, #111111 );
	font-size: var( --pbc-intro-text-mobile-size, var( --pbc-intro-text-desktop-size, 0.9rem ) );
	line-height: var( --pbc-intro-text-mobile-line-height, var( --pbc-intro-text-desktop-line-height, 1.5 ) );
	font-weight: var( --pbc-intro-text-mobile-weight, var( --pbc-intro-text-desktop-weight, 400 ) );
	letter-spacing: var( --pbc-intro-text-mobile-spacing, var( --pbc-intro-text-desktop-spacing, normal ) );
}

.pb-contact__intro-text strong {
	color: var( --pbc-text-color, #111111 );
	font-weight: 700;
}

/* ---- Form tabs (radio-style, matching the reference exactly —
 * a small circle "radio" per tab, not pill/underline tabs). ---- */
.pb-contact__tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 1.25rem;
	margin: 0 0 1.25rem;
}

.pb-contact__tab {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	background: none;
	border: none;
	padding: 0;
	margin: 0;
	cursor: pointer;
	color: color-mix( in srgb, var( --pbc-label-color, #111111 ) 60%, transparent );
	font-size: var( --pbc-tab-label-mobile-size, var( --pbc-tab-label-desktop-size, 0.8rem ) );
	line-height: var( --pbc-tab-label-mobile-line-height, var( --pbc-tab-label-desktop-line-height, 1.4 ) );
	font-weight: var( --pbc-tab-label-mobile-weight, var( --pbc-tab-label-desktop-weight, 400 ) );
	letter-spacing: var( --pbc-tab-label-mobile-spacing, var( --pbc-tab-label-desktop-spacing, normal ) );
}

.pb-contact__tab-dot {
	width: 14px;
	height: 14px;
	border-radius: 50%;
	border: 1.5px solid color-mix( in srgb, var( --pbc-label-color, #111111 ) 40%, transparent );
	flex-shrink: 0;
	position: relative;
	transition: border-color 0.2s ease;
}

.pb-contact__tab-dot::after {
	content: "";
	position: absolute;
	inset: 3px;
	border-radius: 50%;
	background: #6848f4;
	transform: scale( 0 );
	transition: transform 0.2s ease;
}

.pb-contact__tab.is-active {
	color: var( --pbc-label-color, #111111 );
}

.pb-contact__tab.is-active .pb-contact__tab-dot {
	border-color: #6848f4;
}

.pb-contact__tab.is-active .pb-contact__tab-dot::after {
	transform: scale( 1 );
}

.pb-contact__tab:focus-visible {
	outline: 2px solid #6848f4;
	outline-offset: 3px;
	border-radius: 4px;
}

/* ---- Active form / ConvertKit embeds ---- */
.pb-contact__form {
	font-size: var( --pbc-form-text-mobile-size, var( --pbc-form-text-desktop-size, 0.85rem ) );
	line-height: var( --pbc-form-text-mobile-line-height, var( --pbc-form-text-desktop-line-height, 1.4 ) );
	font-weight: var( --pbc-form-text-mobile-weight, var( --pbc-form-text-desktop-weight, 400 ) );
	letter-spacing: var( --pbc-form-text-mobile-spacing, var( --pbc-form-text-desktop-spacing, normal ) );
}

.pb-contact__form-panel[hidden] {
	display: none;
}

.pb-contact__form-embed-note {
	margin: 0;
	padding: 1rem;
	border: 1px dashed color-mix( in srgb, var( --pbc-text-color, #111111 ) 25%, transparent );
	border-radius: 8px;
	color: color-mix( in srgb, var( --pbc-text-color, #111111 ) 50%, transparent );
	font-size: 0.8rem;
}

.pb-contact__form-embed-note--empty {
	color: rgba( 255, 77, 45, 0.8 );
}

/* ConvertKit's own formkit-* classes, restyled to match the dark card
 * (their default form ships light-theme). Selectors are scoped to
 * .pb-contact__form so this never leaks to a ConvertKit form used
 * anywhere else on the site.
 *
 * !important is required throughout this section (2026-07-15,
 * confirmed against a real embed, not guessed): ConvertKit prints a
 * literal `style="color:#000;border-color:#e3e3e3;..."` attribute on
 * every single <input>/<select>, AND ships its own `<style
 * data-uid="...">` block scoped to that exact form instance — both
 * load/apply AFTER this stylesheet and an inline `style` attribute
 * always wins over any external rule regardless of selector
 * specificity, so a plain (non-!important) override here is silently
 * ignored for color/border/background specifically. */
.pb-contact__form .formkit-label,
.pb-contact__form label {
	display: block;
	margin: 0 0 0.35rem;
	color: var( --pbc-label-color, #111111 ) !important;
	font-size: 0.75rem;
}

.pb-contact__form .formkit-input,
.pb-contact__form .formkit-select,
.pb-contact__form input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
.pb-contact__form select,
.pb-contact__form textarea {
	width: 100%;
	background-color: transparent !important;
	background-image: none !important;
	border: none !important;
	border-bottom: 1px solid color-mix( in srgb, var( --pbc-form-text-color, #111111 ) 35%, transparent ) !important;
	border-radius: 0 !important;
	color: var( --pbc-form-text-color, #111111 ) !important;
	padding: 0.6rem 0 !important;
	font-size: inherit;
	font-weight: inherit !important;
	box-sizing: border-box;
	appearance: none;
	-webkit-appearance: none;
}

/* Placeholder is deliberately its own (not color:inherit from the
 * input) — ConvertKit's own stylesheet sets
 * `.formkit-input::placeholder{color:inherit;opacity:.8}`, which would
 * otherwise tie it to --pbc-form-text-color at 80% opacity instead of
 * its own configurable --pbc-placeholder-color token. */
.pb-contact__form .formkit-input::placeholder,
.pb-contact__form input::placeholder,
.pb-contact__form textarea::placeholder {
	color: var( --pbc-placeholder-color, #7A7A7A ) !important;
	opacity: 1 !important;
}

/* Select arrow — ConvertKit draws it as a CSS-triangle ::before on
 * [data-group="dropdown"], colored `#4f4f4f` (dark grey) by default;
 * tied to the same form-text token as the field it belongs to so it
 * stays visible regardless of card background. */
.pb-contact__form [data-group="dropdown"]::before {
	border-color: var( --pbc-form-text-color, #111111 ) transparent transparent transparent !important;
	opacity: 0.7;
}

.pb-contact__form .formkit-input:focus,
.pb-contact__form .formkit-select:focus,
.pb-contact__form input:focus,
.pb-contact__form select:focus,
.pb-contact__form textarea:focus {
	outline: none !important;
	border-bottom-color: #6848f4 !important;
}

/* ---- Field grid — mobile-first single column (spec: "keep mobile
 * stacked in 1 column"); 2-up only from the same 900px desktop
 * breakpoint the rest of the block already switches at, not
 * ConvertKit's own JS-driven min-width breakpoints (their
 * data-stacked/[min-width~="700"] system, which this deliberately
 * bypasses — see .formkit-fields override below). A field wrapping a
 * <textarea> (a "long" field) spans both columns; so does the submit
 * button, which isn't itself a .formkit-field so gets its own rule. */
.pb-contact__form .formkit-fields {
	display: grid !important;
	grid-template-columns: 1fr !important;
	gap: 1rem 1.5rem;
	margin: 0 0 1.25rem;
}

.pb-contact__form .formkit-field:has( textarea ) {
	grid-column: 1 / -1;
}

@media ( min-width: 900px ) {
	.pb-contact__form .formkit-fields {
		grid-template-columns: 1fr 1fr !important;
	}

	/* "Zakelijk" tab only (2026-07-15) — its "Type hier.." field
	 * (fields[vraag]) is a plain <input>, not a <textarea>, so the
	 * :has(textarea) full-width rule above doesn't catch it and it sat
	 * in a single grid cell (half width) instead of matching the
	 * submit button below. Scoped to #pb-contact-panel-zakelijk only —
	 * the "Algemeen" tab has an input with the same fields[vraag] name
	 * and is deliberately left untouched (not part of this request).
	 * Desktop only, matching this media query; the 1-column mobile
	 * grid already renders this field full-width with no rule needed. */
	.pb-contact__form #pb-contact-panel-zakelijk .formkit-field:has( input[name="fields[vraag]"] ) {
		grid-column: 1 / -1;
	}

	/* "Algemeen" tab only (2026-07-15) — reorders that tab's own 2-col
	 * grid to: "Ik heb een vraag over?" (tags[] select) full width,
	 * then Naam / E-mail sharing the next row 50/50, then "Type
	 * hier.." (fields[vraag]) full width again above the (already
	 * full-width) submit button. Scoped to #pb-contact-panel-algemeen
	 * only — doesn't touch Zakelijk's own fields[vraag] rule above, or
	 * Partner worden (different field set entirely, untouched).
	 * Nothing here changes the DOM order or the fields themselves,
	 * just their grid-column — with both the tags[] select and
	 * fields[vraag] explicitly forced to span 1/-1, CSS Grid's default
	 * auto-placement naturally slots Naam/E-mail into the one
	 * remaining 2-up row between them without any rule needed for
	 * those two fields specifically. */
	.pb-contact__form #pb-contact-panel-algemeen .formkit-field:has( select[name="tags[]"] ),
	.pb-contact__form #pb-contact-panel-algemeen .formkit-field:has( input[name="fields[vraag]"] ) {
		grid-column: 1 / -1;
	}
}

/* ---- CTA (ConvertKit submit button) — reuses the site's Primary
 * button (.pb-button--primary, design-system.css) via its own shared
 * --wp--custom--button--primary--* custom properties (2026-07-15),
 * not a second independent button style. Can't put the
 * .pb-button--primary class itself on this element — it's ConvertKit's
 * own generated markup, not ours (no HTML/architecture changes
 * allowed) — so every value below maps one-for-one onto that same
 * token set instead of a hardcoded value, one source of truth either
 * way. Applies identically to all 3 tabs (Algemeen/Zakelijk/Partner
 * worden) since .pb-contact__form is their shared ancestor. Still
 * !important throughout: ConvertKit prints literal inline
 * `style="..."` attributes plus its own scoped stylesheet on this
 * exact element, which is what actually forces that (unique to this
 * embed — .pb-button--primary itself needs none of it elsewhere).
 * No explicit height, same as .pb-button--primary itself — height is
 * padding + line-height driven there too, not a fixed number, so this
 * stays pixel-identical if the Design System tokens ever change.
 * ConvertKit's own stylesheet puts the button's real visual padding on
 * an INNER <span> (button itself is padding:0 by default) — zeroed
 * below so this rule's own padding is what actually shows. width:100%
 * is the one deliberate difference from .pb-button--primary (which is
 * inline-flex/content-sized) — this form's CTA has always needed to
 * span the full field width. */
.pb-contact__form .formkit-fields > .formkit-submit,
.pb-contact__form .formkit-submit,
.pb-contact__form button[type="submit"],
.pb-contact__form input[type="submit"] {
	grid-column: 1 / -1;
	display: flex !important;
	align-items: center;
	justify-content: center;
	width: 100%;
	box-sizing: border-box;
	margin: 0;
	cursor: pointer;
	font-family: var( --wp--custom--button--primary--font, var( --wp--preset--font-family--theme-button ) ) !important;
	background-color: var( --wp--custom--button--primary--background, #111111 ) !important;
	color: var( --wp--custom--button--primary--text, #ffffff ) !important;
	border: var( --wp--custom--button--primary--border-width, 1.5px ) solid var( --wp--custom--button--primary--border-color, #111111 ) !important;
	border-radius: var( --wp--custom--button--primary--radius, 10px ) !important;
	height: 46px !important;
	padding: 0 var( --wp--custom--button--primary--padding-x, 2.25rem ) !important;
	font-size: var( --wp--custom--button--primary--font-size, 0.75rem ) !important;
	font-weight: var( --wp--custom--button--primary--font-weight, 700 ) !important;
	letter-spacing: var( --wp--custom--button--primary--letter-spacing, 0.08em ) !important;
	text-transform: var( --wp--custom--typography--button--transform, none );
	transition: background-color var( --wp--custom--button--primary--transition, 0.25s cubic-bezier( 0.4, 0, 0.2, 1 ) ), color var( --wp--custom--button--primary--transition, 0.25s cubic-bezier( 0.4, 0, 0.2, 1 ) ), transform var( --wp--custom--button--primary--transition, 0.25s cubic-bezier( 0.4, 0, 0.2, 1 ) );
}

.pb-contact__form .formkit-submit > span {
	padding: 0 !important;
	color: inherit;
	font-size: inherit;
	font-weight: inherit;
}

.pb-contact__form .formkit-submit:hover,
.pb-contact__form .formkit-submit:focus-visible,
.pb-contact__form button[type="submit"]:hover,
.pb-contact__form input[type="submit"]:hover {
	background-color: var( --wp--custom--button--primary--hover-background, #2b2b2b ) !important;
	color: var( --wp--custom--button--primary--hover-text, #ffffff ) !important;
	transform: translateY( -2px );
	box-shadow: var( --wp--custom--button--primary--shadow, 0 8px 20px rgba( 17, 17, 17, 0.25 ) );
	outline: none;
}

/* ConvertKit "Built with Kit" branding — hidden site-wide inside the
 * Contact block only, on both breakpoints, without leaving a gap. */
.pb-contact .formkit-powered-by-convertkit-container {
	display: none !important;
}

/* ---- Social links ---- */
.pb-contact__social {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	margin-top: 1.5rem;
}

.pb-contact__social-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border-radius: 50%;
	background: color-mix( in srgb, var( --pbc-label-color, #111111 ) 8%, transparent );
	color: var( --pbc-label-color, #111111 );
	transition: background-color 0.2s ease;
}

.pb-contact__social-icon:hover,
.pb-contact__social-icon:focus-visible {
	background: color-mix( in srgb, var( --pbc-label-color, #111111 ) 18%, transparent );
}

.pb-contact__social-icon svg {
	width: 1rem;
	height: 1rem;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.pb-contact__social-icon.is-hidden {
	display: none;
}

/* Mobile only (<900px, matching the block's own desktop breakpoint
 * elsewhere) — center the row and size the icons up ~25%. Desktop is
 * completely untouched: no rule here overrides the base
 * .pb-contact__social/.pb-contact__social-icon values above at
 * >=900px, and gap stays the base 0.6rem at every width (only the
 * button/glyph size and row alignment change here). */
@media ( max-width: 899.98px ) {
	.pb-contact__social {
		justify-content: center;
		/* Matches the same 1.5rem already used for margin-top above (the
		 * gap below the form's CTA button) — was 0 (inherited), leaving
		 * almost no space before .pb-contact__content's own bottom
		 * padding. Mobile-only; desktop keeps margin-bottom:0. */
		margin-bottom: 1.5rem;
	}

	.pb-contact__social-icon {
		width: 2.5rem;
		height: 2.5rem;
	}

	.pb-contact__social-icon svg {
		width: 1.25rem;
		height: 1.25rem;
	}
}

/* ---- Image — flush inside the shared card now, no border-radius of
 * its own (.pb-contact__inner's overflow:hidden + radius clips it). */
.pb-contact__image {
	position: relative;
	z-index: 1;
	height: var( --pbc-image-height-mobile, 360px );
	min-width: 0;
}

.pb-contact__image-img,
.pb-contact__image-placeholder {
	display: block;
	width: 100%;
	height: 100%;
}

.pb-contact__image-placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	background: rgba( 255, 255, 255, 0.06 );
	color: rgba( 255, 255, 255, 0.5 );
}

/* Mobile only (<900px, same breakpoint as the desktop image sizing
 * below) — same --wc-radius-input value (0.65rem / 10.4px,
 * woocommerce.css:41) the desktop image already uses, so mobile's
 * corners match exactly instead of falling back to
 * .pb-contact__inner's larger shared card radius. overflow:hidden
 * added here too, same as desktop, so the image clips to this
 * radius. Only radius/overflow are added — width/height/aspect
 * ratio (var(--pbc-image-height-mobile) above) are untouched, and
 * this block sits below >=900px so it never reaches the desktop
 * rule further down. */
@media ( max-width: 899.98px ) {
	.pb-contact__image {
		border-radius: var( --wc-radius-input, 0.65rem );
		overflow: hidden;
	}

	.pb-contact__image-img {
		border-radius: var( --wc-radius-input, 0.65rem );
	}
}

/* ---- Desktop: two equal columns, content left / image right ---- */
@media ( min-width: 900px ) {
	.pb-contact {
		padding: 2.5rem;
	}

	.pb-contact__inner {
		flex-direction: row;
		align-items: stretch;
		gap: 0;
	}

	.pb-contact__content {
		flex: 1 1 50%;
		display: flex;
		flex-direction: column;
	}

	.pb-contact__content {
		padding: var( --pbc-padding-desktop, 40px );
	}

	/* Push the social row down to align its bottom with the image
	 * panel's bottom edge (2026-07-15) — .pb-contact__content is now a
	 * column flex container (above) the same height as
	 * .pb-contact__image (both stretch to the row's height via
	 * .pb-contact__inner's align-items:stretch), so an auto top-margin
	 * on the last child absorbs all the remaining free space below the
	 * form and lands the icons flush with the content box's own
	 * bottom padding edge — level with the image's bottom, with the
	 * same 40px breathing room the content's top padding already
	 * gives the heading. Every other child (eyebrow/heading/intro/
	 * tabs/form) keeps its normal top-to-bottom flow and existing
	 * margins untouched; only .pb-contact__social's own position
	 * changes, not its internal icon size/gap (those rules,
	 * .pb-contact__social/.pb-contact__social-icon above, are
	 * untouched). Mobile is unaffected — this is nested inside the
	 * existing >=900px media query only. */
	.pb-contact__social {
		margin-top: auto;
	}

	.pb-contact__heading-text {
		font-size: var( --pbc-heading-text-desktop-size, 2rem );
		line-height: var( --pbc-heading-text-desktop-line-height, 1.15 );
		font-weight: var( --pbc-heading-text-desktop-weight, 700 );
		letter-spacing: var( --pbc-heading-text-desktop-spacing, normal );
	}

	.pb-contact__intro-text {
		font-size: var( --pbc-intro-text-desktop-size, 0.9rem );
		line-height: var( --pbc-intro-text-desktop-line-height, 1.5 );
		font-weight: var( --pbc-intro-text-desktop-weight, 400 );
		letter-spacing: var( --pbc-intro-text-desktop-spacing, normal );
	}

	.pb-contact__tab {
		font-size: var( --pbc-tab-label-desktop-size, 0.8rem );
		line-height: var( --pbc-tab-label-desktop-line-height, 1.4 );
		font-weight: var( --pbc-tab-label-desktop-weight, 400 );
		letter-spacing: var( --pbc-tab-label-desktop-spacing, normal );
	}

	.pb-contact__form {
		font-size: var( --pbc-form-text-desktop-size, 0.85rem );
		line-height: var( --pbc-form-text-desktop-line-height, 1.4 );
		font-weight: var( --pbc-form-text-desktop-weight, 400 );
		letter-spacing: var( --pbc-form-text-desktop-spacing, normal );
	}

	/* Submit button stays full-width at desktop too, per spec — no
	 * width:auto/min-width override here (there used to be one; removed
	 * 2026-07-15, it contradicted "Submit button full width"). */

	/* Fixed 680x534.1 box per spec (2026-07-15) — not part of the 50%
	 * flex split above (flex:0 0 680px, non-growing/non-shrinking), so
	 * .pb-contact__content (still flex:1 1 50% with its own grow:1)
	 * simply fills whatever width is left in the row; nothing else in
	 * this media query changed. .pb-contact__image-img's existing
	 * width:100%/height:100%/object-fit:cover (unchanged, defined
	 * outside this media query) then fills this fixed box exactly.
	 * No explicit align-self: with a definite cross-size, this item
	 * doesn't stretch, so it's positioned at the row's cross-start
	 * (top) — same top edge as .pb-contact__content already had. */
	.pb-contact__image {
		flex: 0 0 680px;
		width: 680px;
		height: 534.1px;
	}

	/* Match the same radius already used on the mini-cart flyout's
	 * small product thumbnail (2026-07-15) — reusing
	 * --wc-radius-input (0.65rem, woocommerce.css:41), not a second
	 * hardcoded value. All 4 corners on both the container and the
	 * <img> itself (previously the image had no radius of its own; it
	 * only picked up the shared card's outer-corner clip from
	 * .pb-contact__inner's overflow:hidden). overflow:hidden added
	 * here too so the image clips to this radius even though it no
	 * longer matches .pb-contact__inner's own 24px value. Desktop
	 * only — mobile's .pb-contact__image/-img keep their existing,
	 * unradiused styles untouched. */
	.pb-contact__image {
		border-radius: var( --wc-radius-input, 0.65rem );
		overflow: hidden;
	}

	.pb-contact__image-img {
		border-radius: var( --wc-radius-input, 0.65rem );
	}
}

/* No horizontal overflow at any width — belt-and-braces alongside the
 * flex min-width:0 rules above (a flex item's default min-width:auto
 * otherwise refuses to shrink below its content's natural width,
 * e.g. a long unbroken ConvertKit input, which is what actually
 * causes mobile overflow in practice). .pb-contact itself is
 * deliberately excluded here — it's the alignfull breakout element
 * (width:100vw), so capping IT at max-width:100% would resolve
 * against its own parent's narrow content-column width and cancel
 * the breakout entirely (confirmed live: shrank the whole section
 * back down to ~736px instead of full-bleed). */
.pb-contact__inner,
.pb-contact__content,
.pb-contact__image {
	max-width: 100%;
}

/* ==========================================================================
 * pb/pricing-section family — PB Pricing Section/Cards/Card, PB Price,
 * PB USP List/Item, PB CTA Group (inc/pricing-section-block.php).
 * Heading/body/image/button children reuse .pb-hc-heading/.pb-hc-body/
 * .pb-hc-image/.pb-button's own existing styling (PB Hero Centered) —
 * only genuinely new classes are styled here.
 * ========================================================================*/
.pb-pricing-section {
	padding: clamp(3rem, 6vw, 5rem) 1.25rem;
	background: #fafafa;
}

.pb-pricing-section.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.pb-pricing-section__inner {
	max-width: 1100px;
	margin: 0 auto;
	text-align: center;
}

.pb-pricing-section__inner > .pb-hc-heading,
.pb-pricing-section__inner > .pb-hc-body {
	max-width: 44rem;
	margin-left: auto;
	margin-right: auto;
}

.pb-pricing-section__inner > .pb-hc-body {
	margin-top: 0.75rem;
	margin-bottom: 2.5rem;
	opacity: 0.75;
}

.pb-pricing-cards {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	justify-content: center;
	gap: var(--pb-pricing-cards-gap, 32px);
	text-align: left;
}

.pb-pricing-card {
	display: flex;
	flex-direction: column;
	flex: 1 1 320px;
	max-width: 420px;
	/* Generous default (theme fallback, overridden per-instance by the
	 * "Padding" panel — window.pbSpacingControls, see edit.js) — extra
	 * horizontal padding doubles as "slightly narrower text column"
	 * without a separate max-width-on-text rule. */
	padding: clamp(2.25rem, 3vw, 3rem) clamp(2rem, 4vw, 3rem);
	gap: var(--pb-pricing-card-gap, 28px);
	background: #fff;
	text-align: center;
}

.pb-pricing-card--featured {
	box-shadow: 0 4px 24px rgba(104, 72, 244, 0.08);
}

.pb-pricing-card .pb-hc-heading,
.pb-pricing-card .pb-hc-body {
	margin: 0;
}

.pb-pricing-card .pb-hc-image {
	align-self: center;
}

@media (max-width: 640px) {
	.pb-pricing-cards {
		flex-direction: column;
		align-items: stretch;
	}

	.pb-pricing-card {
		max-width: 100%;
	}
}

.pb-price {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.25rem;
}

.pb-price__row {
	display: flex;
	align-items: baseline;
	gap: 0.15rem;
}

.pb-price__prefix,
.pb-price__suffix {
	font-size: 1.1rem;
	font-weight: 700;
}

.pb-price__amount {
	line-height: 1;
}

.pb-price__footnote {
	opacity: 0.6;
}

.pb-usp-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--pb-usp-gap, 12px);
	text-align: left;
	align-self: stretch;
}

.pb-usp-item {
	display: flex;
	align-items: flex-start;
	gap: 0.6rem;
}

.pb-usp-item__icon {
	flex-shrink: 0;
	line-height: 1.4;
}

.pb-usp-item__text {
	margin: 0;
}

.pb-cta-group {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.75rem;
	margin-top: auto;
}

.pb-cta-group .pb-hc-cta {
	width: 100%;
	display: flex;
	justify-content: center;
}
