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

/* overflow-x:hidden lives on the root element, not body -- same
 * horizontal-scroll safety net for alignfull/negative-margin breakout
 * blocks sitewide, but a non-visible overflow on body creates an extra
 * scroll-container ancestor that silently breaks position:sticky for
 * every descendant (e.g. .pb-scroll-card), even though the root
 * element's own overflow only governs the viewport and doesn't do
 * that. */
html {
	overflow-x: hidden;
}

body {
	margin: 0;
	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;
}

/* Mobile, transparent-header-over-hero pages only (2026-08-14):
 * position:fixed instead of sticky — the header starts fully hidden on
 * these pages (assets/js/main.js's isMobileHiddenOnLoadPage() gate,
 * hidden via .site-header.is-hidden{transform:translateY(-100%)}), but
 * a sticky element still reserves its own normal-flow height even
 * while transformed off-screen (transform never affects layout), which
 * left the hero starting below that reserved gap instead of flush
 * against the real top of the page. position:fixed removes it from
 * flow entirely, so the hero below always starts at true y=0 —
 * whether the header is currently hidden or (after the scroll-down-
 * then-up gesture) visible and pinned on top of it. Still establishes
 * a containing block for .main-navigation/.site-header__overlay's own
 * position:fixed/absolute exactly like sticky did (any position other
 * than static does), so header.php's own drawer/overlay positioning
 * is unaffected. Scoped via :has() (same technique already used
 * elsewhere in this file, e.g. .wp-block-group:has(> .pb-contact__form--card))
 * to this specific header state, not a page-ID selector — every other
 * page/breakpoint keeps the normal sticky behaviour above untouched. */
@media (max-width: 767.98px) {
	.site-header-wrap:has( .site-header[data-header-style="transparent"] ) {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
	}
}

/* More accurate --pb-header-height fallback below 768px (2026-08-14) —
 * assets/js/business-split.js overwrites this with the real measured
 * height (inline on :root, which always wins over this rule), but
 * before that JS runs — or if it's ever blocked/delayed — every
 * consumer of this variable (.pb-b2b-hero--transparent-header,
 * .pb-hero-centered--transparent-header, etc.) fell back to a single
 * literal 71px used at every breakpoint. That's a close enough estimate
 * of the >=768px header (~69px: 15px+15px padding + a 39px logo at its
 * desktop 78px max-width), which is why "desktop looks correct" — but
 * mobile's own padding/logo size (16px+16px + a 45px logo at 90px
 * max-width) is genuinely taller, ~77px, so the same 71px fallback left
 * a real ~6px gap between the transparent header and the hero's pulled-
 * up background specifically on mobile until JS corrected it. */
@media (max-width: 767.98px) {
	:root {
		--pb-header-height: 77px;
	}
}

/* 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);
}

/* Desktop, this page only (2026-09-11, explicit report + screenshot):
 * the base rule's own 98%-opaque white + 12px backdrop-blur (designed
 * to read as plain solid white against a normal light page) samples
 * whatever scrolls directly behind/under the sticky header — now the
 * pb/b2b-hero block's own near-black background (see that block's own
 * mobile-layout rules above) sits right below it once the header is
 * unlocked, and the blur pulled a visible dark cast up into the
 * header's own bottom edge instead of staying invisible. Forces fully
 * opaque solid white + no blur here only, rather than changing the
 * sitewide base rule other pages' intentional frosted-glass-over-
 * content look still depends on. */
@media ( min-width: 768px ) {
	body.page-id-365 .site-header {
		background-color: #ffffff !important;
		backdrop-filter: none !important;
	}
}

/* Flat, edge-to-edge header below 1024px (2026-08-13) — the rounded
 * bottom corners read as a "floating card" with implied edge gaps on
 * phone/tablet widths, which is also where the Top Bar Menu is hidden
 * (see .site-topbar's own 1024px reveal below) — pairing the two
 * keeps the header a single clean flat bar whenever the topbar isn't
 * part of it, instead of an inconsistent half-rounded look. */
@media (max-width: 1023.98px) {
	.site-header {
		border-radius: 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%);
	}
}

/* pb/logo-reveal-hero intro (2026-09-11, rewritten same day — replaces
 * an earlier version that revealed the header as soon as EITHER the
 * intro finished OR the visitor tried to scroll): header/menu stays
 * fully hidden through the whole intro AND the visitor's first scroll
 * down (even after the intro has finished), only revealing once they
 * scroll back UP after that first down-scroll — see assets/js/main.js's
 * own comment on the pb-lrh-header-hidden body class for the full
 * introComplete/hasScrolledDown/headerUnlocked state machine. Hides the
 * WHOLE .site-header-wrap (not just .site-header) — header.php renders
 * the optional .site-topbar as .site-header-wrap's own direct child,
 * a SIBLING of <header>, printed unconditionally before the
 * $pb_header_visible check; transform-hiding only .site-header left
 * the topbar's own row ("Zakelijk / Fundament / Contact") visibly
 * poking out above the fullscreen intro on load, exactly the
 * "header-space zichtbaar" this task explicitly rules out. Deliberately
 * a separate class from .is-hidden above (that one is scroll-direction
 * state on .site-header itself, permanently neutralized on >=768px by
 * the "always visible" rule right above it — this needs to actually
 * hide everything on desktop too, and shouldn't fight with or be reset
 * by that unrelated toggle). Transform-only (no layout shift on
 * reveal). */
body.pb-lrh-header-hidden .site-header-wrap {
	transform: translateY(-100%);
}

/* .site-header-wrap's own position:sticky (base rule, top of this file)
 * still reserves its full normal-flow height even while transform-
 * hidden (transform never affects layout) — left a blank gap the same
 * height as the header+topbar between the true top of the page and
 * pb/logo-reveal-hero's own 100vh section, instead of that intro
 * filling the ENTIRE screen edge-to-edge as required. Same fix already
 * established for mobile transparent-header pages just above
 * (position:fixed removes it from flow entirely) — reused here for
 * every breakpoint this class is present at, not only mobile, since
 * this intro needs the full-bleed treatment on desktop too. */
body.pb-lrh-header-hidden .site-header-wrap {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
}

/* "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 1024px, revealed in the media query below) — the mobile/
 * tablet 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;
}

/* Reveal breakpoint raised from 768px to 1024px (2026-08-13) — 768px
 * covers tablets (and some large phones in landscape) too, where this
 * bar was showing up as an unwanted extra white strip above the
 * header on what's still effectively a "mobile" browsing experience;
 * matches this theme's own existing tablet-vs-desktop convention
 * elsewhere (e.g. .pb-hc-hide-tablet's own 768-1023.98px tier). */
@media (min-width: 1024px) {
	.site-topbar {
		display: block;
	}

	.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;
}

.doelen-hero-centered .hero__subtitle {
	font-size: 16px;
	color: #ffffff;
}

.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;
}

/* Mobile only (2026-08-04) — true mobile (<640px, the same span the
 * base .campaign-grid__inner gap rule above covers before the 640px
 * tablet/desktop overrides take over) still summed the two grids' own
 * 1.5rem top/bottom padding into a 3rem gap when two Campaign Grid
 * blocks sit back-to-back, dwarfing the 0.25rem gap used *between
 * cards inside* one grid. Not a fixed negative margin: the touching
 * padding on each side of the shared boundary is zeroed via the
 * adjacent-sibling combinator/:has() (only ever matches when a
 * .campaign-grid is directly followed/preceded by another one — an
 * isolated grid, or grids elsewhere on the page separated by other
 * content, are untouched, and each grid's own left/right padding and
 * .campaign-grid__inner's internal card gap are untouched too), and
 * --campaign-grid-mobile-gap (declared above .campaign-grid__inner)
 * supplies the one remaining margin, so the boundary reads as exactly
 * one more card-gap, not a second, larger section gap. Tablet/desktop
 * (>=640px) are outside this query and keep today's spacing exactly. */
@media (max-width: 639.98px) {
	.campaign-grid:has(+ .campaign-grid) {
		padding-bottom: 0;
	}

	.campaign-grid + .campaign-grid {
		padding-top: 0;
		margin-top: var(--campaign-grid-mobile-gap, 0.25rem);
	}
}

/* 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; }

/* Shared with .campaign-grid + .campaign-grid's mobile stacking fix
 * further below, so the gap *within* a grid and the gap *between* two
 * directly adjacent grids are always driven by the same one value
 * instead of two independently-hardcoded numbers. Mobile-only again
 * (2026-08-14) — a same-day tablet reuse of this token was added and
 * then reverted (see the removed 640-1023.98px block further below),
 * so this back to its original 0.25rem rather than the 0.125rem tried
 * in between. */
:root {
	--campaign-grid-mobile-gap: 0.25rem;
}

.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: var(--campaign-grid-mobile-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;
	}
}

/* Grayscale-by-default image treatment (2026-07-31): every Campaign
 * Grid image (the background-image layer set by pb_media_style_attr(),
 * inc/homepage-blocks.php) renders desaturated by default, everywhere,
 * on every device — mobile/touch included, since they can't hover.
 * Desktop-only pointer devices restore full color on hover/focus
 * (below), the same (hover: hover) device detection this file already
 * uses for .campaign-grid--flip's hover-only 3D flip, so a touchscreen
 * never gets stuck in a half-transitioned state from a "sticky"
 * touch-hover. filter is its own independent transition alongside the
 * existing transform one (both fire together, same easing family) —
 * hover-zoom and the color return read as one smooth motion, not two
 * competing ones. */
.campaign-card__media {
	position: absolute;
	inset: 0;
	background: linear-gradient(160deg, #2b2b2b 0%, #000 100%);
	filter: grayscale(1);
	transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1), filter 0.5s ease;
}

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

@media (hover: hover) {
	.campaign-card:hover .campaign-card__media,
	.campaign-card:focus-within .campaign-card__media {
		filter: grayscale(0);
	}
}

/* First card always full color, every device (2026-08-06, widened from
 * touch-only) — was scoped to `@media (hover: none)` so only
 * touchscreens (which can't hover to reveal color) got this; desktop
 * still left its own first card grayscale-by-default like every other
 * card until hovered. Removing that media gate makes the rule
 * unconditional, so it now also overrides the base grayscale(1) on
 * pointer/hover-capable devices — the exact same outcome the
 * `(hover: hover)` hover-reveal rule above already gives every OTHER
 * card on interaction, just applied as this one card's default instead
 * of gated behind hover. Hovering it is still a no-op filter-wise (it's
 * already grayscale(0)) but the separate `transform: scale(1.07)`
 * hover-zoom above is untouched, so the zoom animation still plays
 * normally. Scoped by DOM position (:first-child of
 * .campaign-grid__inner), the same "real sibling position, not a
 * PHP-computed class" technique already used by the :nth-child(4n+N)
 * background alternation below — applies to every pb/campaign-grid
 * instance sitewide, not just Home, with no page-specific selector and
 * no per-item class/inline style. */
.campaign-grid__inner .campaign-card:first-child .campaign-card__media {
	filter: grayscale(0);
}


/* 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;
	}
}

/* 2026-08-16: tablet now matches MOBILE instead of desktop (reverses
 * the 2026-08-14 decision below this comment, which had unified tablet
 * with desktop — explicit new request: the shared flex-wrap layout
 * above let tablet's own narrower width squeeze 2 cards per row too
 * tight to read). Re-declares the exact same properties the mobile
 * base rules above already use (single-column grid, mobile card gap,
 * untruncated always-visible text, no hover-grow) scoped to
 * 640-1023.98px only — desktop (>=1024px) keeps the flex-wrap/hover
 * behavior above completely unchanged. */
@media (min-width: 640px) and (max-width: 1023.98px) {
	.campaign-grid__inner {
		display: grid;
		grid-template-columns: 1fr;
		gap: var(--campaign-grid-mobile-gap, 0.25rem);
	}

	.campaign-card__text {
		max-height: none;
		overflow: visible;
	}
}

/* 2026-08-14: reverted — tablet (640-1023.98px) previously got its own
 * override here (card gap pulled down to the mobile token, plus a
 * mobile-style zero-padding seam between two adjacent Campaign Grid
 * blocks). Explicit follow-up feedback: tablet should instead match
 * whatever desktop already does by default — i.e. simply inherit the
 * SAME shared .campaign-grid__inner{gap:1.5rem} rule desktop uses (the
 * min-width:640px block above, which already spans both tablet and
 * desktop with nothing tablet-specific needed), with no automatic
 * between-grid seam-tightening either (desktop has none by default —
 * any tight look between two specific adjacent grids there, e.g. the
 * homepage's own two Campaign Grid instances, comes from THAT
 * instance's own Spacing panel, min-width:1024px in its saved value,
 * not a shared rule). If a specific instance's tighter desktop gap
 * should also show at tablet, that's a per-instance Spacing-panel
 * setting (Element: Sectie, Breakpoint: Tablet) to add on that block,
 * not a shared/blanket CSS change — the shared spacing system already
 * supports a distinct Tablet tier for exactly this. SUPERSEDED by the
 * tablet-matches-mobile block above — kept only so the history/reasons
 * for the earlier decision aren't lost. */

/* 2026-08-14: the homepage's own two Campaign Grid instances now carry
 * a "Tablet" (782-1023.98px) Spacing-panel value matching their
 * Desktop one (zero gap between the two blocks) — set via Gutenberg,
 * generates its own scoped <style> off each block's own attributes.
 * That generic "Tablet" tier's 782px floor is narrower than what this
 * request needs though: an iPad Mini's own portrait width (744px)
 * falls between Campaign Grid's own mobile/non-mobile boundary (640px,
 * see the sibling-seam rule above) and that 782px floor, so it would
 * still show the wide, un-tightened default gap. This supplements
 * (doesn't replace) that Gutenberg-set value for exactly that gap —
 * same two selectors, same declarations, just the missing
 * 640-781.98px slice — giving full desktop-matching coverage from
 * 640px up, mobile (<640px, "already good") completely untouched.
 *
 * Fragile-by-design note: .cg-c9961c0746 / .cg-560dd62b29 are these
 * two block instances' own auto-generated content-hash classes
 * (md5() of each block's attributes, inc/block-helpers.php). Editing
 * either instance's own attributes again in Gutenberg — including via
 * its own Spacing panel — changes its hash and silently orphans this
 * rule; re-derive the current hash (view page source, or `wp post get
 * 27 --field=post_content`) and update the selector here if that ever
 * happens. */
@media (min-width: 640px) and (max-width: 781.98px) {
	.cg-c9961c0746 {
		padding-top: 0;
		padding-bottom: 0;
	}

	.cg-560dd62b29 {
		margin-top: 0;
		margin-bottom: 0;
		padding-top: 0;
	}
}

@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 removed (2026-08-16, explicit request) -- the
	 * element's own textColor (inline color:#ffffff on this instance)
	 * was rendering correctly, but this opacity dulled it to a faded
	 * white instead of the configured solid #FFFFFF. */
	max-width: 32rem;
}

/* Same root cause already fixed for .pb-hc-body__text strong,
 * .animated-statement__subtitle strong, .pb-image-text__body strong:
 * design-system.css's generic `.pb-type--body strong { font-weight:
 * min(calc(400+100),900) }` (this shared typography class also applies
 * here via pb_typography_class()) only steps 400 -> 500, too subtle to
 * read as bold. Same specificity (class+element); wins on source order
 * since main.css loads after design-system.css. Applies to every
 * pb/campaign-banner-v2-body instance sitewide, not just this text. */
.pb-cb-body__text strong {
	font-weight: 700;
}

.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;
}

/* 2026-07-31: matches the shared PB CTA proportions (.pb-button--primary,
 * design-system.css) -- same radius/padding-x tokens (no new values
 * invented), same fixed 46px height, same 1.5px border thickness
 * (already matched, now via the shared border preset instead of a
 * hardcoded grey). Background stays white per its own requirement
 * (a CTA's own dark/purple fill would be wrong for a search field).
 * Search/autocomplete/keyboard behaviour (assets/js/*) reads this
 * input by id/data attributes only, untouched by any of this. */
.verkooppunten__input {
	width: 100%;
	height: 46px;
	padding: 0 var(--wp--custom--button--primary--padding-x, 2.25rem);
	border: 1.5px solid var(--wp--preset--color--border, rgba(17, 17, 17, 0.12));
	border-radius: var(--wp--custom--button--primary--radius, 10px);
	font-size: 0.9rem;
	font-family: inherit;
	color: #111;
	background: #ffffff;
	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%);
}

/* 2026-08-17, explicit request: Store Locator map fullscreen toggle
 * (assets/js/main.js, initMapFullscreenToggle()). Both map containers
 * (.verkooppunten__map homepage, .verkooppunten-page__map dedicated
 * locator page) already have position:relative, so the two buttons
 * position relative to the map itself. Mobile + tablet + desktop —
 * no breakpoint scoping needed, same behaviour everywhere. */
.pb-map-fullscreen-toggle,
.pb-map-fullscreen-close {
	position: absolute;
	top: 0.75rem;
	right: 0.75rem;
	z-index: 20;
	width: 2.25rem;
	height: 2.25rem;
	align-items: center;
	justify-content: center;
	padding: 0;
	background: #fff;
	border: none;
	border-radius: 0.5rem;
	box-shadow: 0 2px 8px rgba(17, 17, 17, 0.2);
	color: #111;
	cursor: pointer;
	transition: background-color 0.2s ease, transform 0.2s ease;
}

/* display only applies while NOT hidden — a bare `display: flex` on
 * the shared rule above would out-specificity the [hidden] attribute's
 * own display:none (equal specificity, this file loads after the UA
 * stylesheet), keeping both buttons visible/stacked regardless of
 * which one JS actually wants shown. Bug found live: the close (X)
 * button, added later in the DOM, was painting over the expand icon,
 * so only the X was ever visible and expand never worked. */
.pb-map-fullscreen-toggle:not([hidden]),
.pb-map-fullscreen-close:not([hidden]) {
	display: flex;
}

.pb-map-fullscreen-toggle:hover,
.pb-map-fullscreen-close:hover,
.pb-map-fullscreen-toggle:focus-visible,
.pb-map-fullscreen-close:focus-visible {
	background: #f2f2f2;
	outline: 2px solid #6848f4;
	outline-offset: 2px;
}

/* position:fixed !important — this is the ONLY reliable way to cover
 * the full viewport regardless of the map container's own layout
 * context (grid/flex ancestors, negative margins on the dedicated
 * locator page, etc.); every other property here just resets that
 * container's normal-flow styling for as long as this class is
 * present. 100dvh with a 100vh fallback per explicit request. */
.pb-map--fullscreen {
	position: fixed !important;
	inset: 0 !important;
	top: 0 !important;
	left: 0 !important;
	right: 0 !important;
	bottom: 0 !important;
	width: 100vw !important;
	height: 100vh !important;
	height: 100dvh !important;
	max-width: 100vw !important;
	max-height: none !important;
	margin: 0 !important;
	border-radius: 0 !important;
	z-index: 100000 !important;
}

.pb-map--fullscreen .leaflet-container,
.pb-map--fullscreen.verkooppunten__map {
	width: 100% !important;
	height: 100% !important;
}

/* 2026-08-17: desktop-only fix — .verkooppunten-page__map has its own
 * width:900px/height:630px !important at >=1024px (the split sidebar+
 * map layout), same specificity as the base .pb-map--fullscreen rule
 * above and later in the file, so it was winning and silently
 * capping "fullscreen" back down to 900x630px on desktop only (mobile
 * has no such override, which is why it worked there). This compound
 * selector (2 classes) unambiguously outranks that single-class
 * desktop rule regardless of source order. */
.pb-map--fullscreen.verkooppunten-page__map {
	position: fixed !important;
	inset: 0 !important;
	width: 100vw !important;
	height: 100vh !important;
	height: 100dvh !important;
	margin: 0 !important;
	justify-self: auto !important;
}

.pb-map--fullscreen .pb-map-fullscreen-close {
	top: 1rem;
	right: 1rem;
	width: 2.5rem;
	height: 2.5rem;
}

/* Background scroll lock while fullscreen is open (position:fixed +
 * JS-set `top` offset, not plain overflow:hidden — the more reliable
 * cross-browser technique, notably on iOS Safari, for preventing the
 * page behind from scrolling). assets/js/main.js restores the exact
 * scroll position on close. */
.pb-map-fullscreen-lock {
	position: fixed;
	width: 100%;
	overflow: hidden;
}

.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;
	}
}

/* 2026-08-17, explicit request: /verkooppunten/ Store Locator popup
 * (Google Maps InfoWindow, .pb-popup--compact) only — mobile + tablet.
 * The fixed 300x110px box + overflow:hidden was clipping/crowding text
 * that didn't fit. Scoped under .pb-popup--compact specifically (not
 * the bare .pb-popup__media/__info/etc. classes) so the homepage's
 * Leaflet popup and desktop are both completely unaffected. JS side
 * (assets/js/main.js) now also caps the InfoWindow's own maxWidth to
 * the viewport width, so this CSS max-width has room to actually work
 * instead of Google forcing a wider box regardless. */
@media (max-width: 1023.98px) {
	.pb-popup--compact {
		width: auto;
		max-width: min(88vw, 320px);
		height: auto;
		min-height: 110px;
		overflow: visible;
	}

	.pb-popup--compact .pb-popup__media {
		flex: 0 0 76px;
	}

	.pb-popup--compact .pb-popup__media img,
	.pb-popup--compact .pb-popup__media--placeholder {
		min-height: 76px;
	}

	.pb-popup--compact .pb-popup__info {
		padding: 0.75rem 0.85rem;
		min-width: 0;
	}

	.pb-popup--compact .pb-popup__name,
	.pb-popup--compact .pb-popup__address,
	.pb-popup--compact .pb-popup__hours {
		overflow-wrap: anywhere;
		word-break: normal;
		white-space: normal;
	}

	.pb-popup--compact .pb-popup__actions {
		flex-wrap: wrap;
	}

	.pb-popup--compact .pb-popup__route,
	.pb-popup--compact .pb-popup__website {
		white-space: normal;
		text-align: center;
	}
}

/* Very narrow phones (<360px): stack logo above text instead of
 * squeezing them side by side, so the image can't crowd the text
 * column on the smallest screens. */
@media (max-width: 359.98px) {
	.pb-popup--compact {
		flex-direction: column;
	}

	.pb-popup--compact .pb-popup__media {
		flex: 0 0 auto;
		width: 100%;
		height: 64px;
	}
}

.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;
}

/* 2026-07-31: explicit full-width, rather than relying on flex's
 * default align-items:stretch on the sidebar above -- guarantees the
 * search bar's own right edge always reaches exactly as far as
 * .wp-block-pb-store-locator-filter's (align-self:flex-end below), no
 * matter what either block's own default width would otherwise be. */
.verkooppunten-page__sidebar .wp-block-pb-store-locator-search,
.verkooppunten-page__sidebar .verkooppunten__search {
	width: 100%;
}

.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: var(--wp--custom--button--primary--radius, 10px);
	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) {
	/* 2026-10-17: height auto (was a fixed 310px square, sized for the
	 * old square PaperBrain footer image) so non-square logos like the
	 * NewSheeps wordmark keep their own aspect ratio. Nudged slightly
	 * right (margin-left) on request. */
	.site-footer__logo--desktop img {
		width: 310px;
		height: auto;
		max-width: 100%;
		margin-left: 2.5rem;
	}
}

.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 {
	padding: 4rem 1.25rem;
}

/* Shared layout fix (2026-08-05): when FAQ is the *last* content block
 * on a page, its own bottom padding (4rem above) stacks directly
 * against .site-footer__main's own 3rem top padding — 7rem of
 * combined whitespace, reading as far too much. Matched by real DOM
 * position (.entry-content > .faq:last-child), same "match by
 * position, not a page-specific selector" convention already used
 * elsewhere in this file (e.g. .entry-content > .pb-hero-centered:first-child)
 * — a FAQ block followed by more page content (the more common case)
 * keeps its full, symmetric 4rem padding on both sides unchanged.
 * Only padding-bottom is touched: FAQ's own top padding and every
 * internal spacing rule (.faq__inner/__title/__list/etc.) are
 * untouched. No negative margin — the padding value itself is reduced
 * directly. Same rule at every breakpoint (no media query), so mobile
 * and desktop match. */
.entry-content > .faq:last-child {
	padding-bottom: 1.5rem;
}

/* Self-contained full breakout — same established technique as
 * .pb-image-text/.hero/.pb-hero-split/etc. (this theme has no generic
 * .alignfull CSS of its own). Without this, .faq only reached true
 * full width when its container already happened to be edge-to-edge
 * (e.g. Home's plain .entry-content) -- inside a narrower parent (e.g.
 * the WooCommerce PDP content column, pb_wc_render_pdp_content()'s
 * .pb-pdp__content wrapper), the block fell back to that column's own
 * width instead, making the same content look narrower there. Set on
 * the outer section only (max-width/centering below moved to
 * .faq__inner) so this doesn't fight that element's own margin:auto
 * centering -- same two-level split every other full-bleed PB block
 * already uses (outer breakout, inner content column). */
.faq.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.faq__inner {
	max-width: 48rem;
	margin: 0 auto;
}

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

.pb-contact-form-title.faq__title {
	margin-top: 4rem !important;
}

.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;
}

/* FAQ answer inline formatting (2026-08-14): answers are RichText
 * content (bold/italic/link via the normal Gutenberg toolbar, see
 * blocks/faq-item/edit.js's pbRichParagraph.renderContent()), but two
 * sitewide rules silently defeated it here — the global `a{text-
 * decoration:none}` reset (this file, Base section) stripped every
 * link's underline including inside FAQ answers, and design-system.css's
 * `.pb-type--body strong{font-weight:…}` formula only steps bold up to
 * 500 (400 base + 100), which reads as barely different from regular
 * text. Both fixed here, scoped to .faq__answer-text only — no other
 * FAQ styling (layout/accordion/borders/sizes) touched. No !important
 * needed: both new selectors already win the sitewide resets on
 * specificity/cascade-order alone. */
.faq__answer-text strong,
.faq__answer-text b {
	font-weight: 700;
}

.faq__answer-text a {
	color: inherit;
	text-decoration: underline;
	cursor: pointer;
}

.faq__answer-text a:hover {
	text-decoration: underline;
}

/* ==========================================================================
 * 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;
	/* Manually scrollable at every width (2026-08-06, was
	 * overflow: hidden) — native overflow-x:auto coexists safely with
	 * the transform-driven animation on .logo-slider__track below (the
	 * animation only ever paints a visual offset, it never changes the
	 * track's own scrollable layout size), same proven technique
	 * already used for the PB Reviews Carousel
	 * (.pb-reviews-carousel__viewport, this file). Enables touch swipe
	 * on mobile and trackpad/Shift+wheel scrolling on desktop natively,
	 * with no JS needed for either; assets/js/logo-slider.js only adds
	 * click-and-drag + pause-on-interaction on top. */
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

/* 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,
.logo-slider--pause-on-hover:focus-within .logo-slider__track {
	animation-play-state: paused;
}

/* User-driven pause (assets/js/logo-slider.js adds/removes
 * .logo-slider--user-paused on touchstart/scroll/drag-start) — same
 * class/timer approach as .pb-reviews-carousel--user-paused: pauses
 * the autoplay immediately on any manual interaction (touch, drag,
 * trackpad/wheel scroll) and resumes it 5s after the last one, on top
 * of (not instead of) the CSS-only :hover/:focus-within pause above. */
.logo-slider--user-paused .logo-slider__track {
	animation-play-state: paused;
}

/* Desktop: grab/grabbing cursor + hidden (but still functional)
 * scrollbar for the click-and-drag interaction assets/js/logo-slider.js
 * adds — same treatment already proven for
 * .pb-reviews-carousel__viewport. Scrollbar hidden visually only —
 * overflow-x stays "auto" from the base rule above, so scrolling itself
 * (drag/wheel/trackpad/keyboard) keeps working exactly as before. */
@media (min-width: 641px) {
	.logo-slider__viewport {
		cursor: grab;
		scrollbar-width: none;
		-ms-overflow-style: none;
	}

	.logo-slider__viewport::-webkit-scrollbar {
		display: none;
	}

	.logo-slider__viewport.is-dragging {
		cursor: grabbing;
		user-select: none;
	}
}

.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;
}

/* User-driven pause (assets/js/reviews-carousel.js adds/removes
 * .pb-reviews-carousel--user-paused) -- originally mobile-only touch;
 * 2026-08-05: no longer breakpoint-scoped, so the exact same class/
 * timer this already used for touch also pauses for desktop
 * click-and-drag/wheel/trackpad scrolling below, instead of a second,
 * separate pause mechanism. Desktop's own :hover/:focus-within pause
 * above is untouched (still fires independently); mobile's own
 * behaviour is unaffected since it already only ever added this class
 * at mobile widths in the first place. */
.pb-reviews-carousel--user-paused .pb-reviews-carousel__track {
	animation-play-state: paused;
}

/* Desktop only (2026-08-05): same native-scroll treatment the mobile
 * breakpoint below already uses and already documents as conflict-free
 * with the transform-driven autoplay animation ("the animation only
 * ever paints a visual offset, it never changes the track's own
 * scrollable layout size") -- enables trackpad horizontal scroll and
 * Shift+wheel natively (both are just native browser behaviour on any
 * overflow-x:auto element, no JS needed for either), and gives the
 * click-and-drag JS below a real scrollLeft to manipulate. Grab/
 * grabbing cursor communicates the new interaction; .is-dragging is
 * toggled by that same JS while a drag is in progress, disabling
 * scroll-snap (mid-drag snapping fights the pointer) and text
 * selection (dragging across review text shouldn't select it). */
@media (min-width: 641px) {
	.pb-reviews-carousel__viewport {
		overflow-x: auto;
		scroll-snap-type: x proximity;
		cursor: grab;
		/* Scrollbar hidden visually only — overflow-x stays "auto", so
		 * scrolling itself (drag/wheel/trackpad/keyboard) keeps working
		 * exactly as above, nothing here disables it. */
		scrollbar-width: none;
		-ms-overflow-style: none;
	}

	.pb-reviews-carousel__viewport::-webkit-scrollbar {
		display: none;
	}

	.pb-reviews-carousel__viewport.is-dragging {
		cursor: grabbing;
		scroll-snap-type: none;
		user-select: none;
	}
}

.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;
}

/* Optional small subtext under the title (block setting "subheading").
 * The title stays on one row with the link/arrows; the subtext wraps to
 * its own full-width row below (wrapper is display:contents, so title,
 * subtext and controls are all items of the header's flex row). */
.pb-blog-preview__heading {
	display: contents;
}

.pb-blog-preview__subtitle {
	order: 2;
	flex: 0 0 100%;
	margin: -0.5rem 0 0;
	max-width: none; /* must stay 100% wide so it wraps to its own row */
	font-size: 0.95rem;
	line-height: 1.5;
	color: rgba(17, 17, 17, 0.65);
}

.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%;
	}
}

/* Mobile: title left + arrows top-right, cards, "view all" link below
 * the cards on the right. Header/controls become display:contents so
 * their children are placed directly on the section's grid. */
@media (max-width: 767.98px) {
	.pb-blog-preview {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		grid-template-areas:
			"title arrows"
			"cards cards"
			"link link";
		align-items: center;
		row-gap: 1.25rem;
	}

	.pb-blog-preview__header,
	.pb-blog-preview__controls {
		display: contents;
	}

	.pb-blog-preview__title {
		grid-area: title;
	}

	/* With a subtext: title + arrows on one row, subtext on its own row
	 * below. Only when a subtext exists, so blocks without one keep the
	 * exact grid above (no empty row/extra gap). */
	.pb-blog-preview:has(.pb-blog-preview__subtitle) {
		grid-template-areas:
			"title arrows"
			"sub sub"
			"cards cards"
			"link link";
	}

	.pb-blog-preview__subtitle {
		grid-area: sub;
		margin: -0.75rem 0 0;
		font-size: 0.85rem;
	}

	/* !important: design-system.css sets heading size/weight with !important. */
	.pb-blog-preview .pb-blog-preview__title {
		font-size: var(--pb-title-size-mobile, 18px) !important;
		font-weight: 400 !important;
	}

	.pb-blog-preview__arrows {
		grid-area: arrows;
	}

	.pb-blog-preview__viewport {
		grid-area: cards;
		width: 100%;
	}

	.pb-blog-preview__link {
		grid-area: link;
		justify-self: end;
	}

	.pb-blog-preview .pb-blog-preview__link {
		font-weight: 400;
	}
}

@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));
	}

	/* Desktop title matches "New Arrivals"; size set in the editor
	 * (!important: design-system.css sets heading size/weight with it). */
	.pb-blog-preview .pb-blog-preview__title {
		font-size: var(--pb-title-size-desktop, 24px) !important;
		font-weight: 400 !important;
	}

	/* Desktop: no arrows, just a quiet "Bekijk alle blogs ›" top-right. */
	.pb-blog-preview__arrows {
		display: none;
	}

	.pb-blog-preview .pb-blog-preview__link {
		display: inline-flex;
		align-items: center;
		gap: 0.6rem;
		font-size: 0.95rem;
		font-weight: 400;
		text-transform: none;
		letter-spacing: 0;
		color: rgba(17, 17, 17, 0.65);
		border-bottom: none;
		padding-bottom: 0;
	}

	.pb-blog-preview .pb-blog-preview__link::after {
		content: "";
		width: 0.45rem;
		height: 0.45rem;
		border-top: 1.5px solid currentColor;
		border-right: 1.5px solid currentColor;
		transform: rotate(45deg);
	}
}

@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) {
	/* 2026-07-27: manual <br> tags in the statement/subtitle text also
	 * carry real paragraph breaks authored as <br><br> (this component
	 * has no <p> tags of its own — see pb_animated_statement_word_spans(),
	 * inc/animated-statement-block.php) — a previous "hide every <br> on
	 * mobile" rule here (meant only to stop desktop-tuned mid-sentence
	 * wrap points from forcing short lines) hid those paragraph breaks
	 * too, collapsing separate paragraphs into one run and, wherever a
	 * <br> had no adjacent literal space in the authored text, visibly
	 * joining two sentences with no space at all. <br> now renders
	 * identically to desktop at every breakpoint — same authored content,
	 * same reading order/rhythm, no separate mobile-only text treatment
	 * to keep in sync. The former page-id-968-specific reveal hack
	 * further below (re-enabling only specific nth-of-type <br>s) is
	 * removed with it: with nothing hiding <br> anymore, that hack has
	 * nothing left to override. */

	.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;
}

.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;
}

/* Mobile only: `left:50%` (relative to the containing block) plus
 * `margin-left:-50vw` (relative to the viewport) are two independently
 * rounded values -- at some mobile widths their sub-pixel rounding
 * doesn't cancel out exactly, leaving a hairline gap where the page's
 * own white background shows through between adjacent full-bleed
 * sections (e.g. above #je-leefstijl). Single calc() removes the
 * cross-reference so there's nothing left to round differently. */
@media ( max-width: 767.98px ) {
	.pb-hero-centered {
		left: auto;
		width: auto;
		margin-left: calc( 50% - 50vw );
		margin-right: calc( 50% - 50vw );
		/* Layout math confirmed 0 actual gap (getBoundingClientRect:
		 * this section's top lands on the exact same fractional device
		 * pixel as the previous section's bottom) -- the calc() above
		 * alone didn't remove the visible line, because the real cause
		 * is sub-pixel rasterization: the GPU can round two adjacent
		 * elements' matching fractional-pixel edges to DIFFERENT
		 * physical pixels when painting, leaving a hairline seam that
		 * layout coordinates never show. 1px overlap wasn't quite enough
		 * on some devices (still faintly visible, explicit follow-up) --
		 * increased for a decisive margin of safety. */
		margin-top: -3px;
	}
}

/* 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),
.site-main:has(> article > .entry-content > .pb-campaign-banner:first-child),
.site-main:has(> .entry-content > .pb-campaign-banner:first-child),
.site-main:has(> article > .entry-content > .pb-fullscreen-media-hero:first-child),
.site-main:has(> .entry-content > .pb-fullscreen-media-hero:first-child),
.site-main:has(> article > .entry-content > .pb-split-hero:first-child),
.site-main:has(> .entry-content > .pb-split-hero:first-child),
/* pb/intro-overlay (a fixed-position overlay that takes no space) placed
 * before the hero still counts as "hero first" (2026-10-19, Roze Woensdag). */
.site-main:has(> article > .entry-content > .pb-intro-overlay:first-child + .pb-split-hero),
.site-main:has(> .entry-content > .pb-intro-overlay:first-child + .pb-split-hero) {
	padding-top: 0;
}

/* Same residual sub-header gap as .pb-hero-centered/.pb-b2b-hero above
 * — not attributable to any margin/padding in the header/site-main/
 * article/entry-content chain, closed the same way. */
.entry-content > .pb-fullscreen-media-hero:first-child,
.entry-content > .pb-split-hero:first-child {
	margin-top: -22px;
}

.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; }
}

/* Trigger moved onto .is-revealed (2026-08-16, explicit bug report):
 * this used to run unconditionally the instant the element existed in
 * the DOM (page load), completely regardless of scroll position — for
 * an image this far down the page, the 1s animation had always
 * finished long before a visitor actually scrolled to see it, which
 * is indistinguishable from "no animation ever played". Now reuses
 * this theme's own existing shared scroll-reveal observer (main.js —
 * same IntersectionObserver/html.js/reduced-motion machinery already
 * driving [data-pb-reveal] etc., .pb-hc-animate is simply now also in
 * that observer's target list) instead of a second one-off trigger;
 * the animation's own keyframes/timing are untouched, only WHEN they
 * start changed. html.js-scoped, same established convention as that
 * shared system: if JS never runs, nothing here ever applies and the
 * image is simply visible normally (initial opacity is 1 by default). */
html.js .pb-hc-animate {
	opacity: 0;
}

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

/* Pure fade, no slide (2026-08-16, explicit request, opt-in per
 * instance via pb/hero-centered-image's own "fadeOnly" attribute) --
 * stays in its final resting place the whole time, only opacity
 * animates. Higher specificity (3 classes) than the slide rule above
 * (2 classes) — always wins when both toggles are on, per explicit
 * requirement that fade-only takes precedence and disables translate. */
@keyframes pb-hc-fade-in {
	0% { opacity: 0; }
	100% { opacity: 1; }
}

html.js .pb-hc-animate.pb-hc-animate--fade-only.is-revealed {
	animation: pb-hc-fade-in 1.8s ease 0s 1 forwards;
}

@media (prefers-reduced-motion: reduce) {
	/* !important: html.js .pb-hc-animate.pb-hc-animate--fade-only.is-
	 * revealed (above) is 4 classes -- a plain .pb-hc-animate rule here
	 * can never out-specificity that, so without !important this was
	 * silently losing and the animation still ran for reduced-motion
	 * users (confirmed live: opacity mid-transition + animationName
	 * still set despite this rule existing). */
	.pb-hc-animate {
		opacity: 1 !important;
		animation: none !important;
	}
}

/* White logo/icon recolor (2026-08-16, explicit request, opt-in per
 * instance via pb/hero-centered-image's own "whiteFilter" attribute) --
 * turns every opaque pixel of the image white regardless of its
 * original colour (brightness(0) crushes to black, invert(1) flips
 * that to white); transparent areas of the source PNG stay
 * transparent either way, so this only works cleanly on that kind of
 * source image, same assumption as every other logo-recolor-via-
 * filter technique. */
.pb-hc-image--white-filter .pb-hc-image__img {
	filter: brightness(0) invert(1);
}

/* !important: equal specificity to .pb-hc-image.has-shadow's own filter
 * rule further below (2 classes + 1 descendant class each), which
 * would otherwise win by appearing later in source order and drop the
 * white recolor whenever shadow is also enabled on the same instance. */
.pb-hc-image--white-filter.has-shadow .pb-hc-image__img {
	filter: brightness(0) invert(1) drop-shadow(0 30px 40px rgba(20, 20, 30, 0.25)) drop-shadow(0 8px 12px rgba(20, 20, 30, 0.14)) !important;
}

/* ---- 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;
}

/* RichText <strong> inside this subtitle 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 exact issue already
 * fixed for this block family's own .pb-hc-body__text strong (above)
 * and .animated-statement__subtitle strong / .pb-image-text__body
 * strong elsewhere — this subtitle variant was simply missed when
 * those were added. Same specificity as the generic 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 needed. Applies to
 * every pb/hero-centered-subtitle instance sitewide, current and
 * future content alike. */
.pb-hc-subtitle__text strong {
	font-weight: 700;
}

/* ---- Child: Floating Product Image ---- */
.pb-hc-image {
	display: flex;
	align-items: center;
	justify-content: center;
	margin: 0 0 1.5rem;
}

/* Alignment (2026-08-16, explicit request) -- center is the existing
 * default (parent .pb-hero-centered__inner's own align-items:center),
 * untouched here so every existing instance stays backward compatible;
 * left/right are opt-in via align-self on this same flex child. */
.pb-hc-image--align-left {
	align-self: flex-start;
}

.pb-hc-image--align-right {
	align-self: flex-end;
}

.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;
}

/* Shared PB Hero responsive typography system (2026-07-27): consumes
 * the --phc-hc-heading-fs custom property pb_typography_override_style()
 * now emits (inc/hero-centered-block.php's fontSizeOverride handling,
 * opt-in "css var" mode, inc/block-helpers.php) instead of a direct
 * inline font-size. A plain inline `!important` declaration can never
 * be beaten by ANY external stylesheet rule regardless of specificity
 * or its own !important -- which is exactly why a breakpoint-specific
 * override for one instance was previously impossible (the property
 * always resolved to the instance's single inline value everywhere).
 * The custom property itself is emitted inline WITHOUT !important, so
 * ordinary external rules (below) can freely redefine it per
 * breakpoint/page; this rule's own !important is only to beat
 * design-system.css's sitewide `h1{…!important}` reset, same reasoning
 * as pb_typography_override_style()'s plain mode.
 *
 * 2026-07-31 fix: instances with no fontSizeOverride (e.g. Home) never
 * set the variable, but font-size is an *inherited* property -- an
 * !important declaration whose var() is unset doesn't fall through to
 * a lower-specificity rule for the same property, it resolves to
 * "inherit from the parent" instead (CSS Custom Properties spec,
 * "invalid at computed-value time"). That silently broke every
 * instance without an override (parent's own font-size, not
 * design-system.css's intended .pb-type--h1 sizing at all) the moment
 * this rule shipped. Each var() now falls back to the exact same
 * --wp--custom--typography--h-1--size-{mobile,tablet,desktop} tokens
 * design-system.css's own .pb-type--h1 rule uses, at the same
 * breakpoints, restoring identical behaviour for every instance with
 * no override -- Retail's own override (below) is unaffected either
 * way, since its variable is always set. */
.pb-hc-heading__text.pb-type--h1 {
	font-size: var(--phc-hc-heading-fs, var(--wp--custom--typography--h-1--size-mobile, 2.1rem)) !important;
}

@media (min-width: 768px) {
	.pb-hc-heading__text.pb-type--h1 {
		font-size: var(--phc-hc-heading-fs, var(--wp--custom--typography--h-1--size-tablet, 2.6rem)) !important;
	}
}

@media (min-width: 1024px) {
	.pb-hc-heading__text.pb-type--h1 {
		font-size: var(--phc-hc-heading-fs, var(--wp--custom--typography--h-1--size-desktop, 3.25rem)) !important;
	}
}

/* Retail (page-id-363) hero heading: this instance's own
 * fontSizeOverride is 50 (inline --phc-hc-heading-fs:50px, applies at
 * every breakpoint by default via the shared rule above) -- pin
 * desktop/tablet explicitly at that same, already-current value so
 * introducing the mobile-only override below changes nothing else.
 * (The previous desktop-only 65px attempt here never actually took
 * effect -- same inline-!important-can't-be-beaten issue -- so 50px is
 * what desktop/tablet have actually been rendering; kept as-is.) */
@media (min-width: 768px) {
	body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 {
		--phc-hc-heading-fs: 50px !important;
	}
}

/* white-space:nowrap doesn't compete with any inline declaration (the
 * instance's inline style never sets it), so this always applied
 * successfully on its own -- kept, unchanged, at its original 1024px/
 * desktop-only breakpoint and value. */
@media (min-width: 1024px) {
	body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 {
		white-space: nowrap;
	}
}

/* Mobile only, per this task: 45px (was 50px). Tablet/desktop above
 * keep the instance's existing value untouched; line-height, weight,
 * letter-spacing are not set here on purpose -- only font-size
 * changes. */
@media (max-width: 767.98px) {
	body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 {
		--phc-hc-heading-fs: 40px !important;
	}
}

/* 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).
 * 2026-08-17: extended to the homepage (body.page-id-27, "Create the
 * life you want to live" — same pb/hero-centered-heading block, "you
 * want" already wrapped in <strong>) — same exact rule reused, not a
 * duplicate ruleset, per explicit request. */
body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong,
body.page-id-27 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong {
	position: relative;
	display: inline-block;
}

/* 2026-08-17, explicit request: forces "you want to live" (the new
 * .pb-hero-line2 wrapper around the existing <strong>you want</strong>,
 * inc post 27's own block content) onto its own line on mobile only —
 * "Create the life" / "you want to live", exact split, regardless of
 * how the text would otherwise naturally wrap at a given width.
 * display:block only; no font-size/color/weight touched, so the
 * underline/bold styling on "you want" (rule above) is unaffected.
 * Desktop untouched (outside this media query). */
@media ( max-width: 767.98px ) {
	body.page-id-27 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 .pb-hero-line2 {
		display: block;
	}
}

body.page-id-363 .pb-hero-centered .pb-hc-heading__text.pb-type--h1 strong::after,
body.page-id-27 .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,
	body.page-id-27 .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 );
}

/* 2026-08-17, explicit request: Retail page (post 363) hero body text
 * — guarantees a literal 16px on mobile regardless of whatever
 * responsive/rem-based scaling the inherited .pb-type--body typography
 * class applies. Font-family/weight/color/line-height/alignment all
 * come from that same class and are left untouched — only font-size
 * is forced here. Scoped to this page + mobile only. */
@media (max-width: 767.98px) {
	body.page-id-363 .pb-hero-centered .pb-hc-body__text {
		font-size: 16px !important;
	}
}

/* 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;
	}

	/* Hardcoded 240px ceiling removed (2026-08-16, explicit bug report)
	 * -- it capped the image BELOW its own configured --phc-image-width
	 * for any instance set wider than that, so raising the size in
	 * Gutenberg had no visible effect here. The base .pb-hc-image__img
	 * rule's own width:var(--phc-image-width)/max-width:100% (below)
	 * already both applies the configured size AND prevents viewport
	 * overflow — nothing extra needed at this breakpoint. */
}

@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;
	}

	/* Hardcoded ceiling removed again (2026-08-16, explicit bug report
	 * — same underlying issue as the 1024px tier above: even the
	 * 1.15x-scaled 280px cap still silently capped larger configured
	 * widths, so the Gutenberg size setting stopped being the actual
	 * source of truth on mobile past that point). The base
	 * .pb-hc-image__img rule (width:var(--phc-image-width);
	 * max-width:100%) is what actually applies here now — the
	 * configured width renders as-is, shrinking only if the viewport
	 * itself is narrower than that (max-width:100% still prevents
	 * overflow, nothing else needed). */
}

/* ---- 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;
}

/* Mobile only — same rounding-gap fix as .pb-hero-centered above
 * (2026-08-16): `left:50%` and `margin-left:-50vw` round independently
 * and don't always cancel exactly, leaving a hairline white gap
 * between this and an adjacent full-bleed section. Single calc()
 * removes the cross-reference. */
@media ( max-width: 767.98px ) {
	.pb-image-text.alignfull {
		left: auto;
		width: auto;
		margin-left: calc( 50% - 50vw );
		margin-right: calc( 50% - 50vw );
		/* Same sub-pixel rasterization seam as .pb-hero-centered's own
		 * mobile override above -- layout math is exact (0 gap), the
		 * line is a rounding artifact in painting, not spacing. 1px
		 * wasn't quite enough on some devices (explicit follow-up),
		 * increased for a decisive margin of safety. */
		margin-top: -3px;
	}
}

.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;
}

/* 2026-08-17, explicit request: underline formatting for
 * pb/image-text-paragraph — the RichText itself already allows the
 * core/underline format (window.pbRichParagraph -> pb-rich-text-core.js's
 * shared ALLOWED_FORMATS, editable/removable in Gutenberg like bold/
 * italic/link), so no editor/save-side change was needed there; this is
 * purely the visual style for whatever <u> ends up in the saved
 * content. Matches the same accent-underline look already established
 * elsewhere on the site (e.g. the homepage/Retail hero headings' bolded-
 * word underline) — thin bar, small offset, currentColor — instead of
 * the browser's plain default text-decoration. Not scoped to any one
 * page/instance's text: applies to every pb/image-text-paragraph
 * (shared .pb-image-text__body-text class) sitewide, mobile + desktop. */
.pb-image-text__body-text u {
	text-decoration: none;
	position: relative;
	display: inline-block;
}

.pb-image-text__body-text u::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -0.06em;
	width: 100%;
	height: 0.07em;
	background-color: currentColor;
}

.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 > .wp-block-table,
.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 > .wp-block-table,
.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;
}

/* Optional price table inside .pb-image-text__body (2026-09-24,
 * explicit request) — a plain core/table child (now allowed in
 * blocks/image-text-compact/edit.js's InnerBlocks allowedBlocks, same
 * "reuse an existing WP block" approach as core/paragraph and
 * core/list above), styled to match the supplied reference image:
 * thin black borders, black text, white background, compact cells.
 * Purely opt-in per instance — an existing pb/image-text-compact
 * without a table child is completely unaffected. */
.pb-image-text__body .wp-block-table {
	margin: 1.5rem 0 0;
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

.pb-image-text__body .wp-block-table table {
	width: 100%;
	border-collapse: collapse;
	background-color: #ffffff;
	color: #000000;
}

.pb-image-text__body .wp-block-table th,
.pb-image-text__body .wp-block-table td {
	border: 1px solid #000000;
	padding: 0.35rem 0.5rem;
	text-align: left;
	font-size: 0.7rem;
	white-space: nowrap;
}

.pb-image-text__body .wp-block-table th {
	font-weight: 700;
}

/* Mobile/tablet (2026-09-24, explicit report + screenshot: the
 * "Aantal" column rendered far too wide on a real phone, pushing
 * "Gepersonaliseerd*" off-screen) — auto table-layout sizes every
 * column from its widest nowrap cell across the WHOLE table, so one
 * long header ("PaperBrain® los") was inflating every column's share,
 * "Aantal" included. table-layout:fixed + explicit % widths replaces
 * that guesswork with a fixed split below 782px (matches this block's
 * own existing breakpoint). Headers get their own (smaller) font-size
 * and stay nowrap — "Gepersonaliseerd*" wrapping to 2 lines was an
 * explicit follow-up report — safe now that every td value is short
 * too ("Neem contact op" became "Contact" on this breakpoint above),
 * so nothing needs word-wrap as a fallback anymore. The last column
 * gets extra share of the fixed width since "Gepersonaliseerd*" is
 * the longest header. */
@media (max-width: 781.98px) {
	.pb-image-text__body .wp-block-table table {
		table-layout: fixed;
	}

	.pb-image-text__body .wp-block-table th,
	.pb-image-text__body .wp-block-table td {
		white-space: nowrap;
		font-size: 0.65rem;
		padding: 0.3rem 0.3rem;
	}

	.pb-image-text__body .wp-block-table th {
		font-size: 0.58rem;
	}

	.pb-image-text__body .wp-block-table th:first-child,
	.pb-image-text__body .wp-block-table td:first-child {
		width: 14%;
	}

	.pb-image-text__body .wp-block-table th:nth-child(2),
	.pb-image-text__body .wp-block-table td:nth-child(2),
	.pb-image-text__body .wp-block-table th:nth-child(3),
	.pb-image-text__body .wp-block-table td:nth-child(3) {
		width: 24%;
	}

	.pb-image-text__body .wp-block-table th:last-child,
	.pb-image-text__body .wp-block-table td:last-child {
		width: 38%;
	}
}

/* "Neem contact op" (500+ row) becomes the shorter "Contact" on
 * mobile/tablet (2026-09-24, explicit request) — same cell/field on
 * the backend, both text variants are saved in the table content;
 * CSS just shows one or the other per breakpoint, same 781.98px cutoff
 * as the rest of this block. */
.pbc-price-mobile-only {
	display: none;
}

@media (max-width: 781.98px) {
	.pbc-price-desktop-only {
		display: none;
	}

	.pbc-price-mobile-only {
		display: inline;
	}
}

@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);
}

/* 2026-08-17, explicit request: all text within PB Goal Steps (titles,
 * paragraphs, bold/strong) fully black. Scoped to .goal-steps__title/
 * __subtitle and text inside .goal-step specifically — the eyebrow's
 * own intentional accent color and any icons/buttons are untouched.
 * !important since these otherwise inherit sitewide heading/body color
 * tokens. Mobile + tablet + desktop (no breakpoint scoping needed). */
.goal-steps__title,
.goal-steps__subtitle,
.goal-step h1,
.goal-step h2,
.goal-step h3,
.goal-step h4,
.goal-step h5,
.goal-step h6,
.goal-step p,
.goal-step li,
.goal-step strong,
.goal-step b {
	color: #000000 !important;
}

.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;
}

/* First step's own top padding duplicates .goal-steps__header's
 * margin-bottom (4rem) right above it, stacking into ~7rem of empty
 * space between the header and the first step — removing just this
 * step's top padding leaves the header's margin as the single,
 * consistent gap (matching every other step's own internal rhythm). */
.goal-steps__list > .goal-step:first-child {
	padding-top: 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 );
}

/* 2026-08-17, explicit request: reduces this outer gutter ring on
 * mobile/tablet only (desktop's 1.5rem is untouched) — combined with
 * .pb-contact__content's own reduced padding above, lands the total
 * edge-to-field spacing in the requested ~16-20px (mobile) /
 * ~24-32px (tablet) ranges. */
@media (max-width: 767.98px) {
	.pb-contact {
		padding: 8px;
	}
}

@media (min-width: 768px) and (max-width: 1023.98px) {
	.pb-contact {
		padding: 12px;
	}
}

.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;
}

/* 2026-08-17, explicit request: form was reading as excessively
 * indented on mobile/tablet — total edge-to-field gutter is this
 * padding PLUS .pb-contact's own outer ring padding (also reduced
 * below, same breakpoints). Hardcoded (not the --pbc-padding-mobile
 * var) so a larger per-instance custom value can't push this back
 * past the requested ~16-20px/~24-32px ranges. Desktop (>=1024px,
 * --pbc-padding-desktop) is a separate rule further down, untouched. */
@media (max-width: 767.98px) {
	.pb-contact__content {
		padding: 10px;
	}
}

@media (min-width: 768px) and (max-width: 1023.98px) {
	.pb-contact__content {
		padding: 16px;
	}
}

.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;
}

/* Mobile only (2026-08-05): the large gap between the tabs and the
 * active ConvertKit embed isn't from anything in this theme's own
 * Contact CSS (.pb-contact__tabs' own bottom margin is a modest
 * 1.25rem, and .pb-contact__form/-panel carry no spacing of their
 * own) — ConvertKit's embed script (f.convertkit.com/ckjs/ck.5.js)
 * injects the embedded form's CSS itself at runtime, with its own
 * top spacing tuned for a form dropped in standalone, not nested
 * inside an already-padded tab panel; there is no static stylesheet
 * of theirs to read/override at build time, and no existing rule
 * here targeted their classes at all before this. !important is
 * needed since that injected stylesheet can be added to the page
 * after this one regardless of source order. Only the outermost
 * embed wrapper's *top* spacing is reset — every field/label/button
 * spacing inside the embedded form (and the space this rule doesn't
 * touch, .pb-contact__tabs' own bottom margin) is untouched. Desktop
 * is unaffected entirely (outside this media query). */
@media (max-width: 767px) {
	.pb-contact__form-panel.is-active .formkit-form,
	.pb-contact__form-panel.is-active .seva-form,
	.pb-contact__form-panel.is-active [data-style] {
		margin-top: 0 !important;
		padding-top: 0 !important;
	}
}

.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 );
}

/* Editor-only: an embed exists in a slot that "Standaard actief
 * formulier" doesn't currently point at, so it won't render on the
 * frontend at all (2026-08-14) — solid border + stronger color than
 * --empty above, since this is a "you'll lose content" warning, not
 * just an unfilled field. */
.pb-contact__form-embed-note--warning {
	border-style: solid;
	border-color: rgba( 255, 77, 45, 0.6 );
	background: rgba( 255, 77, 45, 0.08 );
	color: rgba( 200, 50, 20, 0.95 );
	font-weight: 600;
}

/* 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-family: inherit;
	font-size: inherit;
	font-weight: inherit !important;
	box-sizing: border-box;
	appearance: none;
	-webkit-appearance: none;
}

/* `appearance: auto` (a prior version of this fix) restored the icon
 * but also brought back the browser's own native widget chrome
 * (height/padding/border), which clashed with this field's custom
 * underline/card styling and looked visibly inconsistent next to every
 * other field on mobile (explicit follow-up report). Keeping
 * appearance:none for the box itself instead, and independently
 * forcing the calendar-picker-indicator pseudo-element back on below
 * -- Chromium/WebKit normally suppress it too as part of removing the
 * native widget, but it can be shown again on its own regardless of
 * the parent's appearance. */
.pb-contact__form input[data-pbc-type="date"]::-webkit-calendar-picker-indicator {
	display: block !important;
	opacity: 1 !important;
	cursor: pointer;
	pointer-events: auto;
	position: absolute;
	right: 0.75rem;
	top: 50%;
	transform: translateY( -50% );
	width: 1.25rem;
	height: 1.25rem;
	z-index: 2;
}

/* iOS Safari border-radius clipping bug (2026-09-11, explicit mobile
 * report + screenshot — a small square notch of the native day/month/
 * year segment box cut into the field's rounded corner; desktop
 * unaffected). WebKit's native <input type="date"> internal rendering
 * doesn't reliably clip itself to an author-set border-radius on iOS;
 * forcing overflow:hidden on the input is a no-op on Chrome/Android
 * (which already clip correctly there) and fixes iOS. The mask-image
 * hack is the standard fallback for WebKit versions where
 * overflow:hidden alone isn't enough on replaced form controls — both
 * kept together for reliability across iOS versions. Doesn't affect
 * the picker-indicator icon above, which sits well inside the box. */
.pb-contact__form input[data-pbc-type="date"] {
	overflow: hidden;
	-webkit-mask-image: -webkit-radial-gradient( white, black );
}

/* 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;
}

/* Remark field ("Opmerking", name=fields[opmerking]) — retyped from
 * ConvertKit's own single-line <input> into a real <textarea> in
 * inc/contact-block.php's pb_contact_active_form_retype_remark_textarea()
 * (2026-09-21, explicit request: bigger writing area + shorter label).
 * The `.formkit-input` class already carries every border/radius/
 * padding/focus/placeholder rule above and in the --card variant below
 * regardless of tag name — this only adds the extra height/resize a
 * single-line input never needed, scoped to this one field via
 * data-pbc-type so no other textarea that might ever exist elsewhere
 * is affected. font-family is explicit (2026-09-22, explicit report +
 * screenshot): every browser's own UA stylesheet sets <textarea>'s
 * font-family directly (usually monospace), which wins over normal
 * inheritance from the form/body — every other field here is a plain
 * <input> and never needed this override, so it went unnoticed until
 * this field existed. */
.pb-contact__form textarea[data-pbc-type="textarea"] {
	min-height: 7.5rem;
	resize: vertical;
	line-height: 1.4;
	font-family: inherit;
}

/* 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;
}

/* Date field placeholder overlay (2026-08-15, rewritten) — the
 * `<input type="date">` inc/contact-block.php's
 * pb_contact_active_form_apply_field_types() renders is never retyped
 * to type="text" anymore (a prior version did that while empty, but
 * that swapped the browser's own native calendar-picker-indicator icon
 * for a CSS-drawn approximation, visibly changing the moment someone
 * clicked in — explicit request to stop). Instead
 * pb_contact_active_form_add_date_placeholder_overlay() inserts a
 * plain sibling `<span class="pbc-date-placeholder">` right after that
 * same <input>, and assets/js/main.js shows/hides it based on the
 * input's own value/focus state — this positions that span exactly on
 * top of the input it belongs to, EXCEPT its own right edge (below) —
 * left short of the input's full width on purpose, 2026-08-15 fix: an
 * earlier version spanned edge-to-edge (inset:0) and its own opaque
 * background ended up hiding the real native calendar-picker-indicator
 * icon too, not just the "dd-mm-jjjj" hint text it was meant to mask
 * (confirmed via screenshot — reproduced, then fixed by reserving a
 * fixed-width gap on the right the overlay never paints into, wide
 * enough for that native icon plus its own internal padding). Within
 * its own (now narrower) box, pointer-events:none still means every
 * click on the overlay's own area lands on the real input underneath;
 * the reserved icon gap was never covered in the first place, so a
 * click there has always gone straight to the native icon/picker
 * regardless. An opaque background matching each variant's own input
 * background (below) is still required, not optional, within that
 * narrower box — without it the browser's own native "dd-mm-jjjj" hint
 * would show through faintly from underneath, doubled up with this
 * span's own placeholder text on top of it. */
.formkit-field:has( input[data-pbc-type="date"] ) {
	position: relative;
}

/* 2026-08-17, explicit request: desktop's native date picker (macOS's
 * own control, shared by Safari/Chrome there) visibly greys out every
 * date before `min`; iOS's scroll-wheel date picker and Android's own
 * calendar widget expose no such disabled-date styling to page CSS at
 * all, so mobile visitors had no visual cue at all. Real DOM text (not
 * another absolutely-positioned overlay -- sits below the field in
 * normal flow instead, assets/js/main.js inserts/updates it), same
 * muted color/font as .pbc-date-placeholder right above so it reads as
 * part of the same field, not a separate warning. Desktop already
 * communicates this via its own native greyed-out days, so hidden there. */
.pbc-date-hint {
	display: none;
}

@media (max-width: 767.98px) {
	.pbc-date-hint {
		display: block;
		margin-top: 0.4rem;
		color: var( --pbc-placeholder-color, #7A7A7A );
		font: inherit;
		font-size: 0.8rem;
	}
}

.pbc-date-placeholder {
	position: absolute;
	/* 1px in from top/left/bottom (2026-08-15 fix) — the input's own
	 * border sits exactly at those outer edges (box-sizing:border-box);
	 * an inset:0 overlay with an opaque background painted directly on
	 * top of it, hiding that border on every side it fully spans (the
	 * right side was already excluded, via the icon gap, so it kept
	 * showing there — that's what made the missing border obvious).
	 * Sitting 1px inside instead leaves the real border fully visible
	 * on all sides while still fully masking the native text under it. */
	top: 1px;
	bottom: 1px;
	left: 1px;
	right: 2rem;
	z-index: 1;
	display: flex;
	align-items: center;
	pointer-events: none;
	padding: 0.6rem 0;
	background: transparent;
	color: var( --pbc-placeholder-color, #7A7A7A );
	font: inherit;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Mirrors .pb-contact__form--card .formkit-input's own padding/radius/
 * background (own rules further below) so the overlay text lines up
 * pixel-for-pixel with where the real input's text sits, and fully
 * masks the native hint text underneath within its own box (opaque
 * white, matching that variant's own white field background). Only the
 * LEFT corners are rounded to match the input's own — the right edge
 * stops short of the field's real right edge (above), so there's no
 * corner there to round. */
.pb-contact__form--card .pbc-date-placeholder {
	padding: 0.65rem 0 0.65rem 0.85rem;
	background: #ffffff;
	border-radius: 10px 0 0 10px;
}

@media (max-width: 767px) {
	.pb-contact__form--card .pbc-date-placeholder {
		padding: 1rem 0 1rem 1.25rem;
		border-radius: 0.75rem 0 0 0.75rem;
		/* Same font-size as every other field/the multiselect trigger
		 * right below it (2026-08-16, explicit request -- a previous
		 * smaller override here made this one field look inconsistent
		 * with its siblings). Wraps to 2 lines instead of truncating —
		 * the same "fits without shrinking text" behaviour the
		 * multiselect trigger's own long label already uses (explicit
		 * follow-up: nowrap+ellipsis was cutting the text off). */
		white-space: normal;
		overflow: visible;
		text-overflow: clip;
		line-height: 1.25;
		/* Now that this can be 2 lines tall inside a taller field
		 * (explicit follow-up), vertical centering made it look like it
		 * was floating in the middle instead of starting at the top
		 * like a normal field's text does. */
		align-items: flex-start;
		text-align: left;
	}

	/* The overlay is sized to match the input, not the other way
	 * around (position:absolute can't make a sibling grow) -- so the
	 * input itself needs the extra height for 2 lines of placeholder to
	 * have room to show.
	 *
	 * 2026-08-17, explicit report ("mist een styling streep"/screenshot):
	 * the original 3.4rem undershot what 2 lines actually need (15px
	 * font-size * 1.25 line-height * 2 lines ≈ 2.34rem text + 2rem
	 * vertical padding ≈ 4.3rem) -- the real input's border was closing
	 * ~1rem too early, so the wrapped second line ("ontvangen?") spilled
	 * out past the input's own bottom border with nothing containing it,
	 * reading as a missing/broken border compared to every other field's
	 * fully-closed box. */
	.pb-contact__form--card .formkit-field:has( input[data-pbc-type="date"] ) input[data-pbc-type="date"] {
		min-height: 4.5rem;
	}
}

/* "Hoe willen jullie PaperBrain® personaliseren?" checkbox group
 * (2026-08-15) — replaces what pb_contact_active_form_multiselect_
 * personalization() (inc/contact-block.php) turns from a single-select
 * Kit tag-dropdown into a `.pbc-multiselect` of real checkboxes, one
 * `<label class="pbc-multiselect__option">` per option. New classes
 * (not shared with anything else), so these rules are inherently safe
 * everywhere else in the site — this markup only ever exists on a form
 * that actually has this exact field. Row look/spacing/tap-target size
 * mirrors the "card" variant's own bordered input treatment (below) so
 * this reads as one visual family with the rest of the form, not a
 * bolted-on control; the "selected" border+tint uses the same purple
 * as the CTA (#6848f4) for a consistent PB accent color. accent-color
 * tints the native checkbox itself the same purple with no extra
 * markup/JS needed for that part. */
.pbc-multiselect {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	width: 100%;
}

/* label.pbc-multiselect__option (type selector added, not just the
 * class) + !important on display/margin/font-size — required to beat
 * .pb-contact__form label's own generic `display:block; margin:0 0
 * 0.35rem; font-size:0.75rem` (main.css, ~90 lines up: every
 * .formkit-field's field CAPTION uses a real <label>, and each
 * .pbc-multiselect__option is ALSO a real <label> semantically wrapping
 * its own checkbox, so that generic rule matches it too) — that rule's
 * own selector (`.pb-contact__form label`, one class + one type) is
 * MORE specific than a bare `.pbc-multiselect__option` class selector
 * alone, so without this the row collapsed to inline/shrink-width
 * content (display:block on a label doesn't itself become flex) and
 * read as small, off-center caption text instead of a normal-sized,
 * left-aligned option row — confirmed via screenshot, not guessed. */
label.pbc-multiselect__option {
	display: flex !important;
	align-items: center;
	gap: 0.6rem;
	margin: 0 !important;
	font-size: inherit !important;
	cursor: pointer;
	user-select: none;
}

.pbc-multiselect__option input[type="checkbox"] {
	width: 1.15rem;
	height: 1.15rem;
	flex-shrink: 0;
	accent-color: #6848f4;
	cursor: pointer;
}

.pbc-multiselect__option span {
	/* Was a hardcoded #000000 (2026-09-22, explicit report + screenshot:
	 * read visibly darker/bolder than every other field's own text
	 * color, which all resolve to the same muted placeholder tone while
	 * empty) — now shares that same token so it reads consistent with
	 * the rest of the form. */
	color: var( --pbc-placeholder-color, #7A7A7A );
	font-size: inherit;
	line-height: 1.4;
	/* Kit's own form CSS defaults to centered text; with no explicit
	 * text-align here that inherits through, only visible once a label
	 * is long enough to wrap to 2 lines (explicit report: "Geen
	 * personalisatie / standaard uitvoering" on mobile). !important to
	 * beat Kit's own stylesheet, same convention used throughout this
	 * form (see doc comment near the top of this section). */
	text-align: left !important;
}

.pb-contact__form--card .pbc-multiselect__option {
	border: 1px solid #e3e3e3;
	border-radius: 10px;
	padding: 0.65rem 0.85rem;
	transition: border-color 0.2s ease, background-color 0.2s ease;
}

.pb-contact__form--card .pbc-multiselect__option:has( input:checked ) {
	border-color: #6848f4;
	background: color-mix( in srgb, #6848f4 6%, transparent );
}

.pb-contact__form--card .pbc-multiselect__option:has( input:focus-visible ) {
	outline: 2px solid #6848f4;
	outline-offset: 2px;
}

@media (max-width: 767px) {
	.pb-contact__form--card .pbc-multiselect__option {
		padding: 1rem 1.25rem;
		border-radius: 0.75rem;
		border-color: #e2e2e2;
	}
}

/* Collapsed-by-default trigger (2026-08-16, explicit request — the
 * checkbox list above was permanently expanded, reading as one huge
 * field taking up as much vertical space as the other 4 combined).
 * The trigger mirrors the SAME two-tier look every other field in this
 * form already uses (plain underline by default, bordered/rounded
 * "card" variant below) so it reads as one more form field, not a
 * bolted-on control, and shows a live "N geselecteerd" count so the
 * selection stays visible even while the list itself is collapsed —
 * see assets/js/main.js for the toggle/count/validation behaviour. */
.pbc-multiselect-wrap {
	position: relative;
	width: 100%;
}

.pbc-multiselect__trigger {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.6rem;
	width: 100%;
	background: transparent;
	border: none;
	border-bottom: 1px solid color-mix( in srgb, var( --pbc-form-text-color, #111111 ) 35%, transparent );
	border-radius: 0;
	padding: 0.6rem 0;
	font: inherit;
	color: var( --pbc-form-text-color, #111111 );
	cursor: pointer;
	text-align: left;
}

.pbc-multiselect__trigger:focus-visible {
	outline: 2px solid #6848f4;
	outline-offset: 2px;
}

.pbc-multiselect__trigger-label {
	flex: 1 1 auto;
}

.pbc-multiselect__trigger-count {
	flex-shrink: 0;
	font-size: 0.8rem;
	color: #6848f4;
	font-weight: 600;
}

/* CSS-triangle chevron -- same technique already used for the native
 * Kit select's own arrow ([data-group="dropdown"]::before, above), so
 * this reads as the same visual language site-wide. */
.pbc-multiselect__trigger-icon {
	flex-shrink: 0;
	width: 0;
	height: 0;
	border-style: solid;
	border-width: 5px 4px 0 4px;
	border-color: var( --pbc-form-text-color, #111111 ) transparent transparent transparent;
	opacity: 0.7;
	transition: transform 0.2s ease;
}

.pbc-multiselect__trigger[aria-expanded="true"] .pbc-multiselect__trigger-icon {
	transform: rotate( 180deg );
}

.pbc-multiselect {
	margin-top: 0.6rem;
}

/* Author-origin `display:flex` on .pbc-multiselect (above) would
 * otherwise win over the UA stylesheet's own `[hidden]{display:none}`
 * (author rules always beat user-agent rules regardless of
 * specificity) -- without this, the `hidden` attribute PHP renders by
 * default would silently fail to actually hide the panel. */
.pbc-multiselect[hidden] {
	display: none;
}

.pbc-multiselect__error {
	color: #d33333;
	font-size: 0.8rem;
	margin: 0.4rem 0 0;
}

.pb-contact__form--card .pbc-multiselect__trigger {
	border: 1px solid #e3e3e3;
	border-radius: 10px;
	background-color: #ffffff;
	padding: 0.65rem 0.85rem;
	font-size: 0.95rem;
}

.pb-contact__form--card .pbc-multiselect__trigger:focus-visible,
.pb-contact__form--card .pbc-multiselect__trigger[aria-expanded="true"] {
	border-color: #6848f4;
}

@media (max-width: 767px) {
	.pb-contact__form--card .pbc-multiselect__trigger {
		padding: 1rem 1.25rem;
	}
}

.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;
}


/* Subtitle below the "Meld je aan" heading (2026-08-06) — a real
 * core/paragraph block (Retail page post_content), so the text itself
 * stays editable from wp-admin at any time; this only sets its visual
 * treatment. Same secondary-text feel as .pb-newsletter__text
 * (font-size 14px, ~0.85 opacity) elsewhere on this same page, just
 * centered and width-capped to read well under the centered heading
 * rather than spanning the full section width. */
.pb-contact-form-subtitle {
	max-width: 32rem;
	margin: 0 auto 0.5rem;
	opacity: 0.85;
	color: inherit;
}

/* Anchor scroll target for the Retail page's Partner worden section
 * (2026-08-06) — both the Hero CTA and the PB Newsletter CTA link to
 * #partner-form (post 363). scroll-margin-top reuses the same
 * --pb-header-height custom property the header itself keeps in sync
 * with its own real, current height (main.css ~line 6942) — same
 * mechanism already relied on elsewhere for "clear the sticky header"
 * offsets, not a new guessed value — so native browser anchor
 * scrolling lands the section below the sticky header with no JS. */
#partner-form {
	scroll-margin-top: var(--pb-header-height, 71px);
}

/* ---- "card" variant (2026-08-06) — large bordered/rounded light-card
 * presentation for standalone placements that don't use pb/contact's
 * own dark background card (e.g. the Retail page's Partner worden
 * form, placed via pb/contact-active-form directly with
 * variant="card" and hasTabs=false — no second copy of the form/CSS,
 * see inc/contact-block.php). Purely additive on top of everything
 * above: the 2-col field grid (@900px), submit button (reusing the
 * same --wp--custom--button--primary--* tokens as every other Primary
 * CTA site-wide), focus states, placeholder handling etc. all still
 * apply unchanged — only the input/select visual treatment (a real
 * border + radius instead of the dark card's underline style) and
 * outer card chrome differ. The default (no variant) Contact page
 * usage is completely untouched by everything below. */
.pb-contact__form--card {
	max-width: 760px;
	margin: 0 auto;
	background: #ffffff;
	border-radius: 20px;
	padding: 1.75rem;
}

.pb-contact__form--card .formkit-input,
.pb-contact__form--card .formkit-select,
.pb-contact__form--card input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
.pb-contact__form--card select {
	border: 1px solid #e3e3e3 !important;
	border-radius: 10px !important;
	background-color: #ffffff !important;
	padding: 0.65rem 0.85rem !important;
	font-size: 0.95rem;
}

.pb-contact__form--card .formkit-input:focus,
.pb-contact__form--card .formkit-select:focus,
.pb-contact__form--card input:focus,
.pb-contact__form--card select:focus {
	border-color: #6848f4 !important;
}

.pb-contact__form--card [data-group="dropdown"]::before {
	border-color: #4f4f4f transparent transparent transparent !important;
}

.pb-contact__form--card .formkit-label,
.pb-contact__form--card label {
	font-size: 0.85rem;
	margin-bottom: 0.3rem;
}

/* Tightened from 1.25rem/1.5rem (2026-08-06) — fields sit closer
 * together. grid-template-columns forced to 2-up unconditionally
 * (2026-08-06, was 1-col below the shared 900px breakpoint,
 * .pb-contact__form's own base rule) — the paired-field layout
 * (Naam+Achternaam, Telefoon+Straat) must look the same on mobile as
 * desktop for this variant specifically; the base rule's mobile
 * single-column behavior still applies unchanged to the Contact page's
 * own (non-card) usage. !important needed to win over that base rule
 * at equal specificity — this selector is later in source order. */
.pb-contact__form--card .formkit-fields {
	grid-template-columns: 1fr 1fr !important;
	row-gap: 0.3rem;
	column-gap: 0.75rem;
	margin-bottom: 0;
}

/* "single-column" fieldsLayout modifier (2026-08-14, opt-in per block
 * instance via pb/contact-active-form's fieldsLayout attribute — see
 * inc/contact-block.php/blocks/contact-active-form/edit.js) — forces
 * every .formkit-field onto its own full-width row instead of the
 * "card" variant's 2-up grid above, for placements where every
 * question needs to read as its own row (e.g. the B2B kerstpakket
 * form) without touching the "card" variant's OTHER usages, which
 * still want their 2-up pairs (Retail page's Naam+Achternaam,
 * Telefoon+Straat). Both classes land on the same element
 * (`pb-contact__form--card pb-contact__form--single-column`), so this
 * only needs to win the grid-template-columns declaration above —
 * !important required for the same reason documented there (equal
 * specificity, this selector wins by being later in source order, but
 * an explicit !important removes any doubt). Column-gap becomes moot
 * with a single column; row-gap and each field's own 6px bottom
 * margin (.pb-contact__form--card .formkit-field below) are left
 * exactly as-is — with every field now its own row, that existing gap
 * automatically becomes the consistent vertical rhythm between every
 * field instead of only between row-groups. No width/height/padding/
 * border/focus/CTA rule touched — the CTA already spans
 * `grid-column:1/-1; width:100%`, which is still full-width & correct
 * in a single-column grid.
 *
 * Mobile only (2026-08-15) — desktop now pairs Naam+Achternaam and
 * E-mail+Telefoon into the "card" variant's own 2-up grid (above)
 * instead of forcing 1-col everywhere; see the field-specific
 * grid-column overrides below for how that pairing is achieved. Below
 * 768px this still forces the original single-column behavior, in
 * keeping with this variant's own name. */
@media (max-width: 767px) {
	.pb-contact__form--single-column .formkit-fields {
		grid-template-columns: 1fr !important;
	}
}

/* Explicit row layout (2026-08-06) for the Partner worden field set —
 * Bedrijfsnaam, E-mail, KVK and the "Partner worden?" select each get
 * their own full-width row (spanning both grid columns forces the
 * next field to start a fresh row too), so Naam+Achternaam naturally
 * pair on the row after Bedrijfsnaam, and Telefoon pairs with the next
 * field (Straat + Huisnummer — there's no separate "Land"/country
 * field in this ConvertKit form to pair it with instead; adding one
 * would mean editing the embed's own field set, not a layout change).
 * Postcode+Stad pair naturally after that. Name-attribute :has()
 * selectors, same pattern already used for the Zakelijk/Algemeen
 * panels above — scoped to this variant only, the Contact page's own
 * Partner worden tab (same embed, default variant) is untouched. */
.pb-contact__form--card .formkit-field:has( input[name="fields[bedrijf]"] ),
.pb-contact__form--card .formkit-field:has( input[name="email_address"] ),
.pb-contact__form--card .formkit-field:has( input[name="fields[kvk]"] ),
.pb-contact__form--card .formkit-field:has( select[name="tags[]"] ),
.pb-contact__form--card .formkit-field:has( .pbc-multiselect ) {
	grid-column: 1 / -1;
}

/* Desktop pairing for the "single-column" variant (2026-08-15) — Naam+
 * Achternaam and E-mail+Telefoon share a row; every other field
 * (Hoeveel PaperBrains, Wanneer, Personaliseren, Bedrijfsnaam,
 * Toelichting/wensen) stays its own full-width row. Same :has()-by-name
 * technique as the Zakelijk/Algemeen panel rules and the Partner-worden
 * row layout just above — grid auto-placement naturally slots the two
 * un-forced pairs into the 2-up grid without naming them directly.
 * Bedrijfsnaam/Personaliseren are already forced full-width by the
 * Partner-worden rule just above (shared --card class) — restated here
 * so this block stays self-contained and doesn't silently depend on
 * that other form's field set staying unchanged. email_address is ALSO
 * forced full-width by that same Partner-worden rule though — un-forced
 * back to auto directly below so it can pair with Telefoon here instead
 * (Retail's own Partner-worden form still needs E-mail full-width, and
 * is untouched — this override is scoped to --single-column only).
 * Equal specificity to the rule above (one ancestor class + one
 * :has() selector); wins by being later in source order, !important
 * added to remove any doubt (same convention already used throughout
 * this file). No media query needed — grid-column:1/-1 is a harmless
 * no-op inside the mobile single-column grid above.
 *
 * Personaliseren's own selector matches `.pbc-multiselect` (2026-08-15
 * update), not `select[name="tags[]"]` — that field is no longer a
 * <select> at all now that pb_contact_active_form_multiselect_
 * personalization() (inc/contact-block.php) replaces it with a
 * checkbox group; the old select-based selector silently stopped
 * matching anything here the moment that shipped, which would have
 * left this one field unforced and half-width with an empty gap next
 * to it (confirmed by tracing the grid auto-placement order, caught
 * before it ever rendered that way live). */
.pb-contact__form--single-column .formkit-field:has( input[name="fields[geef_een_inschatting_van_de_aantallen]"] ),
.pb-contact__form--single-column .formkit-field:has( input[name="fields[wanneer_zouden_jullie_de_paperbrains_willen_ontvangen]"] ),
.pb-contact__form--single-column .formkit-field:has( .pbc-multiselect ),
.pb-contact__form--single-column .formkit-field:has( input[name="fields[bedrijf]"] ),
.pb-contact__form--single-column .formkit-field:has( input[name="fields[opmerking]"] ) {
	grid-column: 1 / -1 !important;
}

.pb-contact__form--single-column .formkit-field:has( input[name="email_address"] ) {
	grid-column: auto !important;
}

/* Field/select/placeholder text color consistency (2026-08-15, explicit
 * request) — filled text, select text and placeholder text (incl.
 * textarea, defensively, even though this embed currently has no
 * textarea field) all read as pure #000000 at full opacity. Scoped to
 * this variant only — the base .pb-contact__form rule's own
 * --pbc-form-text-color/--pbc-placeholder-color tokens (#111111/
 * #7A7A7A, ~40 lines up) are the shared defaults for the Contact page's
 * dark-card (default, non-"card") instance and the Retail page's own
 * --card instance (Partner worden), neither of which was asked for
 * here — changing those shared tokens directly would change colors on
 * pages nobody asked to touch. !important required: ConvertKit prints a
 * literal `style="color:..."` on every <input>/<select>, plus its own
 * uid-scoped <style> block for ::placeholder (color:inherit;
 * opacity:.8) — both load after this stylesheet, so a plain
 * (non-!important) rule is silently ignored, same reasoning already
 * documented on .pb-contact__form's own base color rules above. */
/* 2026-08-17, explicit request: reverted the #000000 overrides below
 * (was a deliberate earlier choice, see the removed comment's own
 * reasoning: "no grey placeholders, filled text and placeholder text
 * the same color") — B2B's form was reading noticeably darker than
 * Retail's, which uses this same .pb-contact__form--card variant
 * WITHOUT --single-column and therefore gets the shared
 * --pbc-form-text-color/--pbc-placeholder-color tokens (#111111 filled
 * text / #7A7A7A placeholder, ~line 10033/10074) instead. This variant
 * now falls back to those exact same shared tokens/values, matching
 * Retail exactly. Field layout (single-column grid) is untouched —
 * only these three color rules changed. */
.pb-contact__form--single-column .formkit-input,
.pb-contact__form--single-column .formkit-select,
.pb-contact__form--single-column input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
.pb-contact__form--single-column select,
.pb-contact__form--single-column textarea {
	color: var( --pbc-form-text-color, #111111 ) !important;
}

.pb-contact__form--single-column .formkit-input::placeholder,
.pb-contact__form--single-column input::placeholder,
.pb-contact__form--single-column textarea::placeholder {
	color: var( --pbc-placeholder-color, #7a7a7a ) !important;
	opacity: 1 !important;
}

.pb-contact__form--single-column .pbc-date-placeholder {
	color: var( --pbc-placeholder-color, #7a7a7a ) !important;
}

/* Same reasoning as .pb-contact .formkit-powered-by-convertkit-container
 * above — that rule is scoped to the .pb-contact wrapper this variant
 * doesn't have, so it needs its own copy here rather than relying on
 * an ancestor that isn't present in a standalone placement. */
/* Individual field bottom-margin (2026-08-07) -- reduced ~40% from
 * the ConvertKit embed's own default (15px, set inline by the embed's
 * own uid-scoped <style> tag, which renders later in the DOM than
 * this stylesheet and so wins at equal specificity -- !important
 * needed to reliably beat it). Combined with the row-gap reduction
 * just above, total vertical space between rows drops by ~40%. */
.pb-contact__form--card .formkit-field {
	margin-bottom: 6px !important;
}

.pb-contact__form--card .formkit-powered-by-convertkit-container {
	display: none !important;
}

/* Mobile only (2026-08-06, rewritten) — matched byte-for-byte against
 * this same page's own .faq block (pb/faq, homepage-blocks.php /
 * main.css ~line 4567) instead of guessed values: .faq itself has
 * `padding: 4rem 1.25rem` (the 1.25rem horizontal is the page's own
 * established side inset — never actually edge-to-edge/0), .faq__list
 * has `gap: 0.75rem` between items, and each .faq__item is its own
 * bordered/rounded card (`border:1px solid #e2e2e2; border-radius:
 * 0.75rem; padding:1rem 1.25rem`). The outer .pb-contact__form--card
 * no longer draws its own second border/background here — each field
 * is already its own bordered "item" below, so this mirrors .faq's
 * actual structure (padded section > flat list of bordered items)
 * instead of a card-within-a-card. */
@media (max-width: 767px) {
	.pb-contact__form--card {
		max-width: none !important;
		margin: 0 !important;
		padding: 0;
		border: none;
		border-radius: 0;
		background: transparent;
	}

	.pb-contact__form--card .formkit-input,
	.pb-contact__form--card .formkit-select,
	.pb-contact__form--card input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
	.pb-contact__form--card select {
		padding: 1rem 1.25rem !important;
		border-radius: 0.75rem !important;
		border-color: #e2e2e2 !important;
	}

	.pb-contact__form--card .formkit-fields {
		row-gap: 0.3rem !important;
		column-gap: 0.75rem !important;
	}

	/* First two fields' placeholders only (2026-08-14) — long Dutch copy
	 * ("Hoeveel PaperBrains willen jullie aanvragen?" / "Wanneer willen
	 * jullie de bestelling ontvangen?") overflowed/got clipped inside the
	 * 2-up mobile grid's narrow columns. Scoped to these two fields by
	 * `name` so every other field's placeholder size is untouched; only
	 * the placeholder text is resized, not what a visitor actually types
	 * (that stays at the card variant's own 0.95rem). !important needed
	 * for the same reason already documented above for
	 * .formkit-input::placeholder — ConvertKit's own uid-scoped <style>
	 * block (loads after this stylesheet) sets its own placeholder
	 * styling that would otherwise win. */
	.pb-contact__form--card input[name="fields[geef_een_inschatting_van_de_aantallen]"]::placeholder,
	.pb-contact__form--card input[name="fields[wanneer_zouden_jullie_de_paperbrains_willen_ontvangen]"]::placeholder {
		font-size: 0.8rem !important;
	}

	/* Root cause found via DevTools (2026-08-06): .site-main (main.css
	 * ~line 834) has its own sitewide 32px left/right padding — every
	 * previous fix here zeroed/matched THIS group's own margin/padding
	 * but never accounted for that outer wrapper, so the group (and
	 * the card inside it) still sat 32px inset from the real screen
	 * edge on top of its own padding. .faq escapes that exact same
	 * padding via its own .alignfull breakout (position:relative;
	 * left:50%;width:100vw;margin-left:-50vw — main.css ~line 4601);
	 * this is the identical technique, applied here so the form's
	 * wrapping section actually matches .faq's real, escaped alignment
	 * instead of just copying its inner 1.25rem padding value onto a
	 * still-inset container. */
	.wp-block-group:has( > .pb-contact__form--card ) {
		position: relative;
		left: 50%;
		width: 100vw;
		max-width: 100vw;
		margin-left: -50vw;
		margin-right: -50vw;
		padding-left: 1.25rem !important;
		padding-right: 1.25rem !important;
		box-sizing: border-box;
	}
}
/* ---- 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;
}

/**
 * PB Benefits Grid (pb/benefits-grid + pb/benefit-item) — reusable,
 * reorderable voordelen/benefits section. Columns/gaps/content width
 * come from the block's own inline custom properties (see
 * pb_render_benefits_grid_block(), inc/benefits-grid-block.php);
 * mobile always stacks to 1 column regardless of the desktop columns
 * setting, tablet steps up to 2, same breakpoint convention as
 * .pb-blog-grid__grid.
 */
.benefits-grid {
	padding: 3.5rem 1.25rem;
	background-color: var(--wp--preset--color--background, #fff);
}

/* True edge-to-edge on desktop (2026-08-06) -- .site-main's own
 * sitewide horizontal padding otherwise still constrains this section
 * even with the alignfull class present; same breakout technique as
 * .faq.alignfull (outer section breaks out full viewport width, inner
 * content column keeps its own max-width/centering). */
.benefits-grid.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.benefits-grid__inner {
	max-width: var(--pb-benefits-content-max-width, 1440px);
	margin: 0 auto;
}

.benefits-grid__title {
	text-align: center;
	margin: 0 0 2rem;
}

.benefits-grid__title .has-weight-regular {
	font-weight: 400 !important;
}

/* Optional title below the item grid (2026-08-06) -- same
 * .benefits-grid__title look, margin flipped to sit above this
 * element instead of below it. */
.benefits-grid__title--below {
	margin: 3.5rem 0 0;
}

.benefits-grid__grid {
	display: grid;
	grid-template-columns: 1fr;
	row-gap: var(--pb-benefits-row-gap, 2rem);
	column-gap: var(--pb-benefits-column-gap, 2rem);
}

.benefits-grid__item {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

.benefits-grid__item--divider {
	padding-top: 1.5rem;
	border-top: 1px solid var(--wp--preset--color--border, rgba(17, 17, 17, 0.12));
}

.benefits-grid__item-title {
	margin: 0;
}

.benefits-grid__item-body {
	margin: 0;
	color: #555;
}

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

	.benefits-grid__grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 1024px) {
	.benefits-grid {
		padding: 5rem 1.25rem;
	}

	.benefits-grid__grid {
		grid-template-columns: repeat(var(--pb-benefits-columns, 3), 1fr);
	}
}

/**
 * PB Retail Segment Grid (pb/retail-segment-grid + pb/retail-segment-card)
 * — reusable, reorderable image+title+text+CTA card grid. Layout pattern
 * inspired by an Upfront.nl reference screenshot, recreated fully with
 * PaperBrain's own design system (no copied branding/content/styling).
 * Mobile always stacks to 1 column, desktop uses the configurable
 * --pb-retail-segment-columns var (default 3), same breakpoint
 * convention as .pb-blog-grid__grid/.benefits-grid__grid.
 */
.retail-segment-grid {
	padding: 3.5rem 1.25rem;
	background-color: var(--wp--preset--color--background, #fff);
	/* Anchor-scroll offset (2026-09-11, explicit request) — same
	 * --pb-header-height token/pattern already used for #partner-form
	 * (main.css, ~line 10673), so a same-page anchor link (e.g. the B2B
	 * Hero CTA's "Ontdek mogelijkheden" -> #b2b-onboarding-aanbod) lands
	 * with the title clear of the sticky header instead of tucked
	 * behind it. Harmless when this block isn't itself an anchor
	 * target. */
	scroll-margin-top: var(--pb-header-height, 71px);
}

/* True edge-to-edge on desktop -- .site-main's own sitewide horizontal
 * padding otherwise still constrains this section even with the
 * alignfull class present; same breakout technique as .faq.alignfull /
 * .benefits-grid.alignfull (outer section breaks out full viewport
 * width, inner content column keeps its own max-width/centering). */
.retail-segment-grid.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

.retail-segment-grid__inner {
	max-width: var(--pb-retail-segment-content-max-width, 1440px);
	margin: 0 auto;
}

.retail-segment-grid__title {
	text-align: center;
	margin: 0 0 2rem;
}

/* Optional subtitle (pb/retail-segment-grid's "subtitle" attribute):
 * absent entirely from the DOM when empty (see render_callback), so an
 * existing grid without one renders byte-identical to before this was
 * added -- no extra spacing leaks in. When present, the title's own
 * bottom margin shrinks to a tighter title-to-subtitle gap (via the
 * --has-subtitle modifier the render_callback adds to the title itself)
 * and the subtitle carries the 2rem gap down to the grid that the title
 * used to provide alone. Both gaps stay overridable per-instance via
 * the block's existing spacing system (see pb_retail_segment_grid_
 * spacing_elements()'s "subtitle" entry / edit.js's SPACING_ELEMENTS). */
.retail-segment-grid__title--has-subtitle {
	margin-bottom: 0.75rem;
}

.retail-segment-grid__subtitle {
	text-align: center;
	/* #111111 (2026-09-11 explicit request: "lichtzwart" read as too
	 * grey) — matches the theme's own "real black" convention used
	 * elsewhere (e.g. --wp--custom--typography--h-1--color default),
	 * not pure #000. */
	color: #111111;
	margin: 0 0 2rem;
}

.retail-segment-grid__grid {
	display: grid;
	grid-template-columns: 1fr;
	row-gap: 2.5rem;
	column-gap: var(--pb-retail-segment-column-gap, 2rem);
}

.retail-segment-card {
	display: flex;
	flex-direction: column;
}

.retail-segment-card__media {
	margin-bottom: 1.25rem;
	overflow: hidden;
	border-radius: var(--pb-retail-segment-image-radius, 16px);
}

.retail-segment-card__image {
	width: 100%;
	height: auto;
	display: block;
	aspect-ratio: var(--pb-retail-segment-image-aspect-ratio, 1/1);
	object-fit: cover;
	border-radius: var(--pb-retail-segment-image-radius, 16px);
	transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

@media (hover: hover) {
	.retail-segment-card:hover .retail-segment-card__image,
	.retail-segment-card:focus-within .retail-segment-card__image {
		transform: scale(1.06);
	}
}
/* Mobile no longer overrides the aspect ratio (2026-08-16, explicit
 * request — images should match desktop's size/height, not the
 * previous shorter 280x180 mobile-only ratio). It now just inherits
 * the same --pb-retail-segment-image-aspect-ratio custom property
 * (default 1/1) as desktop/tablet, from .retail-segment-card__image's
 * own base rule above — object-fit/border-radius/spacing untouched,
 * width stays 100% so this can't cause horizontal overflow. */

.retail-segment-card__body {
	display: flex;
	flex-direction: column;
	flex-grow: 1;
}

.retail-segment-card__title {
	margin: 0 0 0.5rem;
	font-size: 21px !important;
}

.retail-segment-card__text {
	margin: 0;
	color: #666;
	flex-grow: 1;
	font-size: 14px !important;
}

.retail-segment-card__cta-row {
	margin-top: 1rem;
}

.retail-segment-card__link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	color: var(--wp--preset--color--secondary, #111);
	text-decoration: none;
	font-weight: 600;
	font-size: 14px;
	border-bottom: 1px solid transparent;
	transition: border-color 0.2s ease, gap 0.2s ease;
}

.retail-segment-card__link:hover,
.retail-segment-card__link:focus-visible {
	border-bottom-color: currentColor;
}

.retail-segment-card__link:hover .retail-segment-card__arrow,
.retail-segment-card__link:focus-visible .retail-segment-card__arrow {
	transform: translateX(3px);
}

.retail-segment-card__arrow {
	display: inline-block;
	transition: transform 0.2s ease;
}

@media (min-width: 768px) {
	.retail-segment-grid {
		padding: 4.5rem 2.5rem;
	}

	.retail-segment-grid__grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 1024px) {
	.retail-segment-grid {
		padding: 5rem 3rem;
	}

	.retail-segment-grid__grid {
		/* auto-fit + a fixed max column width (computed from the
		 * configured column count) instead of a flat repeat(N, 1fr):
		 * when there are fewer real .retail-segment-card children than
		 * --pb-retail-segment-columns, empty trailing tracks collapse
		 * and justify-content centers the resulting (shorter) row —
		 * cards keep the same width they'd have in a full row, they
		 * just don't stretch to fill it. Full rows render identically
		 * to the previous repeat(N, 1fr) since all tracks are filled. */
		grid-template-columns: repeat(auto-fit, minmax(0, calc((100% - (var(--pb-retail-segment-columns, 4) - 1) * 2rem) / var(--pb-retail-segment-columns, 4))));
		justify-content: center;
		row-gap: var(--pb-retail-segment-row-gap, 2.5rem);
		column-gap: 2rem;
	}
}

/**
 * Transparent header over hero (pb/hero-centered's "Transparante
 * header over hero" toggle, see inc/hero-centered-block.php) — the
 * data-header-style="transparent" variant this codebase already
 * anticipated (see the doc comment at the very top of this file, near
 * .site-header-wrap). Transparent only until .is-scrolled (that class
 * already exists, toggled by assets/js/main.js on scroll) — once
 * scrolled, no override matches and .site-header falls back to its
 * normal solid/white rule above, unchanged.
 */
.site-header[data-header-style="transparent"]:not(.is-scrolled) {
	background: transparent;
	backdrop-filter: none;
	border-bottom-color: transparent;
}

.site-header[data-header-style="transparent"]:not(.is-scrolled) .pb-mini-cart-trigger {
	color: #ffffff;
}

.site-header[data-header-style="transparent"]:not(.is-scrolled) .pb-mini-cart-trigger svg path:nth-child(2) {
	fill: #000000;
}

.site-header[data-header-style="transparent"]:not(.is-scrolled) .menu-toggle {
	color: #ffffff;
}

.site-branding__gold-logo-link {
	display: none !important;
	align-items: center;
}

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

@media (min-width: 768px) {
	.site-branding__gold-logo {
		max-width: var(--pb-header-logo-width-desktop, 78px);
	}
}

.site-header[data-header-style="transparent"]:not(.is-scrolled) .custom-logo-link:not(.site-branding__gold-logo-link) {
	display: none !important;
}

.site-header[data-header-style="transparent"]:not(.is-scrolled) .site-branding__gold-logo-link {
	display: inline-flex !important;
}

.site-header-wrap:has( .site-header[data-header-style="transparent"]:not(.is-scrolled) ) .site-topbar {
	background: transparent;
}

/* Pulls the hero section (and its absolutely-positioned
 * .pb-hc-background child, which fills the section's own box edge to
 * edge) up underneath the transparent header, so the background image
 * visually continues behind it instead of starting below a white bar.
 * !important: needed to beat both the base .pb-hero-centered{margin-
 * top:0} rule and the .entry-content > .pb-hero-centered:first-child
 * -22px correction above (same specificity, earlier in the file). */
.pb-hero-centered--transparent-header {
	margin-top: calc(-1 * var(--pb-header-height, 71px)) !important;
}

/* Adds that same amount back as extra top padding on the *content*
 * only, so the heading/logo/etc. still start at roughly the same
 * visual position as a non-transparent hero -- only the section's own
 * background extends further up, not its actual content. */
.pb-hero-centered--transparent-header .pb-hero-centered__inner {
	padding-top: calc(var(--pb-hc-padding-top, 96px) + var(--pb-header-height, 71px));
}

/* Subtle top-down darkening so the header's own nav/logo/icons/CTA
 * (left at their normal colors -- no light/dark swap, since not every
 * uploaded hero photo can be assumed light or dark) stay readable
 * against whatever is directly behind them, regardless of the photo's
 * own colors. Sits between .pb-hc-background (z-index:-1) and the
 * normal-flow .pb-hero-centered__inner content (effectively on top of
 * both non-positioned and negatively-stacked siblings). */
.pb-hero-centered--transparent-header::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	z-index: 0;
	height: calc(var(--pb-header-height, 71px) + 140px);
	background: linear-gradient(to bottom, rgba(0, 0, 0, 0.32), rgba(0, 0, 0, 0));
	pointer-events: none;
}

@media (max-width: 767.98px) {
	.pb-hero-centered--transparent-header::before {
		height: calc(var(--pb-header-height, 71px) + 90px);
	}
}

/* 2026-08-17, explicit request: Retail page (post 363) hero — mobile
 * fullscreen, matching the B2B kerstpakket hero's own
 * .pb-b2b-hero--transparent-header behaviour (100vh/100svh, svh
 * intentionally used over dvh per that block's own established
 * reasoning: avoids resize/jump as mobile browser chrome shows/hides
 * on scroll). Retail uses the generic pb/hero-centered block, whose
 * --transparent-header modifier only handles the header-overlay
 * positioning, not fullscreen height (it relies on the instance's own
 * fixed paddingTop/paddingBottom instead) — this adds the missing
 * min-height. Scoped to this one page + mobile only, so every other
 * pb/hero-centered instance sitewide and desktop are both untouched. */
@media (max-width: 767.98px) {
	body.page-id-363 .pb-hero-centered--transparent-header {
		/* The section is also pulled UP by margin-top:-headerHeight (its
		 * own --transparent-header rule, unconditional) so it starts
		 * behind the header — with a plain 100svh, its BOTTOM edge then
		 * falls exactly headerHeight short of the true viewport bottom,
		 * reading as "not quite filling the screen". Adding the header
		 * height back here compensates, so the visible area is a genuine
		 * full viewport again. */
		min-height: calc(100vh + var(--pb-header-height, 71px)) !important;
		min-height: calc(100svh + var(--pb-header-height, 71px)) !important;
	}
}

/**
 * PB B2B Hero (pb/b2b-hero + pb/b2b-hero-eyebrow/-heading/-body/-cta) —
 * reusable full-bleed photo hero for B2B landing pages: left-aligned
 * text over a cover background image, product/photo kept visible on
 * the right. See inc/b2b-hero-block.php's own doc comment for the full
 * architecture rationale. Entirely separate class namespace from
 * .pb-hero-centered/.pb-hc-* — this block must never affect any
 * existing pb/hero-centered instance, so nothing here is shared beyond
 * the underlying PHP/JS helper functions (typography, spacing, CTA,
 * focal point), never the CSS classes themselves.
 */
.pb-b2b-hero {
	position: relative;
	z-index: 0;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	margin-top: 0;
	overflow: hidden;
	display: flex;
	align-items: center;
	/* Asymmetric top/bottom padding (less above, more below) biases the
	 * content group toward the upper-middle of the hero rather than
	 * dead-center — same technique already used for .pb-hero-centered's
	 * own instances (paddingTop < paddingBottom), just as this block's
	 * own sane default instead of a per-instance authored value. Fully
	 * overridable via the block's "Sectie" spacing element. */
	padding: 3rem 0 5rem;
	color: #ffffff;
}

.entry-content > .pb-b2b-hero:first-child {
	margin-top: -22px;
}

/* Mobile, transparent-header instance only (2026-08-14) — the generic
 * -22px above is a "looks right" approximation for the normal case,
 * not a full cancellation of .site-main's own 32px top padding (main.css
 * ~line 846, applies on every page without a "-page-wrap" body class,
 * including this one — confirmed via the live page's own
 * class="site-main" with no extra class). That left a real ~10px gap
 * at the true top of the page even after removing the header from flow
 * (.site-header-wrap's position:fixed above). Higher specificity than
 * the plain .pb-b2b-hero--transparent-header{margin-top:0} rule
 * elsewhere in this file (adds .entry-content + :first-child), so this
 * wins for this exact "first block on the page" case without changing
 * that other rule's own fallback for any transparent-header instance
 * that isn't the page's first block. */
@media (max-width: 767.98px) {
	.entry-content > .pb-b2b-hero--transparent-header:first-child {
		margin-top: -32px !important;
	}
}

.pb-b2b-hero__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
	background-size: cover;
	background-repeat: no-repeat;
	background-position: var(--pbh-bg-position, 50% 50%);
}

/* Mobile-only stacked-layout image (2026-09-11) — see
 * inc/b2b-hero-block.php's own comment on this element. Hidden by
 * default/desktop: .pb-b2b-hero__bg above is still the only image
 * desktop ever shows, completely unchanged. */
.pb-b2b-hero__mobile-image {
	display: none;
}

.pb-b2b-hero__overlay {
	position: absolute;
	inset: 0;
	z-index: 1;
	background: var(--pbh-overlay-bg, transparent);
	opacity: var(--pbh-overlay-opacity, 0);
	pointer-events: none;
}

.pb-b2b-hero__inner {
	position: relative;
	z-index: 2;
	width: 100%;
	max-width: var(--wp--custom--section--container-width, 1440px);
	margin: 0 auto;
	padding-left: 5vw;
	padding-right: 5vw;
}

.pb-b2b-hero__content {
	text-align: left;
	color: inherit;
}

.pb-b2b-hero-eyebrow__text {
	margin: 0 0 0.75rem;
	color: inherit;
}

/* Regular base weight, <strong> stands out — same pattern as
 * .pb-cb-heading__text/.pb-image-text__heading (design-system.css's
 * bare h1..h6{font-weight:...!important} reset otherwise leaves the h1
 * preset's own 800 base nearly indistinguishable from <strong>'s
 * generic +100-capped-at-900 boost — bold looked "already on"). An
 * explicit per-instance "Tekstdikte override" still wins: inline
 * !important beats external !important (pb_typography_override_style()
 * now also emits !important for font-weight, same fix). */
.pb-b2b-hero-heading__text {
	margin: 0 0 1rem;
	color: inherit;
	font-weight: 400 !important;
}

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

/* Desktop-only 40px override (2026-09-11, explicit request), scoped to
 * body.page-id-365 (this page's own B2B Hero instance only) so other
 * pb/b2b-hero-heading usages elsewhere are untouched. Only font-size
 * changes — font-family/weight/line-height/color/letter-spacing/
 * transform still all come from the shared .pb-type--h1 base rule
 * (design-system.css). !important required: design-system.css's own
 * bare `h1{font-size:...!important}` reset (same file/pattern already
 * documented above for font-weight) also matches this element and
 * otherwise wins regardless of this rule's higher selector specificity
 * — confirmed live (computed 32px, the theme's --...-size-desktop
 * token value, instead of 40px, before adding !important here).
 * Mobile/tablet keep .pb-type--h1's own mobile/tablet sizes untouched. */
@media ( min-width: 1024px ) {
	body.page-id-365 .pb-b2b-hero-heading__text.pb-type--h1 {
		font-size: 40px !important;
	}

	/* 2026-09-16, explicit request: "je" (of "Een eerste indruk maak je")
	 * should move down to join "maar één keer." on line 2, instead of
	 * staying on line 1 with "Een eerste indruk maak". Line--1 (already
	 * display:inline-block, see the reveal-animation rule above) is one
	 * continuous run that soft-wraps on its own — at this column's full
	 * width (measured live: 518px) "...maak je" (513px, Poppins 400 40px)
	 * just barely fits, "...maak" alone is 467px. max-width:490px sits
	 * between those two, so the browser's own word-wrap naturally breaks
	 * after "maak" instead — no hardcoded <br>, so a shorter/longer edit
	 * to this line's own text still wraps sensibly within that same cap. */
	body.page-id-365 .pb-b2b-hero-heading__line--1 {
		max-width: 490px;
	}
}

.pb-b2b-hero-body__text {
	margin: 0 0 1.5rem;
	color: inherit;
}

/* RichText <strong> inside the hero body was falling through to
 * design-system.css's generic `.pb-type--body strong` formula (400+100
 * = 500, too subtle to read as bold) — same fix already applied to
 * .pb-hc-body__text/.pb-image-text__body strong. No base-weight
 * !important needed here (unlike the heading above): this element is a
 * plain <div> (PB Rich Paragraph's fixed tagName), so there is no bare
 * h1..h6 sitewide reset fighting it — body's own "body" preset already
 * defaults to 400. */
.pb-b2b-hero-body__text strong {
	font-weight: 700;
}

/* display:flex is a no-op for the pre-existing single-CTA case (one
 * flex item lays out identically to a plain block wrapper around it) —
 * it only matters once an optional second CTA is present, laying the
 * two .pb-button links out in a row with a clean gap and wrapping to a
 * new line on narrow viewports instead of overflowing. */
.pb-b2b-hero-cta {
	margin-top: 0.5rem;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
}

/* Sequential fade-in (2026-09-11, explicit request; extended same day
 * to add the body text and switch from load-triggered to scroll-
 * triggered) — heading line 1, then line 2, then the body text, then
 * the CTA row. Reuses pb/animated-statement's own opacity+transform+
 * cubic-bezier "come into view" mechanic and is-revealed class-flip
 * pattern, and (as of this update) its IntersectionObserver scroll-
 * reveal trigger too, at a faster, coarser (heading lines/body/CTA as
 * whole units, not per-word) pace — this hero is the page's SECOND
 * section (below pb/logo-reveal-hero's full-viewport intro), not above
 * the fold, so it needs the same "reveal once it's actually scrolled
 * into view" trigger as pb/animated-statement, not an immediate-on-load
 * one (assets/js/main.js's own comment on the .pb-b2b-hero observer has
 * the full history — an earlier load-triggered version finished
 * animating off-screen before any visitor could see it). html.js-gated
 * the same way as every other progressive-enhancement animation in
 * this file, so nothing is ever permanently hidden without JS, and no
 * layout shift — opacity/transform only, box size/position untouched.
 * Heading lines rendered by pb_render_b2b_hero_heading_block()
 * (inc/b2b-hero-block.php); the body text/CTA row are the existing
 * .pb-b2b-hero-body__text/.pb-b2b-hero-cta elements directly, no markup
 * change needed for either. */
html.js .pb-b2b-hero-heading__line,
html.js .pb-b2b-hero-body__text,
html.js .pb-b2b-hero-cta {
	opacity: 0;
	transform: translateY(10px);
	transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94), transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

html.js .pb-b2b-hero-heading__line {
	display: inline-block;
}

html.js .pb-b2b-hero-heading__line--1 {
	transition-delay: 0.15s;
}

html.js .pb-b2b-hero-heading__line--2 {
	transition-delay: 0.5s;
}

html.js .pb-b2b-hero-body__text {
	transition-delay: 0.85s;
}

html.js .pb-b2b-hero-cta {
	transition-delay: 1.15s;
}

html.js .pb-b2b-hero.is-revealed .pb-b2b-hero-heading__line,
html.js .pb-b2b-hero.is-revealed .pb-b2b-hero-body__text,
html.js .pb-b2b-hero.is-revealed .pb-b2b-hero-cta {
	opacity: 1;
	transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
	html.js .pb-b2b-hero-heading__line,
	html.js .pb-b2b-hero-body__text,
	html.js .pb-b2b-hero-cta {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* Retail-style underline under "Zorg dat die klopt." (2026-09-11,
 * explicit request) — reuses the exact same @keyframes
 * pb-hero-centered-underline / ::after recipe already established for
 * the Retail (body.page-id-363) and homepage (body.page-id-27) hero
 * headings (see that rule, ~line 8730): currentColor, 0.07em thick,
 * 0.7s ease-out draw, same distance-from-text (bottom:-0.06em). Trigger
 * differs on purpose, not by accident: those heroes are always in view
 * on load, so their underline draws 0.5s after page load; this hero is
 * the page's SECOND section (below pb/logo-reveal-hero's full-viewport
 * intro) with its own scroll-triggered sequential fade-in (see
 * .pb-b2b-hero-heading__line rules above) — gated on the same
 * .pb-b2b-hero.is-revealed flip instead of page load, so the underline
 * can only ever start once this hero has actually scrolled into view,
 * with a 1s delay so it only draws once "Zorg dat die klopt." (line 2,
 * fades in 0.5s→1s) has itself finished appearing, per explicit
 * requirement ("pas zichtbaar/animeren wanneer deze tweede tekst in
 * beeld komt"). Scoped to line 2's own <strong> only, not line 1 (not
 * bold, never a match anyway) or any other B2B Hero instance. */
body.page-id-365 .pb-b2b-hero-heading__line--2 strong {
	position: relative;
	display: inline-block;
}

body.page-id-365 .pb-b2b-hero-heading__line--2 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-365 .pb-b2b-hero.is-revealed .pb-b2b-hero-heading__line--2 strong::after {
		animation: pb-hero-centered-underline 0.7s ease-out 1s forwards;
	}
}

@media (prefers-reduced-motion: reduce) {
	body.page-id-365 .pb-b2b-hero-heading__line--2 strong::after {
		transform: scaleX(1);
	}
}

/* CTA 2's "Ghost" style (default) — outlined/transparent button,
 * matching pb/newsletter's own .pb-newsletter__cta.pb-button--ghost
 * exactly (same two-class-specificity trick: equal specificity to
 * design-system.css's sitewide "flatten every Primary/Secondary/Ghost
 * CTA to filled-purple" rule, wins because main.css is enqueued after
 * design-system.css — identical mechanism, not a new one). Better
 * legibility than a plain text link against the hero's dark photo. */
.pb-b2b-hero-cta__cta2.pb-button--ghost {
	background: transparent;
	border-color: #ffffff;
	color: #ffffff;
}

.pb-b2b-hero-cta__cta2.pb-button--ghost:hover,
.pb-b2b-hero-cta__cta2.pb-button--ghost:focus-visible {
	background: #ffffff;
	border-color: #ffffff;
	color: #111111;
}

/* CTA 2's "Link" style (selectable alternative) — plain text CTA, same
 * visual language as pb/faq's own .faq__contact-link above (color/
 * weight/decoration match exactly). Not shared code —
 * pb_button_class('link') deliberately returns no class, so every
 * consuming block supplies its own look (see pb/hero-split's own
 * .pb-hero-split__cta-secondary for the established precedent). */
.pb-b2b-hero-cta__link {
	color: #6848f4;
	font-weight: 700;
	text-decoration: none;
	transition: opacity 0.2s ease;
}

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

@media (max-width: 767.98px) {
	.pb-b2b-hero {
		padding: 2.5rem 0 3rem;
	}

	.pb-b2b-hero__bg {
		background-position: var(--pbh-bg-position-mobile, var(--pbh-bg-position, 50% 50%));
	}

	.pb-b2b-hero__inner {
		padding-left: 20px;
		padding-right: 20px;
	}

	/* Text needs the full column width once it stacks under a narrow
	 * viewport — !important is required here to beat the per-instance
	 * inline max-width:{contentWidth}% the render_callback always sets
	 * on this same element (an inline style otherwise always wins over
	 * a class rule regardless of specificity). */
	.pb-b2b-hero__content {
		max-width: 100% !important;
	}
}

/* 2026-09-15 follow-up: page 365's own mobile-only stacked layout
 * (2026-09-11 through the same-day 2026-09-15 iterations above this
 * comment in git history) removed entirely — explicit request: "zet het
 * op mobiel terug naar de oude settings", matching /zakelijk/b2b/
 * kerstpakket-eindejaarsgeschenk/'s own mobile hero exactly (generic
 * .pb-b2b-hero mobile rules below: photo-as-background via .pb-b2b-hero__bg
 * + __overlay, text on top, no stacking/reorder/dark-bg-color/mobile-image).
 * The scroll-triggered reveal animation (.pb-b2b-hero-heading__line/-body__text/
 * -cta fades, above) and the "Zorg dat die klopt." underline (also above)
 * were never part of this block — both are unscoped/not mobile-only, so
 * they're untouched and still run exactly as before. */

/* Transparent-header-over-hero — identical mechanism to
 * .pb-hero-centered--transparent-header above (pull the section up
 * behind the header, add the same amount back as extra top padding on
 * the content so it visually starts in the same place, then a subtle
 * top-down gradient keeps the header's nav/logo/icons/CTA legible
 * regardless of the photo underneath), adapted to this block's own
 * (simpler, single) vertical-padding model: .pb-hero-centered proxies
 * padding-top through a --pb-hc-padding-top custom property consumed by
 * its __inner element; .pb-b2b-hero sets its own padding directly, so
 * the compensation below adds header-height as *additional* padding-top
 * on .pb-b2b-hero__inner on top of whatever padding is already there,
 * rather than reconstructing a full CSS-var proxy for one modifier. */
.pb-b2b-hero--transparent-header {
	margin-top: calc(-1 * var(--pb-header-height, 71px)) !important;
	/* Screen-filling on load (2026-08-14). .site-header-wrap is
	 * position:sticky (still occupies its own space in normal flow at
	 * the top of the page before scrolling), pushing this section down
	 * by --pb-header-height; the negative margin above pulls it straight
	 * back up by the same amount. The two cancel out exactly, so this
	 * section's real on-screen top always lands at the very top of the
	 * visible page content — y=0, or y=32px/46px when the WP admin
	 * toolbar pushes the whole <body> down (see the body.admin-bar
	 * override below) — regardless of the header's own height. min-height
	 * therefore only needs to fill the viewport itself, not "viewport
	 * minus header". !important beats the block's own inline
	 * min-height:{px} ("Minimale hoogte" control, inc/b2b-hero-block.php)
	 * — full-viewport takes priority over that per-instance floor
	 * whenever the transparent header is on. svh (already used
	 * identically by .hero above) is a fallback-chain progressive
	 * enhancement over vh: browsers without svh support just ignore the
	 * second declaration and keep the vh value; browsers that do support
	 * it get the "smallest possible" viewport height, which never
	 * reflows as mobile browser chrome (URL bar) shows/hides on scroll —
	 * plain 100vh (or 100dvh, which is intentionally NOT used here) would
	 * otherwise resize/jump on every scroll-triggered chrome change. */
	min-height: 100vh !important;
	min-height: 100svh !important;
}

/* WP admin toolbar (logged-in only) pushes the whole <body> down via
 * its own core margin-top (32px desktop, 46px below 782px, both
 * automatic) without shrinking the viewport itself — 100vh/100svh above
 * would otherwise overflow past the visible viewport by exactly that
 * amount, the classic "admin bar overflow" issue. Reuses WP core's own
 * --wp-admin--admin-bar--height custom property (set automatically
 * alongside that margin, wp-includes/css/admin-bar.min.css, already
 * responsive at the same 782px breakpoint) rather than a duplicated/
 * guessed literal. */
body.admin-bar .pb-b2b-hero--transparent-header {
	min-height: calc(100vh - var(--wp-admin--admin-bar--height, 32px)) !important;
	min-height: calc(100svh - var(--wp-admin--admin-bar--height, 32px)) !important;
}

.pb-b2b-hero--transparent-header .pb-b2b-hero__inner {
	padding-top: var(--pb-header-height, 71px);
}

.pb-b2b-hero--transparent-header::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	z-index: 1;
	height: calc(var(--pb-header-height, 71px) + 140px);
	background: linear-gradient(to bottom, rgba(0, 0, 0, 0.32), rgba(0, 0, 0, 0));
	pointer-events: none;
}

@media (max-width: 767.98px) {
	.pb-b2b-hero--transparent-header::before {
		height: calc(var(--pb-header-height, 71px) + 90px);
	}
}

/* Mobile: header stays solid/white always, no transparent-over-hero
 * treatment (2026-08-14, explicit decision) — the transparent variant
 * requires the hero's pulled-up background to line up exactly with the
 * header's own real rendered height (--pb-header-height); despite
 * several rounds of fixing how/when that height gets measured, this
 * couldn't be reliably confirmed closed on real mobile devices, so
 * instead the header simply never switches to its transparent state
 * below 768px — same solid style it already uses everywhere else on
 * the site. Desktop is completely unchanged (nothing here exists
 * outside this media query). Sticky positioning and the scroll-based
 * show/hide behaviour (assets/js/main.js's .is-scrolled/.is-hidden
 * toggling) are both untouched — only which *visual* state (solid vs
 * transparent) is shown is forced here, never whether/how the header
 * sticks or hides. Scoped to the shared --transparent-header modifier
 * class (pb/b2b-hero), not a page-specific selector. */
@media (max-width: 767.98px) {
	.site-header[data-header-style="transparent"] {
		background: var(--pb-header-background, rgba(255, 255, 255, 0.98)) !important;
		backdrop-filter: blur(12px) !important;
		border-bottom-color: var(--pb-header-border, #ececec) !important;
	}

	.site-header[data-header-style="transparent"] .pb-mini-cart-trigger {
		color: var(--pb-header-icon, #111111) !important;
	}

	/* The cart icon's 2nd <path> (its "handle" cutout detail) carries
	 * its own fill="#FFFFFF" presentation attribute in the raw markup
	 * (inc/woocommerce-mini-cart.php) — normally enough on its own,
	 * since a presentation attribute only loses to a REAL CSS rule that
	 * directly targets that same element, not to inherited values from
	 * an ancestor. But .site-header[data-header-style="transparent"]
	 * :not(.is-scrolled) (elsewhere in this file) DOES directly target
	 * this exact path with its own fill:#000000 (an intentional
	 * contrast flip for the dark-photo transparent state, where the
	 * whole bag turns white) — that rule has no !important, but IS more
	 * specific than a plain unconditional override, and it still
	 * matches here too (nothing un-sets :not(.is-scrolled) at page
	 * load). Combined with this block's own color:#111111 override on
	 * the bag body just above, the two ended up nearly the same dark
	 * shade — reading as one black blob instead of a bag with a visible
	 * white cutout. !important restores the cutout back to white,
	 * matching what the SVG's own markup already does correctly on
	 * every other (non-transparent-header) page. */
	.site-header[data-header-style="transparent"] .pb-mini-cart-trigger svg path:nth-child(2) {
		fill: #ffffff !important;
	}

	.site-header[data-header-style="transparent"] .menu-toggle {
		color: var(--pb-header-hamburger, var(--pb-header-icon, #111111)) !important;
	}

	/* Same selector shape (incl. :not(.is-scrolled)) and !important as
	 * the rules being overridden (main.css, transparent-header section
	 * above) — required to actually win: those already use !important
	 * too, so only an equal-or-higher-specificity rule later in the
	 * file can override them. */
	.site-header[data-header-style="transparent"]:not(.is-scrolled) .custom-logo-link:not(.site-branding__gold-logo-link) {
		display: inline-flex !important;
	}

	.site-header[data-header-style="transparent"]:not(.is-scrolled) .site-branding__gold-logo-link {
		display: none !important;
	}

	/* The hero no longer needs pulling up behind a (now always solid)
	 * mobile header — plain in-flow spacing (.pb-b2b-hero--transparent-header::before
	 * is the transparent-only darkening gradient, not needed over a
	 * solid header). */
	.pb-b2b-hero--transparent-header {
		margin-top: 0 !important; align-items: flex-end;
	}

	/* Small extra nudge downward (2026-08-14, explicit request) for the
	 * heading/body/CTA content group — .pb-b2b-hero itself centers that
	 * group vertically (display:flex; align-items:center), so extra
	 * top padding here (vs. none) shifts the vertical center point
	 * downward within the hero's own height, without touching the
	 * hero's own section padding/background or any desktop styling
	 * (this whole block only exists inside the 767.98px media query). */
	.pb-b2b-hero--transparent-header .pb-b2b-hero__inner {
		padding-top: 0;
	}

	.pb-b2b-hero--transparent-header::before {
		display: none;
	}
}

/* 2026-08-17, explicit request: re-enables the transparent mobile
 * header for the Retail page specifically, reversing the sitewide
 * "force solid below 768px" block directly above (that decision — a
 * past reliability concern that could never be confirmed fixed on real
 * devices — is left in place for every other transparent-header page,
 * including the B2B kerstpakket hero, which this task does not ask to
 * change). Scoped via body.page-id-363 (higher specificity than the
 * sitewide rule's plain attribute selector, so this reliably wins) —
 * mirrors the always-transparent desktop rules above line-for-line
 * (background/icon colors), just now also active on mobile for this
 * one page. The hero's own margin-pull-up/padding-compensation
 * (.pb-hero-centered--transparent-header, unconditional, untouched)
 * already assumes a genuinely transparent header — no separate layout
 * fix needed here, only re-enabling the visual transparent state. */
@media (max-width: 767.98px) {
	body.page-id-363 .site-header[data-header-style="transparent"]:not(.is-scrolled) {
		background: transparent !important;
		backdrop-filter: none !important;
		border-bottom-color: transparent !important;
	}

	body.page-id-363 .site-header[data-header-style="transparent"]:not(.is-scrolled) .pb-mini-cart-trigger {
		color: #ffffff !important;
	}

	body.page-id-363 .site-header[data-header-style="transparent"]:not(.is-scrolled) .pb-mini-cart-trigger svg path:nth-child(2) {
		fill: #000000 !important;
	}

	body.page-id-363 .site-header[data-header-style="transparent"]:not(.is-scrolled) .menu-toggle {
		color: #ffffff !important;
	}

	/* Follow-up, explicit request ("het is niet zichtbaar" -> "ik zie
	 * helemaal geen header"): assets/js/main.js's isMobileHiddenOnLoadPage()
	 * hides the header entirely on load on every transparent-header page
	 * below 768px (translateY(-100%) via .is-hidden), only revealing it
	 * after a scroll-down-then-up gesture — desktop never does this, its
	 * header is always visible. Matches "same logic as desktop" for
	 * Retail specifically: neutralizes that transform here so the header
	 * (now genuinely transparent, from the block above) is visible
	 * immediately on load, same as desktop. B2B and other transparent-
	 * header pages keep the existing hide-on-load behaviour untouched. */
	body.page-id-363 .site-header.is-hidden {
		transform: translateY(0) !important;
	}
}

/* ==========================================================================
 * PB Form Section (pb/form-section) — reusable section combining an
 * optional title, optional paragraph and the existing pb/contact-
 * active-form block. Replaces /zakelijk/retail/'s previous separate
 * H2 ("Meld je aan.", .pb-contact-form-title/.faq__title) + Paragraph
 * (.pb-contact-form-subtitle) + Group structure; visual treatment
 * below mirrors those exact rules (uppercase centered title, muted
 * centered subtitle) so the migration preserves the page's look.
 * Underlying PHP/JS helpers only (typography, spacing) — never the CSS
 * classes themselves — are shared with every other PB block.
 * ========================================================================*/
.pb-form-section__content {
	max-width: 48rem;
	margin: 0 auto;
	text-align: center;
}

/* Mobile only (2026-08-14, gutter tightened 2026-08-15): pb/form-section
 * has no "align":"full" support (blocks/form-section/block.json has no
 * "align" in supports), so it always sat inset by .site-main's own 32px
 * left/right padding (main.css ~line 846) — noticeably narrower than
 * the .faq block directly above it on the same page, which escapes that
 * same padding via its own .faq.alignfull breakout (main.css ~line
 * 4667, own gutter: 1.25rem). Same breakout technique here, but with a
 * deliberately tighter 1rem gutter (explicit request, 2026-08-15) so
 * the form itself reads noticeably wider than before — still close
 * enough to .faq's own 1.25rem to read as "the same" edge on mobile,
 * not a mismatch. Desktop is untouched (outside this media query) —
 * .pb-form-section__content's own max-width:48rem/margin:auto already
 * reads correctly there, nested inside .site-main's 800px cap. */
@media (max-width: 767px) {
	.pb-form-section {
		position: relative;
		left: 50%;
		width: 100vw;
		max-width: 100vw;
		margin-left: -50vw;
		padding-left: 1rem;
		padding-right: 1rem;
		box-sizing: border-box;
	}
}

/* Regular base weight, <strong> stands out — same pattern as
 * .pb-cb-heading__text/.pb-image-text__heading (design-system.css's
 * bare h1..h6{font-weight:...!important} reset otherwise leaves the
 * h2 preset's own base weight nearly indistinguishable from
 * <strong>'s generic +100-capped-at-900 boost). An explicit
 * per-instance "Tekstdikte override" still wins: inline !important
 * beats external !important (pb_typography_override_style()). */
.pb-form-section-heading__text {
	margin: 0 0 1.5rem;
	text-transform: uppercase;
	font-weight: 400 !important;
}

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

/* Same secondary-text feel as the subtitle it replaces
 * (.pb-contact-form-subtitle) — muted, width-capped so it reads well
 * under a centered title rather than spanning the full section. */
.pb-form-section-paragraph__text {
	max-width: 32rem;
	margin: 0 auto 1.5rem;
	opacity: 0.85;
	color: inherit;
}

.pb-form-section-paragraph__text strong {
	font-weight: 700;
}

/* ==========================================================================
 * PB Bullet blok (pb/bullet-block + pb/bullet-item) — reusable "large
 * heading + optional CTA left, variable-count item list right" section.
 * Layout inspired by an Asana marketing-page reference but rebuilt
 * entirely with PaperBrain's own design system (no copied Asana code/
 * colors/branding). Container height is always content-driven — no
 * fixed heights or absolute positioning anywhere in this layout, so any
 * number of items (1 through N) grows the box naturally with no overlap
 * or clipped text.
 * ========================================================================*/
/* Always full-bleed (2026-08-14 — was conditional on the "align":"full"
 * attribute via .alignfull, but this block's whole design assumes a
 * full-width band like the Asana reference; requiring a separate
 * manual alignment toggle meant a routine content edit that never
 * touched alignment — e.g. changing the heading text — could silently
 * leave the attribute unset and the section back at normal content
 * width). Unconditional now, same technique as .pb-b2b-hero/.pb-hero-
 * centered, which are never optional either — outer background extends
 * edge-to-edge while the inner card below stays capped/centered via
 * its own max-width. */
.pb-bullet-block {
	color: #111111;
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Matches the Asana reference's own measured dimensions (2026-08-14 —
 * DevTools-inspected on the reference page): margin:0 16px (a plain
 * 16px side gutter, not a capped/centered content-width column, so the
 * container reads as "much wider" — nearly edge-to-edge — matching the
 * reference instead of the modest ~1440px section-width token every
 * other PB block uses) and padding:80px 32px on desktop. The generous
 * max-width is a defensive ceiling only (avoids absurd line-lengths on
 * ultra-wide monitors), not a real-world constraint at normal desktop
 * widths. */
.pb-bullet-block__inner {
	margin: 0 16px;
	max-width: 1920px;
	box-sizing: border-box;
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: center;
	gap: var(--pb-bullet-column-gap, 64px);
	padding: 80px 32px;
	overflow: hidden;
}

.pb-bullet-block__left {
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
	/* Grid items default to min-width:auto, which can push a long
	 * unbreakable heading wider than its column and overflow the
	 * container — same guard as .pb-bullet-block__items below. */
	min-width: 0;
}

/* font-weight:400!important — same "regular base, <strong> stands out"
 * pattern as .pb-bullet-item__title/.pb-cb-heading__text/.pb-b2b-hero-
 * heading__text elsewhere in this file: this renders as a real h1-h6
 * (headingLevel), so design-system.css's bare `h1..h6{font-weight:...
 * !important}` reset otherwise wins over both the visual preset's own
 * weight and that preset's "<strong> = base+100" boost formula. An
 * explicit "Tekstdikte override" on the heading still wins: inline
 * !important beats external !important (pb_typography_override_style()). */
.pb-bullet-block__heading {
	margin: 0;
	color: inherit;
	overflow-wrap: break-word;
	font-weight: 400 !important;
}

.pb-bullet-block__heading strong {
	font-weight: 700;
}

/* 2026-09-16, explicit request: force "Of iets waarmee je laat zien:"
 * fully onto its own line on desktop (was wrapping mid-phrase). Scoped
 * to page-id-365 + desktop only. display:block on the <strong> pushes
 * its entire content onto a new line without an inline <br>, keeping
 * typography/spacing/alignment untouched. */
@media (min-width: 1024px) {
	body.page-id-365 .pb-bullet-block__heading strong {
		display: block;
	}
}

/* Optional text under the heading (2026-08-14) — empty renders nothing
 * (no wrapper), same "no leftover spacing when off" convention as the
 * CTA below. .pb-bullet-block__left is already a flex column with its
 * own gap, so no extra margin is needed here for the heading/subtitle/
 * CTA spacing — the gap handles all three consistently. */
/* color:inherit + opacity:0.8 (was #111111 blended down, reading as
 * grey) replaced with a plain, unattenuated color (2026-08-14) — the
 * default is now #000000, set via pb_render_bullet_block()'s own
 * subtitleColor fallback (inc/bullet-block.php) and emitted as a real
 * inline style by pb_typography_override_style(), so the "Tekstkleur
 * override" color picker in the sidebar can still change it per
 * instance; this class rule is just the fallback for whatever the
 * resolved color is. */
.pb-bullet-block__subtitle {
	margin: 0;
	color: inherit;
	overflow-wrap: break-word;
}

/* Plain <div>, no bare h1..h6 reset fighting it — same reasoning as
 * .pb-bullet-item__body strong above. */
.pb-bullet-block__subtitle strong {
	font-weight: 700;
}

.pb-bullet-block__cta {
	display: flex;
}

.pb-bullet-block__items {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.pb-bullet-item {
	padding: 1.5rem 0;
	border-bottom: 1px solid var(--pb-bullet-divider-color, rgba(17, 17, 17, 0.1));
}

.pb-bullet-item:first-child {
	padding-top: 0;
}

.pb-bullet-item:last-child {
	padding-bottom: 0;
	border-bottom: none;
}

/* Per-item "Divider tonen" toggle (dividerEnabled:false) — independent
 * of the :last-child rule above, which always hides the trailing
 * divider regardless of this setting. */
.pb-bullet-item--no-divider {
	border-bottom: none;
}

/* 23px (was inheriting .pb-type--h4's own 25.6px default) — same-
 * specificity override, wins because main.css loads after design-
 * system.css; an explicit per-item "Lettergrootte override" still
 * wins over this, since pb_typography_override_style() emits its
 * font-size with !important.
 *
 * font-weight:400!important — this is a real <h3> (semantic title),
 * so design-system.css's bare `h3{font-weight:800!important}` reset
 * otherwise wins over .pb-type--h4's own 700, AND over that same
 * preset's own "<strong> = base+100" boost formula (700+100=800) —
 * meaning regular and bold text were computing to the *exact same*
 * 800 weight, not just "too subtle to see" but genuinely identical.
 * Same "regular base, <strong> stands out" pattern already used for
 * .pb-cb-heading__text/.pb-b2b-hero-heading__text/.pb-form-section-
 * heading__text elsewhere in this file. An explicit per-item
 * "Tekstdikte override" still wins: inline !important beats external
 * !important (pb_typography_override_style()). */
.pb-bullet-item__title {
	margin: 0 0 0.5rem;
	color: inherit;
	overflow-wrap: break-word;
	font-size: 23px;
	font-weight: 400 !important;
}

.pb-bullet-item__title strong {
	font-weight: 700;
}

/* color:inherit + opacity:0.8 (was #111111 blended down, reading as
 * grey) replaced with a plain, unattenuated color (2026-08-14) — same
 * reasoning as .pb-bullet-block__subtitle above; default is now
 * #000000 via pb_render_bullet_item_block()'s own bodyColor fallback. */
.pb-bullet-item__body {
	margin: 0;
	color: inherit;
	overflow-wrap: break-word;
}

/* RichText <strong> inside the body was falling through to design-
 * system.css's generic `.pb-type--body strong` formula (400+100=500,
 * too subtle to read as bold) — same fix already applied to .pb-hc-
 * body__text/.pb-image-text__body/.pb-b2b-hero-body__text strong. No
 * base-weight !important needed here (unlike the title above): this
 * element is a plain <div>, so there's no bare h1..h6 reset fighting
 * it — the "body" preset already defaults to 400. */
.pb-bullet-item__body strong {
	font-weight: 700;
}

/* Auto-compact once the list has 4+ items ("spacing moet automatisch
 * compacter worden waar nodig... items mogen iets kleiner worden zodat
 * het ontwerp in balans blijft") — pure CSS sibling-count trick, no JS
 * needed for layout behaviour: :first-child:nth-last-child(n+4) only
 * matches when this item is the first of at least 4 total siblings;
 * combined with the ~ general-sibling selector, every item in a 4+
 * list gets the tighter treatment, while 1-3 item lists are completely
 * unaffected. */
.pb-bullet-item:first-child:nth-last-child(n + 4),
.pb-bullet-item:first-child:nth-last-child(n + 4) ~ .pb-bullet-item {
	padding-top: 1rem;
	padding-bottom: 1rem;
	font-size: 0.95em;
}

.pb-bullet-item:first-child:nth-last-child(n + 4) {
	padding-top: 0;
}

@media (max-width: 767.98px) {
	.pb-bullet-block__inner {
		grid-template-columns: 1fr;
		padding: 2rem 1.25rem;
		gap: 2rem;
	}

	.pb-bullet-block__left {
		gap: 1rem;
	}
}

/* ==========================================================================
 * PB Scroll Cards (pb/scroll-cards + pb/scroll-card) — reusable sticky
 * card stack (2026-08-15). Mobile/tablet (<1024px): each card pins via
 * native CSS `position: sticky` and the next card scrolls up to cover
 * it — pure CSS, no scroll-hijacking, no wheel listeners, no JS
 * animation loop. Desktop (>=1024px): cards render in plain normal
 * flow, one under another — the sticky rules below only ever apply
 * below 1024px, so desktop is completely unaffected by this block.
 *
 * How the "own scroll phase per card" mechanic works: each card's
 * `.pb-scroll-card-wrap` is a TALL box (card's own min-height + extra
 * scroll room). Its child `.pb-scroll-card` is `position: sticky; top:
 * <offset>`, so as the wrap scrolls through the viewport, the card
 * pins at that offset for the wrap's "extra" height, then scrolls away
 * as the wrap ends and the next (taller-offset, higher z-index) card's
 * own sticky card reaches the same pinned position and visually lands
 * on top of it. Once the LAST card's wrap has fully scrolled past, its
 * own sticky card is no longer pinned (nothing below it holds it in
 * place) and the page continues scrolling normally — no JS state/
 * snap needed for that "return to normal scroll" behaviour, it falls
 * out of `position: sticky` on its own.
 *
 * `prefers-reduced-motion: reduce` — the sticky/stacking rules only
 * ever apply inside `(prefers-reduced-motion: no-preference)`, so a
 * reduced-motion visitor gets every card in plain normal-flow document
 * order (same as desktop) with zero extra JS branching.
 * ========================================================================*/
.pb-scroll-cards {
	position: relative;
	background-color: transparent;
}

.pb-scroll-cards.alignfull {
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Edge-to-edge (2026-08-15, explicit request) — no side gutters and no
 * content-width cap, so the card stack fills the full viewport width
 * on every screen size, including desktop monitors wider than the
 * theme's own 1200px section container-width token. */
.pb-scroll-cards__inner {
	width: 100%;
	margin: 0;
	padding: 0;
}

/* ---- Card wrap + card: the sticky stack is the default/unconditional
 * behaviour (2026-08-15, restructured) — a visitor with no motion
 * preference set, or whose browser doesn't recognize the
 * `prefers-reduced-motion: no-preference` keyword specifically (real
 * gap in older/partial implementations), still gets the sticky effect
 * either way; only an EXPLICIT `prefers-reduced-motion: reduce` match
 * (further down) now opts back OUT into plain stacked flow. Same
 * outcome as before for both groups, just no longer dependent on the
 * narrower keyword actually matching. */
/* Truly screen-filling cards (2026-08-15, explicit request — no
 * border-radius/corners, no gap above/below, card = the full
 * available viewport) — height is derived from the viewport itself
 * (100vh minus the sticky top-offset that clears the fixed header),
 * not from --pbsc-card-min-height (that variable now only sizes the
 * CONTENT's own min-height, so short content still centers nicely
 * inside the full-height card; long content can still grow it, or
 * scroll internally via .pb-scroll-card__content's own overflow-y).
 *
 * Getting an actual visual overlap (next card slides up and covers
 * this one while it's still on screen, not just a same-height buffer)
 * needs two cooperating pieces, not one:
 *
 * 1) This wrap needs enough scroll room for BOTH a dwell phase (this
 *    card sits alone, fully pinned, nothing else visible yet) AND a
 *    transition phase (the next card is sliding up and covering it) --
 *    one card-height's worth of scroll for each, so 2 * cardHeight of
 *    buffer on top of the card's own height = 3 * cardHeight total.
 *
 * 2) Critically, every wrap AFTER the first is pulled up by exactly
 *    one card-height via a negative top margin (see
 *    `.pb-scroll-card-wrap:not(:first-child)` below). Without this,
 *    a card's sticky-pin ends (because ITS OWN wrap ran out of room)
 *    at EXACTLY the scroll position where the next wrap's card has
 *    just started to appear at the very bottom edge of the screen --
 *    that's a hard mathematical identity of `position: sticky` when
 *    wraps are simply stacked back-to-back with no gap, true no matter
 *    how large the buffer is (verified live: doubling the buffer alone
 *    changed nothing, the two events stayed perfectly aligned with
 *    zero geometric overlap every time). Pulling each wrap up by one
 *    card-height shifts the next card's whole entrance timeline
 *    earlier so it plays out ENTIRELY while the previous card is still
 *    pinned, instead of only starting the instant the previous card
 *    lets go -- that's what actually produces overlap instead of a
 *    seam.
 *
 * 2 * cardHeight (not 3x) is the minimum that still gives a full,
 * clean transition -- one cardHeight is consumed entirely by the
 * transition itself (see the -1*cardHeight pull-up below, that amount
 * is fixed/required), leaving zero extra "dwell" scroll where a card
 * just sits alone before the next one starts sliding in. That means
 * the next card begins entering the instant this one becomes pinned
 * (2026-08-15, explicit request — 3x cardHeight required ~3 screens of
 * scrolling per card before the next one appeared, which read as
 * unresponsive; this makes it exactly one viewport of scroll per card,
 * transition starting immediately). */
/* Every card holds still and registers for a brief beat (~0.3 of a
 * card-height of scroll) before the next one starts sliding up and
 * covering it (2026-08-15, explicit request -- pure 2x pacing made
 * scrolling feel like it never stopped rolling; this adds a short
 * pause to EVERY transition, not just the first). The extra 0.3 is
 * pure dwell time and doesn't affect the transition itself, which is
 * still exactly one cardHeight either way (see the fixed -1*cardHeight
 * pull-up below) -- it only delays when each transition starts. */
.pb-scroll-card-wrap {
	position: relative;
	min-height: calc( 2.3 * ( 100vh - var( --pbsc-mobile-offset, 88px ) ) );
	/* Only actually animates when assets/js/main.js's openThis()/
	 * closeOpen() set/clear an inline min-height (opening/closing a
	 * detail panel) -- this value never changes during normal sticky
	 * scrolling, so this can't interfere with that. Smooths what would
	 * otherwise be an instant, jarring layout shift into a gradual one,
	 * matching the detail panel's own 0.35s expand/collapse timing. */
	transition: min-height 0.35s ease;
}

.pb-scroll-card-wrap:not( :first-child ) {
	margin-top: calc( -1 * ( 100vh - var( --pbsc-mobile-offset, 88px ) ) );
}

.pb-scroll-card-wrap:last-child {
	min-height: calc( 100vh - var( --pbsc-mobile-offset, 88px ) );
}

.pb-scroll-card {
	position: sticky;
	top: var( --pbsc-mobile-offset, 88px );
	display: block;
	overflow: hidden;
	margin-bottom: 0;
	width: 100%;
	height: calc( 100vh - var( --pbsc-mobile-offset, 88px ) );
	background-size: cover;
	background-repeat: no-repeat;
}

/* Every card sticks at the exact same top offset (see .pb-scroll-card
 * above) so they stack perfectly flush on top of each other -- no
 * per-index "peek" ladder. Covering order comes for free from DOM
 * order: a stickily-positioned box always establishes its own
 * stacking context regardless of z-index, so sibling stacking
 * contexts simply paint in source order and a later card always
 * covers an earlier one. That means covering order tracks the actual
 * Gutenberg block order automatically, for any number of cards, with
 * no generated per-index CSS and no hardcoded card count. */

.pb-scroll-card__overlay {
	position: absolute;
	inset: 0;
	z-index: 0;
	background: var( --pbsc-overlay-bg, transparent );
	opacity: var( --pbsc-overlay-opacity, 0 );
	pointer-events: none;
}

/* Decorative bottom-right icon/pattern (2026-08-17, explicit request,
 * mobile only) — per-card, opt-in via Gutenberg (inc/scroll-cards-block.php),
 * renders nothing at all when no image is chosen (this element simply
 * isn't output server-side in that case, no empty box reserved).
 * z-index:0, same layer as .pb-scroll-card__overlay above and strictly
 * behind .pb-scroll-card__content's z-index:1 — content always paints
 * on top regardless of the icon's own size/position. Negative right/
 * bottom offsets intentionally push it partly outside the card;
 * .pb-scroll-card's own overflow:hidden (base rule, unconditional)
 * crops the rest, matching "deels buiten beeld" without any extra
 * clipping rule needed here. grayscale+brightness filter gives any
 * source image the same subtle dark-grey tint regardless of its own
 * original colors; opacity/size are both per-instance CSS custom
 * properties set inline by the PHP render. Desktop/tablet unchanged —
 * display:none outside this one breakpoint. */
/* 2026-08-17, explicit request: removed from both mobile and desktop —
 * hidden unconditionally, no breakpoint shows it. The underlying
 * Gutenberg attributes/controls (blocks/scroll-card/block.json,edit.js)
 * and PHP render (inc/scroll-cards-block.php) are left in place —
 * harmless/dormant, since no card currently has a decorative image
 * chosen anyway — rather than ripping the feature out entirely, in
 * case it's wanted again later. */
.pb-scroll-card__decorative {
	display: none !important;
	pointer-events: none;
	user-select: none;
}

.pb-scroll-card__content {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	/* Reduced from 0.75rem, then further to 0.15rem (2026-08-16,
	 * explicit request x2) -- tightens title/subtitle spacing
	 * generally; .pb-scroll-card__detail's own negative margin-top
	 * below is kept matching this exactly so the opened detail text
	 * still sits flush against it, not overlapping. */
	gap: 0.15rem;
	padding: 2rem 1.5rem;
	padding-bottom: max( 2rem, calc( 1.5rem + env( safe-area-inset-bottom, 0px ) ) );
	min-height: var( --pbsc-card-min-height, 85vh );
	max-height: inherit;
	overflow-y: auto;
	box-sizing: border-box;
	justify-content: center;
}

.pb-scroll-card__media {
	margin-bottom: 0.5rem;
}

.pb-scroll-card__image {
	display: block;
	max-width: 140px;
	max-height: 140px;
	width: auto;
	height: auto;
	margin: 0 auto;
}

.pb-scroll-card__title {
	margin: 0;
	color: inherit;
}

.pb-scroll-card__body {
	margin: 0;
	color: inherit;
	opacity: 0.92;
	max-width: 34rem;
}

/* Desktop only (2026-08-16, explicit request) -- larger title/subtitle
 * across every PB Scroll Card; font-family/weight/color untouched,
 * line-height nudged just enough to keep the larger text from
 * overlapping. Shared selector, not scoped to any one page. */
@media ( min-width: 1024px ) {
	.pb-scroll-card__title {
		font-size: 36px;
		line-height: 1.2;
	}

	.pb-scroll-card__body {
		font-size: 24px;
		line-height: 1.4;
	}
}

.pb-scroll-card__cta {
	margin-top: 0.5rem;
	flex-shrink: 0;
}

/* A real <button> when it toggles linked detail content instead of
 * navigating (see aria-controls -- inc/scroll-cards-block.php's
 * pb_render_scroll_card_block()) needs an explicit pointer cursor;
 * unlike <a>, browsers don't give <button> one by default. Disappears
 * entirely once open (2026-08-16, explicit request) -- the detail
 * panel's own .pb-scroll-card__detail-close takes over from there. */
.pb-scroll-card__cta[aria-controls] {
	cursor: pointer;
}

.pb-scroll-card__cta[aria-expanded="true"] {
	display: none;
}

/* Expandable "detail content" panel (2026-08-16, revised same day —
 * explicit request: the first version kept the card at its fixed
 * 100vh height and scrolled long detail content WITHIN it, which read
 * as clipped/truncated text with no obvious way to reach the rest).
 * The card itself now grows to fit whatever detail content it has —
 * see .pb-scroll-card--detail-open below — so this panel and its inner
 * content no longer need their own scroll/clip handling at all; they
 * just render at their natural height inside an equally auto-sized
 * card.
 *
 * Reuses another block's own already-rendered HTML verbatim (see
 * pb_scroll_card_get_linked_detail_html(), inc/scroll-cards-block.php)
 * — same markup/typography/component styling as its own original
 * placement on the page.
 *
 * CSS Grid 0fr/1fr row-track technique for the expand/collapse
 * animation — no JS height measurement needed (works for detail
 * content of any length), no heavy animation library. `visibility`
 * (not `display`/`hidden`) removes the collapsed panel from keyboard
 * tab order and the accessibility tree (well-supported browser
 * behaviour), with its own transition delayed so it only flips AFTER
 * the collapse animation finishes closing -- opening flips it back
 * immediately so content is reachable right away as it expands. */
.pb-scroll-card__detail {
	position: relative;
	display: grid;
	grid-template-rows: 0fr;
	visibility: hidden;
	/* .pb-scroll-card__content is a flex column with its own `gap`
	 * between every child -- that gap alone was still leaving visible
	 * space above this element. Negative margin cancels that inherited
	 * gap out; kept matching the content gap's own current value
	 * (0.15rem) exactly, so this sits flush without overlapping. */
	margin-top: -0.15rem;
	width: 100%;
	transition: grid-template-rows 0.35s ease, visibility 0s linear 0.35s;
}

.pb-scroll-card__detail.is-open {
	grid-template-rows: 1fr;
	visibility: visible;
	transition: grid-template-rows 0.35s ease, visibility 0s linear 0s;
}

.pb-scroll-card__detail-inner {
	overflow: hidden;
	min-height: 0;
}

/* Removed everywhere now (2026-08-16, explicit request -- was mobile
 * + tablet only at first, now desktop too); auto-close-on-scroll-away
 * still handles closing (assets/js/main.js unchanged). */
.pb-scroll-card__detail-close {
	display: none;
}

/* Mobile + tablet: same as desktop now (2026-08-16, explicit request
 * to tighten spacing everywhere) -- kept as its own override in case
 * they need to diverge again later. */
@media ( max-width: 1023.98px ) {
	.pb-scroll-card__detail {
		margin-top: -0.15rem;
	}

	/* Subtitle grows from the default mobile 16px to 20px once the CTA
	 * has been clicked and detail content is open (2026-08-16, explicit
	 * request) -- .pb-scroll-card--detail-open is the same class
	 * assets/js/main.js's openThis() already adds to the card. */
	.pb-scroll-card--detail-open .pb-scroll-card__body {
		font-size: 20px;
	}
}

/* The card grows to fit its detail content instead of clipping it
 * (2026-08-16, explicit request). Dropped out of the sticky/fixed-
 * height stacking mechanism for as long as it's open -- becomes a
 * normal, natural-flow block sized to its actual (now much taller)
 * content. Compound class selector (not a single modifier class) and
 * !important: needs to definitely win over the base .pb-scroll-card
 * rule's position/height (same convention already used throughout this
 * stylesheet for this kind of state-based override). */
.pb-scroll-card.pb-scroll-card--detail-open {
	position: relative !important;
	top: auto !important;
	height: auto !important;
	overflow: visible !important;
}

.pb-scroll-card--detail-open .pb-scroll-card__content {
	overflow: visible;
	max-height: none;
}

/* min-height is intentionally NOT reset to `auto` here (a prior
 * version did, via :has() -- explicit bug report: it shrank the wrap
 * down to just the grown card's own height, dropping the buffer the
 * NEXT card's sticky entrance relies on, which pulled that next card
 * up early and made it cover this card's still-open detail content).
 * assets/js/main.js's openThis()/closeOpen() set/clear an inline
 * min-height instead, computed as the wrap's ORIGINAL min-height minus
 * the card's normal closed height plus its actual grown height --
 * preserving the exact same buffer the next card needs either way,
 * just added on top of however tall this card currently is.
 *
 * margin-top is deliberately left alone now too (a prior version also
 * reset it to 0 here — explicit bug report: only the FIRST card, which
 * has no margin-top rule to begin with, behaved correctly; every other
 * card had a second property animating at the same time as the height
 * change and glitched). That pull-up only matters for overlapping the
 * PREVIOUS card while closed and sticky — by the time a card's own CTA
 * is even clickable, the user has already scrolled past the previous
 * one, so it's not actually contributing anything visible once open,
 * and removing it here made every card behave identically regardless
 * of position. */

/* The linked block is reused via render_block() at full fidelity (see
 * pb_scroll_card_get_linked_detail_html()) — same markup/typography/
 * component styling as its own original placement on the page, e.g.
 * pb/hero-centered's heading/subtitle/image/CTA all keep their own
 * existing look. Only the OUTER section chrome that assumes it's a
 * full 300px/265px-padded standalone hero section gets neutralized
 * here, so it reads as compact detail content instead of another full
 * hero crammed inside an already-sized card. Scoped to
 * .pb-scroll-card__detail specifically -- the same block rendered in
 * its own original place on the page is completely unaffected. */
.pb-scroll-card__detail .pb-hero-centered {
	padding: 0 !important;
	min-height: 0 !important;
	background-color: transparent !important;
}

/* 2026-08-16, explicit request: the outer .pb-hero-centered section's
 * own padding is neutralized above, but .pb-hero-centered__inner's OWN
 * padding-top (var(--pb-hc-padding-top, 96px) by default, or whatever
 * the standalone hero section's own instance value is) was still
 * fully active here — the large empty gap before title/subtitle/body
 * inside the open card detail. Scoped to .pb-scroll-card__detail only
 * (2-class specificity beats the base rule's single class, including
 * its own <=767.98px override, without !important) so the block's own
 * standalone placement on the page keeps its normal spacing. Bottom
 * padding is untouched, per "overige spacing onder de tekst behouden". */
.pb-scroll-card__detail .pb-hero-centered__inner {
	padding-top: 0.75rem;
}

/* Same request, same scope: title -> subtitle -> body should read as
 * one compact block. Base rules' own margin-bottom (.pb-hc-heading
 * 1rem, .pb-hc-subtitle 1.5rem) tightened here only; body's own
 * margin-bottom (spacing below the text, toward whatever follows) is
 * untouched. */
.pb-scroll-card__detail .pb-hc-heading {
	margin-bottom: 0.5rem;
}

.pb-scroll-card__detail .pb-hc-subtitle {
	margin-bottom: 0.75rem;
}

.pb-scroll-card__detail .pb-hc-background {
	display: none;
}

.pb-scroll-card__detail .pb-hc-image {
	margin: 0 auto 0.75rem;
}

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

	.pb-scroll-card__detail.is-open {
		transition: none;
	}

	.pb-scroll-card-wrap {
		transition: none;
	}
}

/* Hide the 5 standalone "Blok Je ..." sections on the Fundament page
 * (2026-08-15, extended to all viewports 2026-08-16 — the PB Scroll
 * Card detail-content toggle works desktop/tablet/mobile alike, see
 * "doe ditzelfde ook op desktop" fix, so these are duplicate content
 * everywhere now, not just below 768px) — their content is now ALSO
 * reachable via each matching PB Scroll Card's own expandable detail
 * panel (see pb_scroll_card_get_linked_detail_html(), inc/scroll-
 * cards-block.php). Scoped by each section's own semantic `anchor` id
 * (WP core's normal block-anchor support — "je-denken" etc., the exact
 * same anchor each scroll card's CTA already links to) rather than a
 * page-ID selector, so this can never accidentally match a pb/hero-
 * centered instance anywhere else on the site. `display:none` (not
 * opacity/visibility) — fully removed from layout, tab order and the
 * accessibility tree, not just visually hidden while still focusable;
 * no reserved space is left behind either. */
#je-denken,
#je-houding,
#je-activiteiten,
#je-resultaten,
#je-leefstijl {
	display: none;
}

/* ---- Reduced motion (2026-08-15, restructured — see the doc comment
 * on .pb-scroll-card-wrap above): sticky is now the unconditional
 * default, so this only needs to opt back OUT into plain stacked flow
 * for visitors who explicitly have `prefers-reduced-motion: reduce`
 * set. Matching the positive `reduce` keyword directly (rather than
 * gating the sticky rules behind `no-preference`) means this can never
 * end up silently disabling the effect for a regular visitor merely
 * because their browser doesn't recognize the narrower `no-preference`
 * keyword — the sticky rules above just apply unconditionally unless
 * this block's own condition explicitly matches. */
@media ( prefers-reduced-motion: reduce ) {
	.pb-scroll-card-wrap {
		min-height: 0;
	}

	.pb-scroll-card-wrap:not( :first-child ) {
		margin-top: 0;
	}

	.pb-scroll-card {
		position: relative;
		top: auto;
		margin-bottom: var( --pbsc-card-gap, 14px );
		max-height: none;
	}

	.pb-scroll-card-wrap:last-child .pb-scroll-card {
		margin-bottom: 0;
	}

	.pb-scroll-card__content {
		max-height: none;
		overflow-y: visible;
	}
}

@media ( max-width: 767px ) {
	.pb-scroll-card__content {
		padding: 1.75rem 1.25rem;
		padding-bottom: max( 1.75rem, calc( 1.25rem + env( safe-area-inset-bottom, 0px ) ) );
	}
}


/* ==========================================================================
 * PB Form Section — "split" layout variant (2026-09-11, rebuilt). Opt-in
 * per instance via layoutVariant='split' (blocks/form-section/edit.js +
 * inc/form-section-block.php) — every other pb/form-section usage
 * (e.g. the Retail page's Partner-worden form) keeps layoutVariant=''
 * and is unaffected, since every selector here is anchored on the
 * .pb-form-section--split modifier class.
 *
 * Two REAL wrapper elements (.pb-form-section__left /
 * .pb-form-section__right), built server-side in
 * inc/form-section-block.php by splitting the InnerBlocks-rendered
 * $content string right before the form's own wrapper markup — not a
 * shared CSS Grid with per-item `order` (a first version used that;
 * confirmed via a live layout inspection that mixing `order` with only
 * some items on an explicit grid-column stretches the shorter column's
 * grid ROWS to match the taller column's item at that row index,
 * producing a large visible empty gap in the left column). Two
 * independent flex columns instead — each sizes to its own content,
 * no row-matching involved at all.
 */
.pb-form-section--split .pb-form-section__content {
	display: flex;
	flex-direction: column;
	text-align: left;
}

.pb-form-section--split .pb-form-section__left,
.pb-form-section--split .pb-form-section__right {
	min-width: 0;
}

/* Left text color — configurable via the block's "Linker tekstkleur"
 * setting (--pbfs-left-color, set inline on .pb-form-section__content
 * only when layoutVariant='split', so this is a no-op everywhere
 * else). Unconditional (not desktop-only) so it still applies once
 * stacked on mobile. */
.pb-form-section--split .pb-form-section__left {
	color: var( --pbfs-left-color, inherit );
}

/* Beats the title/body child blocks' own inline text-align (their
 * block default is "center", meant for the stacked/centered layout) —
 * inheriting text-align:left from ancestors isn't enough since
 * inheritance always loses to an explicit inline style on the element
 * itself, regardless of viewport. */
.pb-form-section--split .pb-form-section-heading__text,
.pb-form-section--split .pb-form-section-paragraph__text {
	text-align: left !important;
}

.pb-form-section-eyebrow__text {
	text-transform: uppercase;
	letter-spacing: 0.08em;
	font-weight: 700;
	font-size: 13px;
	margin: 0 0 0.75rem;
}

.pb-form-section--split .pb-form-section-heading__text {
	text-transform: none;
	margin: 0 0 1rem;
}

.pb-form-section--split .pb-form-section-paragraph__text {
	margin: 0 0 1.5rem;
	max-width: none;
}

.pb-form-section-trust__text {
	color: var( --pbfs-left-color, #111111 ) !important;
	margin: 0;
}

.pb-form-section__trust {
	margin: 0 0 1.5rem;
}

.pb-form-section__divider {
	border: none;
	border-top: 1px solid var( --wp--preset--color--border, rgba( 17, 17, 17, 0.15 ) );
	margin: 0 0 1.5rem;
}

.pb-form-section-contact__label {
	margin: 0 0 0.25rem;
	font-size: 0.78rem;
}

.pb-form-section-contact__email {
	font-weight: 600;
	text-decoration: none;
	color: inherit;
	border-bottom: 1px solid currentColor;
}

.pb-form-section-form-heading__text {
	margin: 5px 0 0;
}

@media (min-width: 900px) {
	.pb-form-section--split .pb-form-section__content {
		flex-direction: row;
		align-items: flex-start;
		column-gap: var( --pbfs-gap, 56px );
		max-width: 1200px;
	}

	.pb-form-section--split .pb-form-section__left {
		flex: 0 0 var( --pbfs-left-width, 38% );
	}

	.pb-form-section--split .pb-form-section__right {
		flex: 1 1 auto;
	}

	/* Form heading ("Vertel ons over jullie plannen.") — visually part
	 * of the SAME white card as the form fields below it, even though
	 * it's a separate sibling element (not wrapped inside
	 * pb/contact-active-form's own markup, which stays untouched). Same
	 * background/horizontal padding as .pb-contact__form--card's own
	 * base rule (1.75rem, main.css) with rounded top corners only +
	 * zero bottom padding; the card immediately below has its top
	 * corners squared off and top padding reduced, so the two boxes sit
	 * flush and read as one continuous card. */
	.pb-form-section--split .pb-form-section__form-heading {
		background: var( --pbfs-card-bg, #ffffff );
		border-radius: 20px 20px 0 0;
		padding: 1.75rem 1.75rem 0;
	}

	/* Card background — configurable via the block's "Card
	 * achtergrondkleur" setting (--pbfs-card-bg custom property, set
	 * inline on .pb-form-section__content); falls back to the existing
	 * "card" variant's own white background when not set, so leaving
	 * it unset changes nothing. */
	.pb-form-section--split .pb-contact__form--card {
		background: var( --pbfs-card-bg, #ffffff );
	}

	/* :has() — only squares off the card's top corners when a form
	 * heading is actually present above it (rendered inside
	 * .pb-form-section__right, same parent as the form). Without one
	 * (attribute left empty) the card keeps its normal fully-rounded
	 * corners from the base "card" variant rule. */
	.pb-form-section__right:has( .pb-form-section__form-heading ) .pb-contact__form--card {
		border-radius: 0 0 20px 20px;
		padding-top: 0.75rem;
	}

	/* The block's own "Formulier" spacing control (margin-top on
	 * .wp-block-pb-contact-active-form, via pb_spacing_css()) is meant
	 * for the stacked layout's gap below the intro paragraph — with a
	 * form heading merging the heading + form into one visual card
	 * above, that same margin would reopen the seam this rule exists to
	 * close. Only overridden when a form heading is actually present. */
	.pb-form-section__right:has( .pb-form-section__form-heading ) .wp-block-pb-contact-active-form {
		margin-top: 0 !important;
	}
}

/* ==========================================================================
 * B2B onboarding-geschenk page — form-field-level desktop compacting
 * (2026-09-11). Scoped to `.pb-contact__form--card.pb-contact__form--
 * single-column` together, which only ever occurs on this one form
 * instance (see inc/contact-block.php's variant/fieldsLayout
 * attributes — the Retail page's own Partner-worden form uses "card"
 * alone, without "single-column"). Independent of the split-layout CSS
 * above — this only tunes the form's OWN field sizing/pairing/
 * checkbox presentation, not page layout, so it stays valid whether or
 * not layoutVariant is 'split'.
 */
@media (min-width: 900px) {
	.pb-contact__form--card.pb-contact__form--single-column .formkit-field:has( input[name="fields[geef_een_inschatting_van_de_aantallen]"] ),
	.pb-contact__form--card.pb-contact__form--single-column .formkit-field:has( input[name="fields[wanneer_zouden_jullie_de_paperbrains_willen_ontvangen]"] ) {
		grid-column: auto !important;
	}

	.pb-contact__form--card.pb-contact__form--single-column .formkit-input,
	.pb-contact__form--card.pb-contact__form--single-column .formkit-select,
	.pb-contact__form--card.pb-contact__form--single-column input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
	.pb-contact__form--card.pb-contact__form--single-column select,
	.pb-contact__form--card.pb-contact__form--single-column textarea {
		padding: 0.55rem 0.75rem !important;
		font-size: 0.85rem !important;
		border-radius: 10px !important;
		border-color: var( --wp--preset--color--border, rgba( 17, 17, 17, 0.12 ) ) !important;
		background-color: #ffffff !important;
	}

	.pb-contact__form--card.pb-contact__form--single-column .formkit-input::placeholder,
	.pb-contact__form--card.pb-contact__form--single-column input::placeholder,
	.pb-contact__form--card.pb-contact__form--single-column textarea::placeholder {
		font-size: 0.85rem !important;
	}

	.pb-contact__form--card.pb-contact__form--single-column .formkit-label,
	.pb-contact__form--card.pb-contact__form--single-column label {
		font-size: 0.78rem;
		margin-bottom: 0.3rem;
	}

	.pb-contact__form--card.pb-contact__form--single-column .formkit-fields {
		row-gap: 0.85rem;
		column-gap: 1rem;
	}

	.pb-contact__form--card.pb-contact__form--single-column .pbc-multiselect-wrap .pbc-multiselect__trigger {
		display: none !important;
	}

	.pb-contact__form--card.pb-contact__form--single-column .pbc-multiselect[hidden] {
		display: grid !important;
	}

	.pb-contact__form--card.pb-contact__form--single-column .pbc-multiselect {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 0.6rem;
		margin-top: 0.3rem;
	}
}


/* pb/form-section — "align:full" support (2026-09-11). Same
 * self-contained full-bleed breakout every other PB block in this file
 * uses (this theme has no generic .alignfull CSS of its own): outer
 * section breaks out of .site-main's own max-width/padding edge to
 * edge; .pb-form-section__content keeps its own max-width/centering
 * (48rem stacked, 1200px split variant), so the VISIBLE content still
 * reads as a normal padded column while the section's own background
 * (relevant for the "split" variant's configurable outer background
 * color) spans the full viewport width. Unconditional, not scoped to
 * "split" — applies to any pb/form-section instance an editor sets to
 * full width, same as every other full-bleed block here. */
.pb-form-section.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Mobile only (2026-09-11, explicit request) — "Vertel ons over jullie
 * plannen." (form heading) centered above the form fields, with a bit
 * more breathing room from the contact info above it (phone number).
 * Desktop/tablet untouched — this rule only exists below the 900px
 * breakpoint where the split-layout grid isn't active and the section
 * stacks: left content, then form heading, then form. */
@media (max-width: 899.98px) {
	.pb-form-section--split .pb-form-section__form-heading {
		text-align: center;
		margin-top: 1.5rem;
		margin-bottom: 1.25rem;
	}
}

/* "24 uur" in the trust line — bumped bolder than the default
 * .pb-type--small strong weight (2026-09-11, explicit follow-up
 * request: "nog meer bold"). Scoped to this element only. */
.pb-form-section-trust__text strong {
	font-weight: 800;
}

/* "Kies hieronder/hiernaast de gewenste uitvoering." (2026-09-11) —
 * mobile stacks the form below the text ("hieronder"), the split
 * layout puts it beside the text on desktop ("hiernaast"). Same
 * show/hide-per-breakpoint pattern already used elsewhere in this file
 * (e.g. .pbc-date-hint), same 900px breakpoint the split layout itself
 * switches at. */
.pb-fs-beside {
	display: none;
}

@media (min-width: 900px) {
	.pb-fs-below {
		display: none;
	}

	.pb-fs-beside {
		display: inline;
	}
}

/* ==========================================================================
 * PB Logo Reveal Hero (2026-09-11) — fullscreen intro hero, meant as
 * the very first block on a page. Sizing/positions use transform +
 * opacity only (no width/height animation, no layout reflow) so the
 * grow/fade sequence stays smooth even at a multi-second duration.
 * 100vh with 100svh/100dvh upgrades via @supports (mobile browser UI
 * chrome resize) — see inc/logo-reveal-hero-block.php for how each
 * CSS custom property below is set per-instance. The statement text
 * reuses pb/animated-statement's own .animated-statement__text/
 * __word/.is-revealed rules elsewhere in this file — nothing about
 * that typography/reveal styling is duplicated here.
 */
.pb-logo-reveal-hero {
	position: relative;
	width: 100%;
	height: 100vh;
	min-height: 100vh;
	background: var( --pblrh-bg, #ffffff );
	color: var( --pblrh-text, #111111 );
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
}

@supports ( height: 100svh ) {
	.pb-logo-reveal-hero {
		height: 100svh;
		min-height: 100svh;
	}
}

@supports ( height: 100dvh ) {
	.pb-logo-reveal-hero {
		height: 100dvh;
		min-height: 100dvh;
	}
}

/* Vertical centering once the header is unlocked/visible (2026-09-11
 * explicit report + screenshot: logo/text sat visibly below true
 * screen-center on desktop once the header reappeared after the
 * scroll-down-then-up gesture). While body.pb-lrh-header-hidden is
 * present (the intro + the visitor's first scroll down — header
 * invisible/out of flow, see that class's own rules above) this
 * section stays a TRUE full 100vh/100svh/100dvh, unaffected here, as
 * required. Once that class is removed the header returns to normal
 * sticky flow and pushes this section down by its own real height —
 * the section itself is still exactly one full viewport tall, so its
 * bottom now extends that same amount below the actually-visible
 * viewport, and content centered within the section's own (unchanged)
 * box therefore sits below true screen-center instead of in the
 * middle of the page. Shrinking the section by --pb-header-height (the
 * same token already used sitewide for this exact header-height
 * compensation, e.g. .pb-b2b-hero--transparent-header) keeps the
 * section's own vertical center aligned with the true
 * remaining-viewport center instead. */
body:not( .pb-lrh-header-hidden ) .pb-logo-reveal-hero {
	height: calc( 100vh - var( --pb-header-height, 71px ) );
	min-height: calc( 100vh - var( --pb-header-height, 71px ) );
}

@supports ( height: 100svh ) {
	body:not( .pb-lrh-header-hidden ) .pb-logo-reveal-hero {
		height: calc( 100svh - var( --pb-header-height, 71px ) );
		min-height: calc( 100svh - var( --pb-header-height, 71px ) );
	}
}

@supports ( height: 100dvh ) {
	body:not( .pb-lrh-header-hidden ) .pb-logo-reveal-hero {
		height: calc( 100dvh - var( --pb-header-height, 71px ) );
		min-height: calc( 100dvh - var( --pb-header-height, 71px ) );
	}
}

/* WP admin toolbar (logged-in only — every editor reviewing this page
 * always sees it, so it's real for verification even though real
 * visitors never do) pushes .site-header-wrap (and everything after it
 * in normal flow, including this section once the header is unlocked)
 * down by its own extra height on top of the header — same "classic
 * admin bar overflow" already handled for .pb-b2b-hero--transparent-
 * header above, reusing that identical --wp-admin--admin-bar--height
 * token/pattern rather than a guessed literal. */
body.admin-bar:not( .pb-lrh-header-hidden ) .pb-logo-reveal-hero {
	height: calc( 100vh - var( --pb-header-height, 71px ) - var( --wp-admin--admin-bar--height, 32px ) );
	min-height: calc( 100vh - var( --pb-header-height, 71px ) - var( --wp-admin--admin-bar--height, 32px ) );
}

@supports ( height: 100svh ) {
	body.admin-bar:not( .pb-lrh-header-hidden ) .pb-logo-reveal-hero {
		height: calc( 100svh - var( --pb-header-height, 71px ) - var( --wp-admin--admin-bar--height, 32px ) );
		min-height: calc( 100svh - var( --pb-header-height, 71px ) - var( --wp-admin--admin-bar--height, 32px ) );
	}
}

@supports ( height: 100dvh ) {
	body.admin-bar:not( .pb-lrh-header-hidden ) .pb-logo-reveal-hero {
		height: calc( 100dvh - var( --pb-header-height, 71px ) - var( --wp-admin--admin-bar--height, 32px ) );
		min-height: calc( 100dvh - var( --pb-header-height, 71px ) - var( --wp-admin--admin-bar--height, 32px ) );
	}
}

.pb-logo-reveal-hero__stage {
	position: relative;
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	text-align: var( --pblrh-text-align, center );
	padding: 1.5rem;
	box-sizing: border-box;
}

/* Logo — width is fixed at its final size; only `transform: scale()`
 * and `opacity` ever animate (transform-origin: center, per the
 * "geen harde zoom, premium/kalme animatie" requirement — a long
 * duration + ease-out curve, not a snap-zoom). */
.pb-logo-reveal-hero__logo {
	position: absolute;
	top: 50%;
	left: 50%;
	width: var( --pblrh-logo-end, 420px );
	max-width: 80vw;
	height: auto;
	transform-origin: center center;
	transform: translate( -50%, -50% ) scale( var( --pblrh-scale-start, 0.15 ) );
	opacity: 1;
	transition: transform var( --pblrh-duration, 3.5s ) cubic-bezier( 0.16, 1, 0.3, 1 ), opacity var( --pblrh-fade, 0.9s ) ease;
	will-change: transform, opacity;
}

.pb-logo-reveal-hero__logo.is-growing {
	transform: translate( -50%, -50% ) scale( 1 );
}

.pb-logo-reveal-hero__logo.is-hidden {
	opacity: 0;
	pointer-events: none;
}

.pb-logo-reveal-hero__text {
	position: relative;
	z-index: 1;
	max-width: 48rem;
}

.pb-logo-reveal-hero__second-logo {
	position: relative;
	z-index: 1;
	margin-top: 1.5rem;
	height: auto;
	opacity: 0;
	transform: translateY( 8px );
	transition: opacity 1s ease, transform 1s ease;
}

.pb-logo-reveal-hero.is-revealed .pb-logo-reveal-hero__second-logo {
	opacity: 1;
	transform: translateY( 0 );
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-logo-reveal-hero__logo,
	.pb-logo-reveal-hero__second-logo {
		transition: none !important;
	}
}

/* Post-intro scroll cue (2026-09-14, explicit request, mockup-driven)
 * — assets/js/main.js adds .is-visible at the exact same
 * "introComplete" moment the header-reveal logic already uses, and
 * removes it for good on the visitor's first scroll (see that file's
 * own comment on this element); that JS is unconditional, so this is a
 * CSS-only breakpoint split. pointer-events:none so it never blocks
 * the CTA row/taps beneath it; aria-hidden on the element itself
 * (markup, inc/logo-reveal-hero-block.php) keeps it out of the
 * accessibility tree entirely as pure decoration. Horizontal placement
 * only via left/right/margin (never transform) so the shared float
 * keyframes below can animate a pure translateY without needing a
 * per-breakpoint translateX baked in too. Desktop (2026-09-14
 * same-day follow-up): bottom-right instead of mobile's bottom-
 * center, per the desktop mockup — same element/trigger/animation,
 * just repositioned; this was deliberately NOT added when the mobile
 * version shipped ("desktop doen we later apart" — explicit at the
 * time), now added the same way. */
.pb-logo-reveal-hero__scroll-arrow {
	display: block;
	position: absolute;
	right: max( 32px, env( safe-area-inset-right, 32px ) );
	bottom: max( 48px, env( safe-area-inset-bottom, 48px ) );
	z-index: 2;
	opacity: 0;
	pointer-events: none;
	transform: translateY( 0 );
	transition: opacity 0.6s ease;
}

.pb-logo-reveal-hero__scroll-arrow.is-visible {
	opacity: 1;
	animation: pb-lrh-scroll-arrow-float 2.2s ease-in-out infinite;
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-logo-reveal-hero__scroll-arrow.is-visible {
		animation: none;
	}
}

/* 1024px, not the usual 767.98px mobile cutoff (2026-09-14, explicit
 * request: "op alle devices ... ook dus op ipad ... dezelfde styling
 * als op mobiel") — this is deliberately the same tablet-inclusive
 * breakpoint already used elsewhere in this file (e.g. .site-header's
 * own @media(max-width:1023.98px) rounded-corners rule above), so
 * iPad-width viewports (portrait AND classic-iPad landscape, exactly
 * 1024px) get the identical centered/bottom placement as phones —
 * only genuinely wide desktop viewports above this still get the
 * bottom-right treatment (base rule above). */
@media ( max-width: 1024px ) {
	.pb-logo-reveal-hero__scroll-arrow {
		right: auto;
		left: 50%;
		bottom: max( 40px, env( safe-area-inset-bottom, 40px ) );
		margin-left: -7px;
	}
}

@keyframes pb-lrh-scroll-arrow-float {
	0%,
	100% {
		transform: translateY( 0 );
	}
	50% {
		transform: translateY( 16px );
	}
}

/* Editor canvas — a true 100vh block would make the rest of the page
 * unreachable while editing, so the preview caps its height instead
 * of matching the frontend's real fullscreen sizing. Static end-state
 * only (logo at final size, text + second logo both visible) — no
 * timeline/animation replay in the editor. */
.pb-logo-reveal-hero--editor-preview {
	height: 70vh;
	min-height: 480px;
}

.pb-logo-reveal-hero__logo--preview {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate( -50%, -50% );
	max-width: 70%;
	height: auto;
	opacity: 0.18;
}

.pb-logo-reveal-hero__text-preview {
	position: relative;
	z-index: 1;
	max-width: 48rem;
}

.pb-logo-reveal-hero__second-logo--preview {
	position: relative;
	z-index: 1;
	margin-top: 1.5rem;
	height: auto;
}

/* pb/logo-reveal-hero — "align:full" breakout (2026-09-11 fix — this
 * rule was missing from the block's initial build). Same self-contained
 * pattern every other full-bleed PB block in this file uses (theme has
 * no generic .alignfull CSS of its own): the section breaks out of
 * .entry-content's own max-width edge to edge; .pb-logo-reveal-hero__
 * stage/__text keep their own max-width/centering, so the visible
 * content still reads as a normal centered column while the
 * background spans the full viewport. */
.pb-logo-reveal-hero.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Statement text must stay fully invisible (not the shared "0.26
 * opacity, blurred" resting look pb/animated-statement itself uses
 * while off-screen) until this hero's own timed reveal fires — with
 * this hero always in view from first paint, that resting look was
 * visible immediately on page load, which is explicitly not wanted
 * (2026-09-11 explicit report: "dat is niet de bedoeling. Je moet de
 * tekst nog niet zien"). Specificity note: matches "html.js
 * .animated-statement__word"'s own shape (type+class, then 2 classes)
 * so this reliably wins the *resting* state regardless of source
 * order; the existing, more specific ".animated-statement.is-revealed
 * .animated-statement__word" reveal rule elsewhere in this file still
 * wins once is-revealed is added (unchanged, not touched here), so
 * the actual reveal transition/timing is completely unaffected. */
html.js .pb-logo-reveal-hero .animated-statement__word {
	opacity: 0;
}

/* pb/logo-reveal-hero — scroll locked for the full grow→fade→reveal
 * sequence (2026-09-11 explicit request), toggled via assets/js/
 * main.js on <html>+<body> together (Safari needs both to reliably
 * block scroll). Never applied at all under reduced motion (that path
 * skips straight to the end state, nothing to wait for) and always
 * removed by the same JS timeline that adds it, so a visitor can never
 * get stuck unable to scroll. */
html.pb-lrh-scroll-lock,
body.pb-lrh-scroll-lock {
	overflow: hidden;
	height: 100%;
}

/* pb/logo-reveal-hero statement text — desktop-only (>=900px), slightly
 * smaller font-size (2026-09-11 explicit request: "maak de tekst op
 * desktop ietsjes kleiner, behoud mobiel zoals het nu is") plus the
 * 2-line wrap fix ("Laat vanaf dag één zien waar" / "je organisatie
 * voor staat.") merged into one rule. max-width is in `em` — relative
 * to THIS element's own font-size, not a fixed rem/px value — so the
 * wrap point self-scales as font-size grows with viewport width
 * between 900px and the ~1231px cap; an earlier fixed 51rem version
 * held the split correctly only until this smaller font-size shipped,
 * then let "je" slide back onto line 1 (caught before ship, not a live
 * regression). 15.1em empirically verified to hold the correct split
 * across the full desktop range (900/1000/1231/1440/1800px-equivalent
 * font-sizes all checked). Scoped to this block only (not the shared
 * .animated-statement__text base rule, which the real
 * pb/animated-statement block also uses elsewhere) and only inside the
 * >=900px query, so mobile keeps the exact same clamp()/48rem wrap
 * (.pb-logo-reveal-hero__text base rule) as before — no <br> in the
 * content, which would have also forced onto mobile. */
@media ( min-width: 900px ) {
	.pb-logo-reveal-hero .animated-statement__text {
		font-size: clamp( 1.75rem, 1rem + 2.6vw, 3rem );
		max-width: 15.1em;
	}
}

/* PaperBrain life (page 1183) only — explicit request: "Maak van jouw
 * doel werkelijkheid" keeps the shared 48px desktop ceiling above
 * untouched, but "Stap voor stap." (wrapped in a .pb-lrh-small-text
 * span directly in this instance's statementText — pb_animated_
 * statement_word_spans() preserves span/class per word, see inc/
 * animated-statement-block.php) renders smaller. Desktop-only (matches
 * the >=900px scope of the shared rule above) so mobile's existing
 * sizing is untouched. Doesn't affect the other pages that reuse
 * pb/logo-reveal-hero — none of their statementText has this class. */
@media ( min-width: 900px ) {
	body.page-id-1183 .pb-logo-reveal-hero .animated-statement__text .pb-lrh-small-text {
		font-size: 38px;
	}
}

/* ==========================================================================
   PB Feature Explorer (2026-09-14) — interactive desktop feature
   overview (Apple "compare" page-style: apple.com/nl/iphone-duo/).
   Desktop-first build per explicit request — the @media(max-width:
   1023.98px) block at the end is a SAFETY NET ONLY (keeps the block
   from visually breaking below desktop widths: absolute-positioned
   layout falls back to plain stacked flow), not a finished mobile
   design — that's an explicit separate follow-up task.
   ========================================================================== */

.pb-feature-explorer {
	background-color: var( --pbfe-bg, #f7f5f0 );
	color: var( --pbfe-text, #111111 );
	padding: 5rem 0;
	/* 2026-09-14: section-wide bg now follows the active feature's own
	 * color (see data-bg-color handling in main.js) — transition it so
	 * the whole block fades together with the stage/card crossfade
	 * instead of snapping. */
	transition: background-color var( --pbfe-transition, 0.6s ) ease;
}

.pb-feature-explorer__inner {
	margin: 0 auto;
	/* 2026-09-14 follow-up: "titel en subtitel... uitgelijnd met de kop
	 * blokken" — was 2rem, one size smaller than the tabs' own
	 * left:3rem (.pb-feature-explorer__tabs, further below) and the
	 * cards' right:3rem, so the header text sat 1rem left of where the
	 * tabs/cards start on any viewport narrower than max-width. Now
	 * 3rem on both sides, matching those exactly. */
	padding: 0 3rem;
	box-sizing: border-box;
}

/* Header row: title left, short subtext right (matches the supplied
 * mockup) — both are optional independently (eyebrow/heading/subtext
 * each only render when set, see inc/feature-explorer-block.php), so
 * this still looks correct with only one of the three present. */
.pb-feature-explorer__header {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 2rem;
	/* 2026-09-14 follow-up: "witruimte kleiner... dichter op elkaar" —
	 * tightened again now the header-main column got taller (added
	 * title-subtext line). */
	margin-bottom: 0.5rem;
	flex-wrap: wrap;
}

/* Wraps eyebrow+heading only, so the header's own 2rem gap (which
 * separates the title block from the subtext on the right) no longer
 * also lands between the eyebrow and the heading themselves. */
.pb-feature-explorer__header-main {
	min-width: 0;
}

.pb-feature-explorer__eyebrow {
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.6;
	margin: 0 0 0.4rem;
}

.pb-feature-explorer__heading {
	font-size: clamp( 2rem, 2.6vw, 2.75rem );
	/* 2026-09-16 fix: was a flat 800 on the whole heading regardless of
	 * the editor's own bold/regular toggle (RichText allowedFormats
	 * includes core/bold) — since EVERY word was already rendered at
	 * max weight either way, toggling bold on/off in wp-admin had no
	 * visible effect ("ik kan m niet meer bold of regular maken").
	 * Regular (400) base + <strong> at 800 below restores an actual
	 * visible difference. No change for any existing heading that's
	 * already wrapped entirely in <strong> (still renders fully 800,
	 * identical to before) — only affects headings where an editor
	 * mixes bold/regular words. !important is required: design-
	 * system.css's own bare `h2{font-weight:...!important}` reset
	 * (this is a plain <h2>, no .pb-type--h1/h2 class) otherwise wins
	 * regardless of this rule's higher selector specificity — same
	 * pattern already documented elsewhere in this file (e.g.
	 * .pb-bullet-block__heading above). */
	font-weight: 400 !important;
	line-height: 1.15;
	margin: 0;
	/* Encourages (not forces — real content can still wrap differently)
	 * the "bedoeld voor 2 regels" requirement without a hardcoded <br>,
	 * so an editor's own shorter/longer title still wraps naturally. */
	max-width: 20ch;
}

.pb-feature-explorer__heading strong {
	font-weight: 800;
}

/* 2026-09-16 follow-up, superseding the text-wrap:balance version above
 * in git history (that was for the old, longer "Veel meer dan alleen
 * 'een planner'" copy) — the title has since been shortened to "Méér
 * dan alleen 'een planner'" and the explicit request is now "op 1
 * regel". Root cause turned out NOT to be soft-wrap at all — the
 * editor's own content has a hardcoded <br> ("Méér dan alleen <br>'een
 * planner'"), which forces the break regardless of any CSS wrap
 * property (max-width:none / white-space:nowrap, tried first here, both
 * correctly applied but had zero effect since a real <br> ignores them).
 * Hiding that <br> is the actual fix; max-width:none + nowrap are kept
 * as a second line of defense against ordinary soft-wrapping too, on
 * the off chance the title text itself is edited longer later. Scoped
 * to page-id-365 + desktop only, so no other PB Feature Explorer
 * instance's heading wrapping changes. */
@media ( min-width: 1024px ) {
	body.page-id-365 .pb-feature-explorer__heading {
		max-width: none;
		white-space: nowrap;
	}

	body.page-id-365 .pb-feature-explorer__heading br {
		display: none;
	}
}

/* Second, separate subtext field directly under the title (2026-09-14
 * follow-up: "onder de titel moet ook subtekst komen, los van de
 * tekst die rechts staat") — .pb-feature-explorer__subtext (below)
 * stays exactly as-is on the right; this is an independent attribute
 * (titleSubtext) rendered inside .header-main, right after the
 * heading. Same font styling as the right-hand subtext so the two
 * read as one family; .header-main's flex-end alignment inside
 * .pb-feature-explorer__header (above) is what keeps this one's
 * bottom edge lined up with the right-hand subtext's bottom edge —
 * no separate alignment rule needed here. */
.pb-feature-explorer__title-subtext {
	font-size: 0.95rem;
	line-height: 1.5;
	/* 2026-09-14 follow-up: "ik wil dat de subtekst echt #000" — was
	 * opacity:0.7 (faded gray look shared with the right-hand
	 * subtext); this one now renders as true solid black instead. */
	color: #000000;
	/* 2026-09-14 follow-up: "vak... moet breder" — widened from 32ch;
	 * now the right-hand subtext is gone there's no competing content
	 * to share the row with, so this can comfortably run wider before
	 * wrapping. */
	max-width: 60ch;
	margin: 0.6rem 0 0;
}

.pb-feature-explorer__subtext {
	font-size: 0.95rem;
	line-height: 1.5;
	opacity: 0.7;
	max-width: 260px;
	text-align: right;
	margin: 0;
}

/* The interactive stage — one fixed-height, position:relative box;
 * every child area (background, tabs, visual, card) is absolutely
 * positioned within it, crossfading via opacity only (never
 * display/visibility), which is what keeps feature-switching free of
 * layout shift. --pbfe-height is set inline per-instance (desktopHeight
 * attribute); the var() here is only a fallback for the (rare) case
 * that inline style is ever stripped. */
/* Full-bleed breakout (2026-09-14 follow-up: "de afbeelding het hele
 * blok pakt", "geen witruimte links en rechts") — same left:50%/100vw
 * /-50vw technique already used for .pb-feature-explorer.alignfull
 * itself, applied here too so the stage (and therefore any feature's
 * background image) reaches the true viewport edges instead of being
 * boxed inside .pb-feature-explorer__inner's max-width/padding. The
 * header (eyebrow/title/subtext) is unaffected — it's a separate
 * sibling still inside .inner, so it stays at a readable width while
 * only the stage itself goes edge-to-edge. Rounded corners are
 * dropped (0) since a rounded card look doesn't read as "full-bleed"
 * flush against the browser edge. Tabs/cards keep their existing
 * 3rem edge offsets, now measured from the true viewport edge. */
/* Apple "iphone-duo"-style layout (2026-09-14 follow-up: "ik wil dat
 * je het design overneemt hoe het op apple site werkt", reference
 * apple.com/nl/iphone-duo/) — one big centered image, its description
 * beneath it, then a horizontal row of pill tabs below that. Stage
 * height is no longer fixed: it's driven by its own in-flow content
 * (.pb-feature-explorer__content below) now that nothing inside it is
 * absolutely positioned relative to the stage itself any more (only
 * the full-bleed background layers still are) — --pbfe-height (the
 * "Desktop hoogte" Inspector control) becomes a min-height floor
 * instead of a hard cap, so it still does something useful without
 * clipping/forcing overflow on taller content. */
.pb-feature-explorer__stage {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
	height: var( --pbfe-height, 600px );
	border-radius: 0;
	overflow: hidden;
	isolation: isolate;
}

.pb-feature-explorer__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
}

/* background-color: var(--pbfe-bg, ...) — same custom property as
 * .pb-feature-explorer itself (2026-09-14 follow-up, explicit report +
 * screenshot: a hardcoded #ece8e0 fallback here visibly mismatched the
 * section's own configurable background whenever a feature has no own
 * backgroundColor/backgroundImage set, reading as "a block inside a
 * block" instead of one continuous surface). A feature WITH its own
 * background still overrides this via its own inline style (set
 * directly on this element, inc/feature-explorer-block.php), same as
 * before — this only changes what shows when a feature has none. */
.pb-feature-explorer__bg-layer {
	position: absolute;
	inset: 0;
	background-color: var( --pbfe-bg, #f7f5f0 );
	background-size: cover;
	background-repeat: no-repeat;
	opacity: 0;
	transition: opacity var( --pbfe-transition, 0.6s ) ease;
}

.pb-feature-explorer__bg-layer.is-active {
	opacity: 1;
}

/* 2026-09-14 follow-up: "desktop moest je uberhaupt niet aantasten...
 * dit nieuwe [Apple] design gold alleen voor mobiel" — the Apple-style
 * redesign (centered column, image-on-top, boxless description, etc.)
 * was mobile-only after all; desktop reverts to exactly how it was
 * before that follow-up (tabs left, image beside them, floating white
 * card bottom-right — all rules below this point through .card-body p
 * are back to those original values). .content itself stays in the
 * markup (removing it isn't needed) but does nothing on desktop —
 * display:contents makes it disappear from the box model so
 * .tabs/.visual/.cards below behave as if they were direct children of
 * .stage again, exactly like the original absolute-positioning design
 * expects. Its real (Apple-style) layout is now mobile-only — see
 * .pb-feature-explorer__content inside the @media block further down. */
.pb-feature-explorer__content {
	display: contents;
}

/* Left tab list — vertically centered as a group, per the mockup.
 * align-items:flex-start (+ no fixed width) lets each pill size to its
 * own label instead of stretching to a shared width, which is what
 * makes the one-line-always requirement below possible: a fixed width
 * narrow enough for "Reflectie" would force "Inspiratie & online
 * content" to wrap, and one wide enough for that longest label would
 * leave short labels looking oddly padded. */
.pb-feature-explorer__tabs {
	position: absolute;
	left: 3rem;
	top: 50%;
	transform: translateY( -50% );
	z-index: 2;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.75rem;
}

.pb-feature-explorer__tab {
	display: flex;
	/* row-reverse (2026-09-14 follow-up: "plus en minnetje links van de
	 * koppen") puts the icon before the label visually while the DOM
	 * order stays label-then-icon (inc/feature-explorer-block.php) —
	 * screen readers still announce the label first either way. */
	flex-direction: row-reverse;
	align-items: center;
	justify-content: flex-end;
	gap: 1rem;
	padding: 0.9rem 1.25rem;
	/* Subtle always-on border (2026-09-14 follow-up: inactive pills
	 * "niet zichtbaar als ze wit zijn") — when a feature's own
	 * backgroundColor is set to (near-)pure white, blending the pill's
	 * semi-transparent WHITE fill on top produces literally the same
	 * white, so the pill's shape vanished entirely with no fill-color
	 * tweak able to guarantee a difference for every possible chosen
	 * background color. A border doesn't depend on the fill contrasting
	 * with the background at all, so it stays visible against any
	 * background color/image, not only white. */
	border: 1px solid rgba( 17, 17, 17, 0.12 );
	border-radius: 999px;
	background-color: rgba( 255, 255, 255, 0.6 );
	color: #111111;
	font: inherit;
	font-size: 0.95rem;
	font-weight: 600;
	text-align: left;
	white-space: nowrap;
	cursor: pointer;
	transition: background-color 0.3s ease, color 0.3s ease;
}

.pb-feature-explorer__tab:hover {
	background-color: rgba( 255, 255, 255, 0.85 );
}

.pb-feature-explorer__tab.is-active {
	background-color: #111111;
	color: #ffffff;
}

.pb-feature-explorer__tab:focus-visible {
	outline: 2px solid #6848f4;
	outline-offset: 2px;
}

/* CSS-only +/- indicator — a horizontal bar always, a vertical bar
 * that fades out on the active tab (so plus -> minus), no icon
 * font/image asset needed for two lines. */
.pb-feature-explorer__tab-icon {
	position: relative;
	width: 14px;
	height: 14px;
	flex-shrink: 0;
}

.pb-feature-explorer__tab-icon::before,
.pb-feature-explorer__tab-icon::after {
	content: "";
	position: absolute;
	background-color: currentColor;
	border-radius: 1px;
}

.pb-feature-explorer__tab-icon::before {
	left: 0;
	top: 50%;
	width: 100%;
	height: 1.5px;
	transform: translateY( -50% );
}

.pb-feature-explorer__tab-icon::after {
	top: 0;
	left: 50%;
	width: 1.5px;
	height: 100%;
	transform: translateX( -50% );
	transition: opacity 0.2s ease;
}

.pb-feature-explorer__tab.is-active .pb-feature-explorer__tab-icon::after {
	opacity: 0;
}

/* Right-hand product visual — positioned in the space beside the tab
 * column (not centered behind/under it, 2026-09-14 follow-up: "naast
 * de blokken gepositioneerd"), so it can run much larger. 360px clears
 * the widest default tab label ("Inspiratie & online content", ~274px
 * natural width, measured live) plus its 3rem left offset and a
 * breathing-room gap. right is mirrored to the same 360px (2026-09-14
 * follow-up #2: "meer naar links... ten alle tijden echt in het
 * midden geventreerd") — equal left/right insets put the image's
 * centering point exactly on the stage's own true horizontal center
 * regardless of stage width, instead of biased toward the right edge
 * the way an asymmetric left:360/right:0 box was. Every feature's
 * image is still stacked in the exact same box and crossfades via
 * opacity only, so swapping features never shifts the stage's own
 * size. */
.pb-feature-explorer__visual {
	position: absolute;
	top: 0;
	right: 360px;
	bottom: 0;
	left: 360px;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}

.pb-feature-explorer__fg-image {
	position: absolute;
	max-height: 96%;
	max-width: 88%;
	width: auto;
	height: auto;
	object-fit: contain;
	opacity: 0;
	transition: opacity var( --pbfe-transition, 0.6s ) ease;
}

.pb-feature-explorer__fg-image.is-active {
	opacity: 1;
}

/* Bottom-right white info card. Cards still stack at inset:0 inside
 * this box and crossfade via opacity only (so the CARD ELEMENTS
 * themselves never cause a layout shift mid-fade) — but the box's own
 * height is no longer a fixed constant: it's set inline (px, from
 * main.js, reading the active card's real scrollHeight) on every
 * activate() call, 2026-09-14 follow-up: "het vak... moet ten alle
 * tijden mee bewegen met de grootte en hoeveelheid van de tekst" — a
 * fixed min-height clipped longer bodies (e.g. "Week- & maandplanning")
 * so their text visibly overflowed past the rounded white background.
 * min-height stays as a floor only, for short bodies (e.g. "Gewoonte
 * Trackers") so the card doesn't look cramped; transition makes the
 * resize itself animate instead of snapping. */
.pb-feature-explorer__cards {
	position: absolute;
	right: 3rem;
	/* 2026-09-14 follow-up: "plaats het blok rechts iets naar boven
	 * zodat het goed in beeld is" — nudged up from 3rem so the card
	 * sits with a bit more breathing room from the stage's own bottom
	 * edge (helps it stay clear of the viewport edge on shorter
	 * windows too). */
	bottom: 5.5rem;
	z-index: 2;
	width: 300px;
	min-height: 190px;
	/* 2026-09-14 follow-up: "pas alles zien als je op een knop hebt
	 * geklikt" — the whole card box starts invisible (not just its
	 * individual .card children, which already default to opacity:0
	 * each) so there's no empty white rounded box sitting there before
	 * a first click; main.js adds .is-revealed on the first activate(). */
	opacity: 0;
	transition: height var( --pbfe-transition, 0.6s ) ease, opacity var( --pbfe-transition, 0.6s ) ease;
}

.pb-feature-explorer__cards.is-revealed {
	opacity: 1;
}

.pb-feature-explorer__card {
	position: absolute;
	/* top/left/right only (no bottom) so each card's height stays its
	 * own natural/content-driven height instead of being clamped to
	 * whatever height .pb-feature-explorer__cards currently has — that
	 * clamp is exactly what caused longer bodies to overflow the box
	 * (2026-09-14 follow-up). main.js reads THIS card's real
	 * offsetHeight and applies it to the container. */
	top: 0;
	left: 0;
	right: 0;
	background-color: var( --pbfe-card-bg, #ffffff );
	color: var( --pbfe-card-text, #111111 );
	border-radius: 20px;
	padding: 1.5rem;
	box-sizing: border-box;
	opacity: 0;
	transition: opacity var( --pbfe-transition, 0.6s ) ease, background-color var( --pbfe-transition, 0.6s ) ease;
	pointer-events: none;
}

/* main.js toggles this on .pb-feature-explorer__cards whenever the
 * ACTIVE feature's own background is (near-)white — 2026-09-14
 * follow-up: "wit op wit is niet zichtbaar" for the card too (same
 * clash as the tabs, fixed with a border there; here the request was
 * explicitly for the card's own fill to change, not just an outline).
 * Higher specificity than the --pbfe-card-bg rule above, so it wins
 * without !important; a configured cardBackgroundColor that ISN'T
 * white is untouched since this only applies when the SECTION
 * background itself is near-white. */
.pb-feature-explorer__cards--on-white .pb-feature-explorer__card {
	background-color: #F1F1F1;
}

.pb-feature-explorer__card.is-active {
	opacity: 1;
	pointer-events: auto;
}

.pb-feature-explorer__card-kicker {
	font-size: 0.7rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	opacity: 0.55;
	margin: 0 0 0.5rem;
}

.pb-feature-explorer__card-title {
	font-size: 1.15rem;
	font-weight: 800;
	line-height: 1.3;
	margin: 0 0 0.5rem;
}

/* 2026-09-17, explicit request, desktop too (mobile version above already
 * done same day) — "Nieuwe inzichten. Op papier & online" (the
 * "Inspiratie & online content" card, data-index="4") should read
 * "Nieuwe inzichten." / "Op papier & online", not the natural desktop
 * wrap of "...inzichten. Op" / "papier & online" (measured live: card
 * column is 252px; "...inzichten." is 169px, "...inzichten. Op" is
 * 200px — both fit under 252px, so "Op" stayed on line 1 by default).
 * 185px sits between those two, forcing the break right after
 * "inzichten." instead. Scoped to this one card + page-id-365 +
 * desktop only. */
@media ( min-width: 1024px ) {
	body.page-id-365 .pb-feature-explorer__card[data-index="4"] .pb-feature-explorer__card-title {
		max-width: 185px;
	}
}

.pb-feature-explorer__card-body {
	font-size: 0.9rem;
	line-height: 1.55;
	opacity: 0.75;
}

.pb-feature-explorer__card-body p {
	margin: 0;
}

/* True edge-to-edge on desktop — same breakout technique already used
 * throughout this file for every other full-bleed block (e.g.
 * .pb-b2b-hero.alignfull, .faq.alignfull). */
.pb-feature-explorer.alignfull {
	position: relative;
	left: 50%;
	width: 100vw;
	max-width: 100vw;
	margin-left: -50vw;
}

/* Safety net only below desktop widths (see this section's own top
 * comment) — a plain stacked, non-absolute layout so nothing overlaps
 * or overflows; not a designed mobile/tablet experience yet. */
@media ( max-width: 1023.98px ) {
	.pb-feature-explorer__header {
		flex-direction: column;
		align-items: flex-start;
	}

	/* 2026-09-16, explicit request: align this page's title+subtitle
	 * horizontally with the PB Image Text paragraph directly above it —
	 * that block's own left/right inset is .pb-image-text's section
	 * padding: clamp(1.25rem, 4vw, 3rem) (see that rule). This header
	 * instead inherits .pb-feature-explorer__inner's fixed 3rem (48px)
	 * padding, which is wider than the clamp on most phones, so title/
	 * subtitle sat narrower/more inset than the paragraph above. Negative
	 * margin cancels the inherited 3rem back out to .inner's own edge,
	 * then padding re-applies the image-text-matching clamp instead —
	 * .inner itself (and therefore .stage/tabs/cards below, untouched)
	 * keeps its original 3rem. Scoped to page-id-365 only, so no other
	 * PB Feature Explorer instance is affected. */
	body.page-id-365 .pb-feature-explorer__header {
		margin-left: -3rem;
		margin-right: -3rem;
		padding-left: clamp( 1.25rem, 4vw, 3rem );
		padding-right: clamp( 1.25rem, 4vw, 3rem );
		box-sizing: border-box;
	}

	/* 2026-09-16, explicit request: mobile-only start background (before
	 * any pill/tab is clicked) should be #F1F1F1 (same as the search/
	 * "Zoek een store | Partner worden" bar), not this instance's actual
	 * start color — main.js's syncStageVisual(0) (on load) sets the
	 * SECTION's own background-color inline to the first tab's own
	 * data-bg-color (#ffffff here). .pb-feature-explorer--interacted
	 * (main.js) is only ever added inside activate(), i.e. on a REAL tab
	 * click/keypress, never during that initial load-time call — so
	 * :not(...) reliably means "nothing clicked yet". A :has()-based
	 * version (targeting .cards:not(.is-revealed) instead) was tried
	 * first but didn't reliably invalidate when that descendant's class
	 * changed (confirmed live). !important is required to beat the
	 * inline style. Once a tab is clicked, this class appears, the rule
	 * stops matching, and the existing per-tab JS-driven background
	 * (untouched) takes back over exactly as before. Image/tabs/cards/
	 * everything else unchanged. */
	body.page-id-365 .pb-feature-explorer:not(.pb-feature-explorer--interacted) {
		background-color: #F1F1F1 !important;
	}

	/* 2026-09-16, explicit request: mobile-only start image (before any
	 * pill/tab clicked) — the real tab-0 image (Group-483421.png) has an
	 * opaque background, uploaded replacement (Feature-Explorer-Mobile-
	 * Start.png, transparent PNG) is meant to sit directly on the
	 * #F1F1F1 start background above instead. content:url() is a real
	 * CSS property (supported everywhere modern) that swaps what an
	 * <img> renders without touching its src attribute — same
	 * .pb-feature-explorer--interacted gate as the background-color
	 * rule above, so this only applies pre-interaction; desktop and
	 * every other tab's own image are untouched. */
	body.page-id-365 .pb-feature-explorer:not(.pb-feature-explorer--interacted) .pb-feature-explorer__fg-image[data-index="0"] {
		content: url( "https://paperbrain.store/wp-content/uploads/2026/09/Feature-Explorer-Mobile-Start.png" );
	}

	.pb-feature-explorer__subtext,
	.pb-feature-explorer__title-subtext {
		text-align: left;
		max-width: none;
	}

	.pb-feature-explorer__stage {
		height: auto;
		min-height: 0;
		display: flex;
		flex-direction: column;
		border-radius: 0;
	}

	.pb-feature-explorer__bg {
		display: none;
	}

	/* Mobile-only real layout for .content (desktop neutralizes it via
	 * display:contents, see the base rule above) — centered column,
	 * image/description/tabs stacked in normal flow. This is exactly
	 * what used to be the desktop Apple-style rule before "desktop
	 * moest je uberhaupt niet aantasten" (2026-09-14 follow-up) made it
	 * mobile-only. */
	.pb-feature-explorer__content {
		position: relative;
		z-index: 1;
		width: 100%;
		max-width: 640px;
		margin: 0 auto;
		padding: 4rem 2rem;
		box-sizing: border-box;
		display: flex;
		flex-direction: column;
		align-items: center;
		text-align: center;
	}

	/* 2026-09-14 follow-up: "op mobiel... op een rij van links naar
	 * rechts staan en niet onder elkaar" — one horizontally-scrollable
	 * row (nowrap + overflow-x) instead of wrapping onto multiple lines;
	 * flex-shrink:0 on the pills below keeps each one at its own full
	 * width instead of getting squeezed to fit. justify-content:
	 * flex-start (not center) so scrolling starts from the first pill
	 * flush left, matching how a scrollable row is normally read. */
	.pb-feature-explorer__tabs {
		position: static;
		transform: none;
		/* .pb-feature-explorer__content has align-items:center, so
		 * without an explicit width cap here .tabs just grows to fit
		 * ALL its pills (shrink-to-fit sizing) instead of staying within
		 * the viewport — nothing would then actually overflow ITS OWN
		 * box, so overflow-x:auto never had anything to scroll; the
		 * stage's own overflow:hidden was just silently clipping it
		 * instead. max-width:100% caps the row at its container's real
		 * available width so the pills genuinely overflow inside it and
		 * overflow-x can scroll them. */
		width: 100%;
		max-width: 100%;
		flex-direction: row;
		flex-wrap: nowrap;
		justify-content: flex-start;
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		/* Animates main.js's direct scrollLeft assignment (scrollTabIntoView())
		 * — 2026-09-14 follow-up: bring the just-tapped pill fully into
		 * view instead of leaving it cut off at the edge. */
		scroll-behavior: smooth;
		/* 2026-09-14 follow-up: "teveel witruimte onder de afbeelding...
		 * dichter op de pillen" — was inheriting the desktop rule's
		 * 2.5rem margin-top (meant for the wider gap around a centered
		 * Apple-style image there); tightened for mobile specifically,
		 * on top of .cards's own reduced margin-bottom just above.
		 * 2026-09-15 follow-up: "nog net iets dichter" — trimmed further;
		 * padding split asymmetrically (was flat 1.5rem) so the row keeps
		 * its horizontal breathing room but loses most of its top gap,
		 * which is what actually separated it from the image above. */
		margin-top: 0.25rem;
		padding: 0.75rem 1.5rem;
	}

	.pb-feature-explorer__tab {
		flex-shrink: 0;
	}

	/* 2026-09-14 follow-up: "de bijbehorende tekst en afbeelding
	 * moesten samen gevoegd worden in de stijl van wp:pb/retail-
	 * segment-card" — image + description merged into one unit styled
	 * like .retail-segment-card further up this file (rounded
	 * object-fit:cover image, margin-bottom, plain bold title + gray
	 * body text below, left-aligned) instead of the desktop Apple-style
	 * centered/boxless treatment. The pills below stay exactly as they
	 * were (plain scrollable text pills) — only this image+text unit
	 * changes. */
	.pb-feature-explorer__visual {
		position: static;
		/* 2026-09-16 follow-up, reverted same day: a fixed aspect-ratio
		 * box was tried here to stabilize height, but the explicit
		 * follow-up request clarified content SHOULD keep its natural
		 * size — only the pills' own on-screen position must stay put
		 * (now handled via JS scroll compensation in activate(),
		 * main.js). Back to plain auto height. */
		height: auto;
		/* 2026-09-15 follow-up: "nog net iets dichter tegen de pil
		 * scrollbar aan" — trimmed from 1.25rem alongside .tabs/.cards's
		 * own reductions just below. */
		margin: 0 0 0.75rem;
		/* Safety net (2026-09-14 follow-up: "zorg dat de afbeelding
		 * altijd in t vak past... een stukje valt er net aan de
		 * rechterkant buiten") — belt-and-braces alongside .fg-image's
		 * own sizing below, matching .retail-segment-card__media's own
		 * overflow:hidden. */
		overflow: hidden;
	}

	/* .fg-image still inherited position:absolute from the desktop
	 * rule, but its parent (.visual, just above) is position:static
	 * here, so it fell back to positioning against the nearest
	 * positioned ANCESTOR instead — .pb-feature-explorer__content
	 * (spans image+text+tabs together) — covering the text and tabs
	 * below instead of staying inside .visual's own box. static + the
	 * same display:none/is-active pattern already used for .card below
	 * keeps exactly one image in normal flow.
	 *
	 * object-fit:contain + no forced aspect-ratio (2026-09-14 follow-up:
	 * "zorg dat de afbeelding altijd in t vak past... een stukje valt
	 * er net aan de rechterkant buiten") — these are full marketing
	 * graphics with their own baked-in text, not uniform product
	 * photos, so object-fit:cover on a forced 1:1 box was cropping
	 * (visually reading as "falling outside the box") whichever edge
	 * didn't match a square. width:100%/height:auto with no fixed
	 * aspect-ratio means each image just renders at its own natural
	 * proportions — guaranteed never cropped, never overflowing. */
	.pb-feature-explorer__fg-image {
		position: static;
		display: none;
		width: 100%;
		max-width: 100%;
		height: auto;
		object-fit: contain;
		border-radius: 16px;
	}

	.pb-feature-explorer__fg-image.is-active {
		display: block;
	}

	.pb-feature-explorer__cards {
		position: static;
		width: auto;
		/* 2026-09-14 follow-up: "teveel witruimte onder de afbeelding...
		 * zodat de afbeelding dichter op de pillen zit" — the REAL
		 * culprit: min-height:190px was still inherited from the desktop
		 * rule (only height was overridden below, a different property),
		 * forcing ~190px of empty space before any pill is tapped (no
		 * card carries .is-active yet, so .cards has no visible content
		 * of its own). 0 here removes that floor entirely; margin
		 * trimmed too since its own bottom margin didn't collapse with
		 * .tabs's margin-top below (both are flex children of .content). */
		min-height: 0;
		/* 2026-09-15 follow-up: "nog net iets dichter" — trimmed from
		 * 0.5rem alongside .visual/.tabs's own reductions above/below. */
		margin: 0 0 0.25rem;
		text-align: left;
		/* 2026-09-16 follow-up, reverted same day: a fixed height was
		 * tried here to stabilize the pills' position, but the explicit
		 * follow-up request clarified content SHOULD keep its natural
		 * size ("dit is logisch dat dit steeds anders is") — only the
		 * pills' own on-screen position must stay put (now handled via
		 * JS scroll compensation in activate(), main.js). Back to plain
		 * auto height, overriding the inline px height main.js sets for
		 * the desktop crossfade box. */
		height: auto !important;
	}

	.pb-feature-explorer__card {
		position: static;
		opacity: 1;
		pointer-events: auto;
		display: none;
	}

	.pb-feature-explorer__card.is-active {
		display: block;
	}

	/* Matches .retail-segment-card__title's font-size; body color is
	 * true black rather than retail-segment-card's own #666 gray —
	 * 2026-09-14 follow-up: "zorg dat de tekst echt zwart is #000000
	 * en niet lichtgrijs". */
	.pb-feature-explorer__card-title {
		font-size: 21px;
	}

	/* 2026-09-17, explicit request: force "Nieuwe inzichten. Op papier &
	 * online" (the "Inspiratie & online content" card, data-index="4")
	 * onto two lines — "Nieuwe inzichten." / "Op papier & online" —
	 * instead of the one line it naturally fits on at this column width
	 * (measured live: full text ~397px, column 502px). max-width:205px
	 * sits between "Nieuwe inzichten." (~193px) and "...inzichten. Op"
	 * (~228px), so the browser's own word-wrap breaks right there. Scoped
	 * to this one card + page-id-365 + mobile only, so no other PB
	 * Feature Explorer card/instance wrapping changes. */
	body.page-id-365 .pb-feature-explorer__card[data-index="4"] .pb-feature-explorer__card-title {
		max-width: 205px;
	}

	.pb-feature-explorer__card-body {
		max-width: none;
		margin: 0;
		color: #000000;
		opacity: 1;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-feature-explorer__bg-layer,
	.pb-feature-explorer__fg-image,
	.pb-feature-explorer__card,
	.pb-feature-explorer__tab {
		transition: none;
	}
}

/* ==========================================================================
   PB Editorial Hero (pb/editorial-hero) — reusable full-bleed hero, flat
   single block (no InnerBlocks children): every element (logo/title/
   subtitle/CTA) is exactly one required field, not an optional/
   reorderable layer — same architectural rationale as .pb-b2b-hero.
   Content column left/center/right positionable (right by default so a
   background photo's own subject stays visible on the left half).
   Entrance animation reuses the existing generic [data-pb-reveal]
   scroll-reveal system (pb_reveal_attrs(), inc/block-helpers.php) — no
   new animation implementation.
   ========================================================================== */
.pb-editorial-hero {
	position: relative;
	z-index: 0;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	overflow: hidden;
	display: flex;
	align-items: center;
	padding: 4rem 0;
}

.pb-editorial-hero__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
	background-size: cover;
	background-repeat: no-repeat;
	background-position: var(--peh-bg-position, 50% 50%);
}

.pb-editorial-hero__inner {
	position: relative;
	z-index: 1;
	width: 100%;
	max-width: var(--wp--custom--section--container-width, 1440px);
	margin: 0 auto;
	padding-left: 5vw;
	padding-right: 5vw;
	display: flex;
}

.pb-editorial-hero--align-right .pb-editorial-hero__inner {
	justify-content: flex-end;
}

.pb-editorial-hero--align-center .pb-editorial-hero__inner {
	justify-content: center;
}

.pb-editorial-hero--align-left .pb-editorial-hero__inner {
	justify-content: flex-start;
}

.pb-editorial-hero__content {
	text-align: left;
	color: inherit;
}

.pb-editorial-hero__logo {
	margin: 0 0 1.5rem;
}

.pb-editorial-hero__logo img {
	display: block;
	height: auto;
}

.pb-editorial-hero__title {
	margin: 0 0 1rem;
	color: inherit;
	font-weight: 400 !important;
}

.pb-editorial-hero__subtitle {
	margin: 0 0 1.5rem;
	color: inherit;
}

/* <strong> inside this subtitle 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 exact fix already applied to .pb-hc-subtitle__text/
 * .pb-hc-body__text/.pb-b2b-hero-body__text strong elsewhere — this
 * variant was simply missed when the block was first built. */
.pb-editorial-hero__subtitle strong {
	font-weight: 700;
}

.pb-editorial-hero__cta {
	margin: 0;
}

@media (max-width: 767.98px) {
	.pb-editorial-hero {
		padding: 2.5rem 0;
	}

	.pb-editorial-hero__inner {
		justify-content: center;
	}

	.pb-editorial-hero__content {
		max-width: 100% !important;
		text-align: center;
	}

	.pb-editorial-hero__logo img {
		margin: 0 auto;
	}
}

/* Animated underline under a bold word in the title (e.g. "You Want") —
 * same "streep" treatment as the homepage/Retail hero heading (see
 * body.page-id-27/-363 .pb-hero-centered rules above), reused here via a
 * plain class selector since pb/editorial-hero is a general-purpose
 * reusable block, not a single page's own content — no page-ID hack.
 * Reuses the SAME shared @keyframes pb-hero-centered-underline defined
 * above; no duplicate animation implementation. */
.pb-editorial-hero__title strong,
.campaign-banner__title u,
.pb-cb-heading__text u {
	position: relative;
	display: inline-block;
	text-decoration: none;
}

.pb-editorial-hero__title strong::after,
.campaign-banner__title u,
.pb-cb-heading__text u::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: -0.16em;
	width: 100%;
	height: 0.22em;
	background-color: currentColor;
	-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M4,15 Q50,-2 96,10' fill='none' stroke='black' stroke-width='5.5' stroke-linecap='round'/%3E%3C/svg%3E");
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 20' preserveAspectRatio='none'%3E%3Cpath d='M4,15 Q50,-2 96,10' fill='none' stroke='black' stroke-width='5.5' stroke-linecap='round'/%3E%3C/svg%3E");
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
	clip-path: inset(0 100% 0 0);
}

@media (prefers-reduced-motion: no-preference) {
	.pb-editorial-hero__title strong::after,
	.campaign-banner__title u,
.pb-cb-heading__text u::after {
		animation: pb-editorial-hero-underline-draw 0.6s ease-out 0.5s forwards;
	}
}

@media (prefers-reduced-motion: reduce) {
	.pb-editorial-hero__title strong::after,
	.campaign-banner__title u,
.pb-cb-heading__text u::after {
		clip-path: inset(0 0 0 0);
	}
}

@keyframes pb-editorial-hero-underline-draw {
	to {
		clip-path: inset(0 0 0 0);
	}
}

/* Responsive hero switch (homepage, 2026-09-29, explicit request): show
 * the existing pb/hero-centered "Hero header" only on mobile, and a
 * pb/editorial-hero instance only on tablet+desktop — both blocks exist
 * in post_content, plain CSS class + media query decides which one
 * paints, no page-ID hack, no JS, no layout shift (display:none is
 * applied before first paint since this lives in the main stylesheet). */
@media (min-width: 768px) {
	.pb-only-mobile {
		display: none !important;
	}
}

@media (max-width: 767.98px) {
	.pb-only-tablet-desktop {
		display: none !important;
	}
}

/* "pb/mobile-editorial-hero" — mobile-only full-bleed hero: background
 * photo, title+subtitle positioned top-right via the block's own
 * contentPaddingTop/contentPaddingRight attributes, CTA centered at the
 * bottom via ctaBottomSpacing. Title reuses pb-editorial-hero__title's
 * own emphasis/underline rules above (shared class on the element, no
 * duplicate selector here) and pb-button/pb-type-- for CTA/typography —
 * only this block's own layout/position rules live here. */
.pb-mobile-editorial-hero {
	position: relative;
	z-index: 0;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	overflow: hidden;
}

.pb-mobile-editorial-hero__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
	background-size: cover;
	background-repeat: no-repeat;
	background-position: var(--pmeh-bg-position, 50% 50%);
}

.pb-mobile-editorial-hero__content {
	position: absolute;
	top: 0;
	right: 0;
	z-index: 1;
	box-sizing: border-box;
	text-align: left;
}

.pb-mobile-editorial-hero__title {
	margin: 0 0 0.5rem;
	color: inherit;
	font-weight: 400 !important;
}

.pb-mobile-editorial-hero__subtitle {
	margin: 0;
	color: inherit;
}

.pb-mobile-editorial-hero__subtitle strong {
	font-weight: 700;
}

.pb-mobile-editorial-hero__cta {
	position: absolute;
	left: 50%;
	bottom: 0;
	z-index: 1;
	transform: translateX(-50%);
}

/* ==========================================================================
   PB Instagram Feed (2026-10-09) — pb/instagram-feed. Data is fed
   entirely by the official Instagram API connection (inc/instagram-
   integration.php, Settings -> PB Instagram); this is layout/visual CSS
   only, no data logic here. Breakpoints match the theme's established
   768px/1200px tablet/desktop split. */
.pb-instagram-feed {
	background-color: var( --pbif-bg, #ffffff );
	padding: 3rem 1.25rem;
}

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

.pb-instagram-feed__empty {
	max-width: 640px;
	margin: 0 auto;
	text-align: center;
	color: rgba( 17, 17, 17, 0.6 );
}

.pb-instagram-feed__header {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.5rem;
	max-width: 640px;
	margin: 0 auto 2rem;
	text-align: center;
}

/* Classic Instagram 4-stop gradient ring around the avatar. */
.pb-instagram-feed__avatar-ring {
	display: inline-flex;
	padding: 3px;
	border-radius: 50%;
	background: conic-gradient( from 90deg, #f9ce34, #ee2a7b, #6228d7, #f9ce34 );
}

.pb-instagram-feed__avatar {
	display: block;
	width: 64px;
	height: 64px;
	border-radius: 50%;
	border: 3px solid #ffffff;
	object-fit: cover;
}

.pb-instagram-feed__identity {
	display: flex;
	flex-direction: column;
	gap: 0.1rem;
}

.pb-instagram-feed__name {
	font-weight: 700;
	font-size: 1.1rem;
	color: #111111;
}

.pb-instagram-feed__username {
	font-size: 0.9rem;
	color: rgba( 17, 17, 17, 0.6 );
}

.pb-instagram-feed__stats {
	display: flex;
	gap: 1.5rem;
}

.pb-instagram-feed__stat {
	display: flex;
	flex-direction: column;
	align-items: center;
	font-size: 0.85rem;
	color: rgba( 17, 17, 17, 0.6 );
}

.pb-instagram-feed__stat strong {
	font-size: 1.1rem;
	color: #111111;
}

/* Deliberately Instagram's own blue (not .pb-button--primary's purple)
 * — explicit design requirement to match the reference 1:1. */
.pb-instagram-feed__follow {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	background-color: #0095f6;
	color: #ffffff;
	font-weight: 600;
	font-size: 0.9rem;
	padding: 0.5rem 1.25rem;
	border-radius: 8px;
	text-decoration: none;
}

.pb-instagram-feed__follow:hover {
	background-color: #1877c9;
	color: #ffffff;
}

.pb-instagram-feed__grid {
	position: relative;
	max-width: 1100px;
	margin: 0 auto;
}

.pb-instagram-feed__scroller {
	display: grid;
	grid-auto-flow: column;
	grid-template-rows: repeat( 2, auto );
	column-gap: var( --pbif-gap, 12px );
	row-gap: var( --pbif-gap, 12px );
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
}

.pb-instagram-feed__scroller::-webkit-scrollbar {
	display: none;
}

.pb-instagram-feed__item {
	position: relative;
	display: block;
	aspect-ratio: 1 / 1;
	scroll-snap-align: start;
	width: calc( ( 100% - ( var( --pbif-cols-desktop, 4 ) - 1 ) * var( --pbif-gap, 12px ) ) / var( --pbif-cols-desktop, 4 ) );
	overflow: hidden;
	background-color: #f1f1f1;
}

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

.pb-instagram-feed__type-icon {
	position: absolute;
	top: 8px;
	right: 8px;
	width: 20px;
	height: 20px;
	filter: drop-shadow( 0 1px 2px rgba( 0, 0, 0, 0.5 ) );
}

.pb-instagram-feed__arrow {
	position: absolute;
	top: 50%;
	transform: translateY( -50% );
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	border: none;
	background-color: #ffffff;
	box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.15 );
	cursor: pointer;
	padding: 0;
}

.pb-instagram-feed__arrow svg {
	width: 20px;
	height: 20px;
}

.pb-instagram-feed__arrow--prev {
	left: -20px;
}

.pb-instagram-feed__arrow--next {
	right: -20px;
}

@media ( max-width: 1199.98px ) {
	.pb-instagram-feed__item {
		width: calc( ( 100% - ( var( --pbif-cols-tablet, 2 ) - 1 ) * var( --pbif-gap, 12px ) ) / var( --pbif-cols-tablet, 2 ) );
	}
}

@media ( max-width: 767.98px ) {
	.pb-instagram-feed__item {
		width: calc( ( 100% - ( var( --pbif-cols-mobile, 1 ) - 1 ) * var( --pbif-gap, 12px ) ) / var( --pbif-cols-mobile, 1 ) );
	}

	.pb-instagram-feed__stats {
		gap: 1rem;
	}

	.pb-instagram-feed__arrow--prev {
		left: 4px;
	}

	.pb-instagram-feed__arrow--next {
		right: 4px;
	}
}

/* ==========================================================================
   PB Fullscreen Media Hero (2026-10-12) — pb/fullscreen-media-hero, see
   inc/fullscreen-media-hero-block.php. Height/overlay/position/colors
   are all attribute-driven CSS custom properties (set inline per
   instance) with safe fallbacks here — same "attribute drives the var,
   CSS supplies the fallback" pattern as every other PB hero block.
   Breakpoints match the theme's established 768px/1200px split.
   ========================================================================== */
.pb-fullscreen-media-hero {
	position: relative;
	z-index: 0;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	overflow: hidden;
	min-height: var( --pbfmh-height-mobile, 100vh );
	color: var( --pbfmh-text, #ffffff );
	/* Fallback only — instantly overdrawn by __media's image/video once
	 * one is set. Keeps the default white text legible (and the section
	 * from reading as "broken") on a fresh instance before an admin has
	 * picked a background yet. */
	background-color: #111111;
}

@media ( min-width: 768px ) and ( max-width: 1199.98px ) {
	.pb-fullscreen-media-hero {
		min-height: var( --pbfmh-height-tablet, 100vh );
	}
}

@media ( min-width: 1200px ) {
	.pb-fullscreen-media-hero {
		min-height: var( --pbfmh-height-desktop, 100vh );
	}
}

.pb-fullscreen-media-hero__media {
	position: absolute;
	inset: 0;
	z-index: 0;
}

.pb-fullscreen-media-hero__image,
.pb-fullscreen-media-hero__video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: var( --pbfmh-bg-position, 50% 50% );
}

/* Video sits above the fallback/poster image; never shown at all under
 * reduced motion — the still image remains visible underneath. */
.pb-fullscreen-media-hero__video {
	z-index: 1;
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-fullscreen-media-hero__video {
		display: none;
	}
}

.pb-fullscreen-media-hero__overlay {
	position: absolute;
	inset: 0;
	z-index: 1;
	background-color: var( --pbfmh-overlay-color, #000000 );
	opacity: var( --pbfmh-overlay-alpha, 0 );
	pointer-events: none;
}

.pb-fullscreen-media-hero__inner {
	position: relative;
	z-index: 2;
	min-height: inherit;
	display: flex;
	align-items: var( --pbfmh-align, center );
	justify-content: var( --pbfmh-justify, center );
	padding: 4rem 1.5rem;
	box-sizing: border-box;
}

.pb-fullscreen-media-hero__content {
	text-align: var( --pbfmh-text-align, center );
	color: inherit;
}

.pb-fullscreen-media-hero__title {
	margin: 0 0 1rem;
	color: inherit;
	font-weight: 700 !important;
	/* Respects the admin's configured size as a ceiling, but never
	 * overflows a narrow viewport regardless of how large that value is
	 * set — a safety clamp, not a second configurable size. */
	font-size: clamp( 1.5rem, 8vw, var( --pbfmh-title-size, 56px ) );
	line-height: 1.1;
}

.pb-fullscreen-media-hero__subtitle {
	margin: 0 0 1.5rem;
	color: inherit;
}

.pb-fullscreen-media-hero__subtitle:empty {
	display: none;
}

.pb-fullscreen-media-hero__cta {
	margin: 0;
}

@media ( max-width: 767.98px ) {
	.pb-fullscreen-media-hero__inner {
		padding: 2.5rem 1.25rem;
	}
}

/* ==========================================================================
   PB Triple Image Links (2026-10-12) — pb/triple-image-links, see inc/
   triple-image-links-block.php. Desktop/tablet: 3 equal columns, fixed
   row height per breakpoint. Mobile: stacked, each tile keeping its own
   height. Hover zoom only transforms the <img> (not the tile), so the
   centered title never shifts. Breakpoints match the theme's
   established 768px/1200px split.
   ========================================================================== */
.pb-triple-image-links {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	display: grid;
	grid-template-columns: repeat( 3, 1fr );
	gap: var( --ptil-gap, 2px );
	height: var( --ptil-height-mobile, 320px );
}

@media ( min-width: 768px ) and ( max-width: 1199.98px ) {
	.pb-triple-image-links {
		height: var( --ptil-height-tablet, 420px );
	}
}

@media ( min-width: 1200px ) {
	.pb-triple-image-links {
		height: var( --ptil-height-desktop, 520px );
	}
}

.pb-triple-image-links__tile {
	position: relative;
	display: block;
	overflow: hidden;
	background-color: #222222;
	color: var( --ptil-text, #ffffff );
	text-decoration: none;
}

.pb-triple-image-links__tile--linked {
	cursor: pointer;
}

.pb-triple-image-links__image {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.4s ease;
	will-change: transform;
}

.pb-triple-image-links__tile:hover .pb-triple-image-links__image {
	transform: scale( var( --ptil-zoom, 1.05 ) );
}

.pb-triple-image-links__title {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate( -50%, -50% );
	z-index: 1;
	max-width: 85%;
	font-size: var( --ptil-title-size, 26px );
	font-weight: 600;
	color: var( --ptil-text, #ffffff );
	text-align: center;
	white-space: normal;
	text-shadow: 0 1px 4px rgba( 0, 0, 0, 0.4 );
	pointer-events: none;
}

@media ( max-width: 767.98px ) {
	.pb-triple-image-links {
		grid-template-columns: 1fr;
		height: auto;
	}

	.pb-triple-image-links__tile {
		height: var( --ptil-height-mobile, 320px );
	}

	/* Title keeps its natural width — absolute positioning at left:50%
	 * would otherwise cap it at half the tile and wrap it. */
	.pb-triple-image-links__title {
		width: max-content;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-triple-image-links__image {
		transition: none;
	}
}

/* ==========================================================================
   PB Split Image Hero (2026-10-14) — pb/split-image-hero, see inc/
   split-image-hero-block.php. Desktop/tablet: full-width 50/50 split
   (image | coloured text panel, image side switchable). Mobile: one tall
   full-bleed image with the text centred on a dark overlay.
   ========================================================================== */
.pb-split-hero {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	display: grid;
	grid-template-columns: 1fr 1fr;
	height: var( --psih-height-desktop, 650px );
	overflow: hidden;
}

.pb-split-hero--image-right .pb-split-hero__media {
	order: 2;
}

.pb-split-hero__media {
	position: relative;
	min-height: 0;
	overflow: hidden;
	background-color: #efeeec;
}

.pb-split-hero__media picture {
	display: contents;
}

.pb-split-hero__image {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.pb-split-hero__placeholder {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100%;
	padding: 2rem;
}

.pb-split-hero__overlay {
	display: none;
}

.pb-split-hero__content {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 1.25rem;
	padding: 3rem 2rem;
	background-color: var( --psih-panel, #efeeec );
	color: var( --psih-text, #111111 );
	text-align: center;
}

.pb-split-hero__subtitle {
	margin: 0;
	font-size: 0.8rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: inherit;
}

/* !important: design-system.css sets h2 size/weight/colour with !important. */
.pb-split-hero .pb-split-hero__title {
	margin: 0;
	max-width: 14ch;
	font-size: clamp( 2rem, 3.2vw, 3.25rem ) !important;
	font-weight: 400 !important;
	line-height: 1.1 !important;
	color: inherit !important;
}

.pb-split-hero__cta {
	display: inline-block;
	font-size: 0.8rem;
	font-weight: 500;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: inherit;
	text-decoration: none;
	border-bottom: 1.5px solid currentColor;
	padding-bottom: 0.15rem;
	transition: opacity 0.2s ease;
}

a.pb-split-hero__cta:hover,
a.pb-split-hero__cta:focus-visible {
	opacity: 0.6;
	color: inherit;
}

@media ( max-width: 767.98px ) {
	.pb-split-hero {
		display: block;
		height: calc( var( --psih-height-mobile, 95 ) * 1vh );
		height: calc( var( --psih-height-mobile, 95 ) * 1svh );
	}

	.pb-split-hero__media {
		position: absolute;
		inset: 0;
	}

	.pb-split-hero__overlay {
		display: block;
		position: absolute;
		inset: 0;
		background-color: rgba( 0, 0, 0, var( --psih-overlay, 0.35 ) );
	}

	.pb-split-hero__content {
		height: 100%;
		padding: 2.5rem 1.25rem;
		background-color: transparent;
		color: var( --psih-text-mobile, #ffffff );
	}

	.pb-split-hero .pb-split-hero__title {
		font-size: clamp( 1.9rem, 9vw, 2.6rem ) !important;
	}

	/* Optional logo takes the subtitle's place on mobile. */
	.pb-split-hero__logo {
		display: block;
		width: var( --psih-logo-width, 140px );
		max-width: 80%;
		height: auto;
	}

	.pb-split-hero--has-logo .pb-split-hero__subtitle {
		display: none;
	}
}

/* Logo is mobile-only; desktop keeps the subtitle. */
@media ( min-width: 768px ) {
	.pb-split-hero__logo {
		display: none;
	}
}

/* ==========================================================================
   PB Timeline Slider (2026-10-14) — pb/timeline-slider, see inc/
   timeline-slider-block.php. Scroll/arrow behaviour = the blog slider's
   (assets/js/main.js, data-blog-preview hooks). Cards visible:
   --pbtl-cards-desktop (≥1200px), --pbtl-cards-tablet (768–1199px),
   ~1.2 on mobile.
   ========================================================================== */
.pb-timeline {
	--pbtl-gap: 1.5rem;
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	padding: 4rem 1.5rem;
	background-color: var( --pbtl-bg, #f3f1ed );
}

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

/* !important: design-system.css sets h2 size/weight with !important. */
.pb-timeline .pb-timeline__title {
	margin: 0;
	font-size: clamp( 1.5rem, 2.4vw, 2.1rem ) !important;
	font-weight: 400 !important;
	color: #111111;
}

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

.pb-timeline .pb-timeline__arrow {
	width: 2.25rem;
	height: 2.25rem;
	font-size: 1rem;
}

.pb-timeline__track {
	display: flex;
	gap: var( --pbtl-gap );
	max-width: 1600px;
	margin: 0 auto;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}

.pb-timeline__track::-webkit-scrollbar {
	display: none;
}

.pb-timeline__card {
	flex: 0 0 82%;
	min-width: 0;
	scroll-snap-align: start;
}

/* Same height for every image (fixed portrait ratio), cropped to fit. */
.pb-timeline__media {
	position: relative;
	display: block;
	aspect-ratio: 4 / 5;
	overflow: hidden;
	border-radius: 16px;
	background-color: #e6e3de;
	color: #ffffff;
	text-decoration: none;
}

.pb-timeline__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient( to top, rgba( 0, 0, 0, 0.45 ) 0%, rgba( 0, 0, 0, 0 ) 40% );
	pointer-events: none;
}

.pb-timeline__image {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.4s ease;
}

a.pb-timeline__media:hover .pb-timeline__image {
	transform: scale( 1.03 );
}

.pb-timeline__year {
	position: absolute;
	left: 1rem;
	bottom: 0.9rem;
	z-index: 1;
	font-size: 1.25rem;
	font-weight: 500;
	line-height: 1;
	color: #ffffff;
}

.pb-timeline__text {
	margin: 0.9rem 0.75rem 0;
	font-size: 0.9rem;
	line-height: 1.6;
	color: rgba( 17, 17, 17, 0.8 );
}

@media ( min-width: 768px ) {
	.pb-timeline {
		padding: 5rem 2rem;
	}

	.pb-timeline__card {
		flex-basis: calc( ( 100% - ( var( --pbtl-cards-tablet, 3 ) - 1 ) * var( --pbtl-gap ) ) / var( --pbtl-cards-tablet, 3 ) );
	}
}

@media ( min-width: 1200px ) {
	.pb-timeline {
		padding: 5rem 3rem;
	}

	.pb-timeline__card {
		flex-basis: calc( ( 100% - ( var( --pbtl-cards-desktop, 3 ) - 1 ) * var( --pbtl-gap ) ) / var( --pbtl-cards-desktop, 3 ) );
	}
}

@media ( max-width: 767.98px ) {
	.pb-timeline {
		--pbtl-gap: 0.75rem;
		padding: 3rem 1rem;
	}

	.pb-timeline .pb-timeline__title {
		font-size: 1.35rem !important;
	}
}

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

	.pb-timeline__image {
		transition: none;
	}
}

/* ==========================================================================
   PB Product Showcase Slider (2026-10-12) — pb/product-showcase-slider,
   see inc/product-showcase-slider-block.php. Layout only — all data
   (price/stock/variations/images) comes straight from WooCommerce.
   Breakpoints match the theme's established 768px/1200px split.
   ========================================================================== */
.pb-product-showcase {
	position: relative;
	left: 50%;
	width: 100vw;
	margin-left: -50vw;
	padding: 3rem 1.5rem;
}

.pb-product-showcase__header {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	max-width: 1600px;
	margin: 0 auto 1.5rem;
}

.pb-product-showcase__title {
	margin: 0;
	font-size: 1.3rem;
	font-weight: 600;
	color: #111111;
}

.pb-product-showcase__cta {
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	text-decoration: underline;
	color: #111111;
	white-space: nowrap;
}

.pb-product-showcase__empty {
	max-width: 1600px;
	margin: 0 auto;
	color: rgba( 17, 17, 17, 0.6 );
}

.pb-product-showcase__slider {
	position: relative;
	max-width: 1600px;
	margin: 0 auto;
}

/* Page dashes are mobile-only (see the max-width:767.98px block). */
.pb-product-showcase__dots {
	display: none;
}

.pb-product-showcase__track {
	/* Flexbox, not Grid — a grid item's percentage `width` can resolve
	 * against its own still-indefinite "auto" implicit column track
	 * (circular), which both leaked horizontal page overflow AND
	 * produced the wrong card width when actually tested live. A flex
	 * item's `flex-basis` percentage always resolves against the flex
	 * container's own (definite) content box, with no such ambiguity. */
	display: flex;
	gap: var( --pbpss-gap, 1px );
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
}

.pb-product-showcase__track::-webkit-scrollbar {
	display: none;
}

.pb-product-showcase__card {
	scroll-snap-align: start;
	display: flex;
	flex-direction: column;
	flex: 0 0 calc( ( 100% - ( var( --pbpss-cols-desktop, 4 ) - 1 ) * var( --pbpss-gap, 1px ) ) / var( --pbpss-cols-desktop, 4 ) );
	border-right: 1px solid #e5e3df;
}

.pb-product-showcase__media {
	position: relative;
	height: var( --pbpss-image-height, 420px );
	background-color: #efeeec;
	overflow: hidden;
}

.pb-product-showcase__image-link {
	display: block;
	width: 100%;
	height: 100%;
}

.pb-product-showcase__image {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: contain;
	object-position: center bottom;
}

/* Per-product image arrows — desktop only, shown on image hover
 * (subtle light-grey chevrons, no background). Hidden on tablet/mobile. */
.pb-product-showcase__img-nav {
	display: none;
}

@media ( min-width: 1200px ) {
	.pb-product-showcase__img-nav {
		display: block;
	}

	/* Desktop: full-width row of tall portrait cards; header keeps a gutter. */
	.pb-product-showcase {
		padding-left: 0;
		padding-right: 0;
	}

	.pb-product-showcase__header {
		max-width: none;
		padding: 0 1.5rem;
	}

	/* !important: design-system.css sets h2 size/weight with !important. */
	.pb-product-showcase .pb-product-showcase__title {
		font-size: var( --pb-title-size-desktop, 24px ) !important;
		font-weight: 400 !important;
	}

	.pb-product-showcase__slider {
		max-width: none;
	}

	.pb-product-showcase__media {
		height: auto;
		aspect-ratio: 5 / 7;
	}

	/* Centre of the image: card width × 7/5 ÷ 2. */
	.pb-product-showcase__arrow {
		top: calc( 100vw / var( --pbpss-cols-desktop, 4 ) * 0.7 );
	}

	.pb-product-showcase__arrow--prev {
		left: 16px;
	}

	.pb-product-showcase__arrow--next {
		right: 16px;
	}

	/* Desktop: image fills the whole card media box (no side bands). */
	.pb-product-showcase__image {
		object-fit: cover;
		object-position: center;
	}

	.pb-product-showcase__img-arrow {
		position: absolute;
		top: 50%;
		transform: translateY( -50% );
		z-index: 1;
		display: flex;
		align-items: center;
		justify-content: center;
		width: 32px;
		height: 32px;
		padding: 0;
		border: none;
		background: none;
		color: #b5b5b5;
		cursor: pointer;
		opacity: 0;
		transition: opacity 0.2s ease, color 0.15s ease;
	}

	.pb-product-showcase__img-arrow svg {
		width: 22px;
		height: 22px;
	}

	.pb-product-showcase__img-arrow--prev {
		left: 6px;
	}

	.pb-product-showcase__img-arrow--next {
		right: 6px;
	}

	.pb-product-showcase__media:hover .pb-product-showcase__img-arrow,
	.pb-product-showcase__img-arrow:focus-visible {
		opacity: 1;
	}

	.pb-product-showcase__img-arrow:hover {
		color: #8a8a8a;
	}
}

/* Size bar over the bottom of the image — opened by hovering the plus
 * (desktop) or tapping it (tablet/mobile). */
.pb-product-showcase__card.is-sizes-open .pb-product-showcase__sizes {
	display: flex;
}

.pb-product-showcase__card.is-sizes-open .pb-product-showcase__quick-add {
	opacity: 0;
}

.pb-product-showcase__sizes {
	display: none;
	position: absolute;
	left: 12px;
	right: 12px;
	bottom: 12px;
	z-index: 3;
	align-items: center;
	justify-content: space-around;
	min-height: 52px;
	padding: 0 8px;
	background-color: rgba( 255, 255, 255, 0.85 );
}

.pb-product-showcase__size {
	flex: 1 1 0;
	height: 52px;
	padding: 0;
	border: none;
	background: none;
	font-size: 0.95rem;
	color: #111111;
	cursor: pointer;
	transition: opacity 0.15s ease;
}

.pb-product-showcase__size:hover:not( :disabled ) {
	text-decoration: underline;
	text-underline-offset: 4px;
}

.pb-product-showcase__size:disabled {
	color: #b5b5b5;
	cursor: default;
}

.pb-product-showcase__size[data-busy] {
	opacity: 0.4;
}

.pb-product-showcase__badges {
	position: absolute;
	top: 10px;
	left: 10px;
	z-index: 1;
	display: flex;
	gap: 6px;
}

.pb-product-showcase__badge {
	padding: 3px 8px;
	font-size: 0.65rem;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	background-color: #ffffff;
	color: #111111;
	border-radius: 2px;
}

/* Thin plus inside a small thin circle outline, no fill. */
.pb-product-showcase__quick-add {
	position: absolute;
	bottom: 12px;
	right: 12px;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	border: 1px solid #111111;
	background: none;
	cursor: pointer;
	padding: 0;
	opacity: 0.85;
	transition: opacity 0.15s ease;
}

.pb-product-showcase__quick-add svg {
	width: 14px;
	height: 14px;
}

.pb-product-showcase__quick-add svg path {
	stroke-width: 1.5;
}

.pb-product-showcase__quick-add:hover {
	opacity: 1;
}

.pb-product-showcase__card.is-added .pb-product-showcase__quick-add svg path {
	stroke: #6848f4;
}

.pb-product-showcase__popover {
	position: absolute;
	bottom: 52px;
	right: 10px;
	z-index: 2;
	width: 220px;
	padding: 14px;
	background-color: #ffffff;
	border: 1px solid #e5e3df;
	border-radius: 4px;
	box-shadow: 0 8px 24px rgba( 0, 0, 0, 0.12 );
}

.pb-product-showcase__attr {
	display: block;
	margin-bottom: 10px;
	font-size: 0.75rem;
}

.pb-product-showcase__attr span {
	display: block;
	margin-bottom: 4px;
	font-weight: 600;
	color: #111111;
}

.pb-product-showcase__attr select {
	width: 100%;
	padding: 6px 8px;
	font-size: 0.8rem;
	border: 1px solid #d8d6d2;
	border-radius: 3px;
}

.pb-product-showcase__popover-msg {
	margin: 0 0 8px;
	font-size: 0.7rem;
	color: #b3261e;
	min-height: 1em;
}

.pb-product-showcase__add {
	width: 100%;
	justify-content: center;
}

.pb-product-showcase__thumbs {
	display: flex;
	gap: 6px;
	padding: 8px 10px 0;
}

.pb-product-showcase__thumb {
	width: 28px;
	height: 28px;
	padding: 0;
	border: 1px solid transparent;
	border-radius: 2px;
	overflow: hidden;
	cursor: pointer;
	background: none;
}

.pb-product-showcase__thumb.is-active {
	border-color: #111111;
}

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

.pb-product-showcase__info {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 8px;
	padding: 10px 10px 0;
}

.pb-product-showcase__name {
	font-size: 0.75rem;
	font-weight: 400;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	color: #111111;
	text-decoration: none;
}

.pb-product-showcase__price {
	font-size: 0.8rem;
	color: #111111;
	white-space: nowrap;
}

.pb-product-showcase__desc {
	margin: 2px 10px 10px;
	font-size: 0.7rem;
	color: rgba( 17, 17, 17, 0.55 );
	text-transform: uppercase;
	letter-spacing: 0.03em;
}

.pb-product-showcase__arrow {
	position: absolute;
	top: calc( var( --pbpss-image-height, 420px ) / 2 );
	transform: translateY( -50% );
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	border: none;
	background-color: #ffffff;
	box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.15 );
	cursor: pointer;
	padding: 0;
}

.pb-product-showcase__arrow svg {
	width: 18px;
	height: 18px;
}

.pb-product-showcase__arrow--prev {
	left: -16px;
}

.pb-product-showcase__arrow--next {
	right: -16px;
}

@media ( min-width: 768px ) and ( max-width: 1199.98px ) {
	.pb-product-showcase__card {
		flex-basis: calc( ( 100% - ( var( --pbpss-cols-tablet, 2 ) - 1 ) * var( --pbpss-gap, 1px ) ) / var( --pbpss-cols-tablet, 2 ) );
	}

	/* Tablet (explicit request 2026-10-19): image fills the whole card
	 * media box, same as desktop and mobile — no grey side bands. */
	.pb-product-showcase__image {
		object-fit: cover;
		object-position: center;
	}
}

@media ( max-width: 767.98px ) {
	/* Cards run edge to edge; only the header keeps a side gutter. */
	.pb-product-showcase {
		padding: 2rem 0;
	}

	.pb-product-showcase__header {
		padding: 0 1rem;
	}

	.pb-product-showcase__card {
		flex-basis: calc( ( 100% - ( var( --pbpss-cols-mobile, 1 ) - 1 ) * var( --pbpss-gap, 1px ) ) / var( --pbpss-cols-mobile, 1 ) );
	}

	/* Mobile: swipeable slider, "Kolommen mobiel" cards in view, with
	 * grey page dashes below (.pb-product-showcase__dots, built by
	 * pb-product-showcase-slider.js). */
	.pb-product-showcase__track {
		--pbpss-gap: 3px; /* thin white line between cards */
	}

	.pb-product-showcase__dots {
		display: flex;
		justify-content: center;
		gap: 10px;
		padding: 1.5rem 1rem 0;
	}

	.pb-product-showcase__dots[hidden] {
		display: none;
	}

	.pb-product-showcase__dot {
		width: 40px;
		height: 2px;
		padding: 0;
		border: none;
		background-color: #e3e3e3;
		cursor: pointer;
		/* bigger tap target without changing the visible line */
		box-shadow: 0 0 0 8px transparent;
		background-clip: content-box;
		transition: background-color 0.2s ease;
	}

	.pb-product-showcase__dot.is-active {
		background-color: #8a8a8a;
	}

	.pb-product-showcase__card {
		border-right: none;
	}

	/* Price below the product name, slightly smaller. */
	.pb-product-showcase__info {
		flex-direction: column;
		align-items: flex-start;
		gap: 2px;
	}

	.pb-product-showcase__price {
		font-size: 0.7rem;
	}

	.pb-product-showcase__arrow {
		display: none;
	}

	/* !important: design-system.css sets h2 size/weight with !important. */
	.pb-product-showcase .pb-product-showcase__title {
		font-size: var( --pb-title-size-mobile, 18px ) !important;
		font-weight: 400 !important;
	}

	.pb-product-showcase__media {
		height: auto;
		aspect-ratio: 5 / 7;
	}

	/* Image always fills the (taller) mobile card box. */
	.pb-product-showcase__image {
		object-fit: cover;
		object-position: center;
	}

	.pb-product-showcase__sizes {
		left: 6px;
		right: 6px;
		bottom: 6px;
		min-height: 40px;
		padding: 0 4px;
	}

	.pb-product-showcase__size {
		height: 40px;
		font-size: 0.75rem;
	}
}

/* Inside a product page's linked "PDP content" page (.pb-pdp__content),
 * full/wide blocks are narrowed to the content column on purpose
 * (woocommerce.css). The showcase slider keeps its own full-bleed
 * layout there, and its names/prices stay black instead of taking the
 * entry-content link colour. */
.pb-pdp__content .pb-product-showcase.alignfull {
	max-width: none;
	width: 100vw;
	left: 50%;
	margin-left: -50vw;
	margin-right: 0;
}

.pb-pdp__content .pb-product-showcase__name,
.pb-pdp__content .pb-product-showcase__price {
	color: #111111;
	text-decoration: none;
}

/* WooCommerce's own `.woocommerce img { height: auto }` (product pages)
 * outranks `.pb-product-showcase__image { height: 100% }`, leaving a
 * band under the image — restore the fill. */
.pb-pdp__content .pb-product-showcase__image {
	height: 100%;
}

/* Editor-only: product search/selected-list in the Inspector
 * ("Producten" panel, blocks/product-showcase-slider/edit.js). */
.pb-psc-edit__results,
.pb-psc-edit__selected {
	list-style: none;
	margin: 0 0 1rem;
	padding: 0;
}

.pb-psc-edit__results li {
	margin-bottom: 4px;
}

.pb-psc-edit__selected li {
	display: flex;
	align-items: center;
	gap: 4px;
	padding: 4px 0;
	border-bottom: 1px solid #e5e3df;
	font-size: 0.8rem;
}

.pb-psc-edit__selected li span {
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* ---- pb/slide-in-hero (2026-10-17) — full-width header: background
 * image at its own aspect ratio (desktop); foreground image positioned
 * in % of the stage and sliding in from the left (transform only).
 * Below 1024px: a screen-height, mostly-blue crop with the sheep
 * anchored bottom-left (see the max-width: 1023px block below). See
 * inc/slide-in-hero-block.php. */
.pb-slide-in-hero {
	display: block;
	background: #ffffff;
	overflow: hidden;
}

.pb-slide-in-hero__stage {
	position: relative;
	/* Full screen width, fixed height (block setting
	 * "Hoogte desktop", default 85vh) — the blue background covers the stage;
	 * the sheep is sized by stage HEIGHT (78.87% = its 978px of the
	 * design's 1240px), so it keeps the size it had in the first,
	 * centered version instead of growing with the screen width. */
	width: 100%;
	height: var(--psh-max-h, 85vh);
	min-height: 480px;
	overflow: hidden;
}

.pb-slide-in-hero__bg {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% 50%;
	/* Desktop: blue brush stretched a bit wider (explicit request
	 * 2026-10-17); reset below 1024px. */
	transform: scaleX(1.15);
}

.pb-slide-in-hero__slide {
	position: absolute;
	top: var(--psh-top, 0%);
	left: var(--psh-left, 0%);
	width: auto;
	/* Bottom stays flush with the stage: "Positie boven" (slideTop) sets the size. */
	height: calc(100% - var(--psh-top, 21.13%));
	max-width: none;
	transform: translateX(-110%);
	animation: pb-slide-in-hero-in var(--psh-duration, 1.2s) cubic-bezier(0.22, 1, 0.36, 1) var(--psh-delay, 0.3s) forwards;
	will-change: transform;
}

@keyframes pb-slide-in-hero-in {
	from { transform: translateX(-110%); }
	to { transform: translateX(0); }
}

/* Mobile + tablet (explicit request 2026-10-17, per mockup): the stage
 * fills the screen below the header; the background is cropped to the
 * blue brush area (object-position 28% = just right of the brush's
 * left edge on phone/tablet ratios), and the sheep is sized by stage height and anchored
 * bottom-left so it lands in the same spot on phones and tablets. */
@media (max-width: 1023px) {
	.pb-slide-in-hero__stage {
		aspect-ratio: auto;
		height: calc(100svh - var(--pb-header-height, 77px));
		min-height: 520px;
	}

	.pb-slide-in-hero__bg {
		object-fit: cover;
		object-position: 28% 50%;
		transform: none;
	}

	.pb-slide-in-hero__slide {
		top: auto;
		bottom: 6.5%; /* Was 10.5%; lower (explicit request 2026-10-21). */
		left: -4px; /* Was 0; a few px past the screen edge so no hairline shows left of the body (explicit request 2026-10-21). */
		width: auto;
		height: 58%; /* Was 53.4%; a bit larger (explicit request 2026-10-21). */
	}
}

/* Text over the stage (2026-10-18): heading + subheading in which one
 * word is scribbled out and a handwritten replacement is "written"
 * above it, starting once the sheep has slid in. */
.pb-slide-in-hero__content {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 0 1.5rem 4%;
	text-align: center;
	color: #000000;
	pointer-events: none;
}

.pb-slide-in-hero .pb-slide-in-hero__title {
	margin: 0 !important;
	/* !important: design-system.css sets h2 type with !important. */
	font-family: "Poppins", sans-serif !important;
	font-weight: 700 !important;
	font-size: clamp(1.5rem, 2.4vw, 2.5rem) !important;
	line-height: 1.45 !important;
	letter-spacing: -0.01em !important;
	color: inherit !important;
}

.pb-slide-in-hero .pb-slide-in-hero__subtitle {
	margin: 1.6em 0 0;
	font-family: "Poppins", sans-serif;
	font-weight: 700;
	font-size: clamp(1.05rem, 1.75vw, 1.65rem);
	line-height: 1.3;
	color: inherit;
}

.pb-slide-in-hero__swap {
	position: relative;
	display: inline-block;
}

.pb-slide-in-hero__struck {
	text-decoration: none;
}

.pb-slide-in-hero__strike {
	position: absolute;
	left: -3%;
	top: 28%;
	width: 104%;
	height: 50%;
	overflow: visible;
}

.pb-slide-in-hero__strike path {
	fill: none;
	stroke: #222222;
	stroke-width: 4;
	stroke-linecap: round;
	vector-effect: non-scaling-stroke;
	stroke-dasharray: 1;
	stroke-dashoffset: 1;
	opacity: 0.85;
	animation: pb-slide-in-hero-strike 0.55s ease-in-out calc(var(--psh-delay, 0.3s) + var(--psh-duration, 1.2s)) forwards;
}

.pb-slide-in-hero__written {
	position: absolute;
	left: 50%;
	bottom: 88%;
	font-family: "Gochi Hand", "Segoe Print", "Bradley Hand", cursive;
	font-weight: 400;
	font-size: 1.3em;
	line-height: 1;
	white-space: nowrap;
	transform: translateX(-50%) rotate(-3deg);
	clip-path: inset(-20% 100% -20% 0);
	animation: pb-slide-in-hero-write 0.9s steps(14, end) calc(var(--psh-delay, 0.3s) + var(--psh-duration, 1.2s) + 0.55s) forwards;
}

@keyframes pb-slide-in-hero-strike {
	to { stroke-dashoffset: 0; }
}

@keyframes pb-slide-in-hero-write {
	to { clip-path: inset(-20% -10% -20% 0); }
}

/* Mobile/tablet: text in the upper part of the blue, above the sheep. */
@media (max-width: 1023px) {
	.pb-slide-in-hero__content {
		justify-content: flex-start;
		padding: 18% 1.25rem 0;
	}

	.pb-slide-in-hero .pb-slide-in-hero__title {
		font-size: clamp(1.45rem, 5.8vw, 2.3rem) !important;
		line-height: 1.35 !important;
	}

	.pb-slide-in-hero .pb-slide-in-hero__subtitle {
		font-size: clamp(1rem, 4.1vw, 1.5rem);
		margin-top: 2em;
	}
}

/* Phones: text + CTA a bit lower, closer to the middle (explicit request 2026-10-21). */
@media (max-width: 767.98px) {
	.pb-slide-in-hero__content {
		padding-top: 40%;
	}
}

/* Editor preview: always visible, static end state. */
.pb-slide-in-hero.is-editor {
	display: block;
}

.pb-slide-in-hero.is-editor .pb-slide-in-hero__slide {
	animation: none;
	transform: none;
}

.pb-slide-in-hero.is-editor .pb-slide-in-hero__strike path,
.pb-slide-in-hero.is-editor .pb-slide-in-hero__written {
	animation: none;
	stroke-dashoffset: 0;
	clip-path: none;
}

@media (prefers-reduced-motion: reduce) {
	.pb-slide-in-hero__slide {
		animation: none;
		transform: none;
	}

	.pb-slide-in-hero__strike path,
	.pb-slide-in-hero__written {
		animation: none;
		stroke-dashoffset: 0;
		clip-path: none;
	}
}

/* ---- pb/contact-intro image (2026-10-17) — optional image under the
 * intro text (class name kept from the first, mobile-only version).
 * When set, the right-column Contact Image is hidden at every size:
 * - mobile (< 900px): heading → intro → image → form, stacked;
 * - desktop (>= 900px): heading + intro + image in the left column,
 *   tabs + form + socials in the right column (grid below). */
.pb-contact__intro-mobile-image {
	display: block;
	height: auto;
}

.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__image {
	display: none;
}

@media ( min-width: 900px ) {
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__content {
		flex: 1 1 100%;
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		column-gap: clamp(2rem, 5vw, 5rem);
		align-items: start;
		padding-bottom: 0;
		/* Heading lines up with the header logo: same rule as
		 * .site-header__inner (max-width 1440px centered + its own side
		 * padding P), minus .pb-contact's 2.5rem ring. */
		padding-left: var( --pbc-lead );
	}

	.pb-contact:has( .pb-contact__intro-mobile-image ) {
		--pbc-lead: max(0px, calc((100vw - 1440px) / 2 + 2rem - 2.5rem));
	}

	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__eyebrow,
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__heading {
		grid-column: 1;
	}

	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__intro {
		grid-column: 1;
		grid-row: 2 / span 3;
	}

	/* Right column starts level with the heading's bottom edge, so the
	 * form lines up with the intro text. */
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__tabs {
		grid-column: 2;
		grid-row: 1;
		align-self: end;
	}

	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__form {
		grid-column: 2;
		grid-row: 2;
	}

	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__social {
		grid-column: 2;
		grid-row: 3;
		margin-top: 1.5rem;
	}

	/* Sheep bleeds to the screen's left edge (no gap): back out the
	 * content padding + .pb-contact's own 2.5rem ring; the inner card
	 * may not clip it (same #f7f7f7 as the ring, so no visual change). */
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__inner {
		overflow: visible;
	}

	.pb-contact__intro-mobile-image {
		width: 45%;
		max-width: 300px;
		margin: 2.5rem 0 0 calc(-1 * var( --pbc-lead, 0px ) - 2.5rem);
	}
}

/* Header side padding P per breakpoint (see .site-header__inner). */
@media ( min-width: 1024px ) {
	.pb-contact:has( .pb-contact__intro-mobile-image ) {
		--pbc-lead: max(0px, calc((100vw - 1440px) / 2 + 2.5rem - 2.5rem));
	}
}

@media ( min-width: 1440px ) {
	.pb-contact:has( .pb-contact__intro-mobile-image ) {
		--pbc-lead: max(0px, calc((100vw - 1440px) / 2 + 3rem - 2.5rem));
	}
}

@media ( max-width: 899.98px ) {
	.pb-contact__intro-mobile-image {
		width: 55%;
		max-width: 280px;
		height: auto;
		margin: 2rem 0 1.5rem;
		/* Bleed to the screen's left edge (no white gap) — the intro
		 * column is centered with symmetric padding on mobile. */
		margin-left: calc(50% - 50vw);
	}
}
/* Mobile/tablet (explicit request 2026-10-21): the card's overflow clip
 * cut the sheep off at the card edge, leaving the 8-12px white ring
 * visible left of its body. Let the sheep run past the card to the screen
 * edge (like desktop); the overlay keeps the card's rounded corners. */
@media ( max-width: 899.98px ) {
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__inner {
		overflow: visible;
	}
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__inner::before {
		border-radius: inherit;
	}
	.pb-contact__intro-mobile-image {
		margin-left: calc(50% - 50vw - 4px);
	}
}

/* ---- PDP text links (2026-10-18, explicit request): links inside the
 * product description texts are black instead of the WooCommerce
 * primary purple (`.woocommerce a` in woocommerce.css); underlined so
 * they still read as clickable. Buttons/navigation are not affected. */
.woocommerce .pb-pdp__content a:not(.wp-block-button__link),
.woocommerce .pb-pdp-description__content a:not(.wp-block-button__link) {
	color: #111111;
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

.woocommerce .pb-pdp__content a:not(.wp-block-button__link):hover,
.woocommerce .pb-pdp-description__content a:not(.wp-block-button__link):hover {
	color: #111111;
	opacity: 0.7;
}

/* ---- pb/intro-overlay (2026-10-18) — mobile-only (< 768px) full-screen
 * intro: text fades in, image (sheep) slides in from the bottom-left
 * like pb/slide-in-hero on home, then after --pio-hold the whole layer
 * fades out and stays hidden. Pure CSS, see inc/intro-overlay-block.php.
 * touch-action/overscroll keep the page underneath from scrolling while
 * the intro covers it. */
.pb-intro-overlay {
	display: none;
}

@media (max-width: 767.98px) {
	.pb-intro-overlay {
		position: fixed;
		inset: 0;
		z-index: 100000;
		display: block;
		margin: 0 !important;
		background: var(--pio-bg, #d6386c);
		color: var(--pio-text, #ffffff);
		overflow: hidden;
		touch-action: none;
		overscroll-behavior: contain;
		animation: pb-intro-overlay-out 0.6s ease var(--pio-hold, 3.5s) forwards;
	}

	.pb-intro-overlay__text {
		position: absolute;
		top: 34%;
		left: 0;
		right: 0;
		margin: 0;
		padding: 0 1.5rem;
		font-family: "Poppins", sans-serif;
		font-weight: 700;
		font-size: clamp(1.3rem, 6vw, 1.75rem);
		line-height: 1.55;
		text-align: center;
		color: inherit;
		opacity: 0;
		transform: translateY(12px);
		animation: pb-intro-overlay-text 0.7s ease 0.2s forwards;
	}

	.pb-intro-overlay__image {
		position: absolute;
		left: 0;
		bottom: 4%;
		width: auto;
		height: 60%;
		max-width: none;
		transform: translateX(-110%);
		animation: pb-slide-in-hero-in 1.2s cubic-bezier(0.22, 1, 0.36, 1) 0.5s forwards;
	}
}

@keyframes pb-intro-overlay-text {
	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pb-intro-overlay-out {
	to {
		opacity: 0;
		visibility: hidden;
	}
}

@media (prefers-reduced-motion: reduce) {
	.pb-intro-overlay {
		display: none !important;
	}
}

/* Editor: static preview of the end state, always visible. */
.pb-intro-overlay.is-editor {
	position: relative;
	display: block;
	height: 560px;
	max-width: 320px;
	margin: 0 auto;
	background: var(--pio-bg, #d6386c);
	color: var(--pio-text, #ffffff);
	overflow: hidden;
}

.pb-intro-overlay.is-editor .pb-intro-overlay__text {
	position: absolute;
	top: 34%;
	left: 0;
	right: 0;
	margin: 0;
	padding: 0 1.25rem;
	font-weight: 700;
	font-size: 1.3rem;
	line-height: 1.55;
	text-align: center;
}

.pb-intro-overlay.is-editor .pb-intro-overlay__image {
	position: absolute;
	left: 0;
	bottom: 4%;
	height: 60%;
	width: auto;
	max-width: none;
}

/* ---- pb/sheep-card (2026-10-18, Over ons) — brush-stroke panel
 * (shape = assets/images/brush-mask.png, color per block), watermark
 * word behind the image, heading, optional note, and a "Bekijk" button
 * that expands a numbered list in a white card. Mobile-first single
 * column; on wider screens the same column is centered. */
.pb-sheep-card {
	position: relative;
	overflow-x: clip;
	isolation: isolate;
	max-width: 560px;
	margin: 0 auto;
	padding: 3.5rem 1.5rem 3.75rem;
	text-align: center;
	color: #111111;
}

.pb-sheep-card + .pb-sheep-card {
	margin-top: -1.5rem;
}

.pb-sheep-card__brush {
	position: absolute;
	z-index: -1;
	inset: 0 -4%;
	background: var(--psc-color, #fdf6d6);
	-webkit-mask: url(../images/brush-mask.png) center / 100% 100% no-repeat;
	mask: url(../images/brush-mask.png) center / 100% 100% no-repeat;
	pointer-events: none;
}

.pb-sheep-card__visual {
	position: relative;
	display: flex;
	justify-content: center;
	align-items: flex-end;
	min-height: 120px;
	margin-bottom: 0.75rem;
}

.pb-sheep-card__watermark {
	position: absolute;
	top: 60%;
	left: 54%;
	z-index: 0;
	font-family: "Poppins", sans-serif;
	font-weight: 700;
	font-size: clamp(4rem, 24vw, 7.5rem);
	line-height: 1;
	white-space: nowrap;
	color: #ffffff;
	-webkit-text-stroke: 3px rgba(0, 0, 0, 0.1);
	paint-order: stroke fill;
	transform: translate(-50%, -50%) rotate(-12deg);
	pointer-events: none;
	user-select: none;
}

.pb-sheep-card__image {
	position: relative;
	z-index: 1;
	display: block;
	width: auto;
	max-width: min(100%, 380px);
	max-height: none;
	height: auto;
}

.pb-sheep-card .pb-sheep-card__heading {
	margin: 0 auto;
	max-width: 22rem;
	font-family: "Poppins", sans-serif !important;
	font-weight: 700 !important;
	font-size: 1.05rem !important;
	line-height: 1.45 !important;
	letter-spacing: 0 !important;
	color: inherit;
}

.pb-sheep-card__note {
	margin: 0.5rem auto 0;
	max-width: 22rem;
	font-size: 0.6875rem;
	font-weight: 600;
	line-height: 1.4;
	color: #333333;
}

/* Expand/collapse: grid-rows 0fr -> 1fr. Rendered open; view.js adds
 * .is-enhanced and collapses it. */
.pb-sheep-card__panel {
	display: grid;
	grid-template-rows: 1fr;
	transition: grid-template-rows 0.45s ease, margin 0.45s ease;
	margin-top: 1.5rem;
}

.pb-sheep-card__panel-inner {
	min-height: 0;
	overflow: hidden;
}

.pb-sheep-card.is-enhanced:not(.is-open) .pb-sheep-card__panel {
	grid-template-rows: 0fr;
	margin-top: 0;
}

.pb-sheep-card.is-enhanced:not(.is-open) .pb-sheep-card__panel-inner {
	visibility: hidden;
	transition: visibility 0s linear 0.45s;
}

.pb-sheep-card__list {
	counter-reset: pb-sheep;
	list-style: none;
	margin: 0.25rem 0.25rem 0.75rem;
	padding: 1.25rem 1.25rem 1.25rem 1rem;
	background: #ffffff;
	border-radius: 14px;
	box-shadow: 0 6px 22px rgba(17, 17, 17, 0.12);
	text-align: left;
}

.pb-sheep-card__list li {
	counter-increment: pb-sheep;
	position: relative;
	margin: 0;
	padding: 0 0 0 2.1rem;
	font-size: 0.8125rem;
	line-height: 1.45;
}

.pb-sheep-card__list li + li {
	margin-top: 1rem;
}

.pb-sheep-card__list li::before {
	content: counter(pb-sheep);
	position: absolute;
	left: 0.2rem;
	top: -0.15em;
	font-family: "Poppins", sans-serif;
	font-weight: 700;
	font-size: 1.5rem;
	line-height: 1;
}

.pb-sheep-card__toggle {
	display: none;
}

.pb-sheep-card.is-enhanced .pb-sheep-card__toggle,
.pb-sheep-card.is-editor .pb-sheep-card__toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 9.5rem;
	margin-top: 1.5rem;
	padding: 0.85rem 2rem;
	border: 0;
	border-radius: 6px;
	background: #6848f4;
	color: #ffffff;
	font-family: "Poppins", sans-serif;
	font-weight: 600;
	font-size: 0.875rem;
	line-height: 1;
	cursor: pointer;
	box-shadow: 0 6px 16px rgba(104, 72, 244, 0.3);
}

.pb-sheep-card.is-enhanced .pb-sheep-card__toggle:hover {
	background: #5a3ae6;
}

.pb-sheep-card.is-enhanced .pb-sheep-card__toggle:focus-visible {
	outline: 2px solid #111111;
	outline-offset: 3px;
}

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

/* Over ons intro text (core blocks in a group with class
 * "pb-about-intro"): narrow centered column like the mockup. */
.pb-about-intro {
	max-width: 560px;
	margin: 0 auto;
	padding: 2.5rem 1.5rem 1rem;
}

.pb-about-intro p + p,
.pb-about-intro h1 + p,
.pb-about-intro h2 + p {
	margin-top: 1rem;
}

.pb-about-intro h1,
.pb-about-intro h2 {
	margin: 0 0 1.25rem;
	font-family: "Poppins", sans-serif !important;
	font-weight: 800 !important;
	font-size: 1.6rem !important;
	line-height: 1.25 !important;
}

.pb-about-intro p {
	margin: 0;
	font-size: 0.9375rem;
	line-height: 1.55;
}

.pb-about-intro .wp-block-image {
	margin: 1.75rem 0;
}

.pb-about-intro .wp-block-image img {
	display: block;
	width: 100%;
	height: auto;
}

/* pb/sheep-card on mobile (explicit request 2026-10-18): the yellow/blue
 * brush runs edge to edge — the mask image has empty margins left/right,
 * so the brush is drawn wider than the screen and clipped by the card
 * (overflow-x: clip); the card itself bleeds out of any content padding. */
@media (max-width: 767.98px) {
	.pb-sheep-card {
		max-width: none;
		width: 100vw;
		margin-left: calc(50% - 50vw);
		margin-right: calc(50% - 50vw);
	}

	.pb-sheep-card__brush {
		inset: 0 -20%;
	}
}

/* Over ons illustration: runs edge to edge on mobile, like the mockup. */
@media (max-width: 767.98px) {
	.pb-about-intro .pb-about-intro__image {
		width: 100vw;
		max-width: none;
		margin-left: calc(50% - 50vw) !important;
		margin-right: calc(50% - 50vw) !important;
	}
}

/* Over ons (explicit request 2026-10-18): the text after the
 * illustration runs over the illustration's own yellow shape — pull the
 * following text up into the bottom of the image (below the sheep). */
@media (max-width: 767.98px) {
	.pb-about-intro .pb-about-intro__image {
		margin-bottom: -21.5vw !important; /* complete illustration (573×643): text starts just below the sheep */
	}

	.pb-about-intro .pb-about-intro__image ~ * {
		position: relative;
		z-index: 1;
	}
}

/* pb/sheep-card accordion: a card closed because another one opened
 * collapses instantly (view.js corrects the scroll position for it). */
.pb-sheep-card.is-instant .pb-sheep-card__panel,
.pb-sheep-card.is-instant .pb-sheep-card__panel-inner {
	transition: none !important;
}

/* pb/sheep-card (explicit request 2026-10-18): heading closer to the
 * sheep's feet — the Over ons sheep images have ~19% empty space below
 * the ground shadow, so the image overlaps it with a negative margin
 * (% of the column width = the image's own width here). */
.pb-sheep-card .pb-sheep-card__visual {
	margin-bottom: 0.875rem;
}

.pb-sheep-card .pb-sheep-card__image {
	margin-bottom: max(-15%, -57px); /* -15% of the column, capped at the image's max width (380px) */
}

/* ---- pb/split-image-hero-lead (2026-10-18) — copy of PB Split Image
 * Hero with a subtitle line ("lead") above a bold title, per mockup:
 * regular lead, bold title, small bold "SHOP NU" link below. The
 * original block (.pb-split-hero without --lead) is unchanged. */
.pb-split-hero--lead .pb-split-hero__content {
	gap: 0;
}

.pb-split-hero--lead .pb-split-hero__subtitle {
	margin-bottom: 0.75rem;
}

.pb-split-hero__lead {
	margin: 0;
	font-family: "Poppins", sans-serif;
	font-weight: 400;
	font-size: clamp(1.25rem, 1.75vw, 1.65rem);
	line-height: 1.3;
	color: inherit;
}

.pb-split-hero.pb-split-hero--lead .pb-split-hero__title {
	max-width: none;
	margin-top: 0.2rem;
	font-weight: 700 !important;
	font-size: clamp(1.6rem, 2.5vw, 2.4rem) !important;
	line-height: 1.2 !important;
	letter-spacing: -0.01em !important;
}

.pb-split-hero--lead .pb-split-hero__cta {
	margin-top: 1.75rem;
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	padding-bottom: 0.45rem;
	border-bottom-width: 1px;
}

/* pb/split-image-hero-lead on mobile (explicit request 2026-10-18):
 * slightly larger text. */
@media (max-width: 767.98px) {
	.pb-split-hero__lead {
		font-size: 1.5rem;
	}

	.pb-split-hero.pb-split-hero--lead .pb-split-hero__title {
		font-size: 2.05rem !important;
	}

	.pb-split-hero--lead .pb-split-hero__cta {
		font-size: 0.85rem;
	}
}

/* pb/split-image-hero (+ -lead copy): optional body text under the
 * title (2026-10-19). */
.pb-split-hero__text {
	margin: 0;
	max-width: 34rem;
	font-size: 1.15rem; /* was 1rem; a bit larger per breakpoint (2026-10-19) */
	line-height: 1.5;
	color: inherit;
}

@media ( min-width: 768px ) {
	.pb-split-hero__text {
		font-size: 1.3rem;
	}
}

@media ( min-width: 1200px ) {
	.pb-split-hero__text {
		font-size: 1.4rem;
	}
}

.pb-split-hero--lead .pb-split-hero__text {
	margin-top: 0.75rem;
}

.pb-split-hero__text a {
	color: inherit;
	text-decoration: underline;
}

/* pb/split-image-hero: optional link on the image (2026-10-19). The link
 * covers the whole image area; on mobile the text layer sits on top of
 * the image, so only its actual text/CTA catch taps and the rest of the
 * area falls through to the image link. */
.pb-split-hero__image-link {
	position: absolute;
	inset: 0;
	display: block;
}

.pb-split-hero__image-link:focus-visible {
	outline: 3px solid #111111;
	outline-offset: -3px;
}

@media ( max-width: 767.98px ) {
	.pb-split-hero__content {
		pointer-events: none;
	}

	.pb-split-hero__content > * {
		pointer-events: auto;
	}
}

/* Product pages (explicit request 2026-10-19): the reviews carousel
 * title ("Ontdek wat mensen zeggen…") uses exactly the same type as the
 * "Bekijk ook" product-slider title below it (.pb-product-showcase__title
 * at the same breakpoints). !important: design-system.css sets h2
 * size/weight with !important. */
.pb-pdp__content .pb-reviews-carousel__title.pb-type--h2 {
	font-family: "Poppins", sans-serif !important;
	font-size: var(--wp--custom--typography--h-2--size-tablet, 1.8rem) !important; /* tablet: same as the site-wide h2, like "Bekijk ook" */
	font-weight: var(--wp--custom--typography--h-2--weight, 800) !important;
	line-height: 1.2 !important;
	letter-spacing: -0.02em !important;
	color: #111111 !important;
}

@media (min-width: 1200px) {
	.pb-pdp__content .pb-reviews-carousel__title.pb-type--h2 {
		font-size: 24px !important;
		font-weight: 400 !important;
	}
}

@media (max-width: 767.98px) {
	.pb-pdp__content .pb-reviews-carousel__title.pb-type--h2 {
		font-size: 18px !important;
		font-weight: 400 !important;
	}
}

/* Product slider titles ("Nieuw", "Bekijk ook") never bold — tablet
 * (768–1199px) used to fall back to design-system.css's !important h2
 * weight (800); desktop/mobile were already 400 (explicit request
 * 2026-10-19). The PDP reviews title follows along so both stay equal. */
@media (min-width: 768px) and (max-width: 1199.98px) {
	.pb-product-showcase .pb-product-showcase__title,
	.pb-pdp__content .pb-reviews-carousel__title.pb-type--h2 {
		font-weight: 400 !important;
	}
}

/* Utility class (block "Extra CSS-klasse(n)"): on mobile, the bold part
 * of a PB Split Image Hero title starts on its own line — e.g.
 * "We're not just a brand." / "We tell a story." (Brand new sheep,
 * explicit request 2026-10-19). The title's 14ch width cap is lifted
 * there so the first sentence isn't squeezed. */
@media (max-width: 767.98px) {
	.pb-title-bold-own-line-mobile .pb-split-hero__title {
		max-width: none;
		/* Scales with the screen so "We're not just a brand." stays one line. */
		font-size: min(2.2rem, calc((100vw - 2.5rem) * 0.085)) !important;
	}

	.pb-title-bold-own-line-mobile .pb-split-hero__title strong,
	.pb-title-bold-own-line-mobile .pb-split-hero__title b {
		display: block;
	}
}

/* pb/split-image-hero "Layout desktop: Volledige afbeelding" (2026-10-19):
 * desktop/tablet get the same look as mobile — full-width image, dark
 * overlay, centered text in the mobile text colour — instead of the 50/50
 * split. Height = the block's "Hoogte desktop". */
@media ( min-width: 768px ) {
	.pb-split-hero--desktop-full {
		display: block;
	}

	.pb-split-hero--desktop-full .pb-split-hero__media {
		position: absolute;
		inset: 0;
	}

	.pb-split-hero--desktop-full .pb-split-hero__overlay {
		display: block;
		position: absolute;
		inset: 0;
		background-color: rgba( 0, 0, 0, var( --psih-overlay, 0.35 ) );
	}

	.pb-split-hero--desktop-full .pb-split-hero__content {
		height: 100%;
		padding: 3rem 2rem;
		background-color: transparent;
		color: var( --psih-text-mobile, #ffffff );
		pointer-events: none;
	}

	.pb-split-hero--desktop-full .pb-split-hero__content > * {
		pointer-events: auto;
	}

	.pb-split-hero.pb-split-hero--desktop-full .pb-split-hero__title {
		max-width: 24ch;
		font-size: clamp( 2.4rem, 3.8vw, 4rem ) !important;
	}

	/* Same "bold part on its own line" utility as on mobile. */
	.pb-split-hero--desktop-full.pb-title-bold-own-line-mobile .pb-split-hero__title {
		max-width: none;
	}

	.pb-split-hero--desktop-full.pb-title-bold-own-line-mobile .pb-split-hero__title strong,
	.pb-split-hero--desktop-full.pb-title-bold-own-line-mobile .pb-split-hero__title b {
		display: block;
	}

	.pb-split-hero--desktop-full .pb-split-hero__cta {
		margin-top: 1rem;
		font-size: 0.95rem;
	}
}

/* pb/split-image-hero full layout, "Afbeelding desktop: Passend"
 * (2026-10-19): on desktop/tablet the whole photo stays visible (no
 * zoomed-in crop of e.g. a portrait photo); the sides are filled with a
 * blurred copy of the same photo (--psih-bg-image, set inline), all
 * under the usual dark overlay. Mobile is unchanged. */
@media ( min-width: 768px ) {
	.pb-split-hero--desktop-full.pb-split-hero--fit-contain .pb-split-hero__media::before {
		content: "";
		position: absolute;
		inset: -40px;
		background: var( --psih-bg-image ) center / cover no-repeat;
		filter: blur( 28px ) brightness( 0.85 );
	}

	.pb-split-hero--desktop-full.pb-split-hero--fit-contain .pb-split-hero__image {
		object-fit: contain;
		object-position: center !important;
	}

	.pb-split-hero--desktop-full.pb-split-hero--fit-contain .pb-split-hero__image-link {
		z-index: 1;
	}
}

/* Full layout on desktop: fill the screen below the header, capped by
 * the block's "Hoogte desktop" (2026-10-19). */
@media ( min-width: 768px ) {
	.pb-split-hero.pb-split-hero--desktop-full {
		height: min( var( --psih-height-desktop, 650px ), calc( 100vh - var( --pb-header-height, 77px ) ) );
		height: min( var( --psih-height-desktop, 650px ), calc( 100svh - var( --pb-header-height, 77px ) ) );
		min-height: 520px;
	}
}

/* Over ons intro on desktop (explicit request 2026-10-19, per mockup):
 * left = heading + first paragraphs, right = the illustration, and the
 * second text group under it on the right, over the illustration's own
 * yellow shape. Mobile/tablet (< 1024px) keep the single column above. */
@media (min-width: 1024px) {
	.pb-about-intro {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-template-rows: auto auto;
		column-gap: 2rem;
		max-width: none;
		/* Breaks out of the 800px page column. */
		width: min(1100px, calc(100vw - 4rem));
		margin-left: calc((100% - min(1100px, calc(100vw - 4rem))) / 2);
		padding: 4rem 2rem 2rem;
	}

	.pb-about-intro > * {
		margin: 0 !important;
	}

	.pb-about-intro .pb-about-intro__col--left {
		grid-column: 1;
		grid-row: 1;
		max-width: 22rem;
		margin-left: 2.5rem !important;
		position: relative;
		z-index: 1;
	}

	.pb-about-intro h1,
	.pb-about-intro h2 {
		font-size: 1.45rem !important;
		line-height: 1.6 !important;
		font-weight: 700 !important;
	}

	.pb-about-intro .pb-about-intro__image {
		grid-column: 2;
		grid-row: 1;
		width: 118%;
		margin-left: -18% !important;
		align-self: start;
	}

	.pb-about-intro .pb-about-intro__col--right {
		grid-column: 2;
		grid-row: 2;
		align-self: start;
		max-width: 23rem;
		margin-left: auto !important;
		/* Pulled up onto the illustration's yellow shape, just below the sheep. */
		margin-top: -40% !important;
		position: relative;
		z-index: 1;
	}
}

/* pb/sheep-card on tablet/desktop (explicit request 2026-10-19): stacked
 * like on mobile, brush running edge to edge; the content itself stays
 * a centered column. */
@media (min-width: 768px) {
	.pb-sheep-card {
		max-width: none;
		width: 100vw;
		margin-left: calc(50% - 50vw);
		margin-right: calc(50% - 50vw);
		padding: 4.5rem 1.5rem 5rem;
	}

	.pb-sheep-card__brush {
		inset: 0 -8%;
	}

	.pb-sheep-card__inner {
		max-width: 560px;
		margin: 0 auto;
	}
}

/* pb/sheep-card expanded list on tablet/desktop (explicit request
 * 2026-10-19, per mockup): larger text and numbers, more space between
 * rows, a wider white card with rounder corners. Mobile unchanged. */
@media (min-width: 768px) {
	.pb-sheep-card__inner {
		max-width: 640px;
	}

	.pb-sheep-card__list {
		max-width: 620px;
		margin: 0.5rem auto 1rem;
		padding: 2rem 2.5rem 2.25rem 2rem;
		border-radius: 22px;
		box-shadow: 0 10px 30px rgba(17, 17, 17, 0.14);
	}

	.pb-sheep-card__list li {
		padding-left: 2.6rem;
		font-size: 1.05rem;
		line-height: 1.5;
	}

	.pb-sheep-card__list li + li {
		margin-top: 1.6rem;
	}

	.pb-sheep-card__list li::before {
		left: 0.15rem;
		top: -0.2em;
		font-size: 1.85rem;
	}
}

/* Over ons (explicit request 2026-10-19): on desktop only the first
 * block (PB Image Text with the illustration) is shown — the intro group
 * below it (same text, mobile layout) is hidden from 1024px up. */
@media (min-width: 1024px) {
	.entry-content > .pb-about-intro {
		display: none !important;
	}
}

/* Over ons intro heading on mobile/tablet (explicit request 2026-10-19):
 * "We are the **NewSheeps**" / "and we are on a mission" on two lines —
 * regular weight with only the bold part bold; scales with the screen so
 * each line stays one line. */
@media (max-width: 1023.98px) {
	.pb-about-intro h1,
	.pb-about-intro h2 {
		font-weight: 400 !important;
		font-size: min(1.6rem, calc((100vw - 7rem) / 12.4)) !important; /* line 2 is ~12em wide; text column = screen - 7rem */
		line-height: 1.35 !important;
		white-space: nowrap;
	}

	.pb-about-intro h1 strong,
	.pb-about-intro h2 strong {
		font-weight: 700;
	}
}

/* PB Image Text with pb-text-top (Over ons desktop intro, 2026-10-19):
 * the two heading lines ("We are the NewSheeps" / "and we are on a
 * mission") each stay one line — the size scales down on narrower
 * desktops instead of wrapping. */
@media (min-width: 1024px) {
	.pb-text-top .pb-image-text__heading {
		white-space: nowrap;
		font-size: min(2rem, calc((50vw - 12rem) / 12.4)) !important;
	}
}

/* Over ons on desktop (explicit request 2026-10-19): the intro's second
 * text ("Dit doen wij door…" – "Die keuze is aan jou!") is shown under
 * the desktop intro block, in the right half below the illustration;
 * the intro's heading/first paragraphs and illustration stay hidden
 * there (they're already in the PB Image Text block above). */
@media (min-width: 1024px) {
	.entry-content > .pb-about-intro {
		display: grid !important;
		padding-top: 0;
		padding-bottom: 3rem;
	}

	.entry-content > .pb-about-intro > .pb-about-intro__col--left,
	.entry-content > .pb-about-intro > .pb-about-intro__image {
		display: none !important;
	}

	.entry-content > .pb-about-intro > .pb-about-intro__col--right {
		grid-column: 2;
		grid-row: 1;
		/* Pulled up onto the illustration's yellow shape, below the sheep. */
		margin-top: -13rem !important;
		position: relative;
		z-index: 1;
		margin-left: 0 !important;
		max-width: 26rem;
	}
}

/* pb/sheep-card on tablet/desktop (explicit request 2026-10-19): plain
 * full-width colour blocks, stacked, no brush edges — yellow and blue
 * touching each other. Mobile keeps the brush shape. */
@media (min-width: 768px) {
	.pb-sheep-card__brush {
		inset: 0;
		-webkit-mask: none;
		mask: none;
	}

	.pb-sheep-card + .pb-sheep-card {
		margin-top: 0;
	}
}

/* pb/sheep-card expanded list on tablet/desktop — a bit smaller again
 * (explicit request 2026-10-19: the card and its text were too large). */
@media (min-width: 768px) {
	.pb-sheep-card__list {
		max-width: 520px;
		padding: 1.6rem 2rem 1.75rem 1.6rem;
		border-radius: 18px;
	}

	.pb-sheep-card__list li {
		padding-left: 2.2rem;
		font-size: 0.9375rem;
		line-height: 1.45;
	}

	.pb-sheep-card__list li + li {
		margin-top: 1.1rem;
	}

	.pb-sheep-card__list li::before {
		top: -0.15em;
		font-size: 1.5rem;
	}
}

/* Utility (block "Extra CSS-klasse(n)"): hide a block everywhere without
 * deleting it from the page. */
.pb-hidden {
	display: none !important;
}

/* Over ons on desktop, per mockup (explicit request 2026-10-19): the
 * intro group is the desktop layout again — first text top-left close to
 * the header, the illustration a bit more to the centre starting halfway
 * down that text, and the second text bottom-right over the
 * illustration's yellow shape. Overrides the earlier desktop rules for
 * this group (which showed only the second text). */
@media (min-width: 1024px) {
	.entry-content > .pb-about-intro {
		display: grid !important;
		grid-template-columns: 100%;
		grid-template-rows: auto auto;
		width: min(1100px, calc(100vw - 4rem));
		max-width: none;
		margin-left: calc((100% - min(1100px, calc(100vw - 4rem))) / 2);
		padding: 1.5rem 0 3rem;
	}

	.entry-content > .pb-about-intro > .pb-about-intro__col--left {
		display: block !important;
		grid-row: 1;
		grid-column: 1;
		align-self: start;
		max-width: 22rem;
		margin: 0 0 0 8% !important;
		position: relative;
		z-index: 2;
	}

	.entry-content > .pb-about-intro > .pb-about-intro__image {
		display: block !important;
		grid-row: 1;
		grid-column: 1;
		align-self: start;
		width: 55%;
		margin: 2.25rem 0 0 26% !important;
		position: relative;
		z-index: 0;
	}

	.entry-content > .pb-about-intro > .pb-about-intro__col--right {
		grid-row: 2;
		grid-column: 1;
		max-width: 28rem;
		margin: -7.75rem 0 0 45% !important;
		position: relative;
		z-index: 2;
	}

	.entry-content > .pb-about-intro h1,
	.entry-content > .pb-about-intro h2 {
		font-size: 1.5rem !important;
		font-weight: 400 !important;
		line-height: 1.55 !important;
		white-space: nowrap;
	}

	.entry-content > .pb-about-intro h1 strong,
	.entry-content > .pb-about-intro h2 strong {
		font-weight: 700;
	}

	.entry-content > .pb-about-intro p {
		font-size: 1rem;
		line-height: 1.35;
	}

	.entry-content > .pb-about-intro p + p {
		margin-top: 1.4rem;
	}
}

/* pb/sheep-card with a background-word image (2026-10-19): the word image
 * ("Sheep" / "New Sheep") sits centered behind the sheep; the sheep image
 * itself is a tight crop, so it gets a fixed height and no negative
 * margin (that was only for the older images with built-in whitespace). */
.pb-sheep-card__visual.has-watermark-image {
	--psc-sheep-h: 230px;
	align-items: center;
	min-height: var(--psc-sheep-h);
	margin-bottom: 1rem !important;
}

.pb-sheep-card__watermark-image {
	position: absolute;
	top: 55%;
	left: 50%;
	z-index: 0;
	width: auto;
	max-width: none;
	height: calc(var(--psc-sheep-h) * 0.58);
	transform: translate(-50%, -50%);
	pointer-events: none;
	user-select: none;
}

.pb-sheep-card .has-watermark-image .pb-sheep-card__image {
	width: auto;
	max-width: none;
	height: var(--psc-sheep-h);
	margin-bottom: 0;
}

@media (min-width: 768px) {
	.pb-sheep-card__visual.has-watermark-image {
		--psc-sheep-h: 280px;
	}
}

/* New sheep images have ~18% transparent space above and ~20% below the
 * sheep: draw them larger and cancel that space with negative margins so
 * the sheep itself is ~210px (mobile) / ~265px (tablet+desktop) tall and
 * the heading sits right under its feet. */
.pb-sheep-card__visual.has-watermark-image {
	--psc-sheep-h: 340px;
	min-height: calc(var(--psc-sheep-h) * 0.63);
}

.pb-sheep-card .has-watermark-image .pb-sheep-card__image {
	margin-top: calc(var(--psc-sheep-h) * -0.17);
	margin-bottom: calc(var(--psc-sheep-h) * -0.19);
}

.pb-sheep-card__watermark-image {
	top: 52%;
	height: calc(var(--psc-sheep-h) * 0.42);
}

@media (min-width: 768px) {
	.pb-sheep-card__visual.has-watermark-image {
		--psc-sheep-h: 430px;
	}

	.pb-sheep-card__watermark-image {
		height: calc(var(--psc-sheep-h) * 0.5);
	}
}

/* Over ons intro on mobile/tablet (explicit request 2026-10-19): the
 * illustration's own empty top (~11% of its height) is pulled up so the
 * first text sits close to the drawing. */
@media (max-width: 1023.98px) {
	.pb-about-intro .pb-about-intro__image {
		margin-top: -7vw !important;
	}
}
/* Over ons intro on phones (explicit request 2026-10-21): text aligned
 * with the Sheepy Thoughts block below (18px from the screen edge =
 * .pb-contact 8px + .pb-contact__content 10px) instead of 56px
 * (.site-main 2rem + this group's 1.5rem). */
@media (max-width: 767.98px) {
	.entry-content > .pb-about-intro {
		max-width: none;
		margin-left: -14px;
		margin-right: -14px;
		padding-left: 0;
		padding-right: 0;
	}
}

/* Contact/Sheepy Thoughts heading (explicit request 2026-10-19): the
 * editor's Bold (B) button must visibly work. As soon as part of the
 * heading is marked bold (<strong>), the rest drops to regular and only
 * the <strong> part stays bold. Headings without any <strong> (e.g. the
 * Contact page's "Bèèhee!") keep their existing all-bold look.
 * !important: design-system.css sets h2 weight with !important. */
.pb-contact__heading-text:has( strong, b ) {
	font-weight: 400 !important;
}

.pb-contact__heading-text strong,
.pb-contact__heading-text b {
	font-weight: 800 !important;
}

/* Utility class (block "Extra CSS-klasse(n)") pb-heading-lines-nowrap on a
 * contact heading with a manual line break (<br>), e.g. Weekly Sheepy
 * Thoughts: "Wekelijks nemen we een" / "'Sheepy Thought' onder de loep!":
 * each line stays on exactly one row on every screen size. The
 * font shrinks only when the longest line (~16.4em) wouldn't fit the
 * column; otherwise it keeps its normal size per breakpoint. */
.pb-contact__heading.pb-heading-lines-nowrap {
	container-type: inline-size;
}

.pb-heading-lines-nowrap .pb-contact__heading-text {
	white-space: nowrap;
	font-size: min( 1.65rem, 100cqi / 16.4 ) !important;
}

@media ( min-width: 768px ) {
	.pb-heading-lines-nowrap .pb-contact__heading-text {
		font-size: min( 1.8rem, 100cqi / 16.4 ) !important;
	}
}

@media ( min-width: 1200px ) {
	.pb-heading-lines-nowrap .pb-contact__heading-text {
		font-size: min( 2rem, 100cqi / 16.4 ) !important;
	}
}

/* Utility class (block "Extra CSS-klasse(n)") pb-contact--tight-top on a
 * pb/contact block (explicit request 2026-10-19, Weekly Sheepy
 * Thoughts): desktop title sits close under the header menu instead of
 * 2.5rem section + 40px column padding above it. */
@media ( min-width: 900px ) {
	.pb-contact.pb-contact--tight-top {
		padding-top: 0.75rem;
	}

	.pb-contact.pb-contact--tight-top .pb-contact__content {
		padding-top: 0.75rem;
	}
}

/* "Doorstrepen + schrijven" format (inc/strike-write-format.php,
 * 2026-10-19): reuses the homepage hero's strike + handwriting styles
 * (.pb-slide-in-hero__swap/__strike/__written above) outside the hero.
 * Timing is its own (no slide-in to wait for); pb-strike-write.js pauses
 * it (.is-armed) until the word scrolls into view (.is-inview). */
.pb-strike-write-anim .pb-slide-in-hero__strike path {
	stroke: currentColor;
	stroke-width: 3;
	animation-delay: 0.3s;
}

.pb-strike-write-anim .pb-slide-in-hero__written {
	bottom: 78%;
	animation-delay: 0.85s;
}

.pb-strike-write-anim.is-armed:not( .is-inview ) .pb-slide-in-hero__strike path,
.pb-strike-write-anim.is-armed:not( .is-inview ) .pb-slide-in-hero__written {
	animation-play-state: paused;
}

/* Editor: the raw format span — struck word with the written word shown
 * above it (static), so it's visible what the front end will animate. */
span.pb-strike-write[data-written] {
	position: relative;
	display: inline-block;
	text-decoration: line-through;
	text-decoration-thickness: 2px;
}

span.pb-strike-write[data-written]::before {
	content: attr( data-written );
	position: absolute;
	left: 50%;
	bottom: 78%;
	font-family: "Gochi Hand", "Segoe Print", "Bradley Hand", cursive;
	font-weight: 400;
	font-size: 1.3em;
	line-height: 1;
	white-space: nowrap;
	transform: translateX( -50% ) rotate( -3deg );
	pointer-events: none;
}

/* ---- pb/contact-active-form "spotlight" variant (2026-10-19, Weekly
 * Sheepy Thoughts): the form in a white card that stands out from the
 * section background, with the block's optional title + subtitle
 * centered above it and rounded (pill) fields, one per row. Type sizes
 * follow the page itself: the title matches the contact heading (h2
 * sizes per breakpoint), subtitle/fields/button match the intro text.
 * Other variants and the Contact page are untouched. !important where
 * the base .pb-contact__form rules / Kit's inline styles use it. */
.pb-contact__form--spotlight {
	background: #ffffff;
	border-radius: 16px;
	padding: 1.75rem 1.25rem 1.25rem;
	box-shadow: 0 12px 32px rgba( 17, 17, 17, 0.06 );
	font-size: var( --pbc-intro-text-mobile-size, var( --pbc-intro-text-desktop-size, 0.9rem ) );
}

.pb-contact__form-title {
	margin: 0 0 0.5rem;
	text-align: center;
	font-family: "Poppins", sans-serif !important;
	font-weight: 800 !important;
	font-size: 1.65rem !important;
	line-height: 1.15 !important;
	letter-spacing: normal;
	color: #111111;
}

.pb-contact__form-subtitle {
	margin: 0 0 1.25rem;
	text-align: center;
	font-size: inherit;
	line-height: 1.5;
	color: #111111;
}

.pb-contact__form--spotlight .formkit-fields {
	grid-template-columns: 1fr !important;
	gap: 0.65rem !important;
	margin: 0 0 0.25rem !important;
}

.pb-contact__form--spotlight .formkit-field {
	margin: 0 !important;
}

.pb-contact__form--spotlight .formkit-input,
.pb-contact__form--spotlight .formkit-select,
.pb-contact__form--spotlight input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
.pb-contact__form--spotlight select {
	background-color: #ffffff !important;
	border: 1px solid #e3e3e3 !important;
	border-radius: 999px !important;
	padding: 0.7rem 1.15rem !important;
	font-size: inherit !important;
	line-height: 1.4 !important;
}

.pb-contact__form--spotlight .formkit-input:focus,
.pb-contact__form--spotlight .formkit-select:focus,
.pb-contact__form--spotlight input:focus,
.pb-contact__form--spotlight select:focus {
	border-color: #6848f4 !important;
}

.pb-contact__form--spotlight [data-group="dropdown"]::before {
	right: 1.15rem !important;
	border-color: #4f4f4f transparent transparent transparent !important;
}

.pb-contact__form--spotlight .formkit-submit {
	border-radius: 999px !important;
}

@media ( min-width: 768px ) {
	.pb-contact__form-title {
		font-size: 1.8rem !important;
	}
}

@media ( min-width: 900px ) {
	.pb-contact__form--spotlight {
		padding: 2rem 2rem 1.5rem;
		font-size: var( --pbc-intro-text-desktop-size, 0.9rem );
	}

	/* Card starts level with the heading on the left (spans every row
	 * of the content grid instead of sitting in row 2 under it). */
	.pb-contact:has( .pb-contact__intro-mobile-image ) .pb-contact__form--spotlight {
		grid-row: 1 / span 4;
	}
}

@media ( min-width: 1200px ) {
	.pb-contact__form-title {
		font-size: 2rem !important;
	}
}

/* Inside the contact block the card title shrinks exactly like the
 * two-line contact heading (.pb-heading-lines-nowrap, ~16.4em longest
 * line) so both stay the same size on every screen: the heading column
 * is the full content width when stacked, half of it (minus the column
 * gap) from 900px. */
.pb-contact:has( .pb-contact__form--spotlight ) .pb-contact__content {
	container-type: inline-size;
}

.pb-contact .pb-contact__form-title {
	font-size: min( 1.65rem, 100cqi / 16.4 ) !important;
}

@media ( min-width: 768px ) {
	.pb-contact .pb-contact__form-title {
		font-size: min( 1.8rem, 100cqi / 16.4 ) !important;
	}
}

@media ( min-width: 900px ) {
	.pb-contact .pb-contact__form-title {
		font-size: min( 1.8rem, ( 100cqi - clamp( 2rem, 5vw, 5rem ) ) / 2 / 16.4 ) !important;
	}
}

@media ( min-width: 1200px ) {
	.pb-contact .pb-contact__form-title {
		font-size: min( 2rem, ( 100cqi - clamp( 2rem, 5vw, 5rem ) ) / 2 / 16.4 ) !important;
	}
}

/* Mobile/tablet (stacked layout, < 900px; explicit request 2026-10-19):
 * the spotlight form card comes first, the heading + intro text below
 * it. Desktop (side by side) is unchanged. Opt out per block with the
 * utility class pb-contact--text-first-mobile ("Extra CSS-klasse(n)",
 * e.g. Over ons): text first, form last. */
@media ( max-width: 899.98px ) {
	.pb-contact:not( .pb-contact--text-first-mobile ):has( .pb-contact__form--spotlight ) .pb-contact__content {
		display: flex;
		flex-direction: column;
	}

	.pb-contact:not( .pb-contact--text-first-mobile ):has( .pb-contact__form--spotlight ) .pb-contact__form--spotlight {
		order: -1;
		margin-bottom: 1.75rem;
	}
}

/* "Handschrift" format (pb/handwritten, inc/strike-write-format.php,
 * 2026-10-19): the selection in the same handwritten font as the
 * written word ("resheep"), written on left to right; a "Doorstrepen +
 * schrijven" word inside it starts after the line is written. The
 * negative top inset keeps the written word above the line visible. */
.pb-handwritten {
	font-family: "Gochi Hand", "Segoe Print", "Bradley Hand", cursive;
	font-weight: 400 !important;
	font-size: 1.35em;
	line-height: 1.3;
}

.pb-handwritten .pb-slide-in-hero__written {
	font-size: 1em;
}

.pb-handwritten-anim {
	display: inline-block;
	clip-path: inset( -120% 100% -20% 0 );
	animation: pb-handwritten-write 1.4s steps( 30, end ) 0.2s forwards;
}

@keyframes pb-handwritten-write {
	to { clip-path: inset( -120% -5% -20% 0 ); }
}

.pb-handwritten-anim .pb-strike-write-anim .pb-slide-in-hero__strike path {
	animation-delay: 1.7s;
}

.pb-handwritten-anim .pb-strike-write-anim .pb-slide-in-hero__written {
	animation-delay: 2.25s;
}

.pb-handwritten-anim.is-armed:not( .is-inview ),
.pb-handwritten-anim.is-armed:not( .is-inview ) .pb-slide-in-hero__strike path,
.pb-handwritten-anim.is-armed:not( .is-inview ) .pb-slide-in-hero__written {
	animation-play-state: paused;
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-handwritten-anim {
		animation: none;
		clip-path: none;
	}
}

/* pb/slide-in-hero "Hele subkop in handschrift" (subHandwritten,
 * 2026-10-19 — same look as the Weekly Sheepy Thoughts line): while the
 * sheep slides in, the whole subtitle is written on left to right in
 * the handwritten font, then the word is crossed out and the
 * replacement written above it. */
.pb-slide-in-hero__subtitle.is-handwritten {
	font-family: "Gochi Hand", "Segoe Print", "Bradley Hand", cursive;
	font-weight: 400;
}

.pb-slide-in-hero__subtitle.is-handwritten .pb-slide-in-hero__hand {
	display: inline-block;
	font-size: 1.35em;
	line-height: 1.3;
	clip-path: inset( -120% 100% -20% 0 );
	animation: pb-handwritten-write 1s steps( 24, end ) calc( var( --psh-delay, 0.3s ) + 0.4s ) forwards;
}

.pb-slide-in-hero__subtitle.is-handwritten .pb-slide-in-hero__written {
	font-size: 1em;
	bottom: 78%;
	animation-duration: 0.7s;
	animation-delay: calc( var( --psh-delay, 0.3s ) + 1.85s );
}

.pb-slide-in-hero__subtitle.is-handwritten .pb-slide-in-hero__strike path {
	stroke-width: 3;
	animation-duration: 0.4s;
	animation-delay: calc( var( --psh-delay, 0.3s ) + 1.45s );
}

.pb-slide-in-hero.is-editor .pb-slide-in-hero__subtitle.is-handwritten .pb-slide-in-hero__hand {
	animation: none;
	clip-path: none;
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-slide-in-hero__subtitle.is-handwritten .pb-slide-in-hero__hand {
		animation: none;
		clip-path: none;
	}
}

/* pb/animated-statement subtitle (explicit request 2026-10-19, Roze
 * Woensdag): links inside the text ("Roze Woensdag shirt", "Stichting
 * Roze Woensdag") follow the subtitle's own text color instead of the
 * site-wide black link color, so bold linked words read white on a
 * colored background just like the other bold words. */
.animated-statement__subtitle a,
.animated-statement__subtitle a:visited {
	color: inherit;
}

/* ---- pb/color-card (inc/color-card-block.php, 2026-10-19) — rounded
 * colored panel: image on one side, heading + text on the other.
 * Colors come from the block (--pcc-bg / --pcc-text). Mobile: image on
 * top, text below; from 900px two columns (image left, or right with
 * .pb-color-card--image-right). */
.pb-color-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1.5rem;
	box-sizing: border-box;
	/* Breaks out of the narrow page content column (same idea as
	 * .pb-about-intro): up to 1200px wide, never wider than the screen
	 * minus a 1rem (mobile) / 2.5rem (desktop) side margin. */
	width: calc( 100vw - 2rem );
	max-width: none;
	margin: 2.5rem calc( 50% - 50vw + 1rem );
	padding: 2rem 1.5rem 2.25rem;
	border-radius: 28px;
	background: var( --pcc-bg, #e04c8e );
	color: var( --pcc-text, #ffffff );
}

.pb-color-card__media {
	width: 100%;
	max-width: 340px;
}

.pb-color-card__image {
	display: block;
	width: 100%;
	height: auto;
}

.pb-color-card__placeholder {
	margin: 0;
	padding: 3rem 1rem;
	text-align: center;
	border: 1px dashed currentColor;
	border-radius: 12px;
	opacity: 0.8;
}

.pb-color-card__content {
	width: 100%;
}

.pb-color-card .pb-color-card__heading {
	margin: 0 0 1.25rem;
	font-family: "Poppins", sans-serif !important;
	font-weight: 600 !important;
	font-size: clamp( 1.75rem, 3.6vw, 3.25rem ) !important;
	line-height: 1.15 !important;
	letter-spacing: -0.02em;
	color: inherit !important;
}

.pb-color-card__text {
	font-size: clamp( 1rem, 1.35vw, 1.25rem );
	line-height: 1.65;
	color: inherit;
}

.pb-color-card__text a {
	color: inherit;
	text-decoration: underline;
}

@media ( min-width: 900px ) {
	.pb-color-card {
		flex-direction: row;
		gap: clamp( 2rem, 5vw, 5rem );
		width: min( 1200px, calc( 100vw - 5rem ) );
		margin: 4rem calc( 50% - min( 600px, 50vw - 2.5rem ) );
		padding: clamp( 2.5rem, 5vw, 4.5rem ) clamp( 2rem, 5vw, 5rem );
		border-radius: 40px;
	}

	.pb-color-card--image-right {
		flex-direction: row-reverse;
	}

	.pb-color-card__media {
		flex: 0 0 42%;
		max-width: none;
	}

	.pb-color-card__content {
		flex: 1 1 auto;
	}
}

/* Hint shown when the header "Shoppen" button is used on a product page
 * before a size/variant is chosen (assets/js/woocommerce.js, 2026-10-19). */
.pb-pdp-choose-option-hint {
	margin: 0.5rem 0 0.75rem;
	font-size: 0.9rem;
	font-weight: 600;
	color: #c2185b;
}

/* pb/color-card "Foto tot aan de rand" (imageStyle cover, 2026-10-19):
 * the photo fills its whole side edge to edge (clipped by the card's
 * rounded corners); only the text side keeps the padding. Mobile: photo
 * on top at 4:3, text below. */
.pb-color-card--cover {
	padding: 0;
	gap: 0;
	overflow: hidden;
	align-items: stretch;
}

.pb-color-card--cover .pb-color-card__media {
	max-width: none;
	aspect-ratio: 4 / 3;
}

.pb-color-card--cover .pb-color-card__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.pb-color-card--cover .pb-color-card__content {
	box-sizing: border-box;
	padding: 1.75rem 1.5rem 2rem;
}

@media ( min-width: 900px ) {
	.pb-color-card--cover {
		padding: 0;
		gap: 0;
	}

	.pb-color-card--cover .pb-color-card__media {
		flex: 0 0 50%;
		aspect-ratio: auto;
		min-height: 420px;
	}

	.pb-color-card--cover .pb-color-card__content {
		flex: 1 1 50%;
		align-self: center;
		padding: clamp( 2rem, 4vw, 4rem ) clamp( 2rem, 4vw, 4.5rem );
	}
}

/* pb/color-card cover variant compact (explicit request 2026-10-19): the
 * photo no longer sets the card height (a portrait photo made it very
 * tall) — it is laid over its half and cropped, so the text decides the
 * height. On desktop the card is also narrower than the illustration
 * variant (max 1000px instead of 1200px). */
.pb-color-card--cover .pb-color-card__media {
	position: relative;
	overflow: hidden;
}

.pb-color-card--cover .pb-color-card__image {
	position: absolute;
	inset: 0;
}

@media ( min-width: 900px ) {
	.pb-color-card.pb-color-card--cover {
		width: min( 1000px, calc( 100vw - 5rem ) );
		margin-left: calc( 50% - min( 500px, 50vw - 2.5rem ) );
		margin-right: calc( 50% - min( 500px, 50vw - 2.5rem ) );
	}

	.pb-color-card--cover .pb-color-card__media {
		flex: 0 0 45%;
		min-height: 0;
	}

	.pb-color-card--cover .pb-color-card__content {
		padding: clamp( 1.75rem, 3vw, 2.75rem ) clamp( 1.75rem, 3.5vw, 3.25rem );
	}

	.pb-color-card--cover .pb-color-card__heading {
		font-size: clamp( 1.6rem, 2.6vw, 2.4rem ) !important;
		margin-bottom: 1rem;
	}

	.pb-color-card--cover .pb-color-card__text {
		font-size: clamp( 0.95rem, 1.1vw, 1.05rem );
	}
}

/* Tablet (stacked, 600–899px): same compact cover card — narrower and a
 * wider/shorter photo strip on top. */
@media ( min-width: 600px ) and ( max-width: 899.98px ) {
	.pb-color-card.pb-color-card--cover {
		width: min( 680px, calc( 100vw - 2rem ) );
		margin-left: calc( 50% - min( 340px, 50vw - 1rem ) );
		margin-right: calc( 50% - min( 340px, 50vw - 1rem ) );
	}

	.pb-color-card--cover .pb-color-card__media {
		aspect-ratio: 16 / 9;
	}
}

/* A little extra space between a sheep card and a contact block directly
 * below it (Over ons, explicit request 2026-10-19) — as extra top
 * padding, so it has the contact block's own background color instead of
 * a white strip. Values = the block's own top padding + the extra. */
.entry-content > .pb-sheep-card + .pb-contact {
	padding-top: calc( 8px + 1.5rem );
}

@media ( min-width: 768px ) {
	.entry-content > .pb-sheep-card + .pb-contact {
		padding-top: calc( 12px + 1.5rem );
	}
}

@media ( min-width: 900px ) {
	.entry-content > .pb-sheep-card + .pb-contact {
		padding-top: calc( 0.75rem + 2.5rem );
	}
}

/* pb/product-showcase-slider "Rijen op mobiel en tablet" = 2 (2026-10-20):
 * below 1200px the track becomes a two-row grid that scrolls sideways;
 * one swipe page = 2 rows × the block's column count (e.g. 2 × 2 = 4
 * products). Snap points only on the first card of each page, so a
 * swipe moves a whole page. The page dashes (pb-product-showcase-slider.js)
 * count pages from the track width, so they follow automatically. */
@media ( max-width: 1199.98px ) {
	.pb-product-showcase--two-rows .pb-product-showcase__track {
		--pbpss-rows-cols: var( --pbpss-cols-tablet, 2 );
		display: grid;
		grid-auto-flow: column;
		grid-template-rows: auto auto;
		grid-auto-columns: calc( ( 100% - ( var( --pbpss-rows-cols ) - 1 ) * var( --pbpss-gap, 1px ) ) / var( --pbpss-rows-cols ) );
		gap: var( --pbpss-gap, 1px );
	}

	.pb-product-showcase--two-rows .pb-product-showcase__card {
		flex-basis: auto;
		width: auto;
		min-width: 0;
		scroll-snap-align: none;
	}
}

@media ( min-width: 768px ) and ( max-width: 1199.98px ) {
	.pb-product-showcase--two-rows.pb-product-showcase--tcols-1 .pb-product-showcase__card:nth-child( 2n + 1 ),
	.pb-product-showcase--two-rows.pb-product-showcase--tcols-2 .pb-product-showcase__card:nth-child( 4n + 1 ),
	.pb-product-showcase--two-rows.pb-product-showcase--tcols-3 .pb-product-showcase__card:nth-child( 6n + 1 ),
	.pb-product-showcase--two-rows.pb-product-showcase--tcols-4 .pb-product-showcase__card:nth-child( 8n + 1 ) {
		scroll-snap-align: start;
	}
}

@media ( max-width: 767.98px ) {
	.pb-product-showcase--two-rows .pb-product-showcase__track {
		--pbpss-rows-cols: var( --pbpss-cols-mobile, 2 );
	}

	.pb-product-showcase--two-rows.pb-product-showcase--mcols-1 .pb-product-showcase__card:nth-child( 2n + 1 ),
	.pb-product-showcase--two-rows.pb-product-showcase--mcols-2 .pb-product-showcase__card:nth-child( 4n + 1 ),
	.pb-product-showcase--two-rows.pb-product-showcase--mcols-3 .pb-product-showcase__card:nth-child( 6n + 1 ),
	.pb-product-showcase--two-rows.pb-product-showcase--mcols-4 .pb-product-showcase__card:nth-child( 8n + 1 ) {
		scroll-snap-align: start;
	}
}

/* ---- pb/card-slider (inc/card-slider-block.php, 2026-10-20) — colored
 * section (--pcs-bg) with decorative hearts, a centered title/text/logo
 * and a finger-swipeable card slider: one card centered, neighbours
 * peeking in, CSS scroll-snap (one card per swipe), dots below. ---- */
.pb-card-slider {
	position: relative;
	overflow: hidden;
	padding: 3.5rem 0 2.5rem;
	background: var( --pcs-bg, #fffdf2 );
	color: #111111;
}

.pb-card-slider__hearts {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.pb-card-slider__heart {
	position: absolute;
	display: block;
	height: auto;
	max-width: none;
}

.pb-card-slider__intro {
	position: relative;
	box-sizing: border-box;
	max-width: 24rem;
	margin: 0 auto 2.25rem;
	padding: 0 1.5rem;
	text-align: center;
}

/* Title/text sizes come from the site-wide type presets (.pb-type--h2 /
 * .pb-type--body on the elements, design-system.css) — the same sizes as
 * the rest of the site; only weight/spacing are set here. */

.pb-card-slider .pb-card-slider__title {
	margin: 0 0 1rem;
	font-weight: 600 !important;
	color: inherit !important;
}

.pb-card-slider .pb-card-slider__title strong {
	font-weight: 800 !important;
}

.pb-card-slider__text {
	margin: 0;
	color: inherit;
}

.pb-card-slider__text strong {
	font-weight: 800;
}

.pb-card-slider__logo {
	display: block;
	width: 90px;
	height: auto;
	margin: 1.5rem auto 0;
}

.pb-card-slider__track {
	--pcs-card-w: min( 62vw, 300px );
	position: relative;
	display: flex;
	gap: 0.35rem;
	padding: 0.5rem calc( ( 100% - var( --pcs-card-w ) ) / 2 ) 1.75rem;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
}

.pb-card-slider__track::-webkit-scrollbar {
	display: none;
}

.pb-card-slider__card {
	flex: 0 0 var( --pcs-card-w );
	display: flex;
	flex-direction: column;
	aspect-ratio: 3 / 4;
	overflow: hidden;
	border-radius: 22px;
	background: var( --pcs-card, #9fb0ff );
	box-shadow: 0 10px 18px rgba( 17, 17, 17, 0.22 );
	scroll-snap-align: center;
	scroll-snap-stop: always;
}

.pb-card-slider__image {
	display: block;
	flex: 1 1 auto;
	width: 100%;
	min-height: 0;
	object-fit: cover;
}

.pb-card-slider__card-text {
	margin: 0;
	padding: 0.85rem 1rem 1rem;
	font-size: 0.9rem;
	line-height: 1.4;
	color: #111111;
}

.pb-card-slider__card:not( .has-image ) .pb-card-slider__card-text {
	margin-top: auto;
}

.pb-card-slider__dots {
	position: relative;
	display: flex;
	justify-content: center;
	gap: 6px;
}

.pb-card-slider__dot {
	width: 8px;
	height: 8px;
	padding: 0;
	border: none;
	border-radius: 50%;
	background: #d9d9d9;
	cursor: pointer;
	box-shadow: 0 0 0 8px transparent; /* bigger tap target */
}

.pb-card-slider__dot.is-active {
	background: #6848f4;
}

/* Editor: static preview, cards in a scrollable row. */
.pb-card-slider.is-editor .pb-card-slider__track {
	padding-left: 1.5rem;
	padding-right: 1.5rem;
}

/* Card slider depth (2026-10-20): the centered card is full size, its
 * neighbours shrink (scale set per card by blocks/card-slider/view.js
 * while scrolling, as --pcs-scale) so one big card sits in the middle
 * with a smaller one peeking in on each side. Without JS every card
 * simply stays full size. */
.pb-card-slider__card {
	transform: scale( var( --pcs-scale, 1 ) );
	transform-origin: center center;
	will-change: transform;
}

/* Card slider tweaks (explicit request 2026-10-20): taller cards, a wider
 * text column on mobile, and the background hearts drifting very gently
 * (a few px up/down and a slight sway, each heart at its own pace). The
 * drift uses the separate `translate` property so each heart's own
 * rotation (inline `transform`) stays. Off for reduced-motion users. */
.pb-card-slider__card {
	aspect-ratio: 5 / 8;
}

.pb-card-slider__intro {
	max-width: 28rem;
	padding: 0 1rem;
}

@keyframes pb-card-slider-heart-drift {
	0%,
	100% {
		translate: 0 0;
	}

	50% {
		translate: 2px -5px;
	}
}

.pb-card-slider__heart {
	animation: pb-card-slider-heart-drift 7s ease-in-out infinite;
}

.pb-card-slider__heart:nth-child( 2n ) {
	animation-duration: 9s;
	animation-delay: -2s;
}

.pb-card-slider__heart:nth-child( 3n ) {
	animation-duration: 8s;
	animation-delay: -4.5s;
	animation-direction: reverse;
}

.pb-card-slider__heart:nth-child( 5n ) {
	animation-duration: 10s;
	animation-delay: -1s;
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-card-slider__heart {
		animation: none;
	}
}

/* pb/slide-in-hero optional CTA (2026-10-20): the site's primary button,
 * centered under the subtitle. The text layer ignores pointer events (so
 * the stage stays visible/clickable), the button itself takes them. It
 * fades in once the subtitle has been written. */
.pb-slide-in-hero .pb-slide-in-hero__cta {
	margin-top: 2.25em; /* Was 1.75em; nudged down a little (explicit request 2026-10-20). */
	pointer-events: auto;
	opacity: 0;
	animation: pb-slide-in-hero-cta-in 0.6s ease-out calc( var( --psh-delay, 0.3s ) + 1.6s ) forwards;
}

@keyframes pb-slide-in-hero-cta-in {
	to { opacity: 1; }
}

.pb-slide-in-hero.is-editor .pb-slide-in-hero__cta {
	opacity: 1;
	animation: none;
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-slide-in-hero .pb-slide-in-hero__cta {
		opacity: 1;
		animation: none;
	}
}

/* Footer set to "Alleen op mobiel tonen" on a page (2026-10-20, homepage):
 * hidden from tablet width up. */
@media ( min-width: 768px ) {
	.site-footer--mobile-only {
		display: none !important;
	}
}

/* Mobile (explicit request 2026-10-20): the bold word in the card slider
 * title ("LLOVE") uses exactly the homepage hero title's mobile size
 * ("Het leven is mooi", .pb-slide-in-hero__title below 1024px). */
@media ( max-width: 767.98px ) {
	.pb-card-slider .pb-card-slider__title strong {
		font-size: clamp( 1.45rem, 5.8vw, 2.3rem );
	}
}

/* Card slider on tablet/desktop (explicit request 2026-10-20, homepage):
 * wider text, and a slider window exactly three cards wide — the centered
 * card full size, one smaller card fully visible on each side (scaled by
 * view.js), dots below. Clicking a side card brings it to the middle.
 * The hearts keep their %-positions, so they sit along the left and
 * right edges of the wide section. */
@media ( min-width: 768px ) {
	.pb-card-slider {
		padding: 5rem 0 3.5rem;
	}

	.pb-card-slider__intro {
		max-width: 46rem;
		margin-bottom: 3rem;
	}

	.pb-card-slider__track {
		--pcs-card-w: clamp( 200px, 22vw, 300px );
		--pcs-gap: 1.25rem;
		gap: var( --pcs-gap );
		box-sizing: border-box;
		width: calc( 3 * var( --pcs-card-w ) + 2 * var( --pcs-gap ) );
		max-width: 100%;
		margin: 0 auto;
		padding-left: calc( var( --pcs-card-w ) + var( --pcs-gap ) );
		padding-right: calc( var( --pcs-card-w ) + var( --pcs-gap ) );
	}

	.pb-card-slider__card:not( .is-active ) {
		cursor: pointer;
	}
}

/* Tablet/desktop: a manual line break in the card slider title (e.g. the
 * homepage's "We create to let you<br>LLOVE", there for mobile) is
 * dropped so the title runs on one line; a space replaces it. */
@media ( min-width: 768px ) {
	.pb-card-slider__title br {
		display: none;
	}

	.pb-card-slider__title br + *::before {
		content: " ";
	}
}

/* Extra side hearts — wide screens only (pb_card_slider_hearts_wide());
 * below 1100px the cards reach too close to the edges. */
@media ( max-width: 1099.98px ) {
	.pb-card-slider__heart--wide {
		display: none;
	}
}

/* Card slider (explicit request 2026-10-20): slightly wider cards; the
 * cards that are not in the middle get a soft white haze + a light blur
 * over their photo (--pcs-fade from view.js: 0 centered, 1 at the side),
 * so the focus is on the front card; and the hearts drift more clearly
 * (larger float + a little sway) on every screen size. */
.pb-card-slider__track {
	--pcs-card-w: min( 66vw, 320px );
}

@media ( min-width: 768px ) {
	.pb-card-slider__track {
		--pcs-card-w: clamp( 220px, 24vw, 330px );
	}
}

.pb-card-slider__card {
	position: relative;
}

.pb-card-slider__card::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background: radial-gradient( ellipse at center, rgba( 255, 255, 255, 0.45 ) 0%, rgba( 255, 255, 255, 0.7 ) 100% );
	opacity: var( --pcs-fade, 0 );
	pointer-events: none;
}

.pb-card-slider__image {
	filter: blur( calc( var( --pcs-fade, 0 ) * 1.5px ) );
}

@keyframes pb-card-slider-heart-drift {
	0%,
	100% {
		translate: 0 0;
		rotate: 0deg;
	}

	50% {
		translate: 6px -14px;
		rotate: 8deg;
	}
}

.pb-card-slider__heart {
	animation-duration: 5s;
}

.pb-card-slider__heart:nth-child( 2n ) {
	animation-duration: 6.5s;
}

.pb-card-slider__heart:nth-child( 3n ) {
	animation-duration: 5.8s;
}

.pb-card-slider__heart:nth-child( 5n ) {
	animation-duration: 7s;
}

/* Card slider hearts may drift past the section's top edge (explicit
 * request 2026-10-20): they stay visible over the block above (e.g. the
 * homepage hero's blue) instead of being cut off. Only the vertical
 * overflow is let through — sideways stays clipped (no horizontal page
 * scroll). The section sits above the previous block so the hearts paint
 * over it; the sticky header still covers everything. */
.pb-card-slider {
	overflow: visible;
	overflow-x: clip;
	z-index: 2;
}

.pb-card-slider__hearts {
	z-index: 0;
}

.pb-card-slider__intro,
.pb-card-slider__track,
.pb-card-slider__dots {
	z-index: 1;
}

/* Card slider: a card with a link (2026-10-20) is the whole card as an
 * <a>; keep the card's own look (no underline/link color). */
a.pb-card-slider__card,
a.pb-card-slider__card:hover,
a.pb-card-slider__card:focus-visible {
	color: inherit;
	text-decoration: none;
}

a.pb-card-slider__card.is-active {
	cursor: pointer;
}

a.pb-card-slider__card:focus-visible {
	outline: 3px solid #6848f4;
	outline-offset: 3px;
}

/* Card slider — photo cards in "hero" style (explicit request 2026-10-20,
 * like the 4Daagse header): the photo fills the whole card under a dark
 * layer (darker towards the bottom), the card title sits white and
 * centered at the bottom, with the "Ontdek" link (same look as the
 * "SHOP NU" link, .pb-split-hero__cta) under it on cards with a URL.
 * Layers: photo → dark layer (::before) → title/link → white haze of the
 * side cards (::after, above everything). Cards without a photo keep the
 * card color with dark text at the bottom. */
.pb-card-slider__card-body {
	margin-top: auto;
	padding: 0.85rem 1rem 1rem;
	text-align: center;
}

.pb-card-slider__card-body .pb-card-slider__card-text {
	margin: 0;
	padding: 0;
}

.pb-card-slider__card-cta {
	display: inline-block;
	margin-top: 0.6rem;
	font-size: 0.8rem;
	font-weight: 500;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: inherit;
	border-bottom: 1.5px solid currentColor;
	padding-bottom: 0.15rem;
	transition: opacity 0.2s ease;
}

a.pb-card-slider__card:hover .pb-card-slider__card-cta {
	opacity: 0.7;
}

.pb-card-slider__card.has-image .pb-card-slider__image {
	position: absolute;
	inset: 0;
	height: 100%;
	z-index: 0;
}

.pb-card-slider__card.has-image::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: linear-gradient( to bottom, rgba( 0, 0, 0, 0.12 ) 0%, rgba( 0, 0, 0, 0.2 ) 45%, rgba( 0, 0, 0, 0.62 ) 100% );
	pointer-events: none;
}

.pb-card-slider__card.has-image .pb-card-slider__card-body {
	position: relative;
	z-index: 2;
	padding: 1.25rem 1.25rem 1.5rem;
	color: #ffffff;
}

.pb-card-slider__card.has-image .pb-card-slider__card-text {
	color: #ffffff;
	font-size: 1.15rem;
	font-weight: 500;
	line-height: 1.25;
}

.pb-card-slider__card::after {
	z-index: 3;
}

/* Card slider: optional subtitle under the card title (2026-10-20),
 * all screen sizes. */
.pb-card-slider__card-subtitle {
	margin: 0.35rem 0 0;
	font-size: 0.85rem;
	line-height: 1.4;
	color: inherit;
}

.pb-card-slider__card.has-image .pb-card-slider__card-subtitle {
	color: rgba( 255, 255, 255, 0.92 );
}

/* pb/color-card heading (explicit request 2026-10-20): same title size as
 * the rest of the site — the site-wide h2 preset (.pb-type--h2 on the
 * element: 1.65rem mobile / 1.8rem tablet / 2rem desktop, as e.g. the
 * "Poster Programma" heading) instead of the block's own larger sizes
 * (both the illustration and the photo variant). */
.pb-color-card .pb-color-card__heading.pb-type--h2,
.pb-color-card--cover .pb-color-card__heading.pb-type--h2 {
	font-size: var( --wp--custom--typography--h-2--size-mobile, 1.65rem ) !important;
	line-height: var( --wp--custom--typography--h-2--line-height, 1.2 ) !important;
}

@media ( min-width: 768px ) {
	.pb-color-card .pb-color-card__heading.pb-type--h2,
	.pb-color-card--cover .pb-color-card__heading.pb-type--h2 {
		font-size: var( --wp--custom--typography--h-2--size-tablet, 1.8rem ) !important;
	}
}

@media ( min-width: 1024px ) {
	.pb-color-card .pb-color-card__heading.pb-type--h2,
	.pb-color-card--cover .pb-color-card__heading.pb-type--h2 {
		font-size: var( --wp--custom--typography--h-2--size-desktop, 2.1rem ) !important;
	}
}

/* pb/image-text "imageFloaters" (2026-10-20, Poster programma question
 * marks): cut-out parts laid exactly over their spot in the image, each
 * wobbling a little (small sway + float), out of step with each other. */
.pb-image-floaters {
	position: relative;
	display: block;
}

.pb-image-floaters > .pb-image-text__image,
.pb-image-floaters > a > .pb-image-text__image {
	display: block;
}

.pb-image-floater {
	position: absolute;
	height: auto;
	max-width: none;
	pointer-events: none;
	transform-origin: 50% 80%;
	animation: pb-image-floater-wobble 3.6s ease-in-out infinite alternate;
}

.pb-image-floater:nth-of-type( 2n ) {
	animation-duration: 4.4s;
}

.pb-image-floater:nth-of-type( 3n ) {
	animation-duration: 3.1s;
}

@keyframes pb-image-floater-wobble {
	0% {
		rotate: -6deg;
		translate: 0 0;
	}

	100% {
		rotate: 6deg;
		translate: 0 -6%;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-image-floater {
		animation: none;
	}
}

/* ---- pb/poster-slider (inc/poster-slider-block.php, 2026-10-20) —
 * title/text centered, framed posters to page through (same scroll-snap
 * + scaling as pb/card-slider via blocks/card-slider/view.js: centered
 * poster full size, neighbours smaller, click/swipe/dots), a handwritten
 * note with a drawn arrow and a button. Type sizes = site presets. ---- */
.pb-poster-slider {
	position: relative;
	overflow-x: clip;
	padding: 3.5rem 0 3rem;
	background: var( --pps-bg, #ffffff );
	color: #111111;
}

.pb-poster-slider__intro {
	box-sizing: border-box;
	max-width: 46rem;
	margin: 0 auto 2.25rem;
	padding: 0 1rem;
	text-align: center;
}

.pb-poster-slider .pb-poster-slider__title {
	margin: 1.4em 0 1.25rem; /* room for the handwritten word above the line */
	color: inherit !important;
}

.pb-poster-slider__text {
	margin: 0;
	color: inherit;
}

.pb-poster-slider__track {
	--pcs-card-w: min( 58vw, 240px );
	--pcs-gap: 1rem;
	position: relative;
	z-index: 1;
	display: flex;
	gap: var( --pcs-gap );
	box-sizing: border-box;
	padding: 0.75rem calc( ( 100% - var( --pcs-card-w ) ) / 2 ) 1.75rem;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
}

.pb-poster-slider__track::-webkit-scrollbar {
	display: none;
}

.pb-poster-slider__poster {
	flex: 0 0 var( --pcs-card-w );
	scroll-snap-align: center;
	scroll-snap-stop: always;
	transform: scale( var( --pcs-scale, 1 ) );
	transform-origin: center center;
	transition: opacity 0.2s ease;
	opacity: calc( 1 - var( --pcs-fade, 0 ) * 0.25 );
}

.pb-poster-slider__poster:not( .is-active ) {
	cursor: pointer;
}

/* Brushed-metal frame like the example, with a soft drop shadow. */
.pb-poster-slider__frame {
	box-sizing: border-box;
	padding: 6%;
	border-radius: 3px;
	background: linear-gradient( 135deg, #c9cbcf 0%, #f2f3f5 22%, #9a9da3 48%, #e6e7ea 72%, #a7aaaf 100% );
	box-shadow: 0 8px 18px rgba( 17, 17, 17, 0.28 ), inset 0 0 0 1px rgba( 255, 255, 255, 0.5 );
}

.pb-poster-slider__image,
.pb-poster-slider__empty {
	display: block;
	width: 100%;
	height: auto !important; /* WP width/height attributes would override aspect-ratio */
	max-width: none;
	aspect-ratio: 1 / 1.414; /* A-size poster */
	object-fit: cover;
	background: #ffffff;
	box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.08 );
}

.pb-poster-slider__empty {
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 0.85rem;
	color: #777777;
}

.pb-poster-slider__dots {
	margin-bottom: 1.75rem;
}

.pb-poster-slider__note {
	display: flex;
	align-items: flex-end;
	justify-content: center;
	gap: 1rem;
	max-width: 46rem;
	margin: 0 auto 2rem;
	padding: 0 1rem;
}

.pb-poster-slider__note p {
	margin: 0;
	font-family: "Gochi Hand", "Segoe Print", "Bradley Hand", cursive;
	font-size: clamp( 1.1rem, 1.8vw, 1.4rem );
	line-height: 1.5;
	text-align: center;
	transform: rotate( -3deg );
}

.pb-poster-slider__arrow {
	flex: 0 0 auto;
	width: clamp( 70px, 9vw, 120px );
	height: auto;
	fill: none;
	stroke: #111111;
	stroke-width: 3;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.pb-poster-slider__cta {
	text-align: center;
}

@media ( max-width: 767.98px ) {
	.pb-poster-slider__arrow {
		display: none;
	}
}

@media ( min-width: 768px ) {
	.pb-poster-slider {
		padding: 4.5rem 0 4rem;
	}

	/* Window exactly three posters wide, like the card slider on desktop. */
	.pb-poster-slider__track {
		--pcs-card-w: clamp( 190px, 18vw, 260px );
		--pcs-gap: clamp( 1.5rem, 4vw, 4rem );
		width: calc( 3 * var( --pcs-card-w ) + 2 * var( --pcs-gap ) );
		max-width: 100%;
		margin: 0 auto;
		padding-left: calc( var( --pcs-card-w ) + var( --pcs-gap ) );
		padding-right: calc( var( --pcs-card-w ) + var( --pcs-gap ) );
	}
}

/* Editor: static row of posters. */
.pb-poster-slider.is-editor .pb-poster-slider__track {
	width: auto;
	padding-left: 1.5rem;
	padding-right: 1.5rem;
}

/* pb/poster-slider spans the full screen width (breaks out of the page's
 * narrow content column, like the other full-width PB sections), so the
 * three-poster window fits on desktop. */
.entry-content > .pb-poster-slider {
	box-sizing: border-box;
	width: 100vw;
	max-width: 100vw;
	margin-left: calc( 50% - 50vw );
	margin-right: calc( 50% - 50vw );
}

/* Poster slider note on mobile (explicit request 2026-10-20): the lines
 * run on as one paragraph (the manual line break applies from tablet
 * up), and a small version of the drawn arrow sits under the text. */
@media ( max-width: 767.98px ) {
	.pb-poster-slider__note {
		flex-direction: column;
		align-items: center;
		gap: 0.5rem;
	}

	.pb-poster-slider__note br {
		display: none;
	}

	.pb-poster-slider .pb-poster-slider__arrow {
		display: block;
		width: 56px;
	}
}

/* Tablet/desktop: each line of the note stays on one line (no wrapping
 * inside a line) — "De posters … opgehangen." / "Denk aan …" — with the
 * arrow beside it. */
@media ( min-width: 768px ) {
	.pb-poster-slider__note {
		max-width: none;
	}

	.pb-poster-slider__note p {
		white-space: nowrap;
	}
}

/* One price on posters product pages (body.pb-pdp-single-price, 2026-10-20):
 * the chosen format's price replaces the main price (woocommerce.js), so
 * WooCommerce's own second price under the options is hidden. */
.pb-pdp-single-price .woocommerce-variation-price {
	display: none !important;
}

/* pb/poster-slider text: bold really is bold (700), same as
 * .pb-image-text__body strong — the generic `.pb-type--body strong`
 * formula in design-system.css only steps 400 up to 500 (explicit
 * request 2026-10-20). */
.pb-poster-slider__text strong,
.pb-poster-slider__text b {
	font-weight: 700;
}

/* pb/poster-slider tweaks (explicit request 2026-10-20):
 * - as the first block of a page it sits closer under the header menu
 *   (no page top padding, smaller own top padding — the title keeps its
 *   room above for the handwritten word);
 * - bigger posters on every screen size;
 * - the handwritten note a bit smaller. */
.site-main:has( > article > .entry-content > .pb-poster-slider:first-child ),
.site-main:has( > .entry-content > .pb-poster-slider:first-child ) {
	padding-top: 0;
}

.entry-content > .pb-poster-slider:first-child {
	padding-top: 1.25rem;
}

.pb-poster-slider__track {
	--pcs-card-w: min( 68vw, 300px );
}

.pb-poster-slider__note p {
	font-size: clamp( 0.95rem, 1.3vw, 1.15rem );
}

@media ( min-width: 768px ) {
	.entry-content > .pb-poster-slider:first-child {
		padding-top: 1.75rem;
	}

	.pb-poster-slider__track {
		--pcs-card-w: clamp( 220px, 23vw, 330px );
	}
}

/* Poster slider: posters linking to their product (source "category"). */
a.pb-poster-slider__poster {
	display: block;
	color: inherit;
	text-decoration: none;
}

/* Poster slider with product images (source "category", 2026-10-20): the
 * product photos already show the framed poster, so no extra CSS frame
 * and the whole photo is shown (no crop). */
.pb-poster-slider--products .pb-poster-slider__frame {
	padding: 0;
	background: none;
	box-shadow: none;
	border-radius: 0;
}

.pb-poster-slider--products .pb-poster-slider__image {
	aspect-ratio: auto;
	object-fit: contain;
	background: none;
	box-shadow: none;
}

/* "Alle posters" overview (core/shortcode [products category="posters"]
 * with class pb-poster-grid on Poster programma, 2026-10-20): the shop's
 * own product cards, but wide (out of the narrow text column) and four
 * per row on desktop; below 1024px the shop's own columns apply. */
.entry-content > .pb-poster-grid,
.entry-content > .wp-block-shortcode.pb-poster-grid {
	box-sizing: border-box;
	width: min( 1280px, calc( 100vw - 2.5rem ) );
	max-width: none;
	margin: 1rem calc( 50% - min( 640px, 50vw - 1.25rem ) ) 4rem;
}

@media ( min-width: 1024px ) {
	.pb-poster-grid .woocommerce ul.products,
	.pb-poster-grid ul.products {
		grid-template-columns: repeat( 4, minmax( 0, 1fr ) ) !important;
	}
}

/* Poster slider with product images: the shop photos are square with the
 * framed poster in the middle (x 25–74 %, y 16–84 % — the same template
 * for every poster product). The slider shows just that framed poster,
 * filling the slide: a window with the poster's own proportions, the
 * photo scaled up (1 / 0.49) and shifted so the white margin falls
 * outside it. */
.pb-poster-slider--products .pb-poster-slider__frame {
	position: relative;
	overflow: hidden;
	aspect-ratio: 49 / 68;
}

.pb-poster-slider--products .pb-poster-slider__image {
	position: absolute;
	top: -23.5%;
	left: -51%;
	width: 204% !important;
	height: 146.9% !important;
	max-width: none;
	object-fit: fill;
}

/* WooCommerce's clearfix pseudo-elements on ul.products become empty grid
 * cells in a CSS grid (first cell empty) — not needed here. */
.pb-poster-grid ul.products::before,
.pb-poster-grid ul.products::after {
	content: none !important;
	display: none !important;
}

/* Poster slider as a stacked deck (explicit request 2026-10-20): three
 * posters visible on each side — the nearest neighbour as before, two
 * more tucked in under it (smaller, a little lighter, a strip of each
 * visible; --pcs-shift/--pcs-dist from blocks/card-slider/view.js).
 * Tablet/desktop: the track spans the full width so the deck fits. */
.pb-poster-slider__poster {
	transform: translateX( var( --pcs-shift, 0px ) ) scale( var( --pcs-scale, 1 ) );
	opacity: calc( 1 - min( var( --pcs-dist, 0 ), 3 ) * 0.1 );
}

@media ( min-width: 768px ) {
	.pb-poster-slider__track {
		--pcs-card-w: clamp( 220px, 21vw, 300px );
		width: 100%;
		max-width: 1280px;
		padding-left: calc( ( 100% - var( --pcs-card-w ) ) / 2 );
		padding-right: calc( ( 100% - var( --pcs-card-w ) ) / 2 );
	}
}

/* Deck: further posters lighter via a white veil instead of transparency
 * (transparent posters let the ones behind them show through). */
.pb-poster-slider__poster {
	position: relative;
	opacity: 1;
}

.pb-poster-slider__poster::after {
	content: "";
	position: absolute;
	inset: 0;
	background: #ffffff;
	opacity: calc( min( var( --pcs-dist, 0 ), 3 ) * 0.14 );
	pointer-events: none;
}

/* Poster slider arrow on mobile/tablet (explicit request 2026-10-20): not
 * under the note but up beside the dots, in the space right of the
 * centered poster's lower corner. Positioned from the note (the section
 * is full width, so 50% is the centered poster's middle); --pps-arrow-card
 * mirrors the centered poster width (--pcs-card-w) per breakpoint. */
@media ( max-width: 1023.98px ) {
	.pb-poster-slider__note {
		position: relative;
		--pps-arrow-card: min( 68vw, 300px );
		--pps-arrow-w: 56px;
	}

	.pb-poster-slider .pb-poster-slider__arrow {
		display: block;
		position: absolute;
		bottom: calc( 100% + 1.75rem );
		left: min( calc( 50% + var( --pps-arrow-card ) / 2 + 0.25rem ), calc( 100% - var( --pps-arrow-w ) - 0.25rem ) );
		width: var( --pps-arrow-w );
		/* A quarter turn to the left (explicit request 2026-10-20). */
		transform: rotate( -90deg );
	}
}

@media ( min-width: 768px ) and ( max-width: 1023.98px ) {
	.pb-poster-slider__note {
		--pps-arrow-card: clamp( 220px, 21vw, 300px );
		--pps-arrow-w: 90px;
	}
}

/* Sections with an HTML anchor (e.g. #assortiment on Poster programma,
 * 2026-10-20): when a button jumps to them, they land with some room
 * under the fixed header instead of right against it. */
.entry-content > section[id] {
	scroll-margin-top: 2.5rem;
}

/* Poster slider page indicator as the shop's grey dashes (explicit
 * request 2026-10-20) — same look as .pb-product-showcase__dot: 40×2px
 * lines, light grey, the current one darker. Only on pb/poster-slider;
 * the homepage card slider keeps its round dots. With many posters the
 * dashes shrink to fit narrow screens. */
.pb-poster-slider .pb-poster-slider__dots {
	gap: 10px;
	padding: 0 1rem;
}

.pb-poster-slider .pb-card-slider__dot {
	flex: 0 1 40px;
	min-width: 10px;
	width: 40px;
	height: 2px;
	border-radius: 0;
	background-color: #e3e3e3;
	background-clip: content-box;
	transition: background-color 0.2s ease;
}

.pb-poster-slider .pb-card-slider__dot.is-active {
	background-color: #8a8a8a;
}

@media ( max-width: 767.98px ) {
	.pb-poster-slider .pb-poster-slider__dots {
		gap: 6px;
	}
}

/* Mobile/tablet: the dash row stays as wide as the centered poster, so
 * it doesn't run under the arrow beside that poster. */
@media ( max-width: 1023.98px ) {
	.pb-poster-slider .pb-poster-slider__dots {
		box-sizing: border-box;
		max-width: min( 68vw, 300px );
		margin-left: auto;
		margin-right: auto;
		padding: 0;
		gap: 6px;
	}
}

@media ( min-width: 768px ) and ( max-width: 1023.98px ) {
	.pb-poster-slider .pb-poster-slider__dots {
		max-width: clamp( 220px, 21vw, 300px );
	}
}

/* Deck shift on the frame, not on the slide itself (2026-10-20): the
 * slide is the scroll-snap child, and a translated snap child moves its
 * snap point — the dashes then couldn't reach the first/last posters.
 * The slide keeps only the scale (centred, so its snap point stays);
 * the frame inside carries the sideways shift (divided by the scale, as
 * it is applied inside the scaled slide) and the white veil. */
.pb-poster-slider__poster {
	transform: scale( var( --pcs-scale, 1 ) );
}

.pb-poster-slider__poster::after {
	content: none;
}

.pb-poster-slider__frame {
	position: relative;
	transform: translateX( calc( var( --pcs-shift, 0px ) / var( --pcs-scale, 1 ) ) );
}

.pb-poster-slider__frame::after {
	content: "";
	position: absolute;
	inset: 0;
	background: #ffffff;
	opacity: calc( min( var( --pcs-dist, 0 ), 3 ) * 0.14 );
	pointer-events: none;
}

/* PB Product Showcase "Desktop: alle producten in rijen onder elkaar"
 * (desktopAllRows, 2026-10-20): from 1200px no sliding row — the cards
 * wrap into rows of "Kolommen desktop", as many rows as there are
 * products; no arrows. Mobile/tablet stay the swipe slider. */
@media ( min-width: 1200px ) {
	.pb-product-showcase--desktop-rows .pb-product-showcase__track {
		flex-wrap: wrap;
		row-gap: 2.5rem;
		overflow-x: visible;
		scroll-snap-type: none;
	}

	.pb-product-showcase--desktop-rows .pb-product-showcase__arrow {
		display: none !important;
	}
}

/* pb/color-card heading like the pb/image-text heading (explicit request
 * 2026-10-20, "Mét humor maken we …"): regular weight and normal letter
 * spacing, with words made bold in the editor (B button) at 700 — so
 * bold / not bold is chosen per word in the editor. */
.pb-color-card .pb-color-card__heading,
.pb-color-card--cover .pb-color-card__heading {
	font-weight: 400 !important;
	letter-spacing: normal;
}

.pb-color-card .pb-color-card__heading strong,
.pb-color-card .pb-color-card__heading b {
	font-weight: 700;
}

/* Card slider on desktop (explicit request 2026-10-20): cards a little
 * wider (max 330px -> 360px); the aspect ratio goes from 5/8 to 15/22 so
 * the height stays the same and only the width grows. */
@media ( min-width: 1024px ) {
	.pb-card-slider__track {
		--pcs-card-w: clamp( 240px, 26vw, 360px );
	}

	.pb-card-slider__card {
		aspect-ratio: 15 / 22;
	}
}

/* pb/animated-statement floating images (explicit request 2026-10-20,
 * Roze Woensdag "wolkjes"): the chosen image scattered around the text
 * like the homepage card slider hearts — a mix of small and medium ones
 * (positions/sizes per spot: pb_animated_statement_float_spots()), each
 * drifting very gently at its own pace. The extra spots beside the text
 * (--wide) only show from 1200px; below that the text is too wide.
 * Static for reduced-motion users. */
.animated-statement__floats {
	position: absolute;
	inset: 0;
	z-index: -1;
	overflow: hidden;
	pointer-events: none;
}

.animated-statement__float {
	position: absolute;
	display: block;
	height: auto;
	max-width: none;
	transform: rotate( var( --asf-rot, 0deg ) );
	animation: pb-as-float-drift 7s ease-in-out infinite alternate;
}

.animated-statement__float:nth-child( 3n ) {
	animation-duration: 8.5s;
}

.animated-statement__float:nth-child( 3n + 1 ) {
	animation-duration: 6s;
}

@keyframes pb-as-float-drift {
	from { transform: translate( 0, 0 ) rotate( var( --asf-rot, 0deg ) ); }
	to   { transform: translate( 3px, -6px ) rotate( calc( var( --asf-rot, 0deg ) + 6deg ) ); }
}

@media ( max-width: 1199.98px ) {
	.animated-statement__float--wide {
		display: none;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.animated-statement__float { animation: none; }
}

/* pb/image-text photo stack (explicit request 2026-10-20, Roze Woensdag;
 * pb_image_text_stack_html()): each layer is the stack so far (1 photo,
 * 2, 3 …), all in one grid cell. Once in view (.is-revealed, main.js)
 * they are laid down one after the other — dropping in slightly
 * turned and larger — and the layer underneath fades away once the next
 * one lies on top, so the last (full) stack stays. Reduced motion: only
 * the final stack, no animation. */
.pb-image-stack {
	display: grid;
	width: 100%;
}

.pb-image-stack__layer {
	grid-area: 1 / 1;
	align-self: center;
	display: block;
	width: 100%;
	height: auto;
	opacity: 0;
}

.pb-image-stack.is-revealed .pb-image-stack__layer {
	animation: pb-image-stack-drop 0.75s cubic-bezier( 0.22, 1, 0.36, 1 ) calc( var( --pis-i, 0 ) * 1.1s ) forwards;
}

.pb-image-stack.is-revealed .pb-image-stack__layer:not( :last-child ) {
	animation:
		pb-image-stack-drop 0.75s cubic-bezier( 0.22, 1, 0.36, 1 ) calc( var( --pis-i, 0 ) * 1.1s ) forwards,
		pb-image-stack-hide 0.3s linear calc( ( var( --pis-i, 0 ) + 1 ) * 1.1s + 0.6s ) forwards;
}

@keyframes pb-image-stack-drop {
	from { opacity: 0; transform: translateY( -28px ) scale( 1.06 ) rotate( -4deg ); }
	to   { opacity: 1; transform: none; }
}

@keyframes pb-image-stack-hide {
	from { opacity: 1; }
	to   { opacity: 0; }
}

@media ( prefers-reduced-motion: reduce ) {
	.pb-image-stack.is-revealed .pb-image-stack__layer,
	.pb-image-stack.is-revealed .pb-image-stack__layer:not( :last-child ) {
		animation: none;
	}

	.pb-image-stack .pb-image-stack__layer:last-child {
		opacity: 1;
	}
}

/* Mobile drawer (explicit request 2026-10-21, see header.php): below
 * 768px the drawer shows the footer-columns list
 * (.main-navigation__menu--drawer) instead of the primary menu list;
 * from 768px up only the primary menu list (desktop header nav). */
@media (max-width: 767.98px) {
	#site-navigation > .main-navigation__menu:not(.main-navigation__menu--drawer) {
		display: none;
	}
}
@media (min-width: 768px) {
	#site-navigation > .main-navigation__menu--drawer {
		display: none !important;
	}
}
/* Drawer accordion titles match the drawer's own item style (grey,
 * uppercase — like "Contact" and the PaperBrain drawer), sub-links a
 * little larger than in the footer. */
@media (max-width: 767.98px) {
	#site-navigation .main-navigation__menu--drawer .site-footer__col-summary {
		padding: 1.15rem 0;
	}
	#site-navigation .main-navigation__menu--drawer .site-footer__col-title {
		font-size: 17px;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.02em;
		color: #9ea3ae;
	}
	#site-navigation .main-navigation__menu--drawer .site-footer__links a {
		font-size: 15px;
		line-height: 2;
	}
}

/* Card slider sharpness (explicit request 2026-10-21): no will-change, so
 * mobile browsers redraw the card at its current scale instead of zooming
 * a cached bitmap, and no leftover blur on the centered card. */
.pb-card-slider__card {
	will-change: auto;
}
.pb-card-slider__card.is-active .pb-card-slider__image {
	filter: none;
}
